/***********************************************
**** AVR BootLoader运用典范 ***
**** ***
**** 作者: HJJourAVR ***
**** 编译器:WINAVR20050214 ***
**** ***
****www.OurAVR.com2005.10.17 ***
***********************************************/
//程序参阅 马潮教师的M128 Boot_load运用的实例,ICCAVR版别
//Stephen更改:9600bps, MEGA16, 8M INTERNAL RC,BOOTSZ1=0, BOOTSZ0=0, BOOTRST=1
/*
本程序简略的演示了AVR ATMEGA16的IAP运用,完结智能晋级
Boot Loader
XMODEM-CRC传输协议
CRC16校验
出于简化程序考虑,各种数据没有对外输出,学习时主张运用JTAG ICE硬件仿真器。
熔丝位设置
BOOTSZ1=0
BOOTSZ0=0 Boot区为1K字(2K字节)巨细。
BOOTRST=0 复位向量坐落Boot区。//Stephen: 设BOOTRST=1,答应发动
makefile中的程序基地址偏移
LDFLAGS += -Wl,–section-start=.text=0x3800 //0x3800字节=0x1C00字
移植程序时,可依据实践巨细设定Boot区,但要留意更改makefile和更改BootAdd常数,以及页写的巨细分配;
选用115200bps的通讯速率,晋级14KB程序需求耗时约5秒[上位机是WINDOWS 2000的超级终端]
疑问:
1 用HEX文件烧录作业正常,但elf仿真有问题。
用AVRstudio仿真elf(熔丝设定BOOTRST=0,程序基地址偏移=0x3800)时,一切SRAM变量丢掉初始化,
表现为put_s()的都是乱码或不行见字符。
但假如改成运用程序(熔丝设定BOOTRST=1,没有程序基地址偏移),则put_s()能够正常显现
2 XMODEM的完毕应对(EOT/CAN)后需加 delay_ms(500)的延时(程序优化,一致写在跳转到用户程序前),
不然鄙人面的状况将会无法正常完毕XMODEM的传输,但其实程序现已晋级成功
特殊状况:用户程序里边运用了串口,而且波特率较低(如9600bps)且开机即发送很大都据
*/
#include <avr/io.h>
#include
//时钟定为外部晶体7.3728MHz,F_CPU=7372800 运用USART,115200bps
#include
/*
boot_page_erase ( address )
擦除FLASH 指定页,其间address 是以字节为单位的FLASH 地址
boot_page_fill ( address, data )
填充BootLoader 缓冲页,address 为以字节为单位的缓冲页地址(对mega16 :0~128),
而data 是长度为两个字节的字数据,因而调用前address 的增量应为2。
此刻data 的高字节写入到高地址,低字节写入到低地址。
boot_page_write ( address )
boot_page_write 履行一次的SPM 指令,将缓冲页数据写入到FLASH 指定页。
boot_rww_enable ( )
RWW 区读使能
依据自编程的一起是否答应读FLASH 存储器,FLASH 存储器可分为两种类型:
可一起读写区( RWW Read-While-Write ) 和 非一起读写区( NRWW NotRead-While-Write)。
关于MEGA16 RWW 为前14K 字节 NRWW 为后2K 字节。
引导加载程序对RWW 区编程时MCU 仍能够从NRWW 区读取指令并履行,而对NRWW 区编程时MCU 处于挂起暂停状况。
在对RWW 区自编程(页写入或页擦除)时,由硬件确定RWW 区 , RWW 区的读操作被制止
在对RWW 区的编程完毕后应当调用boot_rww_enable() 使RWW 区敞开。
*/
#include
/*
GCCAVR内置函数,能够不必头痛CRC16了
关于CRC的具体阐明,能够检查一下网站:
http://www.nongnu.org/avr-libc/user-manual/group__avr__crc.html
函数原形
static __inline__ uint16_t _crc16_update(uint16_t __crc, uint8_t __data);
多项式Polynomial: x^16 + x^15 + x^2 + 1 (0xa001)
crc初始值Initial value: 0xffff
一般用于磁盘操控器(disk-drive controllers)
static __inline__ uint16_t _crc_xmodem_update(uint16_t __crc, uint8_t __data);
多项式Polynomial: x^16 + x^12 + x^5 + 1 (0x1021)
crc初始值Initial value: 0x0
专用于XMODEM通讯协议,等效于C写的
uint16_t crc_xmodem_update (uint16_t crc, uint8_t data)
{
int i;
crc = crc ^ ((uint16_t)data << 8);
for (i=0; i<8; i++)
{
if (crc & 0x8000)
crc = (crc << 1) ^ 0x1021;
else
crc <<= 1;
}
return crc;
}
static __inline__ uint16_t _crc_ccitt_update (uint16_t __crc, uint8_t __data)
多项式Polynomial: x^16 + x^12 + x^5 + 1 (0x8408)
crc初始值Initial value: 0xffff
专用于PPP和IrDA通讯协议
*/
//管脚界说
#define PIN_RXD 0 //PD0
#define PIN_TXD 1 //PD1
//常数界说
#define SPM_PAGESIZE 128 //M16的一个Flash页为128字节(64字)
#define DATA_BUFFER_SIZE SPM_PAGESIZE //界说接纳缓冲区长度
#define BAUDRATE 9600 //115200 //波特率选用115200bps
//#define F_CPU 7372800 //体系时钟7.3728MHz
//界说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_WAIT_CHAR C
//界说大局变量
struct str_XMODEM
{
unsigned char SOH; //开端字节
unsigned char BlockNo; //数据块编号
unsigned char nBlockNo; //数据块编号反码
unsigned char Xdata[128]; //数据128字节
unsigned char CRC16hi; //CRC16校验数据高位
unsigned char CRC16lo; //CRC16校验数据低位
}
strXMODEM; //XMODEM的接纳数据结构
unsigned long FlashAddress; //FLASH地址
#define BootAdd 0x3800 //Boot区的首地址(运用区的最高地址)
/* GCC里边地址运用32位长度,习惯一切AVR的容量*/
unsigned char BlockCount; //数据块累计(仅8位,无须考虑溢出)
unsigned char STATUS; //运转状况
#define ST_WAIT_START 0x00 //等候发动
#define ST_BLOCK_OK 0x01 //接纳一个数据块成功
#define ST_BLOCK_FAIL 0x02 //接纳一个数据块失利
#define ST_OK 0x03 //完结
//长延时 max 65536ms
void delay_ms(unsigned int t)
{
while(t–)
{
_delay_ms(1);
}
}
//更新一个Flash页的完好处理
void write_one_page(void)
{
unsigned char i;
unsigned char *buf;
unsigned int w;
boot_page_erase(FlashAddress); //擦除一个Flash页
boot_spm_busy_wait(); //等候页擦除完结
buf=&strXMODEM.Xdata[0];
for(i=0;i
w =*buf++;
w+=(*buf++)<<8;
//boot_page_fill(FlashAddress+i, w); //原句
boot_page_fill(i, w); //仅仅低7位(128字节/页)有用
}
boot_page_write(FlashAddress); //将缓冲页数据写入一个Flash页
boot_spm_busy_wait(); //等候页编程完结
}
//发送选用查询办法
void put_c(unsigned char c) //发送选用查询办法
{
loop_until_bit_is_set(UCSRA,UDRE);
UDR=c;
}
//发送字符串
void put_s(unsigned char *ptr)
{
while (*ptr)
{
put_c(*ptr++);
}
put_c(0x0D);
put_c(0x0A); //完毕发送回车换行
}
//接纳指定字节数据(带超时操控,Timer0的1ms时基)
// *ptr 数据缓冲区
// len 数据长度
// timeout 超时设定,最长65.536S
// 返回值 已接纳字节数目
unsigned char get_data(unsigned char *ptr,unsigned char len,unsigned int timeout)
{
unsigned count=0;
do
{
if (UCSRA & (1<
*ptr++=UDR; //假如接纳到数据,读出
count++;
if (count>=len)
{
break; //够了?退出
}
}
if(TIFR & (1<
TIFR|=(1<
}
}
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() __attribute__((section(“.stephen_bootloader”))); //stephen_bootloader
int main()
{
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<
//这个BootLoader没有运用中止。
//初始化USART 115200 8, n,1 PC上位机软件(超级终端)也要设成相同的设置才干通讯
UCSRC = (1<
UBRRH = (F_CPU/BAUDRATE/16-1)/256;
UCSRA = 0x00;
UCSRB = (1<
OCR0 = 28;
TCCR0 = (1<
//向PC机发送开端提示信息
put_s(“************************************************************”);
//put_s(” “);
put_s(“IC ATMega16 Firmware 智能晋级引导程序(Bootloader)VER20070107″);
put_s(” 运用Windows2000/xp 超级终端 串口发送 9600bps,8-N-1 “);
put_s(“如需更新用户程序,请在3秒钟内按下[d]键,不然3秒后运转用户程序 “);
put_s(“>”);
//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(“请挑选BIN文件,运用XMODEM协议传输,最大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(“退出Bootloader晋级程序!”);
delay_ms(500); //很古怪,见顶部的阐明
loop_until_bit_is_set(UCSRA,UDRE); //等候完毕提示信息回送完结
GICR = (1<
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/I%&&&&&% 等)来晋级指定方针芯片的软件。
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的操控字符:
2 XMODEM有两种校验方法:
一种是一字节的checksum校验方法,不常用。
另一种是2字节的CRC16校验方法(X^16 + X^12 + X^5 + 1),纠错率高达99.9984%。
两种方法的挑选由接纳端发送的发动操控符来决议,发动发送后不能切换。
当发送端收到“NAK”操控字符时,它将会开端以checksum校验办法发送数据块。
当发送端收到“C”操控字符时,它将会开端以CRC校验办法发送数据块。
3 Xmodem-CRC传输数据块格局:“
其间
<255-BlockNO>是前一字节的反码;
接下来是长度为128字节的数据块;
最终的
5 接纳端收到一个数据块并校验正确时,回送;接纳过错回送
6 BlockNO的初值为0x01,每发送一个新的数据块
7 发送端收到后,可持续发送下一个数据块(BlockNO+1);而收到
8 发送端发送
*/
makefile中的程序基地址偏移
LDFLAGS += -Wl,–section-start=.text=0x3800 //0x3800字节=0x1C00字
即添加下图中的27行
然后在options 中勾择Use External Makefile 选中方才改的Makefile
这是,编译完结的hex文件大约15k?? 好像是5k
晋级的程序,不能是HEX文件,由于HEX文件是内含格局且每行信息能够不等长的(下图)。关于这个BOOTLOADER晋级程序,只能接
收原始的二进制文件信息并覆写到相应的flash区内,因而只能运用BIN格局。将HEX转为BIN有一个小软件
而BIN文件是接连且等长的