标题:
AVR单片机BootLoader功能应用源码与详细介绍
[打印本页]
作者:
waynehuge
时间:
2018-8-2 09:17
标题:
AVR单片机BootLoader功能应用源码与详细介绍
一、 题目:
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功能,包含在<avr/boot.h>中,主要几个宏有:
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<<TXC0)));
UCSR0A |= (1<<TXC0);
#if RS485
RS485Disable();
#endif
}
//等待串口数据
unsigned char WaitCom()
{
while(!(UCSR0A & (1<<RXC0)));
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<<OCF1A))
{
TIFR1 |= (1 << OCF1A);
if(cl == CONNECTCNT)
break;
wdt_reset();
cnt--;
if(cnt == 0)
{
putstr(msg2);
quit();
}
}
if(DataInCom())
{
if(ReadCom() == KEY[cl])
cl++;
else
cl = 0;
}
}
putstr(msg3);
cnt = TimeOutCntC;
while(1)
{
if(TIFR1 & (1<<OCF1A))
{
TIFR1 |= (1 << OCF1A);
WriteCom(XMODEM_RWC) ;
wdt_reset();
cnt--;
if(cnt == 0)
{
putstr(msg2);
quit();
}
}
if(DataInCom())
{
if(ReadCom() == XMODEM_SOH)
break;
}
}
TCCR1B = 0;
packNO = 0;
bufptr = 0;
cnt = 0;
FlashAddr = 0;
do
{
packNO++;
ch = WaitCom();
cl = ~WaitCom();
if ((packNO == ch) && (packNO == cl))
{
for(li = 0; li < BUFFERSIZE; li++)
{
buf[bufptr++] = WaitCom();
}
crch = WaitCom();
crcl = WaitCom();
crc16(&buf[bufptr - BUFFERSIZE], BUFFERSIZE);
if((crch == ch) && (crcl == cl))
{
if(FlashAddr < BootStart)
{
while(bufptr > 0)
{
write_one_page(&buf[BUFSIZE - bufptr]);
FlashAddr += SPM_PAGESIZE;
bufptr -= SPM_PAGESIZE;
}
}
else
{
bufptr = 0;
}
WriteCom(XMODEM_ACK);
cnt = 0;
wdt_reset();
}
else
{
WriteCom(XMODEM_NAK);
cnt++;
}
}
……………………
…………限于本文篇幅 余下代码请从51黑下载附件…………
复制代码
0.png
(48.38 KB, 下载次数: 138)
下载附件
2018-8-2 15:30 上传
所有资料51hei提供下载:
AVR单片机BootLoader功能应用.rar
(88 KB, 下载次数: 144)
2018-8-2 09:16 上传
点击文件名下载附件
下载积分: 黑币 -5
作者:
huaishang
时间:
2018-8-2 23:17
谢谢谢谢分享,顶一个!!
作者:
xinghai333
时间:
2019-3-15 16:26
学习学习,
谢谢谢谢分享,顶一个!!
作者:
yys3210
时间:
2019-10-31 23:28
多谢,多谢!
作者:
dm800s
时间:
2019-11-24 16:07
谢谢分享 正需要这个
作者:
幸运大亨
时间:
2020-2-6 22:12
这方面资料比较少,谢谢楼主分享
作者:
ziteng00
时间:
2020-7-3 23:56
感谢分享,资料很不错
作者:
hulilanyua
时间:
2020-8-6 22:09
感谢分享,资料很不错,以前知道用并口专用编程器,可用ISP, JTAG下载程序,单位有一台特灵的冷机,有一个温度传感器坏了,厂家说不能自已换,要写编号和代码,厂家来的人换上新的温度传感器,用一永磁块靠近传感器,然后用电脑下载程序,后来把坏的传感器拆开,里面是ATmega8a和485通信模块,就奇怪ATmega8a怎么能通过RS232串口下载程序呢,后来查资料才知道要先下载BootLoader引导程序,这个还不会用
作者:
cyrs
时间:
2021-1-29 09:30
我的Atmega32在Arduino Pro Mini 上无发刷BootLoader引导程序
作者:
cyrs
时间:
2021-1-29 10:49
我的ATmega328适用次方法不
作者:
yjwpm
时间:
2024-7-4 14:58
我的ATmega328适用次方法不
作者:
lmn2005
时间:
2024-7-17 11:02
这个不知道是否适合于ATmega 16.
欢迎光临 (http://www.51hei.com/bbs/)
Powered by Discuz! X3.1