最近自分の中で USB マイコンブームが来ていまして、これまで AVR, LPC と触ってきました。
今やいろんなマイコンで USB ペリフェラルが見られますが、ホストや OTG を実装しているものが多いんですよね〜。そうなると色々レジスタ等の設定が込み入ってしまって、僕の弱い頭では理解ができなくなるわけです。現に Longan Nano (GD32VF マイコンのボード)でも実装しようとして力尽きましたし。
で、色々と模索していると ATmega16U2 というマイコンが見つかったわけです。他にも 16 が 32 になった ROM 32KB 版とか、2 が 4 になった 44pin 版とかあるわけですが、小ささを優先したのでこの型番です。
類似の ATmega16U4 のマニュアルには、low-speed USB が crystal-less で使用可能、なんて書いてあったわけですが、このマイコンのデータシートには書いてませんねえ。でもまあちょっとしたバージョン違いだから出来るっしょということで色々設定しました。途中アクシデントで時間を浪費しましたが...
記事執筆の動機
このマイコンをいじっていく上でググったりしましたが、Arduino の記事ばっかり出てきて非常に遺憾だったので、本記事は素の 16U2 についてめもします。
デフォルトのヒューズビット
以下でした。外部クリスタルと CPU クロック8分周を使う、典型的なセッティングでしたね。
Byte | Value |
---|---|
Low | 5E |
High | D9 |
Ext. | F4 |
詳細はこちら。
USB ブートローダ
デフォルトで USB DFU ブートローダが書き込まれています。これによって、あの面倒くさい ISP 回路を組まなくてもプログラムの書き込み等が可能です。ブートローダからはヒューズビットの書き換えができないことは注意。
データシートで「D- と D+ は 22Ω の抵抗を入れてくれ」とあります。逆に言うとそれ以外の抵抗器は要らないみたいです。お手軽ですね。自分は 22Ω の抵抗さえ入れませんでしたが、動いているのでいいでしょう(テキトー
あと、外部クロックとして、8MHz のクリスタルとコンデンサが必須です。avrdude
で書き込む時は別に 12MHz だろうが 10MHz だろうが良かったんですが、USB クロックを駆動する関係で、ピッタリ 8MHz でなければならないようです。
書き込みはお得意の dfu-programmer
で。ブートローダを起動させるには PD7(#HWB) を Low にする必要があります。High にすると直接アプリケーションが起動する感じです。
$ dfu-programmer atmega16u2 erase
Checking memory from 0x0 to 0x2FFF... Empty.
Chip already blank, to force erase use --force.
$ dfu-programmer atmega16u2 flash Register.hex
Checking memory from 0x0 to 0x1FF... Empty.
0% 100% Programming 0x200 bytes...
[>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>] Success
0% 100% Reading 0x3000 bytes...
[>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>] Success
Validating... Success
0x200 bytes written into 0x3000 bytes memory (4.17%).
$ dfu-programmer atmega16u2 launch
ただ、注意点があります。
avrdudeの引数に要注意
ということです。僕自身は FT232RL を用いてプログラムの書き換えをしようとしたのですが、これが大間違い。-c ft232r
と指定した場合、 avrdude
はブートローダを認識してくれるわけはなく、単なる Flash/ヒューズビットの書き換えツールとして機能します。結果、無事 USB ブートローダをぶっ壊しました。
avrdude
を使うのであれば、FT232RL で接続せずに、直に USB で接続して -c avr109
というパラメータを指定すれば良いようです(自分は未確認)。ご注意を。
ブートローダの復旧法
この Atmega16U2 に関してはあまり情報がないようなので、記述します。
まずは、avrdude
で使えるように配線を行ってください。クリスタルもお忘れなく。手前味噌ですが、こちらの記事が参考になるかと。
そうしたら次はブートローダのイメージを入手します。ところが検索しても出てこない。ちょっとこれを紹介するのも迷惑かなあと思うんですが、こちらに LUFA というプロジェクトのものがありました。こちらの DFU-AVR8-atmega16u2-BOARD_NONE-BOOT_4KB-8MHz.hex を利用させてもらいました。
書き込みは以下。
$ avrdude -P ft0 -c ft232r -p m16u2 -B 4800 -U flash:w:DFU-AVR8-atmega16u2-BOARD_NONE-BOOT_4KB-8MHz.hex
結構時間かかるので、お茶でも飲みながら待ちます。書き込み後のベリファイ(読み出し)はまあスキップしても良いんじゃないですかね。
これで再度 USB で接続・リセットし、dfu-programmer
から反応があれば復旧完了です。僕はほっとしました。
ウォッチドッグタイマ問題
実際に動作させるとなると、初期のヒューズでは問題が生じました。一定周期でマイコンが再起動するのです。
例えば以下のプログラムで「LED をつけろ!」と言っているだけなのですが、何故か LED がチカチカします。「ウォッチドッグタイマでLチカ!」なんて記事にしようかと思ったんですが、面白くないので没になりました。
#include <avr/io.h>
int main(void) {
DDRB |= 1 << 0;
PORTB |= 1 << 0;
while(1) {
}
}
ちなみに周期はよく覚えていませんが、35msec〜45msec だったかと思います。原因を調べると、ウォッチドッグタイマがシステムリセットを繰り返しているという事が分かりました。なるほど、比較的新しい AVR ではウォッチドッグタイマが一度動作すると、その後もシステムリセットを繰り返すよう。以下のサイトが非常に参考になりました。
これって仕様らしいです。エラッタとして取り上げられてないですしね。
でも初期のヒューズビットによるとウォッチドッグタイマ無効化しているはずだよね??どうして?まあともかく、こちらを別ファイルに書いておくことで、この再起動問題は落ち着きました。
#include <stdint.h>
#include <avr/wdt.h>
uint8_t mcusr_mirror __attribute__ ((section (".noinit")));
void get_mcusr(void) __attribute__((naked)) __attribute__((section(".init3")));
void get_mcusr(void)
{
mcusr_mirror = MCUSR;
MCUSR = 0;
wdt_disable();
}
アセンブリしたい人は以下を記述して、予め呼んでおけば OK。
wdt_disable:
cli
; Reset Watchdog Timer
wdr
; Clear WDRF in MCUSR
in r16, MCUSR
cbr r16, 1 << 3
out MCUSR, r16
; Write logical one to WDCE and WDE
; Keep old prescaler setting to prevent unintentional time-out in r16, WDTCSR
lds r17, WDTCSR
mov r16, r17
ori r16, (1<<3) | (1<<4)
sts WDTCSR, r16
; Turn off WDT
andi r16, ~(1<<3)
sts WDTCSR, r16
sei
ret
オレオレ仕様にカスタム
ということで、取り敢えずの問題は解決したので、ついにオレオレ仕様にしていきます。具体的には以下です。
- クロック設定
- 使用する周波数を内部 8MHz に
- CPU クロックの8分周を無効に
- BOD 設定
- BOD を無効に
クロックの設定
外部でクリスタルを用意するのは面倒なので、内部 RC クロックを用い、8 分周を無効化してブーストします。
BOD 設定
電圧低下時に電源に忖度する(?) BOD ですが、趣味でマイコンいじいじする分にはなくても困らないので無効化します。
設定値
以上を設定したものがこちらです。こちらを avrdude
で書き込みます。Flash には手を加えないので、avrdude
は使っても問題ありません。
〆
以上で、ATmega16U2 を使うためにやったこと、難儀したことをまとめました。やっぱり素のチップをいじるのは大事です。あと地味にデータシートの誤植が多いですね...