ArduinoISPの罠
TL;DR: ArduinoUNOやそれ以前のArduinoをArduinoISPとして使う場合、avrdudeには-c avrisp -b 19200
を設定する必要がある。
はじめに
SMKiJで、 ATMega32Aに、ArduinoISPでプログラムを書き込もうとした場合に、 書き込みが失敗するという質問がでていた。
詳しく聞いてみたら、興味深い挙動をしていたのでまとめる。
前提
書き込みにはArduinoUNOを使用していた、 ArduinoUNOの内部まで含む接続は次のようになっている。
接続1
STK500
◄────────────────────────────────────►
CDC
◄───────────────────►
┌─────────────────────────────────────┐
│ Arduino UNO │
│ │ ┌────────────┐
┌──────┐ │ ┌───────────────┐ ┌───────────────┐ │ │ │
│ │ USB │ │ │ │ │ │ SPI │ │
│ PC ├─────┼─┤ ATMega16U ├─┤ ATMega328p ├─┼─────┤ ATMega32A │
│ │ │ │ USB to Serial │ │ │ │ │ │
└──────┘ │ │ │ │ │ │ │ │
│ └────┬──────────┘ └───────────────┘ │ │ │
│ │ ▲ │ └────────────┘
│ │ ┌───────┐ │ │
│ │ DTR │ │ │ │
│ └──────►│ RESET ├────────┘ │
│ │ │ │
│ └───────┘ │
│ │
│ │
└─────────────────────────────────────┘
PCとATMega16UはUSB上でCDCで通信していて仮想シリアルポートを作る、 その上をSTK500のプロトコルが流れ、ATMega328pに渡される。
インターネットで検索すると、ArduinoISPには、Arduino LeonardoやProMicroを使っている場合が多いようだ。 その場合は次のような接続になっている。
接続2
┌─────────────────────────────────────┐
│ Arduino Leonardo │
│ │ ┌────────────┐
┌──────┐ │ ┌───────────────┐ │ │ │
│ │ USB │ │ │ │ SPI │ │
│ PC ├─────┼─┤ ATMega32U ├───────────────────┼─────┤ ATMega32A │
│ │ │ │ │ │ │ │
└──────┘ │ │ │ │ │ │
│ └───────────────┘ │ │ │
│ │ └────────────┘
│ │
│ │
│ │
│ │
│ │
│ │
│ │
└─────────────────────────────────────┘
セットアップとしては、ArduinoのメインコントローラであるATMega328pやATMega32UにArduinoISPを書き込んでおいてある。
問題1 リセット
報告された問題は、デバイスシグネチャが正常に読めず、ATMega32Aのものではなく、ATMega328pの物が読めるという問題だ。 同じコマンドで、ProMicroを使っている場合は正常に書き込めるようだ。
avrdude -c arduino -p atmega32 -P "COM4" -U flash:w:main.hex:i
avrdude.exe: AVR device initialized and ready to accept instructions
Reading | ################################################## | 100% 0.01s
avrdude.exe: Device signature = 0x1e950f (probably m328p)
avrdude.exe: Expected signature for ATmega32 is 1E 95 02
Double check chip, or use -F to override this check.
avrdude.exe done. Thank you.
調査の結果、次のような事がおこっている事がわかった。
- avrdudeは、ライタとして
-c arduino
`が指定されている場合、DTRを用いたリセット信号を送る。 - ArduinoUNOに搭載されているATMega16は、DTR信号をリセット回路に送る。
- ArduinoのATMega328pがリセットされる。
- ATMega328pはリセットされ制御がArduinoISPからブートローダに渡る。
- avrdudeは、ArduinoISPと通信を開始するつもりだったが、ブートローダと通信をはじめる。
- デバイスシグニチャの確認が行われる。ブートローダは自分はATMega328pだと主張する。
ではなぜ、LeonardoやProMicroではこのリセットがおこらないかを調べると、 Leonardoでは、リセットは、DTR信号だけではなく、通信スピードが1200bpsに設定されている時だけリセットをするようになっている。 avrdudeが1で送るリセット命令は無視され、ArduinoISPとの通信が開始される。
avrdudeに設定するライタを、-c avrisp
のように設定すると、avrdudeはリセットを行なわなくなるため、
この問題を回避できる。
単に失敗するのではなく、思ってるのと別のデバイスシグネチャが読めてしまうのは、 かなり謎に見える挙動だ。 これは、ArduinoISP、ブートローダ、avrispがstk500のプロトコルを使いまわしているため発生した。
Arduinoのリセット/書き込みは、革新的機能でArduinoのコアバリューを提供しているが、 最初のArduinoが作られた2005年から増築改築がくりかえされ、ちょっと古い感じになっているのかもしれない。
問題2 通信速度
avrdudeに-c avrisp
をつけても、まだ、次のようなエラーがでたようだ。
avrdude.exe: stk500_recv(): programmer is not responding
avrdude.exe: stk500_getsync() attempt 1 of 10: not in sync: resp=0x00
avrdude.exe: stk500_getsync() attempt 2 of 10: not in sync: resp=0xe0
avrdude.exe: stk500_getsync() attempt 3 of 10: not in sync: resp=0xe0
avrdude.exe: stk500_getsync() attempt 4 of 10: not in sync: resp=0xe0
avrdude.exe: stk500_getsync() attempt 5 of 10: not in sync: resp=0x00
avrdude.exe: stk500_getsync() attempt 6 of 10: not in sync: resp=0xe0
avrdude.exe: stk500_getsync() attempt 7 of 10: not in sync: resp=0xe0
avrdude.exe: stk500_getsync() attempt 8 of 10: not in sync: resp=0xe0
avrdude.exe: stk500_getsync() attempt 9 of 10: not in sync: resp=0x00
avrdude.exe: stk500_getsync() attempt 10 of 10: not in sync: resp=0xe0
試行錯誤の末、-b 19200
オプションを付けるとArduinoISPとの通信が成功するという事がわかった。
このオプションはATMega16UとATMega328pの間の通信速度を設定するものだ。
man avrdudeには
-b baudrate
Override the RS-232 connection baud rate specified in the respective programmer's entry of the configura‐
tion file.
とあり、設定する必要はないように見える。
調べた所、ArduinoISP側には、定数で19200ボーが設定されている。
Avrdudeのソースでは、ライタのデフォルト値が115200ボーになっている
予想では、オリジナルのstk500やavrispでは115200ボーを使っているが、ArduinoISPでは遅めの速度に設定しているのではないかと思う。
LeonardoやProMicroでは、CDCの終端をしているのもArduinoISPが動いているのも、同じマイコン内であり、 物理的なシリアル通信線があるわけではないので、そもそもこの通信速度はどこでも使われてないため、 ここを設定しなくても動作しているようだ。
まとめ
ArduinoUNOやそれ以前のArduinoではavrdudeのオプションに注意する必要がある事がわかった。
とても勉強になった。議論に参加して頂いたL4Phさんとhsgwさんに感謝する。