📄 bootloader_main.c
字号:
{
TIFR|=(1<<OCF0); //清除标志位
timeout--; //倒计时
}
}
while (timeout);
return count;
}
//计算CRC16
unsigned int calcrc(unsigned char *ptr, unsigned char count)
{
unsigned int crc = 0;
while (count--)
{
crc =_crc_xmodem_update(crc,*ptr++);
}
return crc;
}
//主程序
int main(void)
{
unsigned char c;
unsigned char i;
unsigned int crc;
//考虑到BootLoader可能由应用程序中跳转过来,所以所用到的模块需要全面初始化
DDRA=0x00;
DDRB=0x00;
DDRC=0x00;
PORTA=0xFF; //不用的管脚使能内部上拉电阻。
PORTB=0xFF;
PORTC=0xFF;
PORTD=0xFF;
DDRD=(1<<PIN_TXD); //串口的输出
GICR = (1<<IVCE);
GICR = (0<<IVCE)|(1<<IVSEL); //将中断向量表迁移到Boot区头部
asm volatile("cli": : ); //关全局中断
//这个BootLoader没有使用中断。
//初始化USART 115200 8, n,1 PC上位机软件(超级终端)也要设成同样的设置才能通讯
UCSRC = (1<<URSEL)|(1<<UCSZ1)|(1<<UCSZ0); //异步,8位数据,无奇偶校验,一个停止位,无倍速
UBRRL = (F_CPU/BAUDRATE/16-1)%256; //设定波特率
UBRRH = (F_CPU/BAUDRATE/16-1)/256;
UCSRA = 0x00;
UCSRB = (1<<RXEN)|(1<<TXEN); //使能接收,使能发送
//初始化T/C0,CTC模式,256分频,1ms自动重载
OCR0 = 28;
TCCR0 = (1<<WGM01)|(1<<CS02)|(0<<CS01)|(0<<CS00);
//CTC模式下,溢出标志是输出比较匹配OCF0,对应的中断是输出比较匹配中断;
//向PC机发送开始提示信息
put_s(" ");
put_s(" ");
put_s("这是个AVR ATMEGA16通过串口升级用户程序(IAP应用)的范例程序");
put_s(" www.ouravr.com");
put_s("如需更新用户程序,请在3秒钟内按下[d]键,否则3秒后运行用户程序");
//3秒种等待PC下发“d”,否则退出Bootloader程序,从0x0000处执行应用程序
c=0;
get_data(&c,1,3000); //限时3秒,接收一个数据
if ((c=='d')||(c=='D'))
{
STATUS=ST_WAIT_START; //并且数据='d'或'D',进入XMODEM
put_s("请使用XMODEM协议传输BIN文件,最大14KB");
}
else
{
STATUS=ST_OK; //退出Bootloader程序
}
//进入XMODEM模式
FlashAddress=0x0000;
BlockCount=0x01;
while(STATUS!=ST_OK) //循环接收,直到全部发完
{
if (STATUS==ST_WAIT_START)
{//XMODEM未启动
put_c(XMODEM_WAIT_CHAR); //发送请求XMODEM_WAIT_CHAR
}
i=get_data(&strXMODEM.SOH,133,1000); //限时1秒,接收133字节数据
if(i)
{
//分析数据包的第一个数据 SOH/EOT/CAN
switch(strXMODEM.SOH)
{
case XMODEM_SOH: //收到开始符SOH
if (i>=133)
{
STATUS=ST_BLOCK_OK;
}
else
{
STATUS=ST_BLOCK_FAIL; //如果数据不足,要求重发当前数据块
put_c(XMODEM_NAK);
}
break;
case XMODEM_EOT: //收到结束符EOT
put_c(XMODEM_ACK); //通知PC机全部收到
STATUS=ST_OK;
put_s("用户程序升级成功");
break;
case XMODEM_CAN: //收到取消符CAN
put_c(XMODEM_ACK); //回应PC机
STATUS=ST_OK;
put_s("警告:用户取消升级,用户程序可能不完整");
break;
default: //起始字节错误
put_c(XMODEM_NAK); //要求重发当前数据块
STATUS=ST_BLOCK_FAIL;
break;
}
}
if (STATUS==ST_BLOCK_OK) //接收133字节OK,且起始字节正确
{
if (BlockCount != strXMODEM.BlockNo)//核对数据块编号正确
{
put_c(XMODEM_NAK); //数据块编号错误,要求重发当前数据块
continue;
}
if (BlockCount !=(unsigned char)(~strXMODEM.nBlockNo))
{
put_c(XMODEM_NAK); //数据块编号反码错误,要求重发当前数据块
continue;
}
crc=strXMODEM.CRC16hi<<8;
crc+=strXMODEM.CRC16lo;
//AVR的16位整数是低位在先,XMODEM的CRC16是高位在先
if(calcrc(&strXMODEM.Xdata[0],128)!=crc)
{
put_c(XMODEM_NAK); //CRC错误,要求重发当前数据块
continue;
}
//正确接收128个字节数据,刚好是M16的一页
if (FlashAddress<(BootAdd-SPM_PAGESIZE))
{ //如果地址在应用区内
write_one_page(); //将收到128字节写入一页Flash中
FlashAddress+=SPM_PAGESIZE; //Flash页加1
}
else
{
put_c(XMODEM_CAN); //程序已满,取消传送
put_c(XMODEM_CAN);
put_c(XMODEM_CAN);
STATUS=ST_OK;
put_s(" 程序已满,取消传送");
break;
}
put_c(XMODEM_ACK); //回应已正确收到一个数据块
BlockCount++; //数据块累计加1
}
}
//退出Bootloader程序,从0x0000处执行应用程序
put_s("LET'S GO!");
delay_ms(500); //很奇怪,见顶部的说明
loop_until_bit_is_set(UCSRA,UDRE); //等待结束提示信息回送完成
GICR = (1<<IVCE);
GICR = (0<<IVCE)|(0<<IVSEL); //将中断向量表迁移到应用程序区头部
/* 无论BootLoader是否使用中断,将中断向量表迁移到应用程序区头部,会增强程序的健壮性*/
boot_rww_enable (); //RWW区读允许,否则无法马上执行用户的应用程序
asm volatile("jmp 0x0000": : ); //跳转到Flash的0x0000处,执行用户的应用程序
}
/*
FLASH程序存储器的编程方法常见的有以下几种:
(1)传统的并行编程方法;
(2)通过串行口进行在线编程ISP(In System Programmability) 对器件或电路甚至整个系统进行现场升级或功能重构;
(3)在运行中,应用程序控制下的应用在线编程IAP (In Applocation Programing) 简单地说就是在某一个section中运行程序,同时对另一个section进行擦除、读取、写入等操作。
ISP方式相对于传统方式有了极大的进步,它不需要将芯片从电路板上卸下就可对芯片进行编程,减少了开发时间,简化了产品制造流程,并大大降低了现场升级的困难。
而IAP方式是对芯片的编程处于应用程序控制之下,对芯片的编程融入在通信系统当中,通过各种接口(UART/SPI/IIC 等)来升级指定目标芯片的软件。
BootLoader 功能介绍
BootLoader 提供我们通常所说的IAP(In Applicaion Program)功能。
多数Mega系列单片机具有片内引导程序自编程功能(BootLoader)。
MCU 通过运行一个常驻FLASH 的BootLoader 程序,利用任何可用的数据接口读取代码后写入自身FLASH存储器中 ,实现自编程目的
基本设计思想(参考了马潮老师的文章)
1. Boot Loader程序的设计要点
Boot Loader程序的设计是实现IAP的关键,它必须能过通过一个通信接口,采用某种协议正确的接收数据,再将完整的数据写入到用户程序区中。本例Boot Loader程序的设计要点有:
1 采用ATmega16的USART口实现与PC之间的简易RS232三线通信;
2 采用Xmodem通信协议完成与PC机之间的数据交换;
3 用户程序更新完成后自动转入用户程序执行;
2. Xmodem通信协议
Xmodem协议是一种使用拨号调制解调器的个人计算机通信中广泛使用的异步文件运输协议。
这种协议以128字节块的形式传输数据,并且每个块都使用一个校验和过程来进行错误检测。
如果接收方关于一个块的校验和与它在发送方的校验和相同时,接收方就向发送方发送一个认可字节。
为了便于读者阅读程序,下面简要说明该协议的主要特点,有关Xmoden的完整的协议请参考其它相关的资料。
1 Xmodem的控制字符:<soh> 01H、<eot> 04H、<ack> 06H、<nak> 15H、<can> 18H、<eof> 1AH、'c' 43H。
2 XMODEM有两种校验模式:
一种是一字节的checksum校验模式,不常用。
另一种是2字节的CRC16校验模式(X^16 + X^12 + X^5 + 1),纠错率高达99.9984%。
两种模式的选择由接收端发送的启动控制符来决定,启动发送后不能切换。
当发送端收到“NAK”控制字符时,它将会开始以checksum校验方式发送数据块。
当发送端收到“C”控制字符时,它将会开始以CRC校验方式发送数据块。
3 Xmodem-CRC传输数据块格式:“<soh> <BlockNO> <255-BlockNO> <…128个字节的数据块…> <checksum_crc16>”。
其中<soh>为起始字节;
<BlockNO>为数据块编号字节,每次加一;
<255-BlockNO>是前一字节的反码;
接下来是长度为128字节的数据块;
最后的<checksum_crc16>是128字节数据的CRC校验码,长度为2个字节,crc16hi,crc16lo。
5 接收端收到一个数据块并校验正确时,回送<ack>;接收错误回送<nak>;而回送<can>表示要发送端停止发送。
6 BlockNO的初值为0x01,每发送一个新的数据块<BlockNO>加1,加到OxFF后下一个数据块的<BlockNO>为零,即8位无符号数。
7 发送端收到<ack>后,可继续发送下一个数据块(BlockNO+1);而收到<nak>则可再次重发上一个数据块。
8 发送端发送<eot>表示全部数据发送完成。如果最后需要发送的数据不足128个字节,用<eof>填满一个数据块。
*/
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -