Atmega32的Bootloader程序,下面是源码。
在使用时需要将熔丝位进行配置:
1.编程BOOTSZ0、BOOTSZ1和BOOTRST三处,然后通过下载器将BOOTLOADER的hex下载到单片机中,重启后就从BOOTLOADER处运行了。
2.注意本例子使用的是内部8MHz和9600的波特率。
3.启动后可以看到:

然后按下‘D’键进入Bootloader等待下载。
#include <avr/io.h>
#include <avr/boot.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <util/crc16.h>
//IO定義
#define PIN_RXD 0 //PD0
#define PIN_TXD 1 //PD1
//常數定義
#define SPM_PAGESIZE 128
#define DATA_BUFFER_SIZE SPM_PAGESIZE
#define BAUDRATE 9600
#undef F_CPU
#define F_CPU 8000000
//定義xmodem控制字符
#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;
unsigned long FlashAddress; //FLASH地址
#define BootAdd 0x7000 //Boot区的首地址(应用区的最高地址)
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 //完成
#define PROG_START 0x0000
//延時
void delay_ms(unsigned int t)
{
while(t--)
{
_delay_ms(1);
}
}
void write_one_page(void)
{
unsigned char i;
unsigned char *buf;
unsigned int w;
boot_page_erase(FlashAddress);
boot_spm_busy_wait();
buf=&strXMODEM.Xdata[0];
for(i=0;i<SPM_PAGESIZE;i+=2)
{
w=*buf++;
w+=(*buf++)<<8;
boot_page_fill(i,w);
}
boot_page_write(FlashAddress);
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); //结尾发送回车换行
}
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))
{
TIFR|=(1<<OCF0);
timeout--;
}
}while (timeout);
return count;
}
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(void)
{
unsigned char c = 0;
unsigned char i;
unsigned int crc;
WDTCR &= ~(1<<WDE);
DDRD=(1<<PIN_TXD); //串口的输出
GICR = (1<<IVCE);
GICR = (0<<IVCE)|(1<<IVSEL);
cli();
UCSRC = (1<<URSEL)|(1<<UCSZ1)|(1<<UCSZ0);
UBRRL = (F_CPU/BAUDRATE/16-1)%256; //设定波特率
UBRRH = (F_CPU/BAUDRATE/16-1)/256;
UCSRA = 0x00;
UCSRB = (1<<RXEN)|(1<<TXEN);
OCR0 = 28;
TCCR0 = (1<<WGM01)|(1<<CS02)|(0<<CS01)|(0<<CS00);
//CTC模式下,溢出标志是输出比较匹配OCF0,对应的中断是输出比较匹配中断;
put_c(0x0c);
put_c(0x0c);
put_c(0x0c); //超級終端清屏
put_s("User want updata the programme,please touch [d]\r\nOtherwise the MCU run the old programme.\r\n");
get_data(&c,1,3000); //限时3秒,接收一个数据
if ((c=='d')||(c=='D'))
{
STATUS=ST_WAIT_START; //并且数据='d'或'D',进入XMODEM
put_s("Please use xmodem transmit the *.bin file.");
}
else
{
STATUS=ST_OK; //退出Bootloader程序
}
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);
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("Updata the programme success.");
break;
case XMODEM_CAN: //收到取消符CAN
put_c(XMODEM_ACK); //回应PC机
STATUS=ST_OK;
put_s("Warning! It canceled by user to updata the programme.");
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;
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("The programme is out of the flash,cancle transmitted. ");
break;
}
put_c(XMODEM_ACK); //回应已正确收到一个数据块
BlockCount++; //数据块累计加1
}
}
put_s("LET'S GO!");
loop_until_bit_is_set(UCSRA,UDRE); //等待结束提示信息回送完成
GICR = (1<<IVCE);
GICR = (0<<IVCE)|(0<<IVSEL); //将中断向量表迁移到应用程序区头部
/* 无论BootLoader是否使用中断,将中断向量表迁移到应用程序区头部,会增强程序的健壮性*/
boot_rww_enable (); //RWW区读允许,否则无法马上执行用户的应用程序
asm volatile("jmp 0x0000"); //跳转到Flash的0x0000处,执行用户的应用程序
}