続いて、ブートローダーの基本となるシリアル通信を実装します。
各種コマンドの実装は次回です。焦らず進んでいこうね。
ブートローダーの核となるUART
ブートローダーの役割を簡単に確認します。
そもそもブートローダーは主に以下の役割を担うと認識しています(多分正式ななにかは存在しない):
- プログラム領域へのデータ読み書き
- EEPROM など、不揮発性メモリへのデータアクセス
- メインプログラムへのジャンプ(実行)
多分これに尽きると思います。他にも独自プロトコルのデバッグプローブ接続とかあると思いますが、細かいところは除外します。
プログラム
こんな感じよ。既存のコードを一部コピペしただけですが、HFUSE を 0xD8 (ブートローダー起動) にするか 0xD9 (ユーザープログラムコード実行) にするだけで挙動が変わるプログラムを作成したという点で目新しいと思います。
.include "../m328pdef.inc"
.device atmega328p
.def tmp = r16
.cseg
.org 0x0000
jmp main
main:
sbi DDRB, 1
sbi PORTB, 1
jmp main
.org 0x3800
jmp bootloader_main
uart_init:
ldi tmp, 0
sts UBRR0H, tmp
ldi tmp, 51
sts UBRR0L, tmp ; set baud rate to 9600 bps
ldi tmp, (1<<RXEN0) + (1<<TXEN0)
sts UCSR0B, tmp ; enable tx and rx
ldi tmp, (0b11<<UCSZ00)
sts UCSR0C, tmp ; Set frame format: 8data, 1stop bit
ret
.def data = r18
USART_Transmit:
; Wait for empty transmit buffer
lds tmp, UCSR0A
sbrs tmp, UDRE0
rjmp USART_Transmit
; Put data into buffer, sends the data
sts UDR0, data
ret
USART_Receive:
lds tmp, UCSR0A
sbrs tmp, RXC0
rjmp USART_Receive
lds data, UDR0
ret
bootloader_main:
rcall uart_init
blmain_loop:
rcall USART_Receive
rcall USART_Transmit
rjmp blmain_loop
これを書き込んでブートローダーから起動するように Fuse を設定してあげると、PB1 に取り付けた LED は消灯したままシリアル通信データがエコーバックされると思います。念の為、以下に UART のピン配置を示しておきます。書き込み用の FT232RL を使っている場合、UART 用の FT232RL も必要となるということですね。金を積んで買おう!!
マイコンピン番号 | マイコン側ピン名 |
---|---|
2 | RXD |
3 | TXD |
動作
HFUSE を 0xD9 にするとユーザープログラムが実行され、LEDが点灯します。
$ avrdude -c ft232r -p m328p -U hfuse:w:0xD9:m # ユーザープログラム実行。PB1のLEDが点灯。
一方、HFUSE を 0xD8 にすると BOOTRST ビットがセットされ、ブートローダー起動の後に、UART でエコーバックされます。
$ avrdude -c ft232r -p m328p -U hfuse:w:0xD8:m # ブートローダー起動。9600bpsで送信されたデータをそのまま返す。
以下が実行風景の再現です。
1234ABC AVR # こちらが入力データ
1234ABC AVR # このようにそのまま文字列が返ってくる
〆
進捗は多くはないですが、これにてブートローダー経由で UART 通信をすることが可能と確認できました!
あとはこれを利用してフラッシュ読み出し、フラッシュ書き込みコマンドを実装すればとりあえずオレオレブートローダーを設計できますね!
次回はこのオレオレブートローダーを実装して AVR アーキテクチャの機械語手打ちという変態行為を行うことを目標とします。