?? 自引導iap的應用設計..c
字號:
自引導IAP的應用設計
自引導IAP的應用設計
作者:佚名 AVR單片機來源:不詳 點擊數: 更新時間:2007-8-17
5.2 自引導IAP的應用設計
ATmega128具備引導加載支持的用戶程序自編程功能(In-Sysytem Programming by On-chip Boot Program),它提供了一個真正的由MCU本身自動下載和更新(采用讀/寫同時“Read-While-Write”進行的方式)程序代碼的系統程序自編程更新的機制。利用AVR的這個功能,可以實現在應用編程(IAP)以及實現系統程序的遠程自動更新的應用。
IAP的本質就是,MCU可以靈活地運行一個常駐Flash的引導加載程序(Boot Loader Program),實現對用戶應用程序的在線自編程更新。引導加載程序的設計可以使用任何的可用的數據接口和相關的協議讀取代碼,或者從程序存儲器中讀取代碼,然后將代碼寫入(編程)到Flash存儲器中。
引導加載程序有能力讀寫整個Flash存儲器,包括引導加載程序所在的引導加載區本身。引導加載程序還可以對自身進行更新修改,甚至可以將自身刪除,使系統的自編程能力消失。引導加載程序區的大小可以由芯片的熔絲位設置,該段程序區還提供兩組鎖定位,以便用戶選擇對該段程序區的不同級別的保護。
本節將給出一個實際的的Boot Loader程序,它可以配合Windows中的超級終端程序,采用Xmodem傳輸協議,通過RS232接口下載更新用戶的應用程序。
5.2.1 基本設計思想
1. Boot Loader程序的設計要點
Boot Loader程序的設計是實現IAP的關鍵,它必須能過通過一個通信接口,采用某種協議正確的接收數據,再將完整的數據寫入到用戶程序區中。本例Boot Loader程序的設計要點有:
l 采用ATmega128的USART口實現與PC之間的簡易RS232三線通信;
l 采用Xmodem通信協議完成與PC機之間的數據交換;
l 用戶程序更新完成后自動轉入用戶程序執行;
l Boot Loader程序采用C語言內嵌AVR匯編方式編寫,閱讀理解方便,可移植性強,代碼小于1K字。
2. Xmodem通信協議
Xmodem協議是一種使用撥號調制解調器的個人計算機通信中廣泛使用的異步文件運輸協議。這種協議以128字節塊的形式傳輸數據,并且每個塊都使用一個校驗和過程來進行錯誤檢測。如果接收方關于一個塊的校驗和與它在發送方的校驗和相同時,接收方就向發送方發送一個認可字節。為了便于讀者閱讀程序,下面簡要說明該協議的主要特點,有關Xmoden的完整的協議請參考其它相關的資料。
l Xmodem的控制字符:<soh> 01H、<eot> 04H、<ack> 06H、<nak> 15H、<can> 18H、<eof> 1AH。
l Xmodem傳輸數據塊格式:“<soh> <packNO> <255-packNO> <…128個字節的數據塊…> <cksum>”。其中<soh>為起始字節;<packNO>為數據塊編號字節,每次加一;<255-packNO>是前一字節的反碼;接下來是長度為128字節的數據塊;最后的<cksum>是128字節數據的CRC校驗碼,長度為2個字節。
l 接收端收到一個數據塊并校驗正確時,回送<ack>;接收錯誤回送<nak>;而回送<can>表示要發送端停止發送。
l 發送端收到<ack>后,可繼續發送下一個數據塊(packNO+1);而收到<nak>則可再次重發上一個數據塊。
l 發送端發送<eot>表示全部數據發送完成。如果最后需要發送的數據不足128個字節,用<eof>填滿一個數據塊。
l 控制字符“C”有特殊的作用,當發送端收到“C”控制字符時,它回重新開始以CRC校驗方式發送數據塊(packNO = 1)。
l 每發送一個新的數據塊<packNO>加1,加到OxFF后下一個數據塊的<packNO>為零。
l 校驗方式采用16位CRC校驗(X^16 + X^12 + X^5 + 1)。
5.2.2 源程序代碼
下面給出的源程序是在ICCAVR中實現的。
/*****************************************************
采用串行接口實現Boot_load應用的實例
華東師大電子系 馬 潮 2004.07
Compiler: ICC-AVR 6.31
Target: Mega128
Crystal: 16Mhz
Used: T/C0,USART0
*****************************************************/
#include <iom128v.h>
#define SPM_PAGESIZE 256 //M128的一個Flash頁為256字節(128字)
#define BAUD 38400 //波特率采用38400bps
#define CRYSTAL 16000000 //系統時鐘16MHz
//計算和定義M128的波特率設置參數
#define BAUD_SETTING (unsigned char)((unsigned long)CRYSTAL/(16*(unsigned long)BAUD)-1)
#define BAUD_H (unsigned char)(BAUD_SETTING>>8)
#define BAUD_L (unsigned char)BAUD_SETTING
#define DATA_BUFFER_SIZE SPM_PAGESIZE //定義接收緩沖區長度
//定義Xmoden控制字符
#define XMODEM_NUL 0x00
#define XMODEM_SOH 0x01
#define XMODEM_STX 0x02
#define XMODEM_EOT 0x04
#define XMODEM_ACK 0x06
#define XMODEM_NAK 0x15
#define XMODEM_CAN 0x18
#define XMODEM_EOF 0x1A
#define XMODEM_RECIEVING_WAIT_CHAR 'C'
//定義全局變量
const char startupString[]="Type 'd' download, Others run app.\n\r\0";
char data[DATA_BUFFER_SIZE];
long address = 0;
//擦除(code=0x03)和寫入(code=0x05)一個Flash頁
void boot_page_ew(long p_address,char code)
{
asm("mov r30,r16\n"
"mov r31,r17\n"
"out 0x3b,r18\n"); //將頁地址放入Z寄存器和RAMPZ的Bit0中
SPMCSR = code; //寄存器SPMCSR中為操作碼
asm("spm\n"); //對指定Flash頁進行操作
}
//填充Flash緩沖頁中的一個字
void boot_page_fill(unsigned int address,int data)
{
asm("mov r30,r16\n"
"mov r31,r17\n" //Z寄存器中為填沖頁內地址
"mov r0,r18\n"
"mov r1,r19\n"); //R0R1中為一個指令字
SPMCSR = 0x01;
asm("spm\n");
}
//等待一個Flash頁的寫完成
void wait_page_rw_ok(void)
{
while(SPMCSR & 0x40)
{
while(SPMCSR & 0x01);
SPMCSR = 0x11;
asm("spm\n");
}
}
//更新一個Flash頁的完整處理
void write_one_page(void)
{
int i;
boot_page_ew(address,0x03); //擦除一個Flash頁
wait_page_rw_ok(); //等待擦除完成
for(i=0;i<SPM_PAGESIZE;i+=2) //將數據填入Flash緩沖頁中
{
boot_page_fill(i, data[i]+(data[i+1]<<8));
}
boot_page_ew(address,0x05); //將緩沖頁數據寫入一個Flash頁
wait_page_rw_ok(); //等待寫入完成
}
//從RS232發送一個字節
void uart_putchar(char c)
{
while(!(UCSR0A & 0x20));
UDR0 = c;
}
//從RS232接收一個字節
int uart_getchar(void)
{
unsigned char status,res;
if(!(UCSR0A & 0x80)) return -1; //no data to be received
status = UCSR0A;
res = UDR0;
if (status & 0x1c) return -1; // If error, return -1
return res;
}
//等待從RS232接收一個有效的字節
char uart_waitchar(void)
{
int c;
while((c=uart_getchar())==-1);
return (char)c;
}
//計算CRC
int calcrc(char *ptr, int count)
{
int crc = 0;
char i;
while (--count >= 0)
{
crc = crc ^ (int) *ptr++ << 8;
i = 8;
do
{
if (crc & 0x8000)
crc = crc << 1 ^ 0x1021;
else
crc = crc << 1;
} while(--i);
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -