📄 bootloader_main.c
字号:
/***********************************************
**** AVR BootLoader应用范例 ***
**** ***
**** 作者: HJJourAVR ***
**** 编译器:WINAVR20050214 ***
**** ***
**** www.OurAVR.com 2005.10.17 ***
***********************************************/
//程序参考 马潮老师的M128 Boot_load应用的实例,ICCAVR版本
/*
本程序简单的示范了AVR ATMEGA16的IAP应用,实现智能升级
Boot Loader
XMODEM-CRC传输协议
CRC16校验
出于简化程序考虑,各种数据没有对外输出,学习时建议使用JTAG ICE硬件仿真器。
熔丝位设置
BOOTSZ1=0
BOOTSZ0=0 Boot区为1K字(2K字节)大小。
BOOTRST=0 复位向量位于Boot区。
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 <avr/delay.h>
//时钟定为外部晶体7.3728MHz,F_CPU=7372800 使用USART,115200bps
#include <avr/boot.h>
/*
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 <avr/crc16.h>
/*
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 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<SPM_PAGESIZE;i+=2) //将数据填入Flash缓冲页中
{
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<<RXC))
{
*ptr++=UDR; //如果接收到数据,读出
count++;
if (count>=len)
{
break; //够了?退出
}
}
if(TIFR & (1<<OCF0)) //T0溢出 1ms
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -