Repository navigation
USB_ATML_DFU
- AVR DU 系列の USB 周辺機能実装
- USB 2.0 制御エニュメレーション
- USB 記述子
- USBクラス別詳細
この項の記述は、以下の参考文献からの、USB協会 : DFU規約 Revision 1.1 との相違点を表す、ベンダー固有実装の例。策定されたのは DFU規約 1.0 の時代である。
Microccip (Atmel) : USB DFU Bootloader Datasheet (doc7618.pdf) / PDF
- 決定的な相違として、"DFU_DNLOAD" は固有コマンド送信にも使用され、イメージ書き込み時は "Write Command" + (任意個の)"Memory Image" + "DFU Suffix" という制御パケット列で編成されることにある。
- つまり DFU 1.1 付録B の記載("DFU Suffix" 送信禁止)とは矛盾する。しかしながらこれは "16バイトの将来用の予約情報" であるとも説明されており、実際には装置側で無視する(中身は確認せずCRC32も計算しない)ように実装される。実のところ ブートローダー格納領域に制約のある legacy/classicAVR 系統以前では、CRC32 計算器をソフトウェア的に格納する余地が少ない事情もある。
- legacyAVR/classicAVR 系統に属する 8製品で採用されており、Flash 末尾に製造時書込された状態で出荷される。これら以外の(Xmega系列の)製品は "Atmel USB FLIP DFU" ブートローダー規約が適合する。
使用しない。"Run-Time" 動作から "DFU Mode" 動作への遷移は、Hard Reset でのみ行われる。このためジャンパーピン操作や、Hard Reset 2度押しが利用される。
| Ofs | Size | Field | Value | Description |
|---|---|---|---|---|
| 0 | 1 | bLength | 0x12 | 記述子全体長 |
| 1 | 1 | bDescriptorType | 0x01 | DEVICE descriptor type. |
| 2 | 2 | bcdUSB | 0x0100 | USB版番号(BCD) |
| 4 | 1 | bDeviceClass | 0xFE | Application Specific Class Code. |
| 5 | 1 | bDeviceSubClass | 0x01 | Device Firmware Upgrade Code. |
| 6 | 1 | bDeviceProtocol | 0x00 | This device does not use the base protocol. |
| 7 | 1 | bMaxPacketSize0 | 0x20 | エンドポイントEP0 のパケット最大長(32固定) |
| 8 | 2 | idVendor | 0x03EB | Vendor ID is "ATML" specific. |
| 10 | 2 | idProduct | 0x2F** | The Product ID is unique to each product model. |
| 12 | 2 | bcdDevice | 0x0000 | Device release number in binary coded decimal. |
| 14 | 1 | iManufacturer | 0x00 | An index of unused String Descriptors. |
| 15 | 1 | iProduct | 0x00 | An index of unused String Descriptors. |
| 16 | 1 | iSerialNumber | 0x00 | An index of unused String Descriptors. |
| 17 | 1 | bNumConfigurations | 0x01 | 構成定義数(必ず 1) |
- 複合デバイスを構成しないので、
bDeviceClassとbDeviceSubClassは、この時点で DFUクラスであることを表明している。 -
bMaxPacketSize0は 32(0x20)値で固定。 -
idVendorは(Microchip製品になって以後も)"MCHP" ではなく "ATML" を表明する IDが使われている。 -
iManufacturer他の 文字列記述子指標番号は、すべて 0(未使用)が指定される。
legacyAVR/classicAVR 系統に属する 8製品で使用されるのが明記されている。これらでのブートローダー領域とその先頭実行アドレスは、FUSEビットで指定された容量で、Flash の最後尾に配置する。
| Production | Product ID | Version | Bootloader Address | Size |
|---|---|---|---|---|
| AT90USB1287 | 0x2FFB | 1.0.1 | $F000 (Word) | 4KiB (Word) |
| AT90USB1286 | ↑ | ↑ | ↑ | ↑ |
| AT90USB647 | 0x2FF9 | 1.0.1 | $7800 (Word) | 2KiB (Word) |
| AT90USB646 | ↑ | 1.0.1 | ↑ | ↑ |
| AT90USB162 | 0x2FFA | 1.0.0/1/5 | $1800 (Word) | ↑ |
| AT90USB82 | 0x2FF7 | ↑ | $0800 (Word) | ↑ |
| ATmega32U4 | 0x2FF4 | 1.0.0 | $3800 (Word) | ↑ |
| ATmega16U4 | 0x2FF3 | ↑ | $1800 (Word) | ↑ |
- AT90USB162 と AT90USB82 は、出荷時期によって マイナーバージョン(と支援される機能)が 3種類ある。
- ATmega8/16/32U2 等は、この表に記載がないことに注意。原書発行後に発売開始され、かつ改訂・更新がされていない。
"DFU 1.0/1.1" と相違なし。bNumInterfaces は必ず 1 を指定する。
| Ofs | Size | Field | Value | Description |
|---|---|---|---|---|
| 0 | 1 | bLength | 0x09 | 記述子全体長 |
| 1 | 1 | bDescriptorType | 0x04 | INTERFACE descriptor type. |
| 2 | 1 | bInterfaceNumber | 0x00 | このIFを特定する指標番号(必ず 0x00) |
| 3 | 1 | bAlternateSetting | 0x00 | 代替IF番号 |
| 4 | 1 | bNumEndpoints | 0x00 | このIFで使用されるエンドポイント数(なし) |
| 5 | 1 | bInterfaceClass | 0xFE | Application Specific Class Code. |
| 6 | 1 | bInterfaceSubClass | 0x01 | Device Firmware Upgrade Code. |
| 7 | 1 | bInterfaceProtocol | 0x00 | The device does not use a class specific protocol on this interface. |
| 8 | 1 | iInterface | 0x00 | An index of unused String Descriptors. |
-
bInterfaceProtocolには 2("DFU Protocol")ではなく、0(未定義)を指定する。 - この インターフェースは、"DFU Mode" で使用される唯一の インターフェースである。従って
bInterfaceNumberは常に0x00である。 - しかしながら、以下の記述("DFU Mode"全体)では インターフェース指標番号を示すべき
wIndexには(存在しないはずの)4を常に指定するかのように読める記述がされている。 - 要求内の
wValue項が示すwBlockNumについての説明はどこにも記載がなく、これも不明瞭である。一応 DFU規約1.x どおりに記述すれば良いだろう。
使用されない。従って "Run-Time Mode" と、wDetachTimeOut、wTransferSize、bcdDFUVersion に関係する機能は使用できない。
応用アプリケーションがこれを独自に実装することは、自由。
Atmel DFU は、"DFU_DNLOAD" を介して 6バイトの、これから実行すべき固有定義コマンドを装置に送る。
Request from Host
| Class-Specific Request | bmRequestType | bRequest | wValue | wIndex | wLength | Data |
|---|---|---|---|---|---|---|
| DFU_DNLOAD | 0 01 00001 | 0x01 | wBlockNum | Interface (4) | Length | Firmware |
Summary of Frames from Host
| Command | ID | data[0] | data[1] | data[2] | data[3] | data[4] | Description |
|---|---|---|---|---|---|---|---|
| (Byte Offset) | 0 | 1 | 2 | 3 | 4 | 5 | |
| Prog_Start | 0x01 | 0x00 | start_address (MSB) | ← (LSB) | end_address (MSB) | ← (LSB) | Init FLASH programming |
| ↑ | ↑ | 0x01 | ↑ | ↑ | ↑ | ↑ | Init EEPROM programming |
| Display_Data | 0x03 | 0x00 | ↑ | ↑ | ↑ | ↑ | Display FLASH Data |
| ↑ | ↑ | 0x01 | ↑ | ↑ | ↑ | ↑ | Blank Check |
| ↑ | ↑ | 0x02 | ↑ | ↑ | ↑ | ↑ | Display EEPROM Data |
| Write_Command | 0x04 | 0x00 | 0xFF | - | - | - | Full chip Erase (bits at FFh) |
| ↑ | ↑ | 0x03 | 0x00 | - | - | - | Hardware reset |
| ↑ | ↑ | ↑ | 0x01 | program address (MSB) | ← (LSB) | - | LJMP address |
| Read_Command | 0x05 | 0x00 | 0x00 | - | - | - | Read Bootloader Version |
| ↑ | ↑ | ↑ | 0x01 | - | - | - | Read Device boot ID1 |
| ↑ | ↑ | ↑ | 0x02 | - | - | - | Read Device boot ID2 |
| ↑ | ↑ | 0x01 | 0x30 | - | - | - | Read Manufacturer Code (Signature 0) |
| ↑ | ↑ | ↑ | 0x31 | - | - | - | Read Family Code (Signature 1) |
| ↑ | ↑ | ↑ | 0x60 | - | - | - | Read Product Name (Signature 2) |
| ↑ | ↑ | ↑ | 0x61 | - | - | - | Read Product Revision |
| Change_Base_Address | 0x06 | 0x03 | 0x00 | "PP" | (0x00) | (0x00) | Select “PP” 64 KiB flash page number |
- (文書中では言及されていないが、その用途が自明な)Change_Base_Address は、Prog_Start で示されるメモリアドレスが 64KiB 境界を越える時、その直前に発行されるはずだ。示される "PP" は、
0x00か、そうでないかだろう。この情報はグローバル値として保存され、以後変更されるまで、あるいは別のメモリセグメントが指示されるまで、内部的に保持しなければならない。 - Write_Command の LJMPアドレスは、ワード粒度で示される。このため Flash 最大量は 128 KiB に制限されるが、この DFUローダー対象製品は 128 KiB までしか製造されていないので、問題ではない。
Write Command Sequence
Firmware イメージは、不揮発メモリページサイズ(メモリセグメントによって異なる)に合わせて分割される。さらに EP0パケットサイズ(32バイト固定長)に分割し、コマンド(6バイト)、本体、DFU Suffix(16バイト)の順に送信される。
SETUP と OUT はホスト側からの要求/指令、IN は 装置側からの応答。
- SETUP - DFU_DNLOAD (see wLength)
- OUT - Prog_Start (6 bute) + Padding (26 byte)
- OUT - X offset padding (0 to 31 byte) + Firmware Packet 1 (1 to 32 byte)
- OUT - Firmware Packet 2 (32 byte) ...
- OUT - Firmware Packet N (0 to 31 byte) + DFU Suffix (1 to 16 byte)
- (OUT - Continuing with DFU Suffix (1 to 15 byte) )
- (OUT - ZLP)
- IN - ZLP
- SETUP - DFU_GETSTATUS
- IN - Status Answers(OK、errWRITE、errPROG、etc)
- (SETUP - DFU_CLRSTATUS)
- (IN - ZLP)
- ホストは最初に DFU_DNLOAD で 6バイトの Prog_Start コマンドを発行し、メモリセグメント種別、start_address と end_address を通知する。
- start_address が 64 KiB 境界を越える場合、このシーケンスに先立って Change_Base_Address コマンドシーケンスが送信される。装置は与えられた "PP"をグローバル値として保持しておき、オフセットページ番号として解釈して、次回からの address に加算しなければならない。
- EP0パケットサイズは 32バイト固定長であるので、その境界に合わせるために Prog_Start コマンドには 26バイトのパディングが後置される。換言すれば、装置は十分な量のバッファメモリを有しているならば、受信パケットを一列に格納することができる。装置はこのパディングを読み飛ばし、メモリに書き込んではならない。パディングは
0x00の羅列であるべきだが、必ずしもそうであるとは限らない。 - 最初の Firmware Packet は、不揮発メモリページ境界と(自ずとパケット境界とも)一致しない start_address(バイト粒度)が示された場合、必要な分量のダミーバイトが前置される。装置はこれを読み飛ばし、決してメモリに書き込んではならない。パディングは
0x00または0xFFの羅列であるべきだが、必ずしもそうであるとは限らない。 - 最後の Firmware Packet には、end_address の位置から パディングなしで "DFU Suffix" が後置する。装置はこれを棄却しなければならず、メモリに書き込んではならない。換言すれば、装置は常に
wLengthと end_address を意識する必要がある。両者が一致した位置が "DFU Suffix" の開始点だ。なお "DFU Suffix" は途中で次の(真の最終)パケットに分断されうる。 - Flash 書き込みイメージは ワード粒度なので、かならず偶数倍の長さを持っているはずである。start_address は常に偶数であるし、end_address(最終書き込み番地+1)も偶数のはずである。そうでなければ余分なダミーバイトを 前または後の該当位置に補完しなければならない。これは既にメモリ上にある既知のバイト値を読み取って適用するか、
0xFFを適用する。(不揮発メモリは NANDロジックなので、0xFFは既知のデータを上書きしても破壊されない) - 最終パケットは 32バイトに満たないこともあれば、32バイトちょうどの場合もありうる。そのためホストは 転送完了を示すために、パケット長の端数がない場合は ZLP を送信するかもしれない。
- 装置は最終パケットまでの全ての転送パケットを受け取ったら(そこまでの総バイト数と
wLengthとの比較で判断して)ZLP で応答する。しかしこれは実際の 不揮発メモリページ 書き込み操作結果が判明するまで、保留するのがよい。(自ずから、装置側周辺機能の AutoZLP 送信設定は有効にすべきではない) - ホストは 装置から ZLP を受け取ると(直ちに。あるいは然るべき待機時間経過後に)DFU_GETSTATUS で、書き込み操作結果を要求するだろう。
Firmware イメージは、不揮発メモリページサイズ(メモリセグメントによって異なる)に合わせて分割される。さらに EP0パケットサイズ(32バイト固定長)に分割して順に送信される。
Read Command Sequence
- SETUP - DFU_DNLOAD (wLength == 6)
- OUT - Display_Data (6 byte)
- IN - ZLP
- SETUP - DFU_UPLOAD (see wLength)
- IN - Firmware Packet 1
- IN - Firmware Packet 2...
- IN - Firmware Packet N
- OUT - ZLP
- SETUP - GET_STATUS
- IN - Status Answers(OK、err_VENDOR、etc)
- ホストは最初に DFU_DNLOAD 指令で 6バイトの Display_Data コマンドを発行し、メモリセグメント種別、start_address と end_address を通知する。装置はこれに対して ZLP で応答する。
- start_address が 64 KiB 境界を越える場合、このシーケンスに先立って Change_Base_Address コマンドシーケンスが送信される。装置は与えられた "PP"をグローバル値として保持しておき、オフセットページ番号として解釈して、次回からの address に加算しなければならない。
- 続いてホストは DFU_UPLOAD 要求を装置に求める。
- プログラミング時(ダウンロード)と異なり、アップロードでの start_address(読出開始アドレス)と end_address(最終アドレス+1) はバイト粒度で、メモリページ境界は意識しない。よってホストに送信すべき イメージデータ量
wLengthは、end_address - start_addressに必ず一致する。これを 32バイトパケットに細分化してホストに送信する。最終パケットが端数であっても、パディング調整は行わない。(USB周辺機能がマルチパケットに対応している品種だと、細分化作業は考えなくて良い) - 装置は "LOCK" されたメモリを読むことはできないので、その場合は
wLength全量を返す前に ZLP で転送を中断することができる。 - ホストは受信データ量が、要求した
wLengthで満たされると、装置に対して ZLP を通知し、転送状態を終了する。
Blank Check Command Sequence
- SETUP - DFU_DNLOAD (wLength == 6)
- OUT - Display_Data #Blank Check (6 byte)
- IN - ZLP
- SETUP - GET_STATUS
- IN - Status Answers(OK、errCHECK_ERASED、etc)
- DFU_DNLOAD 要求による 6バイトの Display_Data コマンドが "Blank Check" 種別である場合、装置は 指定メモリ範囲が全て
0xFFで満たされていることを確認する。このメモリスキャン操作が終わるまで ZLP 応答は遅滞させるのが良い。 - ホストは続けて GET_STATUS 要求で、装置の動作結果を求める。期待される値は "OK"(
0xFFで満ちている)または "errCHECK_ERASED" である。そうであれば装置は dfuIDLE 状態 を維持する。さもなければ dfuERROR 状態に遷移する。
ホストは 構成情報または製造元情報 の読み取りを要求することができ、装置は 1バイトの 構成情報または製造元情報 を返すことができる。
Information Command Sequence
- SETUP - DFU_DNLOAD (wLength == 2)
- OUT - Read_Command (2 byte)
- IN - ZLP
- SETUP - DFU_UPLOAD (wLength == 1)
- IN - Byte value Answers(1 byte)
- SETUP - GET_STATUS
- IN - Status Answers(OK、err_VENDOR、etc)
- DFU_DNLOAD 要求による Read_Command コマンドの長さは、2バイトである。
- 装置は "LOCK" されたメモリを読むことはできないので、その場合は
wLength全量を返す前に ZLP で転送を中断することができる。
ホストは Write_Command で、Flash 全領域の消去を 装置に求めることができる。
Erasing Command Sequence
- SETUP - DFU_DNLOAD (wLength == 3)
- OUT - Write_Command #Full chip Erase (3 byte)
- IN - ZLP
- SETUP - GET_STATUS
- IN - Status Answers(OK、errWRITE、etc)
- ブートローダー領域は、ふつうは消去対象に含まれない。それ以外の Flash 領域が消去対象であり、それが
0xFFで満たされたなら、装置は "OK" 状況を報告してよい。さもなければ "errWRITE" 状況("errERASE"ではない)を報告する。 - USB をセルフホスティングしてブートローダーを走行させている構造の装置は、自分自身に ネイティブなチップ消去 を行えないので "EEPROM"、"FUSE" ビット、"LOCK"ビットをクリアすることはできず、ホストもまたそれを期待してはならない。ブートローダーが物理的に別の MCU で動いている場合は ネイティブなチップ消去 を試行できるため、"errERASE" を報告しうる。
ホストは Write_Command で、ブートローダーから直接、応用アプリケーションの起動を試みることができる。
DFU_ABORT 指令についての言及はないが、目的はほぼ同じである。
Starting Application Command Sequence
- SETUP - DFU_DNLOAD (wLength == 3 or 5)
- OUT - Write_Command #Hardware reset (3 byte) or #LJMP address (5 byte)
- IN - ZLP
- SETUP - DFU_DNLOAD (wLength == 0)
- IN - ...(USB Detach)
- 装置はまず最初の DFU_DNLOAD で続く実行内容を指示する。装置は 2個目の(Data が空の)DFU_DNLOAD を受信したら、指示内容の実行を図る。
- 装置は "LJMP address" を指示されると、自身が使用した周辺機能を停止・無効化し(USBバスからも切り離し)、汎用アドレスやスタックも可能な限り再初期化したした上で、指定された応用アプリケーション開始アドレス(しかし普通は 0x0000 だろう)への LJMP を実行する。
- 装置は "Hardware reset" を指示されると、通常は Watch Dog Timer of Timeout Reset を試みる。外部 MCU で動作している場合は、対象 MCU に 真の Hardware Reset を行い、自身は不活性状態になる。(USBバスのアクセス権も 対象 MCU に委譲される)
- 重要:legacyAVR/classicAVR では、ひとたび起動した WDT は、Hard Reset 後も継続動作しつづけるのが一般的である。実行先のアプリケーションがそれを確実に無効化しない場合、WDT Reset が無限に再発生するだろう。(ブートローダー自身もまたそうである)
"Hard Reset" 2度押しで "DFU Mode" に入れる実装(ATmega 系列等)は、EEPROM領域に切り替えフラグを記録して見分ける仕組みになっている。(そしてそれが可能なのはだいたい、USB FLIP DFU 規約動作である)
言及なし。従って "dfuMANIFEST" 状況は実装されていないと判断される。
USBブートローダー接続が開始されると、自動的に(MCU 固有の "LOCK" ビット設定とは無関係に)メモリの読み書きを制限されたセキュリティ動作に入るのが望ましい。この場合許可される DFU コマンドは「フルチップ消去」だけであり、それに成功しない限り 他のメモリ読み書きコマンドは 失敗を返されるのが望まれうる。
なんかすごく分かりづらい。原書の前半は DFU規約1.0(?)の複写なので、実際の DFUローダー実装では機能していないような部分が多い。
-
dfu-programmer / Github
-
arguments.c
- Atmel 対応製品の DFU対応 Product ID リストを記載している。以下の原書で言及がない製品も多く含まれる。
-
arguments.c
Twitter(X): @askn37
BlueSky Social: @multix.jp
GitHub: https://github.com/askn37/
Product: https://askn37.github.io/
Copyright (c) 2026 askn (K.Sato) multix.jp
Released under the MIT license
https://opensource.org/licenses/mit-license.php
https://www.oshwa.org/
multix.jp/てくにかるむ(休眠中)
Multix Zinnia Product SDK [*AVR]
AVR.JP(日本語訳)
AVR-LIBC(日本語訳)