当前位置:首页 >> IT/计算机 >>

在mega48上实现bootloader功能

在 mega48 上实现 bootloader 功能,对于高手来说应该不算很难,但是对于新手来说就太不容易了,而悲摧的我,刚练习 AVR,就开始了这一段崎岖的征程。好 在皇天不负苦心人,最终还是被我搞定了。当时就想把整个过程写出来,同时把相关资料和大家分享,可是因为自己有点忙(或者说有点空儿也要偷懒)的缘 故,所以一直没发出来。 刚看到在以前的帖子后面有人需要这个程序,想想自己曾经郁闷抓狂的时候,就忍不住的要把以前那点东西发出来,以能让像我这样的新手少走一些弯路。 好了,搞技术的像我这样废话的人估计不是很多,那我也就打住吧,直接进入正题。 我采用的编程软件是:WinAVR-20071221(最高版本优化时出现过问题,解决办法之一是采用老版本,或将 temp 变量定义为 volatile) ,调试采用 avrstudio 在实现 bootloader 过程中的难点有几个: 1、怎样将 mega48 的复位向量设置到自己想要的位置? 请参考附件中“AVR 绝对定位面面观.doc”的介绍。 顺便说一下,我的作法是在 Makefile 文件的 Linker Options 中增加这一项: LDFLAGS += -Wl,--section-start=.text=0x0b80 0x0b80 以字节为单位,即可将编译后的 bootloader 程序编译在 flash 的指定位置(0x0b80)之后。 2、用 bootloader 下载主程序时,主程序的复位向量(0x0000)中的内容要修改为跳转到 0x0b80,即下载完主程序后,重新上电,程序自动跳转运行 bootloader 程序。这个实现方法就是在下载时修改第一个下载数据,修改为 0xc5bf(可参考 bootloader.c 和 AVR 机器码和汇编指令.doc),而 0xc5bf 刚好是跳转到 0x0b80 的机器码(具体修改值可根据你 bootloader 程序中设定的复位向量位置而改变) 。 3、要下载的主程序最后要更改为二进制文件,我从网上下载的工具软件都不好用,最后使用双龙的下载软件,用它的“编辑”功能保存为二进制文件。注意在保 存为二进制文件的时候,保存地址最好不要大于 0x0b80。 4、用的工具软件为超级终端,波特率等与你的软件设置一致,而“数据流控制”则要选“无”。 大概就这些吧。因为离写好这个程序有段日子了,有些细节也记得不是很清楚,虽然当时碰到了种种的问题,但最难的似乎就是这几个。程序的主体和相 关的软件、参考资料都是免费从网上下载的,非常感谢高手的无私帮助,希望我这篇东西可以对和我一样的新手有些帮助。

顺便加一句:因为做试验时老怀疑通讯延迟的问题,所以实际是通过 232 芯片和电脑串口连接,是全双工的,所以没有 485 的收发使能问题。

#include <avr/io.h> #include <util/delay.h> #include <avr/boot.h> #define SPM_PAGESIZE 64 #define BAUD 4800 #define CRYSTAL 1000000 #define uchar unsigned char #define uint unsigned int #define SET_1(a,b) a|=(1<<b) #define CLR_0(a,b) a&=~(1<<b) //M48 的一个 Flash 页为 64 字节(32 字) //波特率采用 4800bps //系统时钟 1MHz

//计算和定义 M48 的波特率设置参数 #define BAUD_H 0x00 #define BAUD_L 0x0c #define DATA_BUFFER_SIZE 128 //定义接收缓冲区长度,由 XModem 协议规定

//定义 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_RECIEVING_WAIT_CHAR 'C'

//定义全局变量 typedef void (*pFunc)(void); const char startString[]="Type 'd' within 3s";// download. ,Others run app uchar data[DATA_BUFFER_SIZE]; uint address = 0; //uint addr_jump=0; pFunc appReset=(pFunc)0x001a; uint temp=0; //app 区返回地址

//擦除(code=0x03)和写入(code=0x05)一个 Flash 页 /*void boot_page_ew(uint p_address,uchar code) { asm("movw r30,%0\n" : :"r"(p_address) :"r30","r31"); SPMCSR = code; asm("spm\n"); }*/ //将页地址放入 Z 寄存器 //寄存器 SPMCSR 中为操作码 //对指定 Flash 页进行操作

