📄 ref.c~
字号:
正在编写M128原理与使用指南中的一小节,请提意见和建议?
?cma?发表于?2004-8-7?04:12?
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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -