找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 10070|回复: 11
打印 上一主题 下一主题
收起左侧

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程序中设置升级条件,条件满足时升级模块程序,否则跳转到应用程序区。

单片机源程序如下:
  1. #include "bootloader.h"

  2. //串口初始化
  3. void ComInit(void)                                                               
  4. {                                                                                       
  5.         UBRR0H = BAUDREG/256;                                       
  6.         UBRR0L = BAUDREG%256;                                       
  7.         UCSR0A = 0;                                                               
  8.         UCSR0B = (1 << RXEN0)|(1 << TXEN0);               
  9.         UCSR0C = (1 << UCSZ00)|(1 << UCSZ01);       
  10. }

  11. //使用定时器1:产生以毫秒为单位的时间
  12. void TimerInit()                                                      
  13. {                                                                 
  14.         OCR1A  = (unsigned int)(timeclk * (F_CPU  / (1024 * 1000.0f)));
  15.         TCCR1A = 0;                                    
  16.         TCCR1B = (1 << WGM12)|(1 << CS12)|(1 << CS10);
  17. }

  18. //更新一个Flash页
  19. void write_one_page(unsigned char *buf)
  20. {
  21.         boot_page_erase(FlashAddr);                  
  22.         boot_spm_busy_wait();
  23.         for(pagptr = 0; pagptr < SPM_PAGESIZE; pagptr += 2)
  24.         {
  25.                 boot_page_fill(pagptr, buf[pagptr] + (buf[pagptr + 1] << 8));
  26.         }
  27.         boot_page_write(FlashAddr);                 
  28.         boot_spm_busy_wait();            
  29. }

  30. //跳转到用户程序
  31. void quit()
  32. {
  33.         boot_rww_enable();
  34.         (*((void(*)(void))PROG_START))();
  35. }

  36. //写入数据到串口
  37. void WriteCom(unsigned char dat)
  38. {
  39. #if RS485
  40.         RS485Enable();
  41. #endif

  42.         UDR0 = dat;
  43.         while(!(UCSR0A & (1<<TXC0)));
  44.         UCSR0A |= (1<<TXC0);

  45. #if RS485
  46.         RS485Disable();
  47. #endif
  48. }

  49. //等待串口数据
  50. unsigned char WaitCom()
  51. {
  52.         while(!(UCSR0A & (1<<RXC0)));
  53.         return UDR0;
  54. }

  55. //向串口输出字符串
  56. void putstr(const char *str)
  57. {
  58.         while(*str)
  59.                 WriteCom(*str++);
  60.         WriteCom(0x0D);
  61.         WriteCom(0x0A);
  62. }

  63. //CRC校验
  64. void crc16(unsigned char *buf, unsigned char n)
  65. {
  66.         unsigned char j;
  67.         unsigned char i;
  68.         unsigned int crc, t;

  69.         crc = 0;
  70.         for(j = n; j > 0; j--)
  71.         {
  72.             crc = (crc ^ (((unsigned int) *buf) << 8));
  73.                 for(i = 8; i > 0; i--)
  74.                 {
  75.                         t = crc << 1;
  76.                         if(crc & 0x8000)
  77.                                 t = t ^ 0x1021;
  78.                         crc = t;
  79.                 }
  80.                 buf++;
  81.         }
  82.         ch = crc / 256;
  83.         cl = crc % 256;
  84. }

  85. int main(void)
  86. {
  87.         unsigned char cnt;
  88.         unsigned char packNO;
  89.         unsigned char crch, crcl;
  90.         unsigned char li;

  91.         asm volatile("cli": : );

  92.         wdt_enable(WDTO_1S);
  93.         TimerInit();

  94. #if RS485
  95.         DDRREG(RS485PORT) |= (1 << RS485TXEn);
  96.         RS485Disable();
  97. #endif

  98.         ComInit();

  99.         putstr(msg1);

  100.         cnt = TimeOutCnt;
  101.         cl = 0;
  102.         while(1)
  103.         {
  104.                 if(TIFR1 & (1<<OCF1A))   
  105.                 {
  106.                         TIFR1 |= (1 << OCF1A);

  107.                         if(cl == CONNECTCNT)
  108.                                 break;

  109.                         wdt_reset();

  110.                         cnt--;
  111.                         if(cnt == 0)
  112.                         {
  113.                         putstr(msg2);

  114.                                 quit();
  115.                         }
  116.                 }

  117.             if(DataInCom())
  118.             {
  119.                         if(ReadCom() == KEY[cl])
  120.                                 cl++;
  121.                         else
  122.                                 cl = 0;
  123.             }
  124.         }

  125.         putstr(msg3);

  126.         cnt = TimeOutCntC;
  127.         while(1)        
  128.         {
  129.                 if(TIFR1 & (1<<OCF1A))   
  130.                 {
  131.                         TIFR1 |= (1 << OCF1A);
  132.                         WriteCom(XMODEM_RWC) ;

  133.                         wdt_reset();

  134.                         cnt--;
  135.                         if(cnt == 0)
  136.                         {
  137.                         putstr(msg2);
  138.                         quit();
  139.                         }
  140.                 }

  141.                 if(DataInCom())
  142.                 {
  143.                         if(ReadCom() == XMODEM_SOH)
  144.                                 break;
  145.                 }
  146.         }

  147.         TCCR1B = 0;

  148.         packNO = 0;
  149.         bufptr = 0;
  150.         cnt = 0;
  151.         FlashAddr = 0;
  152.         do
  153.         {
  154.                 packNO++;
  155.                 ch =  WaitCom();                     
  156.                 cl = ~WaitCom();
  157.                 if ((packNO == ch) && (packNO == cl))
  158.                 {
  159.                         for(li = 0; li < BUFFERSIZE; li++)     
  160.                         {
  161.                                 buf[bufptr++] = WaitCom();
  162.                         }
  163.                         crch = WaitCom();
  164.                         crcl = WaitCom();
  165.                         crc16(&buf[bufptr - BUFFERSIZE], BUFFERSIZE);
  166.                         if((crch == ch) && (crcl == cl))
  167.                         {
  168.                                 if(FlashAddr < BootStart)            
  169.                                 {
  170.                                         while(bufptr > 0)            
  171.                                         {
  172.                                                 write_one_page(&buf[BUFSIZE - bufptr]);
  173.                                                 FlashAddr += SPM_PAGESIZE;      
  174.                                                 bufptr -= SPM_PAGESIZE;
  175.                                         }
  176.                                 }
  177.                                 else                              
  178.                                 {
  179.                                         bufptr = 0;                        
  180.                                 }

  181.                                 WriteCom(XMODEM_ACK);
  182.                                 cnt = 0;

  183.                                 wdt_reset();

  184.                         }
  185.                         else
  186.                         {
  187.                                 WriteCom(XMODEM_NAK);
  188.                                 cnt++;
  189.                         }
  190.             }
  191. ……………………

  192. …………限于本文篇幅 余下代码请从51黑下载附件…………
复制代码


所有资料51hei提供下载:

AVR单片机BootLoader功能应用.rar (88 KB, 下载次数: 144)




评分

参与人数 1黑币 +50 收起 理由
admin + 50 共享资料的黑币奖励!

查看全部评分

分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏4 分享淘帖 顶 踩
回复

使用道具 举报

沙发
ID:25103 发表于 2018-8-2 23:17 | 只看该作者
谢谢谢谢分享,顶一个!!
回复

使用道具 举报

板凳
ID:85043 发表于 2019-3-15 16:26 | 只看该作者
学习学习,
谢谢谢谢分享,顶一个!!
回复

使用道具 举报

地板
ID:455955 发表于 2019-10-31 23:28 | 只看该作者
多谢,多谢!
回复

使用道具 举报

5#
ID:108023 发表于 2019-11-24 16:07 | 只看该作者
谢谢分享  正需要这个
回复

使用道具 举报

6#
ID:438099 发表于 2020-2-6 22:12 | 只看该作者
这方面资料比较少,谢谢楼主分享
回复

使用道具 举报

7#
ID:738095 发表于 2020-7-3 23:56 | 只看该作者
感谢分享,资料很不错
回复

使用道具 举报

8#
ID:413383 发表于 2020-8-6 22:09 | 只看该作者
感谢分享,资料很不错,以前知道用并口专用编程器,可用ISP, JTAG下载程序,单位有一台特灵的冷机,有一个温度传感器坏了,厂家说不能自已换,要写编号和代码,厂家来的人换上新的温度传感器,用一永磁块靠近传感器,然后用电脑下载程序,后来把坏的传感器拆开,里面是ATmega8a和485通信模块,就奇怪ATmega8a怎么能通过RS232串口下载程序呢,后来查资料才知道要先下载BootLoader引导程序,这个还不会用
回复

使用道具 举报

9#
ID:81138 发表于 2021-1-29 09:30 | 只看该作者
我的Atmega32在Arduino Pro Mini  上无发刷BootLoader引导程序
回复

使用道具 举报

10#
ID:81138 发表于 2021-1-29 10:49 | 只看该作者
我的ATmega328适用次方法不
回复

使用道具 举报

11#
ID:128321 发表于 2024-7-4 14:58 | 只看该作者
我的ATmega328适用次方法不
回复

使用道具 举报

12#
ID:71233 发表于 2024-7-17 11:02 | 只看该作者
这个不知道是否适合于ATmega 16.
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表