もくもく4thステップ
- 作者: 坂井弘亮
- 出版社/メーカー: カットシステム
- 発売日: 2010/05
- メディア: 単行本
- 購入: 24人 クリック: 252回
- この商品を含むブログ (39件) を見る
ROMの書き換え上限回数はメーカー保証で100回。コード書き換える毎に書きこんでたら直ぐに超えちゃう。
ROM上にはブートローダだけ書いておき、ブートローダがプログラムをRAMへダウンロードすれば毎度ROMを書き換えずに済む。
組み込み開発では、開発時はブートローダを使ってRAM上で開発し、出荷時にROMへ書きこむという手法が取られる→ROM化(そうしないと、再起動する度にプログラムをダウンロードしなきゃならない)。
シリアル経由でOSをロード出来るブートローダを作る。
プロトコルにはXMODEMを使う。
XMODEMの仕様
送信側
- NAK(0x15)を受け取ったら送信開始
- 固定長ブロックで送信 ブロックサイズ > 残りデータ量の場合はEOF(0x1a)で埋める
- ブロック送信毎にACK(0x06)・NAKを待つ ACKなら続き NAKなら再送
- 送信終了したらEOT(0x04) ACKを受け取り正常終了
- 中断時はCAN(0x18)を送信/受信
受信側
- 受信可能ならNAK
- SOH(0x01)から始まるブロックを受信 ACK/NAKを返す
- EOTを受けてACKを返す
- 中断時はCAN(0x18)を送信/受信
ブロック
a | 1b | SOH(0x01) |
b | 1b | 1-255の連番 255の次が0 |
c | 1b | ブロック番号のビット反転 |
d | 128b | データ |
e | 1b | checksum |
ソースコード
ld.scr
MEMORY { romall(rx) : o = 0x000000, l = 0x080000 /* 512KB */ vectors(r) : o = 0x000000, l = 0x000100 /* top of ROM */ rom(rx) : o = 0x000100, l = 0x07ff00 ramall(rwx) : o = 0xffbf20, l = 0x004000 /* 16KB */ buffer(rwx) : o = 0xffdf20, l = 0x001d00 /* 8KB */
SECTIONS { .vectors : { vector.o(.data) } > vectors .text : { _text_start = . ; *(.text) _etext = . ; } > rom .rodata : { _rodata_start = . ; *(.strings) *(.rodata) *(.rodata.*) _erodata = . ; } > rom .buffer : { _buffer_start = . ; } > buffer
.buffer == bufferがOSを読み込む位置。
main.c
gets(buf); if (!strcmp(buf, "load")) { loadbuf = (char *)(&buffer_start); size = xmodem_recv(loadbuf);
プロンプトにloadって入力されてたら、xmodem_recv()を呼んでる。これが受信処理。
xmodem.c
long xmodem_recv(char *buf) { int r, receiving = 0; long size = 0; unsigned char c, block_number = 1; while (1) { if (!receiving) xmodem_wait(); /* 受信開始されるまで送信要求を出す */
xmodem_wait()ではSOHが来るまでのNAK送信を行う
int serial_is_recv_enable(int index) { volatile struct h8_3069f_sci *sci = regs[index].sci; return (sci->ssr & H8_3069F_SCI_SSR_RDRF); }
static int xmodem_wait(void) { long cnt = 0; while (!serial_is_recv_enable(SERIAL_DEFAULT_DEVICE)) { if (++cnt >= 2000000) { cnt = 0; serial_send_byte(SERIAL_DEFAULT_DEVICE, XMODEM_NAK);
SCIのssrレジスタにRDRFビット(受信完了)が立たない間はNAKを贈り続ける
c = serial_recv_byte(SERIAL_DEFAULT_DEVICE);
一バイト受信して比較
if (c == XMODEM_EOT) { /* 受信終了 */ serial_send_byte(SERIAL_DEFAULT_DEVICE, XMODEM_ACK); break;
EOTならACKを送って正常終了
} else if (c == XMODEM_CAN) { /* 受信中断 */ return -1;
CANなら中断なので異常終了
} else if (c == XMODEM_SOH) { /* 受信開始 */ receiving++; r = xmodem_read_block(block_number, buf); /* ブロック単位での受信 */
SOHなら後に続くブロックを受信
static int xmodem_read_block(unsigned char block_number, char *buf) { unsigned char c, block_num, check_sum; int i; block_num = serial_recv_byte(SERIAL_DEFAULT_DEVICE); if (block_num != block_number) return -1;
bフィールド(ブロック番号)の受信
block_num ^= serial_recv_byte(SERIAL_DEFAULT_DEVICE); if (block_num != 0xff) return -1;
cフィールド(ブロック番号の反転)の受信とブロック番号のチェック
check_sum = 0; for (i = 0; i < XMODEM_BLOCK_SIZE; i++) { c = serial_recv_byte(SERIAL_DEFAULT_DEVICE); *(buf++) = c; check_sum += c; }
dフィールド(データ)128バイト受信、checksumを計算
check_sum ^= serial_recv_byte(SERIAL_DEFAULT_DEVICE); if (check_sum) return -1;
eフィールド(checksum)の受信と比較