//填充 Flash 缓冲页中的一个字 void boot_page_f(uchar address,uint data) { asm volatile("movw r0,%1;" "mov r30,%0\n" "clr r31" : :"r"(address),"r"(data) :"r0"); SPMCSR = 0x01; asm("spm\n"); }

//等待一个 Flash 页的写完成并重新使能 SPM 指令 void wait_page(void) { while(SPMCSR&0x01); SPMCSR = 0x11; asm("spm\n"); while(SPMCSR&0x01); }

//更新 Flash 页的完整处理 void write_two_page(void) { uchar i; boot_page_erase(address); wait_page(); //等待擦除完成 //擦除一个 Flash 页

//boot_rww_enable ( ); for(i=0;i<SPM_PAGESIZE;i+=2) { boot_page_fill_safe(i,data+((uint)data[i+1]<<8)); } boot_page_write(address); wait_page(); //_delay_ms(200); boot_page_erase(address+SPM_PAGESIZE); wait_page(); for(i=0;i<SPM_PAGESIZE;i+=2) { temp=data[65+i]; temp<<=8; temp+=data[64+i]; boot_page_fill_safe(i,temp); } boot_page_write(address+SPM_PAGESIZE); wait_page(); } //将缓冲页数据写入一个 Flash 页 //等待写入完成 //擦除两个 Flash 页 //将缓冲页数据写入一个 Flash 页 //将数据填入 Flash 缓冲页中

//等待擦除完成 //将数据填入 Flash 缓冲页中

//等待写入完成

//从 RS232 发送一个字节 void uart_putchar(uchar c) { //SET_1(PORTC,PC1); //_delay_us(100); //485 输出使能 //等待电平稳定 //等待 UDR 为空

while(!(UCSR0A & 0x20)); UDR0 = c; while(!(UCSR0A & 0x40)); SET_1(UCSR0A,6); //CLR_0(PORTC,PC1); }

//等待发送完成标志

//从 RS232 接收一个字节 int uart_getchar(void) { uchar status,res; // CLR_0(PORTC,PC1);

//

_delay_us(20); if(!(UCSR0A & 0x80)) return -1; status = UCSR0A; res = UDR0; if (status & 0x1c) return -1; return res; // If error, return -1 //no data to be received

}

//等待从 RS232 接收一个有效的字节 uchar uart_waitchar(void) { int c; while((c=uart_getchar())==-1); return (uchar)c; }

//计算 CRC uint calcrc(uchar *ptr, uchar count) { uint crc = 0; uchar i; while(count--) { crc=crc^((uint)(*ptr)<<8); ptr++; for(i=0;i<8;i++) { if (crc&0x8000) crc=(crc<<1)^0x1021; else crc<<=1; } } return crc; }

//退出 Bootloader 程序,从 0x001a 处执行应用程序 void quit(void) { uart_putchar('O'); uart_putchar('K'); uart_putchar(0x0d); uart_putchar(0x0a);

while(!(UCSR0A&0x20)); _delay_ms(500);

//等待结束提示信息回送完成

//等待结束提示信息回送完成 MCUCR = 0x01; MCUCR = 0x00; //asm("ldi r30,0x00\n" // "ldi r31,0x00\n" //将中断向量表迁移到应用程序区头部

// "ijmp\n"); appReset(); } //跳转到应用程序区

//主程序 int main(void) { uchar i = 0; uchar timercount = 0; uchar packNO = 1; uint crc=0;

//初始化 M48 的 USART0

UCSR0C = 0x0E; UBRR0H = BAUD_H; UBRR0L = BAUD_L; UCSR0B = 0x18;

//Set frame. format: 8data, 2stop bit

//Set baud rate //Enable Receiver and Transmitter

//初始化 M48 的 T/C0,64 分频,普通模式,定时 16.32ms TCCR0B = 0x03;

//初始化通信使能引脚为输出 SET_1(DDRC,DDC1); SET_1(PORTC,PC1); //平时处于接收状态

//向 PC 机发送开始提示信息 while(startString!='\0') { uart_putchar(startString); i++; }

//3 秒种等待 PC 下发"d",否则退出 Bootloader 程序,执行应用程序 while(1) { if(uart_getchar()=='d') {

