Arduinoを使ってみよう


今回は、小学生でもプログラミングしてロボットなどを制御できるArduinoです。
ええい!俺はコテコテの組み込み屋だ!!
などと仰る方も、プロト作成なんかには便利此上ありません。

今日は、そんなArduinoの使い方の基本についてです。



 目次:          

 ・ Arduinoとは?

 ・ Arduinoのライセンス

 ・ Arduinoの開発環境

 ・ Arduinoのプログラミング

 ・ まずは、「Hello World」

 ・ LEDを点滅させる(Lチカ!)

 ・ 別のシリアルも使ってみる

 ・ I2Cのデバイスを使ってみる(LCD)


なお、この記事では開発用のPCには、Windows 10 (1903)
使用するArduinoボードは、Microを使っています。



Arduinoとは?

「Arduino:アルデュイーノ」と読みます。
イタリアンな感じの発音、そう思った方は正解です。

それは2005年、北イタリアのイヴレーアという街のインタラクションデザイン工科大学(IIDI)から始まりました。

5人の人物たちが学生でも気軽に使えるような安価で、かつ、簡単に使えるコントローラー、つまりワンボードマイコンを作って販売する「Arduinoプロジェクト」から始まりました。

そして、彼らは、シンプルな設計のボードだけではなく技術者ではなくても開発が可能な開発環境の開発に成功し、ハードウェア・ソフトウェアともにオープンとなものとして公開しました。

オープンで安価、簡単ということもあり、一気に普及が進み本家Arduinoからも、いくつもの種類のボードがリリースされ、それでなく、クローンやピン互換のボードなどがたくさん販売されています。(悲しいことに非公式クローンとかも沢山!)

ところで、このArduinoという名前は、プロジェクトの一人であるマッシモ・バンジーの行きつけのバールの「Bar di Re Arduino」から取られたとか?


Arduinoのライセンス


ハードウェアはオープンハードウェアで、Creative Commons Attribution Share-Alike 2.5でライセンスされています。

また、基盤の回路図も公開されています。
 → Arduino Microの回路図はコチラ

ソフトウェアは、開発環境である Arduino IDEはGPLv2、基板上のライブラリは LGPLでライセンスされています。

商用利用時は、商業利用の形態により対応が異なるので、専門の法務関係者にご相談することをおすすめします。

参考まで、SWITCH SCIENCEのページ

以下、プロの方向けに:
ちなみにArduinoそのものを商用の製品として使うにはオススメしません。
なぜなら余計な機能がいっぱい載っていて、マイコン本来のパワーに対して余計な負荷がたくさんあるからです。

ただし、プロト作成には十分なのでマスプロ展開するわけではない場合には適しています。

小規模なお客さんはコレで満足する場合もしばしば。
そんな時は、お客様とは「プロトタイプ開発」として契約、あくまでもプロトタイプとして運用の枠内でライセンス運用してはいかがでしょうか?


Arduinoの開発環境


Arduinoのトップページのメニューから「SOFTWARE」→「DOWNLOADS」を選択します。



Windows zip file for non admin installを選択します。



寄付を求められますので、各自ご判断を。


インストールは、ダウンロードしたarduino-1.8.10-windows.zipを解答する、
だけ!

Zipファイルの中にあるarduino-1.x.x フォルダの中にarduino.exeがあるので、このファイルを実行します。

このようなウインドウが開きます。



ココでArduinoのプログラミングを行います。

IDEのバージョンアップも同様の手順で行います。


Arduinoのプログラミング

Arduinoではプログラムのことをスケッチと呼んでいます。
C言語のような書き方をしますが、下記の2つの基本的な関数が自動的に作成されます。

基本の関数:
void setup()
電源ONまたはリセットの直後に1回だけ実行されます。
使いたいピンやデバイスの初期設定を行います。

void loop()
上記のsetup()が実行されたあと繰り返し、ず~っと実行され続ける関数です。

ボードの設定
プログラミングの前に、まず、Arduino MicroをUSBマイクロケーブルでPCと接続します。
PCとArduinoをつなげるとUSBシリアルデバイスとして認識されますので、デバイスマネージャ等で確認してください。
(このPCではCOM3になっていますが使用するPC環境により変わってきます)

この記事ではArduino Microを使っていますので、
メニューから、[ツール]→[ボード]→[Arduino/Genuino Micro]を選択します。



シリアルポートを、[ツール]→[シリアルポート]でCOM3を指定します。
ちゃんと認識できるか、[ツール]→[ボード情報を取得]で確認してください、



設定したボードが確認できればOKです。


まずは、「Hello World」

Arduinoでも”Hello World”から入ってみましょう。

void setup() {

  Serial.begin(115200);

}



void loop() {

  Serial.println("Hello World");

  delay(1000);

}

これは、シリアル出力を115200bpsに設定しています。
  Serial.begin(115200);

最初に1回だけ行えばよいので、setup()の中に書いています。

そして、loop()の中では
  Serial.println("Hello World");

  delay(1000);

シリアルに対してSerial.println()を使って"Hello World"の文字列を出力しています。
その後、delay()を使って1000ミリ秒待ちあわせます。
このあとは、自動的にloop()がまた呼び出されるようになっています。

このシリアルの出力はドコですか?
設定しているシリアルは、PCと接続しているUSBシリアルです。
メニューの[ツール]→[シリアルモニタ]でシリアルモニタを開いて速度を115200bpsに設定しましょう。
もちろん、このシリアルモニタでなくてもTeraTermなどでも問題ありません。

このコードをコンパイルしてボードに送るには→アイコンをクリックします。
自動的にコンパイルして、ボードへの書き込みまで行ってくれます。

「Hello World」が1秒おきに表示されましたか?

このUSBシリアルは、ArduinoがUSBからの給電で動作していることもあり、デバッグで使われることが多いです。

注意!
このシリアルが動作したままの状態では、Aruduino Microのプログラムを書き換えられません。
Arduino上のフラッシュを書き換えるとき、リセット動作を行うようにできています。
Arduino Microではこのシリアルを1200bpsでOpen/Closeすることでリセット動作がかかります。
(リセット動作は、他のArduinoでは別の動作になります。)

このため、シリアルを受信しているターミナルソフトを閉じないと、リセット動作できずプログラムの書き換えができません。




LEDを点滅させる(Lチカ!)


では、今度は先程のプログラムに追記してボード上のLEDを点滅する機能を追加してみましょう。


void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  pinMode(LED_BUILTIN, OUTPUT);

}

uint8_t led = 0;

void loop() {
  // put your main code here, to run repeatedly:
  Serial.println("Hello World");
  led = ~led;
  digitalWrite(LED_BUILTIN, led);
  delay(1000);

}

LEDをON/OFFするためにボード上のLEDに接続されたPINをGPIOの出力モードに設定しています。
  pinMode(LED_BUILTIN, OUTPUT);

LEDのON/OFFは
  digitalWrite(LED_BUILTIN, led);
で、行いますが毎回反転させたいのでledという変数を反転しています。
  led = ~led;

最初の"Hello World"が出力されながら、LEDも1秒毎に点滅しています。


別のシリアルも使ってみる


USBシリアルをデバッグに使っていると、他のピンをシリアルにしたくなります。
回路図でArduino Microのピンを確認すると、J5の3がTxで4がRxです。

このシリアルは、Arduinoの中ではSerial1として認識されています。
そこで、このSerial1を9600bpsで初期化してこちらで"Hello World"を表示し、USBシリアルの方は"DEBUG"と表示しましょう。

void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  Serial1.begin(9600);

  pinMode(LED_BUILTIN, OUTPUT);

}

uint8_t led = 0;

void loop() {
  // put your main code here, to run repeatedly:
  Serial.println("DEBUG");
  Serial1.println("Hello World");

  led = ~led;
  digitalWrite(LED_BUILTIN, led);
  delay(1000);

}

setup()でSerial1を9600bpsで初期化します。
  Serial1.begin(9600);

loop()の中では、Serial1に"Hello World"を出力。
  Serial1.println("Hello World");

表示するには、BeagleBone Blackの記事でも使った3.3VのUSB変換ケーブルを使います。


秋月電子で扱っているFTDIの3.3V USB変換ケーブルでは、コネクタの黄色がRXDなので、J5の3番ピンをジャンパー線などで接続します。

そしてTeraTermなどの端末ソフトで受信してください。


I2Cのデバイスを使ってみる(LCD)


ブレッドボードにArduino microを載っけたので、手元にI2CのLCDモジュールAQM1602Y-RN-GBW があったので、接続してみたくなりました。

