搜索
bottom↓
回复: 11

编写mega48的bootloader程序经验

[复制链接]

出0入0汤圆

发表于 2012-3-31 19:18:09 | 显示全部楼层 |阅读模式
本帖最后由 souching 于 2012-4-1 12:25 编辑

  在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       //M48的一个Flash页为64字节(32字)
#define BAUD 4800                //波特率采用4800bps
#define CRYSTAL 1000000            //系统时钟1MHz
#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的波特率设置参数
#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;   //app区返回地址
uint temp=0;


//擦除(code=0x03)和写入(code=0x05)一个Flash页
/*void boot_page_ew(uint p_address,uchar code)
{
        asm("movw r30,%0\n"
                :
                :"r"(p_address)
                :"r30","r31");          //将页地址放入Z寄存器
        SPMCSR = code;             //寄存器SPMCSR中为操作码
        asm("spm\n");              //对指定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);        //擦除一个Flash页
        wait_page();     //等待擦除完成
        //boot_rww_enable ( );
           for(i=0;i<SPM_PAGESIZE;i+=2)                //将数据填入Flash缓冲页中  
        {                
                boot_page_fill_safe(i,data+((uint)data[i+1]<<8));  
        }
        boot_page_write(address);              //将缓冲页数据写入一个Flash页  
        wait_page();
        //_delay_ms(200);
        boot_page_erase(address+SPM_PAGESIZE);   //擦除两个Flash页  
           wait_page();                                 //等待擦除完成  
           for(i=0;i<SPM_PAGESIZE;i+=2)                         //将数据填入Flash缓冲页中  
    {        
                temp=data[65+i];
                temp<<=8;
                temp+=data[64+i];
                boot_page_fill_safe(i,temp);  
    }      
        boot_page_write(address+SPM_PAGESIZE);      //将缓冲页数据写入一个Flash页  
           wait_page();                                    //等待写入完成                      //等待写入完成
}

//从RS232发送一个字节
void uart_putchar(uchar c)
{
        //SET_1(PORTC,PC1);   //485输出使能
        //_delay_us(100);       //等待电平稳定
        while(!(UCSR0A & 0x20));     //等待UDR为空
        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;     //no data to be received
        status = UCSR0A;
        res = UDR0;
        if (status & 0x1c)  
                return -1;        // If error, return -1
        return res;
}

//等待从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;            //Set frame. format: 8data, 2stop bit
        UBRR0H = BAUD_H;
        UBRR0L = BAUD_L;            //Set baud rate
        UCSR0B = 0x18;            //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)              //timer0 overflow
                {
                        if (++timercount > 200)  
                                quit();      //200*16.32ms = 3.264s
            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);   //send a 'C'
                                timercount=0;
                        }
             TIFR0 |=0x04;
                }
        }

        //开始接收数据块
        do
        {
                if((packNO==uart_waitchar())&&(packNO==(uchar)(~uart_waitchar())))
        {    //核对数据块编号正确
                        for(i=0;i<128;i++)             //接收128个字节数据
                        {
                                data= uart_waitchar();                               
                        }
                   crc = uart_waitchar()<<8;
            crc += uart_waitchar();        //接收2个字节的CRC校验字
                        if(calcrc(data,128)==crc)    //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中
                                address=address+2*SPM_PAGESIZE;    //Flash页加2
                                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);                    //通知PC机全部收到       
        quit();
        return 0;
}

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

阿莫论坛20周年了!感谢大家的支持与爱护!!

曾经有一段真挚的爱情摆在我的面前,我没有珍惜,现在想起来,还好我没有珍惜……

出0入0汤圆

发表于 2012-4-1 08:20:48 | 显示全部楼层
mega48 bootloader ,收藏, 感谢楼主分享。

出0入0汤圆

发表于 2012-4-1 17:34:55 | 显示全部楼层
感谢分享!赞!

出0入0汤圆

发表于 2012-4-1 18:00:11 来自手机 | 显示全部楼层
谢谢分享

出0入0汤圆

发表于 2012-4-6 17:18:25 | 显示全部楼层
研究1个月未果,今天终于发现这个了,万分感谢,下下来研究。

出0入0汤圆

发表于 2012-4-6 21:49:07 | 显示全部楼层
mega48 bootloader

出0入0汤圆

发表于 2012-4-7 08:44:04 | 显示全部楼层
感谢楼主的分享。

出0入0汤圆

发表于 2012-4-7 23:13:34 | 显示全部楼层
刚接触BootLoader,谢谢楼主分享资料和经验;

出0入0汤圆

发表于 2013-6-20 16:19:51 | 显示全部楼层
我最近在做128的升级。打算从U盘中读取数据来升级。但是现在我下载应用区的数据时就会刷掉bootloader中的程序。下载bootloader中的程序就会下载应用区的程序。下载器上的那个  洗骗擦出钩钩我已经去掉了

出0入0汤圆

发表于 2013-6-20 22:05:33 | 显示全部楼层
下下来,研究研究,感谢楼主。

出0入0汤圆

发表于 2013-6-21 10:36:32 | 显示全部楼层
感谢楼主,正好手里有很多m48

出0入0汤圆

发表于 2013-6-21 10:36:50 | 显示全部楼层
感谢楼主,正好手里有很多m48
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片。注意:要连续压缩2次才能满足要求!!】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-8-26 23:23

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

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