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.

調査の結果、次のような事がおこっている事がわかった。

  1. avrdudeは、ライタとして-c arduino`が指定されている場合、DTRを用いたリセット信号を送る
  2. ArduinoUNOに搭載されているATMega16は、DTR信号をリセット回路に送る。
  3. ArduinoのATMega328pがリセットされる。
  4. ATMega328pはリセットされ制御がArduinoISPからブートローダに渡る。
  5. avrdudeは、ArduinoISPと通信を開始するつもりだったが、ブートローダと通信をはじめる。
  6. デバイスシグニチャの確認が行われる。ブートローダは自分は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さんに感謝する。