break; } if (TIFR0 & 0x04) { if (++timercount > 200) quit(); //200*16.32ms = 3.264s //timer0 overflow

TIFR0 |=0x04; //写 1 清除溢出标志 } }

//每秒向 PC 机发送一个控制字符"C",等待控制字〈soh〉 while(uart_getchar()!=0x01) { //wait for the start of Xmodem

if(TIFR0 & 0x04) {

//timer0 over flow

if(++timercount > 62) {

//wait about 1 second

uart_putchar(XMODEM_RECIEVING_WAIT_CHAR); timercount=0; } TIFR0 |=0x04; } }

//send a 'C'

//开始接收数据块 do { if((packNO==uart_waitchar())&&(packNO==(uchar)(~uart_waitchar()))) { //核对数据块编号正确 for(i=0;i<128;i++) { data= uart_waitchar(); } crc = uart_waitchar()<<8; crc += uart_waitchar(); //接收 2 个字节的 CRC 校验字 //CRC 校验验证 //接收 128 个字节数据

if(calcrc(data,128)==crc) {

//正确接收 128 个字节数据 if(address==0) { //更改复位向量,跳转到 boot 区

//addr_jump=data[0]+((uint)data[1]<<8)-0xc000; //appReset=(pFunc)addr_jump; data[0]=0xbf; data[1]=0xc5;

} else if(address>=0xb80) //如果程序超过了应用区大小,输出错误后停止 { uart_putchar('E'); uart_putchar('R'); uart_putchar('R'); uart_putchar('O'); uart_putchar('R'); uart_putchar('!'); uart_putchar(0x0d); uart_putchar(0x0a); while(!(UCSR0A&0x20)); // _delay_ms(1); return 0; } write_two_page(); //写入一页 Flash 中 //Flash 页加 2 //等待结束提示信息回送完成

address=address+2*SPM_PAGESIZE; packNO++;

//数据块编号加 1 //正确收到一个数据块

uart_putchar(XMODEM_ACK); } else { uart_putchar(XMODEM_NAK); } } else { uart_putchar(XMODEM_NAK); } }while(uart_waitchar()!=XMODEM_EOT); uart_putchar(XMODEM_ACK); quit(); return 0; }

//要求重发数据块

//要求重发数据块

//循环接收,直到全部发完 //通知 PC 机全部收到


相关文章:
mega48的MPR121程序.doc
基于mega48 的 MPR121 程序,采用模拟 IIC 协议操作(普通 IO
MEGA48PA.doc
MEGA48PA_信息与通信_工程科技_专业资料。ATMEGA48...o o o 两个具有独立预分频器和比较器功能的 8 ...通过片上 Boot 程序实现系统内编程 真正的同时读写...
基于MEGA48单片机控制的压控恒流源的设计及实现1_图文.pdf
(2010)06-2002502 基于 M EGA48 单片机控制的压控恒流源的设计及实现 Design and implementation of constantcurrent source based on MEGA48 (中南林业...
Mega48_AD.doc
Mega48_AD_信息与通信_工程科技_专业资料。本程序采用MEGA48芯片,GCC程序设计,将采集到的数据用异步串行通信发送出去 本程序采用 MEGA48 芯片,GCC 程序设计,将...
AVR单片机ATMEGA48的C程序举例.doc
} } //把变量放保存在 EEPROM 上/赋值与否并不影响 //比较匹配时 OC0A=0...的按键防抖动功能***/ #include <mega48.h> //外部 PCINT16-PCINT23 共享...
ATmega48P及ATmega88P及168P到ATmega48PA88PA168PA迁徙.pdf
ATmega48P及ATmega88P及168P到ATmega48PA88PA168PA迁徙_电子/电路_工程科技_专业资料。AVR528: Migrating from ATmega48P/88P/168P to ATmega48PA/88PA/168PA...
AVR mega4888在BLDC上的应用.txt
:AVR mega48/88在BLDC上的应用 贴子发表于:2008/6/19 20:08:47 欢迎参与调查 AVR mega48/88在BLDC上的应用 赵振锋 2008-06 深圳 BLDC是Brushless DC motor...
ATmega48P 88P 168P 与 ATmega48PA 88PA 168PA 区别.doc
ATmega48P 88P 168P 与 ATmega48PA 88PA 168PA 区别_电子/电路_工程科技_专业资料。ATmega48P/88P/168P 系列过去的型号都停产了,目前都是新型号 ATmega48PA...
ATmega48P88P168P与ATmega48PA88PA168PA 区别.doc
ATmega48P88P168P与ATmega48PA88PA168PA 区别_
更多相关标签: