一、题目:
AVR单片机BootLoader功能应用
二、特色芯片技术介绍、使用说明:
AVR单片机中多数Mega系列单片机具有片内引导程序自编程功能即BootLoader功能。MCU 通过运行一个常驻FLASH的BootLoader 程序,利用任何可用的数据接口读取代码后写入自身的FLASH存储器中,实现自编程目的。
BootLoader功能将FLASH程序存储器其分为应用程序区和引导加载区,通过设置熔丝位BOOTSZ0和BOOTSZ1可以配置不同大小的引导加载区空间;通过设置熔丝位BOOTRST用于设置复位向量,当BOOTRST未被编程时器件复位后从应用程序区首地址开始执行,当BOOTRST被编程时器件复位后从引导区首地址开始执行。在设置好BOOTSZ0、BOOTSZ1和BOOTRST熔丝位后,需要把BootLoader程序定位并写入到引导区中,其首地址由熔丝位ROOTSZ0和BOOTSZ1的编程状态决定。在单片机上电复位后BootLoader程序开始执行,BootLoader程序可以通过USART、TWI或其它方式从计算机或其它数据源读取应用程序代码并写入到应用区中。
avr-libc提供一组C程序接口API来支持BootLoader功能,包含在中,主要几个宏有:
boot_page_erase ( address ):擦除FLASH指定页,address是以字节为单位的FLASH地址。
boot_page_fill ( address, data ):填充BootLoader 缓冲页,address为以字节为单位的缓冲页地址(对mega8:0~64),而data是长度为两个字节的字数据,因此调用前address 的增量应为2。此时data的高字节写入到高地址,低字节写入到低地址。
boot_page_write ( address ):boot_page_write执行一次的SPM指令,将缓冲页数据写入到FLASH指定页。
boot_rww_enable ( ):RWW区读使能,根据自编程的同时是否允许读FLASH存储器。RWW(Read-While-Write)可同时读写区,在对RWW 区自编程即页写入或页擦除时,由硬件锁定RWW区,RWW区的读操作被禁止,在对RWW区的编程结束后应当调用boot_rww_enable()使RWW区开放。
三、驱动程序的流程图
本应用以实际使用的Mega系列单片机Mega168为例,说明AVR单片机BootLoader的功能应用。BootLoader程序通过串口与计算机进行通信,执行读、写以及跳转到FLASH应用区的操作。单片机与计算机通信使用Xmodem通信协议,Xmodem通信协议见相关文档。其程序流程如下图。(可见附件中“流程图.vsd”文件)
四、驱动程序的源程序
对应Mega168的BootLoader程序包括bootloader.c和bootloader.h。
源程序清单如下:(可见附件中“bootloader.c”和“bootloader.h”文件)
五、设计及调试技巧
BootLoader程序不使用中断,以查询的方式读写UART数据。退出BootLoader程序后程序指针跳转到应用程序区首地址,如果要重新执行BootLoader程序以加载应用区程序,必须使用硬件复位。
六、典型问题及解决办法
在程序升级过程中遇到多个模块通过485总线连接在一起时,引起多个模块响应,造成误擦除,升级不能成功,在硬件及Bootloader程序中设置升级条件,条件满足时升级模块程序,否则跳转到应用程序区。
单片机源程序如下:
#include "bootloader.h"
//串口初始化
void ComInit(void)
{
UBRR0H = BAUDREG/256;
UBRR0L = BAUDREG%256;
UCSR0A = 0;
UCSR0B = (1 << RXEN0)|(1 << TXEN0);
UCSR0C = (1 << UCSZ00)|(1 << UCSZ01);
}
//使用定时器1:产生以毫秒为单位的时间
void TimerInit()
{
OCR1A = (unsigned int)(timeclk * (F_CPU / (1024 * 1000.0f)));
TCCR1A = 0;
TCCR1B = (1 << WGM12)|(1 << CS12)|(1 << CS10);
}
//更新一个Flash页
void write_one_page(unsigned char *buf)
{
boot_page_erase(FlashAddr);
boot_spm_busy_wait();
for(pagptr = 0; pagptr < SPM_PAGESIZE; pagptr += 2)
{
boot_page_fill(pagptr, buf[pagptr] + (buf[pagptr + 1] << 8));
}
boot_page_write(FlashAddr);
boot_spm_busy_wait();
}
//跳转到用户程序
void quit()
{
boot_rww_enable();
(*((void(*)(void))PROG_START))();
}
//写入数据到串口
void WriteCom(unsigned char dat)
{
#if RS485
RS485Enable();
#endif
UDR0 = dat;
while(!(UCSR0A & (1<
UCSR0A |= (1<
#if RS485
RS485Disable();
#endif
}
//等待串口数据
unsigned char WaitCom()
{
while(!(UCSR0A & (1<
return UDR0;
}
//向串口输出字符串
void putstr(const char *str)
{
while(*str)
WriteCom(*str++);
WriteCom(0x0D);
WriteCom(0x0A);
}
//CRC校验
void crc16(unsigned char *buf, unsigned char n)
{
unsigned char j;
unsigned char i;
unsigned int crc, t;
crc = 0;
for(j = n; j > 0; j--)
{
crc = (crc ^ (((unsigned int) *buf) << 8));
for(i = 8; i > 0; i--)
{
t = crc << 1;
if(crc & 0x8000)
t = t ^ 0x1021;
crc = t;
}
buf++;
}
ch = crc / 256;
cl = crc % 256;
}
int main(void)
{
unsigned char cnt;
unsigned char packNO;
unsigned char crch, crcl;
unsigned char li;
asm volatile("cli": : );
wdt_enable(WDTO_1S);
TimerInit();
#if RS485
DDRREG(RS485PORT) |= (1 << RS485TXEn);
RS485Disable();
#endif
ComInit();
putstr(msg1);
cnt = TimeOutCnt;
cl = 0;
while(1)
{
if(TIFR1 & (1<
{
TIFR1 |= (1 << OCF1A);
if(cl == CONNECTCNT)
break;
wdt_reset();
cnt--;
if(cnt == 0)
{
『本文转载自网络,版权归原作者所有,如有侵权请联系删除』