Arduino(アルドゥイーノ)は,ATMEL社のAVRチップ(ATmega8, ATmega168, ATmega328Pなど)を搭載したボードと,Arduino言語とそれらを使用して開発するための統合開発環境(IDE)から構成されるシステムのことらしい。
イタリアのIDIIで学んでいたHernando Barragán氏が修士論文のテーマとして,「電子回路のハードウェアを抽象化して,より一般的な人が開発できるようにすること」を研究していた。実際に開発したものが後にArduinoというプラットフォームとなったようだ。当時のハードウェアや開発ツールに比べてできるだけ安価にすることがポイントだったらしい。
Hernando Barragán氏の論文はここで読むことが出来る。
その後,Hernando Barragán氏の論文アドバイザーだったMassimo Banzi氏が他の4人の人とかってに立ち上げたのがArduinoプロジェクト。Hernando Barragán氏には相談もなく,彼は参加もしていない。よりによって教育者の立場にある者が,生徒の成果を利用したということになる。
ハードウェアの情報は公開されていて,誰でも機能が同じボードを作成することが出来る。実際にArduinoのオリジナルボード以外にも,多くのボードが市販されている。
現在では入門用IoT機器の定番として扱われるようになり,世界的に人気になっているみたい。
最近まで知らなかったんだけど便利らしいんで,Arduinoの開発用ソフトウェアのArduino IDEを試しに自分の作成したボードで使用できるかどうか試してみた。
Arduinoのオフィシャルボードとして販売されているボードは,後述のArduinoボード・スペックのような物がある。
ATmega168PA-20AUとFT232BLを実装した自作のボードがあるんで,そのボードがArduino IDEを使ってプログラム開発が出来るか試してみた。
オフィシャルボードとは,CPU(Arduinoでは使われていないATmega168P)やポートの使用方法(LEDはPB0など)が違う。
Arduino IDEからプログラムを書き込む時には,ソフトウェア的にはUART経由で書き込む。自作のボードはFTDIのUSB-シリアルコンバーターを実装しているんで,PCからは仮想COMポートでアクセスする。
Arduino IDEと呼ばれる統合開発環境はクロスプラットフォームで動作するJavaアプリケーションで,エディタ,コンパイラ,ボードへのプログラム転送機能,さらに各種ライブラリ,サンプルプログラム,Java JREなどを含んだALL IN ONEツール。
下回りには,コンパイラがAVR-GCCで,ボードへプログラムを書き込むのには avrdude といった,ATMEL AVR開発時に使われる一般的なツールが使われている。
プログラミングスタイルはC/C++言語風の構文で,Arduinoのボード向けに最適化されているみたい。ちなみに,Arduinoではプログラムのことをスケッチと呼んでいる。
ArduinoのサイトからArduino IDE version1.0.1のWindows版(2012/05/21リリース)をダウンロードした。 1系の最終版version1.8.19のzip版 と2系の最新版version2.3.0はこちらからダウンロード出来る。
09/14/2022にメジャーアップデートとなるArduino IDE 2.0.0がリリースされた。実行環境がJavaからElectronに変更されている。
適当なディレクトリに解凍(現在はversion1.8.19を使用している)すればセットアップは完了。
Windows版のArduino 1.0.1では,avr-gccにはWinAVR-20081205が使われている。すでにWinAVR-20100110を使っているのだが気にしないでおく。
なぜかMinGWをインストールしていてCPLUS_INCLUDE_PATH環境変数を設定していると,Arduinoでコンパイルがうまく行かなくなっちゃうので,CPLUS_INCLUDE_PATH環境変数を削除した。
自作のボードをArduino IDEから使えるように,いくつかのファイルを編集する。
zipファイルを解凍したディレクトリにある*1hardware\arduino\boards.txtのmenu.cpu=Processorの行の下あたり(バージョン1.0.1だと先頭)に以下を追加する。
############################################################## piramp8.name=Yuji's AVR Board(8MHz) piramp8.vid.0=0x0403 piramp8.pid.0=0x6001 piramp8.upload.tool=avrdude piramp8.upload.protocol=arduino piramp8.upload.maximum_size=14336 piramp8.upload.speed=19200 piramp8.bootloader.low_fuses=0xc2 piramp8.bootloader.high_fuses=0xdf piramp8.bootloader.extended_fuses=0xf8 piramp8.bootloader.file=atmega/ATmegaBOOT_168P_piramp_8mhz.hex piramp8.bootloader.unlock_bits=0x3F piramp8.bootloader.lock_bits=0x2F piramp8.build.mcu=atmega168p piramp8.build.f_cpu=8000000L piramp8.build.board=AVR_PIRAMP8 piramp8.build.core=arduino piramp8.build.variant=piramp ############################################################## piramp16.name=Yuji's AVR Board(16MHz) piramp16.vid.0=0x0403 piramp16.pid.0=0x6001 piramp16.upload.tool=avrdude piramp16.upload.protocol=arduino piramp16.upload.maximum_size=14336 piramp16.upload.speed=19200 piramp16.bootloader.low_fuses=0xcf piramp16.bootloader.high_fuses=0xdf piramp16.bootloader.extended_fuses=0xf8 piramp16.bootloader.file=atmega/BOOT_168P_piramp_16mhz.hex piramp16.bootloader.unlock_bits=0x3F piramp16.bootloader.lock_bits=0x2F piramp16.build.mcu=atmega168p piramp16.build.f_cpu=16000000L piramp16.build.board=AVR_PIRAMP16 piramp16.build.core=arduino piramp16.build.variant=piramp ############################################################## usbio8.name=Yuji's USBIO Board(8MHz) usbio8.vid.0=0x0403 usbio8.pid.0=0x6001 usbio8.upload.tool=avrdude usbio8.upload.protocol=arduino usbio8.upload.maximum_size=28672 usbio8.upload.maximum_data_size=2048 usbio8.upload.speed=19200 usbio8.bootloader.tool=avrdude usbio8.bootloader.low_fuses=0xc2 usbio8.bootloader.high_fuses=0xd8 usbio8.bootloader.extended_fuses=0xff usbio8.bootloader.file=atmega/ATmegaBOOT_328_usbio_8mhz.hex usbio8.bootloader.unlock_bits=0x3F usbio8.bootloader.lock_bits=0x2F usbio8.build.mcu=atmega328 usbio8.build.f_cpu=8000000L usbio8.build.board=AVR_USBIO8 usbio8.build.core=arduino usbio8.build.variant=usbio ##############################################################
hardware\arduino\programmers.txtファイルの先頭へ以下を追加する。
avrisp2.name=AVR ISP2 old one avrisp2.communication=serial avrisp2.protocol=stk500v2 yujiisp.name=Yuji Ueno's ISP Cable yujiisp.communication=par yujiisp.protocol=yuji
これはbootloaderを書き込む時に使用するライターを指定する時に使われる。手持ちの古いAVR ISP(V2プロトコル版にしてある)を使用出来るようにするため。
hardware\arduino\variants\pirampディレクトリを作成して,その中へpiramp_pins_arduino.zipの中にあるpins_arduino.hをコピーする。
同様に,hardware\arduino\variants\usbioディレクトリを作成して,その中へusbio_pins_arduino.zipの中にあるpins_arduino.hをコピーする。
これは自作ボードでATmega168P/ATmega328のPB6,PB7やポートの機能違いを吸収するため。
hardware\tools\avr\etc\avrdude.confファイルに,自作のAVRライター用の定義とCPU定義を追加しておく。
Arduino IDE 1.0.1用:avrdude.conf ATmega168P用の定義はすでにあった。ATmega328用の定義がなかったので追加した。
バージョン1.8.19なんかの新しいバージョン:avrdudeはversion6.3になっているのでavrdude.confも問題ないので,この作業は必要ない。
Arduinoの直接の機能ではないが,Arduino IDEからプログラム(スケッチ)をボードのFlash ROMに専用AVR Writerを使わず書き込むには,Arduino IDEからスケッチをコンパイルしたオブジェクトをボードに書き込む機能を持ったbootloaderを自作のボードに書き込んでおく必要がある。
ビルド済みのbootloaderもArduinoのパッケージに含まれているんだけど,ATmega168Pに対応していない事やオンボードLEDのポートが違うということもあり,同じような機能を持ったbootloader*2を作成してビルド後Flash ROMに書き込だ。
bootloaderをボードのCPUのFlash ROMエリアに書き込むには,AVRISP等の専用AVRライターを使う必要がある。 bootloaderが書き込んだ後はシリアルポートからFlash ROMを書き込めるようになる。
一般的に販売されているArduinoボードは,このようなbootloaderが書き込み済みで販売されている。
hardware\arduino\avr\bootloaders\atmegaディレクトリに以下のzipファイル(自前のbootloader)を解凍してファイルをコピーする。
piramp_usbio.zip
こちらは自作のbootloaderではなくArduinoボードにも採用されているoptibootのversionを自作ボード用にビルドしたもの。
optiboot.zip
サイズが512byteに収まっているので,ユーザープログラムの最大サイズが大きくなる利点がある。
既にbootloaderが書き込んである市販ボードではこの作業は必要ないが,AVRを使った自作ボードをArduino IDEで使用する場合はbootloaderを書き込む必要がある。
bootloaderを書き込んだらbootloaderエリアをロックビットを使ってプロテクトしておく。
bootloaderはArduino IDEからも書き込むことが出来る。IDEのツール>マイコンボードで基板を選んで,ツール>書き込み装置で専用Writer(AVR ISP等を選択)を指定して,ツール>ブートローダーを書き込むで書き込むことが出来る。(avrdudeを使っている。)
スケッチを書き込む時はボードをリセットしbootloaderを立ち上げる必要がある。
この目的のために市販されているArduinoボードは,USB-シリアルコンバーターのDTR信号とCPUのResetピン間に0.1uFのコンデンサーを実装して,DTR信号でCPUのリセットを行えるようになっている。
自作のボードにはリセット・スイッチもこのような回路も無いため,6pin ISPコネクタのRST端子とGNDをショートさせて手動でリセットさせる。
数秒後にユーザープログラムが立ち上がるまでにArduino IDEからプログラムを書き込む必要がある。(この操作が少しめんどくさい。)
また自作のボードでCPUクロックが8MHzのボードは内蔵RC発振器を使っている。16MHzのボードではX'talを搭載している。
AVRの内蔵RC発振器の周波数精度は±10%とあまり精度が良くない。このため8MHzのボードではArduino IDEからプログラムをブートローダーを使って書き込む時に低いbaudrateを使うようにしてある。
まず,ターゲットのボードをPCのUSBポートに接続する。
Arduino IDEを起動して,「ツール>マイコンボード」からYuji's PIRAMP Board(8MHz)を選択する。
「ツール>シリアルポート」で,ボードのCOMポートを設定する。
「ファイル>スケッチの例>01.Basics>Blink」を開く。
Arduino IDE 1.0.1の場合は,以下のように
int led = 13; ↓ int led = LED_BUILTIN;
変更する。
ツールアイコンの左から2番めにある→を押す。
これでソースコードがビルドされボードのユーザープログラム領域に書き込まれる。(この時,手動のリセットが必要)
プログラムが書き込まれた後,しばらくするとボード上のLEDが1秒毎に点滅した。
これで,自作のボードのプログラム作成にArduino IDEが使えることがわかった。
Arduino IDEは,ソースコードを編集する機能(エディター)とオブジェクトをターゲット基板に書き込むI/Fが主な機能になっている。
また,Arduino言語と呼ばれているdefineで定義したマクロを使用して,多少プログラムが書きやすくなるように工夫をしている。
また,多くのユーザーが開発したプログラムをソースコード・ライブラリという形で利用できることが人気の一つでもある。
しかし,AVR-GCCと使い慣れたエディターやmakeツールでも同様にプログラム開発は行えるので,特にArduino IDEを使う必要があるかと言うと,必要はないなってのが感想。
また,AVRチップメーカーのATMEL社から純正の統合開発環境のAVR Studioがリリースされている。
現在はATMEL社を買収したMicrochip社からMicrochip Studioとしてリリースされている。なので統合開発環境で開発したい場合はこちらを使うのが良いかも。
Arduino IDE 1.8.5でコンパイルしてみると,エラーが出て失敗した。
エラー直前の処理がctagsだったので,ctagsを単独で動かすと,appendモードについてのエラーが出ていた。
ctags: append mode is not compatible with tags to stdout
%HOME%/.ctagsを確認すると,
--append=yes --recurse=yes --langmap=PHP:+.inc --php-kinds=cfd
になっていた。しょうがないんで,
--append=no --recurse=yes --langmap=PHP:+.inc --php-kinds=cfd
に変更。
これで無事コンパイル出来るようになった。
arduino-1.0.1\hardware\arduino\cores\arduino\main.cppを覗いてみると,
#include <Arduino.h> int main(void) { init(); #if defined(USBCON) USBDevice.attach(); #endif setup(); for (;;) { loop(); if (serialEventRun) serialEventRun(); } return 0; }
となっていた。(バージョン1.0.1)
なので,setup()とloop()を書き忘れると,エラーとなるわけです。
それでは,init()はどこに書かれているかというと,arduino-1.0.1\hardware\cores\arduino\wiring.cにありました。
void init() { // this needs to be called before setup() or some functions won't // work there sei(); // on the ATmega168, timer 0 is also used for fast hardware pwm // (using phase-correct PWM would mean that timer 0 overflowed half as often // resulting in different millis() behavior on the ATmega8 and ATmega168) #if defined(TCCR0A) && defined(WGM01) sbi(TCCR0A, WGM01); sbi(TCCR0A, WGM00); #endif // set timer 0 prescale factor to 64 #if defined(__AVR_ATmega128__) // CPU specific: different values for the ATmega128 sbi(TCCR0, CS02); #elif defined(TCCR0) && defined(CS01) && defined(CS00) // this combination is for the standard atmega8 sbi(TCCR0, CS01); sbi(TCCR0, CS00); #elif defined(TCCR0B) && defined(CS01) && defined(CS00) // this combination is for the standard 168/328/1280/2560 sbi(TCCR0B, CS01); sbi(TCCR0B, CS00); #elif defined(TCCR0A) && defined(CS01) && defined(CS00) // this combination is for the __AVR_ATmega645__ series sbi(TCCR0A, CS01); sbi(TCCR0A, CS00); #else #error Timer 0 prescale factor 64 not set correctly #endif // enable timer 0 overflow interrupt #if defined(TIMSK) && defined(TOIE0) sbi(TIMSK, TOIE0); #elif defined(TIMSK0) && defined(TOIE0) sbi(TIMSK0, TOIE0); #else #error Timer 0 overflow interrupt not set correctly #endif // timers 1 and 2 are used for phase-correct hardware pwm // this is better for motors as it ensures an even waveform // note, however, that fast pwm mode can achieve a frequency of up // 8 MHz (with a 16 MHz clock) at 50% duty cycle #if defined(TCCR1B) && defined(CS11) && defined(CS10) TCCR1B = 0; // set timer 1 prescale factor to 64 sbi(TCCR1B, CS11); #if F_CPU >= 8000000L sbi(TCCR1B, CS10); #endif #elif defined(TCCR1) && defined(CS11) && defined(CS10) sbi(TCCR1, CS11); #if F_CPU >= 8000000L sbi(TCCR1, CS10); #endif #endif // put timer 1 in 8-bit phase correct pwm mode #if defined(TCCR1A) && defined(WGM10) sbi(TCCR1A, WGM10); #elif defined(TCCR1) #warning this needs to be finished #endif // set timer 2 prescale factor to 64 #if defined(TCCR2) && defined(CS22) sbi(TCCR2, CS22); #elif defined(TCCR2B) && defined(CS22) sbi(TCCR2B, CS22); #else #warning Timer 2 not finished (may not be present on this CPU) #endif // configure timer 2 for phase correct pwm (8-bit) #if defined(TCCR2) && defined(WGM20) sbi(TCCR2, WGM20); #elif defined(TCCR2A) && defined(WGM20) sbi(TCCR2A, WGM20); #else #warning Timer 2 not finished (may not be present on this CPU) #endif #if defined(TCCR3B) && defined(CS31) && defined(WGM30) sbi(TCCR3B, CS31); // set timer 3 prescale factor to 64 sbi(TCCR3B, CS30); sbi(TCCR3A, WGM30); // put timer 3 in 8-bit phase correct pwm mode #endif #if defined(TCCR4A) && defined(TCCR4B) && defined(TCCR4D) /* beginning of timer4 block for 32U4 and similar */ sbi(TCCR4B, CS42); // set timer4 prescale factor to 64 sbi(TCCR4B, CS41); sbi(TCCR4B, CS40); sbi(TCCR4D, WGM40); // put timer 4 in phase- and frequency-correct PWM mode sbi(TCCR4A, PWM4A); // enable PWM mode for comparator OCR4A sbi(TCCR4C, PWM4D); // enable PWM mode for comparator OCR4D #else /* beginning of timer4 block for ATMEGA1280 and ATMEGA2560 */ #if defined(TCCR4B) && defined(CS41) && defined(WGM40) sbi(TCCR4B, CS41); // set timer 4 prescale factor to 64 sbi(TCCR4B, CS40); sbi(TCCR4A, WGM40); // put timer 4 in 8-bit phase correct pwm mode #endif #endif /* end timer4 block for ATMEGA1280/2560 and similar */ #if defined(TCCR5B) && defined(CS51) && defined(WGM50) sbi(TCCR5B, CS51); // set timer 5 prescale factor to 64 sbi(TCCR5B, CS50); sbi(TCCR5A, WGM50); // put timer 5 in 8-bit phase correct pwm mode #endif #if defined(ADCSRA) // set a2d prescale factor to 128 // 16 MHz / 128 = 125 KHz, inside the desired 50-200 KHz range. // XXX: this will not work properly for other clock speeds, and // this code should use F_CPU to determine the prescale factor. sbi(ADCSRA, ADPS2); sbi(ADCSRA, ADPS1); sbi(ADCSRA, ADPS0); // enable a2d conversions sbi(ADCSRA, ADEN); #endif // the bootloader connects pins 0 and 1 to the USART; disconnect them // here so they can be used as normal digital i/o; they will be // reconnected in Serial.begin() #if defined(UCSRB) UCSRB = 0; #elif defined(UCSR0B) UCSR0B = 0; #endif }
AVRマイコンを使用するプログラムを作成する場合,ポートの入出力方向の設定,クロック速度の設定,割込処理の設定など・・・,AVRマイコンの動作を決定する各種設定を行うためのプログラムが必要になります。
sbi()関数は,レジスタの任意のビットを有効にするためのgccで定義されているマクロです。
Arduino IDEで開発を行う時には,これらをユーザーに負担にならないようにあらかじめ用意してあるわけです。
オフィシャルでリリースされているArduinoボードのスペック。
新しくコメントをつける