ヨシ、やってみよう!

さて、Arduinoの回路図を確認するとI2CはJ5の7番ピンがSDA、8番ピンがSCLです。
結線は下記のようになります。



J5
3: Tx → USB変換ケーブルRXD
7: SDA → AQM1602 7 SDA
8: SCL → AQM1602 8 SCL

J6
4: GND → AQM1602 6 VSS
16: 3.3V → AQM1602 5 VDDと 9 /RES

AQM1602
2: VDDを1.0uf経由で接続
3と4を1.0uf経由で接続

※後から気が付きましたが。。。
AQM1602Yは3.3V動作、Arduinoは5V動作なので、電源は3.3Vを接続しました。
しかし、信号線も5V規定で動作するのでSDAとSCLを分圧するか、レベルシフタを入れて5V←→3.3V変換してやったほうがよいです。
データシートを確認するとVDDは4.5VがMax、SDA/SCLのMAXはVDDとありますが、実際にオシロで確認すると、SDA/SCLとも3.9Vくらいあり、VDD以上の電圧が入っていました。デバイスの実力で動いている状態です。

 レベルシフタを入れる、或いは、AQM1602XA-RN-GBWなどの5V動作を保証するデバイスを利用することをオススメします。



実際の状況はこんな感じ。




ArduinoでI2Cを使うには?
実は、Arduinoの基本部分のソフトウェアにはI2Cの機能が入っていません。
Arduinoでは拡張的な機能はライブラリとして提供されています。I2Cは"Wire Libraly"として利用することができます。

外部のライブラリを使うには、そのライブラリのヘッダファイルをインクルードする必要があります。

コードの先頭に下記のコードを追記します。
#include <Wire.h>

これで、Wireライブラリが使えるようになりました。

ではI2Cを初期化するために、setup()に下記のコードを追記します。
  Wire.begin();             // I2C有効

これだけで、I2C自体は使えるようになりました。

LCDモジュールは初期化しなくては使えない!
電源投入後、LCDモジュールを初期化するための呪文が必要です。

詳細は、AQM1602Y-RN-GBWのデータシート(秋月電子)を!

初期化シーケンス
電源投入後40ミリ秒待ってから、
コマンドで0x38、0x39、0x14、0x73、0x56、0x6Cを送り、

200ミリ秒待って、
0x38、0x0C、0x01を送って、

1ミリ秒待てばOK

デバイスのスレーブアドレスは0x3E
です。
データシートによるとコントロールバイトに続いてデータバイトを送ります。
このコントロールバイトで続くデータバイトがLCDモジュールの制御コマンドなのか表示データなのかを指定します。
具体的にはコントロールバイトのRSビット0x40が0のときコマンド、1のときデータとなります。

いきなりコーディングを始めると、プログラムが長~くなってしまうので、今回は関数化します。

まずは、コマンド送信関数
void sendCommand(uint8_t cmd) {

  Wire.beginTransmission(AQM1602);
  Wire.write(0x00);
  Wire.write(cmd);
  Wire.endTransmission();

  return;
}

指定したスレーブアドレスに対して通信を開始します。
  Wire.beginTransmission(AQM1602);

AQM1602はconstで0x3Eとしています。
const uint8_t AQM1602 = 0x3E;

write()でバイトを送信します。コントロールバイトに0x00を送って続くデータバイトがコマンドであることを示します。
  Wire.write(0x00);
  Wire.write(cmd);

最後に、通信完了とします。
  Wire.endTransmission();


同様に、データの出力関数も作成しておきましょう。
void sendData(uint8_t data) {

  Wire.beginTransmission(AQM1602);
  Wire.write(0x40);
  Wire.write(data);
  Wire.endTransmission();

  return;
}


さて、これらの関数を元に簡単なタイマーを作ってみましょう。

以下、全ソースコード(完成品)

/*!
 * Copyright <2020> <lightning Brains>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
 * associated documentation files (the "Software"), to deal in the Software without restriction,
 * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do
 * so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or substantial
 * portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
 * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#include <Wire.h>


// Const
const char LIGHTNING_BRAINS[] = {"LightningBrains"};
const uint8_t AQM1602 = 0x3E;

// Global
uint8_t led = 0;
int hh = 0;
int mm = 0;
int ss = 0;


/*
 * sendCommand(uint8_t cmd)
 *   AQM1602 Command data send
 *
 *   cmd: command data
 */
void sendCommand(uint8_t cmd) {

  Wire.beginTransmission(AQM1602);
  Wire.write(0x00);
  Wire.write(cmd);
  Wire.endTransmission();

  return;
}


/*
 * sendData(uint8_t data)
 *   AQM1602 Display data send
 *
 *   data: display character code
 */
void sendData(uint8_t data) {

  Wire.beginTransmission(AQM1602);
  Wire.write(0x40);
  Wire.write(data);
  Wire.endTransmission();

  return;
}


/*
 * clearLCD()
 *   Clear the screen of AQM1602
 */
void clearLCD() {

  sendCommand(0x38);
  sendCommand(0x0C);
  sendCommand(0x01);
  delay(1);

  return;
}


/*
 * initLCD()
 *   Initialize AQM1602 LCD module
 */
void initLCD() {

  delay(40);          // 40ミリ秒待つ
  sendCommand(0x38);
  sendCommand(0x39);
  sendCommand(0x14);
  sendCommand(0x73);
  sendCommand(0x56);
  sendCommand(0x6C);
  delay(200);         // 200ミリ秒待つ

  clearLCD();         // Clear screen

  return;
}


/*
 * sendText(char* txt, int size)
 *   Text send to AQM1602 screen
 *
 *   txt: display text
 *   size: text length
 */
int sendText(char* txt, int size) {

  int sent = 0;

  // 全てのデータを1バイトづつ出力
  for(sent = 0; sent < size; sent ++) {
    sendData((uint8_t)*(txt +sent));
  }

  return sent;
}


/*
 * setCursor(int x, int y)
 *   Set display location in AQM1602 screen
 *
 *   x: X position, 0-15
 *   y: Y position, 0-1
 */
bool setCursor(int x, int y) {

  uint8_t cursor = 0x80;    // Set DDRAM Address command

  if(x > 15)                // 16桁
    return false;
  if(y > 1)                 // 2行
    return false;

  // 下位7ビットにDDRAMアドレスを設定
  cursor |= x;
  if(y) {
    cursor |= 0x40;
  }

  sendCommand(cursor);      // コマンド送信

  return true;
}


void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);     // USBシリアル
  Serial1.begin(9600);      // J5-3/4

  pinMode(LED_BUILTIN, OUTPUT); // ボード上のLED出力有効

  Wire.begin();             // I2C有効
  initLCD();                // AQM1602初期化

  setCursor(1, 0);          // 表示位置 1, 0
  sendText(LIGHTNING_BRAINS, strlen(LIGHTNING_BRAINS));

}


void loop() {
  // put your main code here, to run repeatedly:
  // シリアル出力
  Serial.println("DEBUG");
  Serial1.println(LIGHTNING_BRAINS);

  // LED反転
  led = ~led;
  digitalWrite(LED_BUILTIN, led);

  char  buf[18];            // 表示バッファ

  // 経過時刻表示
  sprintf(buf, "%02d:%02d:%02d", hh, mm, ss);
  setCursor(4, 1);
  sendText(buf, strlen(buf));

  // 経過時刻更新
  ++ss;
  if(ss > 59) {
    ss = 0;
    ++mm;
    if(mm > 59) {
      mm = 0;
      ++hh;
      if(hh > 23)
        hh = 0;
    }
  }

  delay(1000);              // 1秒待つ

}


プログラムの説明:

LCDモジュールの初期化
void initLCD()
 前述の初期化呪文を唱えます。

LCDの表示クリア
void clearLCD()
 表示をクリアしたいときに使います。

カーソル位置を設定します。
bool setCursor(int x, int y)
 文字を表示したい位置にカーソルを移動します。

テキストを表示します。
int sendText(char* txt, int size)
 テキスト文字列をまとめて表示します。
 文字列のポインタと長さを指定します。


このコードはリセット後(または電源投入後)からの経過時間をひたすら表示します。
23:59:59を超えるとリセットされます。

ボタンとかを追加すれば、ストップウォッチなど簡単に実装できそうですね。





Have a Happy Hucking!!

Lightning Brains

コメント

このブログの人気の投稿

Linuxシステムコール、共有メモリの使い方

Linuxシステムコール、メッセージキューの使い方

Linuxシステムコール、セマフォの使い方