搜索
bottom↓
回复: 125

自引导IAP(boot load)的应用设计--armok转贴

[复制链接]

出0入0汤圆

发表于 2004-12-5 08:04:23 | 显示全部楼层 |阅读模式
正在编写M128原理与使用指南中的一小节,请提意见和建议



cma 发表于 2004-8-7 04:12

5.2 自引导IAP的应用设计

ATmega128具备引导加载支持的用户程序自编程功能(In-Sysytem Programming by On-chip Boot Program),它提供了一个真正的由MCU本身自动下载和更新(采用读/写同时“Read-While-Write”进行的方式)程序代码的系统程序自编程更新的机制。利用AVR的这个功能,可以实现在应用编程(IAP)以及实现系统程序的远程自动更新的应用。

IAP的本质就是,MCU可以灵活地运行一个常驻Flash的引导加载程序(Boot Loader Program),实现对用户应用程序的在线自编程更新。引导加载程序的设计可以使用任何的可用的数据接口和相关的协议读取代码,或者从程序存储器中读取代码,然后将代码写入(编程)到Flash存储器中。

引导加载程序有能力读写整个Flash存储器,包括引导加载程序所在的引导加载区本身。引导加载程序还可以对自身进行更新修改,甚至可以将自身删除,使系统的自编程能力消失。引导加载程序区的大小可以由芯片的熔丝位设置,该段程序区还提供两组锁定位,以便用户选择对该段程序区的不同级别的保护。

本节将给出一个实际的的Boot Loader程序,它可以配合Windows中的超级终端程序,采用Xmodem传输协议,通过RS232接口下载更新用户的应用程序。

5.2.1 基本设计思想

1.    Boot Loader程序的设计要点

Boot Loader程序的设计是实现IAP的关键,它必须能过通过一个通信接口,采用某种协议正确的接收数据,再将完整的数据写入到用户程序区中。本例Boot Loader程序的设计要点有:

l    采用ATmega128的USART口实现与PC之间的简易RS232三线通信;

l    采用Xmodem通信协议完成与PC机之间的数据交换;

l    用户程序更新完成后自动转入用户程序执行;

l    Boot Loader程序采用C语言内嵌AVR汇编方式编写,阅读理解方便,可移植性强,代码小于1K字。

2.    Xmodem通信协议

Xmodem协议是一种使用拨号调制解调器的个人计算机通信中广泛使用的异步文件运输协议。这种协议以128字节块的形式传输数据,并且每个块都使用一个校验和过程来进行错误检测。如果接收方关于一个块的校验和与它在发送方的校验和相同时,接收方就向发送方发送一个认可字节。为了便于读者阅读程序,下面简要说明该协议的主要特点,有关Xmoden的完整的协议请参考其它相关的资料。

l    Xmodem的控制字符:<soh> 01H、<eot> 04H、<ack> 06H、<nak> 15H、<can> 18H、<eof> 1AH。

l    Xmodem传输数据块格式:“<soh> <packNO> <255-packNO> <…128个字节的数据块…> <cksum>”。其中<soh>为起始字节;<packNO>为数据块编号字节,每次加一;<255-packNO>是前一字节的反码;接下来是长度为128字节的数据块;最后的<cksum>是128字节数据的CRC校验码,长度为2个字节。

l    接收端收到一个数据块并校验正确时,回送<ack>;接收错误回送<nak>;而回送<can>表示要发送端停止发送。

l    发送端收到<ack>后,可继续发送下一个数据块(packNO+1);而收到<nak>则可再次重发上一个数据块。

l    发送端发送<eot>表示全部数据发送完成。如果最后需要发送的数据不足128个字节,用<eof>填满一个数据块。

l    控制字符“C”有特殊的作用,当发送端收到“C”控制字符时,它回重新开始以CRC校验方式发送数据块(packNO = 1)。

l    每发送一个新的数据块<packNO>加1,加到OxFF后下一个数据块的<packNO>为零。

l    校验方式采用16位CRC校验(X^16 + X^12 + X^5 + 1)。



5.2.2 源程序代码

下面给出的源程序是在ICCAVR中实现的。

/*****************************************************

采用串行接口实现Boot_load应用的实例

华东师大电子系 马 潮 2004.07

Compiler:    ICC-AVR 6.31

Target:    Mega128

Crystal:    16Mhz

Used:        T/C0,USART0

*****************************************************/

#include <iom128v.h>

#define SPM_PAGESIZE 256              //M128的一个Flash页为256字节(128字)

#define BAUD 38400                //波特率采用38400bps

#define CRYSTAL 16000000            //系统时钟16MHz

//计算和定义M128的波特率设置参数

#define BAUD_SETTING (unsigned char)((unsigned long)CRYSTAL/(16*(unsigned long)BAUD)-1)

#define BAUD_H (unsigned char)(BAUD_SETTING>>8)

#define BAUD_L (unsigned char)BAUD_SETTING



#define DATA_BUFFER_SIZE SPM_PAGESIZE        //定义接收缓冲区长度

//定义Xmoden控制字符

#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'

//定义全局变量

const char startupString[]="Type 'd' download, Others run app.
\r\0";

char data[DATA_BUFFER_SIZE];

long address = 0;

//擦除(code=0x03)和写入(code=0x05)一个Flash页

void boot_page_ew(long p_address,char code)

{

    asm("mov r30,r16
"

        "mov r31,r17
"

        "out 0x3b,r18
");            //将页地址放入Z寄存器和RAMPZ的Bit0中

    SPMCSR = code;                //寄存器SPMCSR中为操作码

    asm("spm
");                    //对指定Flash页进行操作

}        

//填充Flash缓冲页中的一个字

void boot_page_fill(unsigned int address,int data)

{

    asm("mov r30,r16
"

        "mov r31,r17
"             //Z寄存器中为填冲页内地址

        "mov r0,r18
"

        "mov r1,r19
");            //R0R1中为一个指令字

    SPMCSR = 0x01;

    asm("spm
");

}

//等待一个Flash页的写完成

void wait_page_rw_ok(void)

{

      while(SPMCSR & 0x40)

     {

         while(SPMCSR & 0x01);

         SPMCSR = 0x11;

         asm("spm
");

     }

}

//更新一个Flash页的完整处理

void write_one_page(void)

{

    int i;

    boot_page_ew(address,0x03);                    //擦除一个Flash页

    wait_page_rw_ok();                            //等待擦除完成

    for(i=0;i<SPM_PAGESIZE;i+=2)                //将数据填入Flash缓冲页中

    {

        boot_page_fill(i, data+(data[i+1]<<8));

    }

    boot_page_ew(address,0x05);                    //将缓冲页数据写入一个Flash页

    wait_page_rw_ok();                            //等待写入完成

}        

//从RS232发送一个字节

void uart_putchar(char c)

{

    while(!(UCSR0A & 0x20));

    UDR0 = c;

}

//从RS232接收一个字节

int uart_getchar(void)

{

    unsigned char status,res;

    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接收一个有效的字节

char uart_waitchar(void)

{

    int c;

    while((c=uart_getchar())==-1);

    return (char)c;

}

//计算CRC

int calcrc(char *ptr, int count)

{

    int crc = 0;

    char i;

   

    while (--count >= 0)

    {

        crc = crc ^ (int) *ptr++ << 8;

        i = 8;

        do

        {

        if (crc & 0x8000)

            crc = crc << 1 ^ 0x1021;

        else

            crc = crc << 1;

        } while(--i);

    }

    return (crc);

}

//退出Bootloader程序,从0x0000处执行应用程序

void quit(void)

{

      uart_putchar('O');uart_putchar('K');

uart_putchar(0x0d);uart_putchar(0x0a);

     while(!(UCSR0A & 0x20));            //等待结束提示信息回送完成

  MCUCR = 0x01;

     MCUCR = 0x00;                    //将中断向量表迁移到应用程序区头部

     RAMPZ = 0x00;                    //RAMPZ清零初始化

     asm("jmp 0x0000
");                //跳转到Flash的0x0000处,执行用户的应用程序

}

//主程序

void main(void)

{

    int i = 0;

    unsigned char timercount = 0;

    unsigned char packNO = 1;

    int bufferPoint = 0;

    unsigned int crc;

//初始化M128的USART0

    UBRR0H = BAUD_H;   

    UBRR0L = BAUD_L;            //Set baud rate

    UCSR0B = 0x18;            //Enable Receiver and Transmitter

    UCSR0C = 0x0E;            //Set frame format: 8data, 2stop bit

//初始化M128的T/C0,15ms自动重载

  OCR0 = 0xEA;

  TCCR0 = 0x0F;   

//向PC机发送开始提示信息

    while(startupString!='\0')

    {

        uart_putchar(startupString);

        i++;

    }

//3秒种等待PC下发“d”,否则退出Bootloader程序,从0x0000处执行应用程序

    while(1)

    {

        if(uart_getchar()== 'd') break;

        if (TIFR & 0x02)                        //timer0 over flow

        {

               if (++timercount > 200) quit();        //200*15ms = 3s

            TIFR = TIFR|0x02;

        }

    }

    //每秒向PC机发送一个控制字符“C”,等待控制字〈soh〉

    while(uart_getchar()!=XMODEM_SOH)        //receive the start of Xmodem

    {

         if(TIFR & 0x02)                    //timer0 over flow

        {

            if(++timercount > 67)                        //wait about 1 second

            {

                uart_putchar(XMODEM_RECIEVING_WAIT_CHAR);    //send a "C"

                timercount=0;

            }

            TIFR=TIFR | 0x02;

        }

    }

    //开始接收数据块

    do

    {

        if ((packNO == uart_waitchar()) && (packNO ==(~uart_waitchar())))

        {    //核对数据块编号正确

            for(i=0;i<128;i++)                //接收128个字节数据

            {

                data[bufferPoint]= uart_waitchar();

                bufferPoint++;   

            }

            crc = (uart_waitchar()<<8);

            crc += uart_waitchar();            //接收2个字节的CRC效验字

            if(calcrc(&data[bufferPoint-128],128)==crc)    //CRC校验验证

            {    //正确接收128个字节数据

                while(bufferPoint >= SPM_PAGESIZE)

                {    //正确接受256个字节的数据

                    write_one_page();            //收到256字节写入一页Flash中

                    address += SPM_PAGESIZE;    //Flash页加1

                    bufferPoint = 0;

                }   

                uart_putchar(XMODEM_ACK);        //正确收到一个数据块

                packNO++;                        //数据块编号加1

            }

            else

            {

                uart_putchar(XMODEM_NAK);        //要求重发数据块

            }

        }

        else

        {

            uart_putchar(XMODEM_NAK);                //要求重发数据块

        }

    }while(uart_waitchar()!=XMODEM_EOT);                //循环接收,直到全部发完

    uart_putchar(XMODEM_ACK);                        //通知PC机全部收到

   

    if(bufferPoint) write_one_page();                //把剩余的数据写入Flash中

    quit();                //退出Bootloader程序,从0x0000处执行应用程序

}



    程序的主体部分采用C高级编写,结构性好,程序的相应部分都给出了比较详细的注释说明,读者非常容易读懂和理解。下面再对程序做进一步的说明。

l    函数“void  write_one_page(void)” 实现了对ATmega128一个Flash页的完整编程处理。当程序从串口正确接收到256个字节后,(ATmega128一个Flash页为128个字),便调用该函数将其写入ATmega128一个Flash页中。函数先将一个指定的Flash页进行擦除;然后将数据填入Flash    的缓冲页中,最后将Flash    缓冲页的数据写入到该指定的Flash页中(详细技术细节见第二章相关内容的介绍)。

l    一个Flash页的擦除、写入,以及填充Flash缓冲页的函数采用内嵌AVR汇编完成,在ICCAVR中,寄存器R16、R17、R18、R19用于传递一个C函数的第1、2个参数(int类型)或第1个乘数(long类型),具体参考ICCAVR应用说明。

l    函数“void quit(void)”的用途是退出Bootloader程序,从Flash的0x0000处执行用户的应用程序。在执行强行跳转指令“jmp 0x0000”前,对寄存器MCUCR的操作是将中断向量地址迁移回应用程序区的头部,因为在ICCAVR环境中编译Bootloader程序时,其自动把中断向量地址迁移到了Bootloader区的头部。为了保证能正确执行用户的程序,在跳转前需要把中断向量地址迁再移回应用程序区的头部。

l    在这段Bootloader程序中使用的硬件资源为T/C0和USART0,用户在编写其应用程序时,应首先对这两个硬件资源相关的寄存器重新做初始化。

l    Bootloader程序占具并住留在Flash的最高1K字空间内,因此实际的应用程序空间为63K字(126K字节),所以用户编写的应用程序不得超出126K字节。同时应将ATmega128的熔丝位BLB12、BLB11的状态设置为“00”,禁止SPM和LPM指令对Bootloader区的读写操作,已确保Bootloader程序不被改写和擦除。

5.2.3 IAP的实现与应用

1.    Bootloader程序的编译与下载

首先在ICCAVR中新建一个工程项目,并按照生成Bootloader程序代码的要求进行正确的设置。打开Project –> Options的Compiler Options设置选项窗口,见图5.1:

l    在Device Configration栏中选定器件ATMega128;

l    选定Use RAMPZ/ELPM项(ATMega128的Flash > 64K字节);

l    Program Type选定为Boot Loader;

l    Boot Size选择1K Words。





图5.1 在ICCAVR中编写Bootloader程序的编译属性设置



正确设置好编译选项后输入C的源代码,然后编译生成.HEX的下载代码程序。

在下载HEX文件前还要对ATmega128芯片的熔丝位进行正确的配置:

l    配置M103C熔丝位,使芯片工作于ATmega128方式;

l    配置BOOTSZ1和BOOTSZ0熔丝位,设定BOOTLOADER区的大小为1024个字,起始首地址为0xFC00;

l    配置BOOTRST熔丝位,设定芯片上电起动从BOOTLOADER区的起始地址处开始,既每次RESET复位后从0xFC00处执行Bootloader程序;

l    下载Bootloader程序的HEX文件;

l    配置LB2和LB1熔丝位,加密程序;

l    配置BLB12和BLB11熔丝位,对BOOTLOADER区进行安全锁定。

特别注意的是,以上对芯片熔丝位的配置以及Bootloader程序的下载,需要由ISP、或JTAG、或并行方式实现,既要实现IAP,首先还需要使用一次非IAP的编程方式来建立IAP的应用环境。



2.    IAP应用

当你按照上面的方法将Bootloader程序下载完成后,就可以使用它来下载你的应用程序了。具体操作如下。

l    编写你的应用程序,编译生成HEX文件;

l    使用HEX2BIN.EXE转换程序,将HEX文件转换成BIN文件;

l    使用普通的RS232电缆将PC机的串口与ATmega128的串口连接;

l    打开WINDOWS中的超级终端软件,正确设置COM口的参数:38400,1,8,无,2,无(使用2位停止位提高通信可靠性);

l    ATmega128上电,在PC超级终端收到“Type 'd' download, Others run app.”的Bootloader程序启动的提示详细;

l    3秒钟内在PC上按下“d”键,通知Bootloader程序转入接收数据并更新应用程序的处理。3秒钟内没有按“d”键,PC超级终端收到“OK”提示,Bootloader程序退出,自动转入执行芯片内原有的用户应用程序(如果有的话,否则再次启动Bootloader程序);

l    当PC超级终端收到“C”(一秒钟一个),说明Bootloader程序转入接收数据和更新应用程序的处理流程,正在等待PC下发数据;

l    在PC超级终端上的工具栏中选择“传送->发送文件”,在发送文件窗口选择协议“Xmodem”,文件栏中选定要下载应用程序的BIN文件,单击发送按钮;

l    此时出现文件发送窗口,显示文件发送的过程和进度,以及是否出错;

l    当文件全部正确发送完成后,PC超级终端收到“OK”提示,Bootloader程序退出,自动转入执行刚更新的用户应用程序。

在ATmega128中烧入这样一个Bootloader程序,建立了IAP后,最基本的开发AVR的环境就简化成“PC+RS232电缆+目标板”。读者在掌握了Bootloader程序编写的原理后,可以编写自己的Bootloader程序,实现系统程序的自动远程网络更新等应用。

AVR的BOOTLOADER功能同其它一些芯片不同,它的BOOTLOADER程序没有固化(固定)在芯片内部(出厂为空),而是需要由用户设计实现(实际上,你第一次下载BOOTLOADER程序还必须使用其它的方式编程,如ISP、JTAG等),因此对一般的用户掌握起来有一定的困难,不如一些其它芯片的固化IAP使用方便。但对高手来讲,可以根据实际需要编写高级、高效、专用的BOOTLOADER程序,如从一个U盘读取数据,更新用户的应用程序;编写一个时间炸_弹,或对用户的密码进行验证,10次不对则将系统程序销毁等等。简单意味着使用方便,但灵活和适应性差,而灵活性需要你具备更高的能力去驾驭它。可能会有一天,在单片机的系统上也出现了“病毒”程序,其原因就是使用了固化的BOOTLOADER程序。由于固化(固定)的程序采用规定公开(开放)的接口,那么用一个带“病毒”的应用程序更新原来的应用程序也就轻而易举了。







(首次发表:21ic bbs)




-----此内容被machao于2005-03-07,22:31:00编辑过

出0入0汤圆

发表于 2004-12-29 18:17:43 | 显示全部楼层
Excellent!I am thirsty for it!:)Thanks a lot to Mr Ma and armok!

出0入0汤圆

发表于 2004-12-30 02:42:55 | 显示全部楼层
不错,谢谢马朝老师

出0入0汤圆

发表于 2005-1-6 17:36:59 | 显示全部楼层
"对寄存器MCUCR的操作是将中断向量地址迁移回应用程序区的头部,因为在ICCAVR环境中编译Bootloader程序时,其自动把中断向量地址迁移到了Bootloader区的头部。为了保证能正确执行用户的程序,在跳转前需要把中断向量地址迁再移回应用程序区的头部"



    对寄存器MCUCR的操作,怎么理解?~

出0入0汤圆

 楼主| 发表于 2005-1-6 21:13:31 | 显示全部楼层
请看M128的器件手册,了解寄存器MCUCR的作用,“对寄存器MCUCR的操作”其字面的含义就是将一个字节的16进制数写入寄存器MCUCR,写入什么数,其作用为何,参看M128的器件手册或《M128》一书中相关部分的介绍。

出0入0汤圆

发表于 2005-1-6 21:26:58 | 显示全部楼层
明白了~



^_^



这个跟M8还有点区别~

出0入0汤圆

 楼主| 发表于 2005-1-6 21:36:55 | 显示全部楼层
M8与M128在Bootloader的功能和使用上是相同的,但由于芯片本身的不同,如FLASH容量的不同等,在一些细节方面有区别,使用时,还要看具体芯片的手册。

出0入0汤圆

发表于 2005-1-6 21:57:23 | 显示全部楼层
恩~~



刚才听“马潮教授在2004年8月20日TLG公司讲座上的发言.mp3”,环境噪音太大,啥时候能现场听一次就好了,最好在北京~呵呵

出0入0汤圆

发表于 2005-1-7 09:27:59 | 显示全部楼层
"在这段Bootloader程序中使用的硬件资源为T/C0和USART0,用户在编写其应用程序时,应首先对这两个硬件资源相关的寄存器重新做初始化。"

若不初始化.则是不是应用原来的配置?

Bootloader程序现在看到的程序内部都没有使用过中断,若在其中使用中断和用户的app程序是否会有冲突或者应该注意些什么?马老师能稍微讲解下么?

出0入0汤圆

 楼主| 发表于 2005-1-7 20:45:02 | 显示全部楼层
不同芯片的硬件资源以及寄存器不同,编写Bootloader程序时当然要做相应的变化,不仅硬件资源相关的寄存器重新做初始化,其它的地方,如FLASH页的长度,Bootloader程序区的大小定义等。例程是提供参考的。有关它的介绍,我在本网还有些介绍。重点要了解其实现的基本方法,在完全弄明白后根据实际情况编写自己的程序。



    在Bootloader程序中可以使用中断,但相应的更加复杂,加上该程序的功能单一,所以一般现在看到的程序内部都没有使用过中断。



    若在其中使用中断,以及它和用户的app程序中的中断之间的转换关系,在AVR中可以通过中断向量迁移处理,使用的好不会冲突。但不提倡在Bootloader程序中使用APP的中断,以及在APP中调用Bootloader程序中的中断。

出0入0汤圆

发表于 2005-1-7 22:12:41 | 显示全部楼层
哈,说到中断搬移我倒想到ICC的一个BUG,以前我就上过当:)



在编译mega16或mega32的boot区的时候(我只试过这两个),ICC还以为MCUCR的低位是控制中断向量的位置呢!

看一下汇编就知道,程序一跳转就开始对MCUCR进行操作,先是写1,然后写2,哈,编译器居然没想到Mega16(32)的IVSEL和IVCE位是在GICR里面,哈哈哈哈~~~!

出0入0汤圆

发表于 2005-1-7 22:29:01 | 显示全部楼层
GCCAVR自带CRC库函数...不过ICC也能看懂一些...正在学习之中...



向马老师致敬!!!

出0入0汤圆

发表于 2005-1-8 08:34:05 | 显示全部楼层
谢谢马老师.我先前以为boot不用中断,是因为spm指令的周期要求<xxx周期内写xxx>,频繁的开关中断又可能影响到程序处理.看来是想错了.

出0入0汤圆

发表于 2005-1-11 10:21:55 | 显示全部楼层
我想问一个问题:能否在APP利用某暂时没用的寄存器(或I/O)设个标志,然后吧指针转道BOOT区,这时状态能保存吗?

出0入0汤圆

 楼主| 发表于 2005-1-11 16:04:49 | 显示全部楼层
看不懂你的问题,“指针转道BOOT区”?数据指针?程序运行的PC指针?请具体举个例子,说明一下你想做什么。

出0入0汤圆

发表于 2005-1-11 19:04:06 | 显示全部楼层
我指的是程序指针

我没有实验板,也没有程序.我只是在够想在程序运行中,能在无上位PC机下重改程序

出0入0汤圆

发表于 2005-1-11 19:26:05 | 显示全部楼层
可能我表述的不清楚.因为我也没具体方案,我是初次了解BOOTLOAD的功能.我想知道它到底有多大的实用功能,我能用到那些.你以前提到的那些,我想只是它的一小部分,而且对我从事的工作没有太大的用处.

  感谢你关心我提的问题.可能您也不好回答(主要我问的太模糊),不过没关系,我只是在构想,有点好奇罢了.

  当然能学的多更好,所以情不自禁问一下.希望能向您多学一点.谢谢了

出0入0汤圆

 楼主| 发表于 2005-1-11 22:11:44 | 显示全部楼层
你还没有真正了解BOOT的作用,使用它的一个目的,就是实现MCU程序在运行中由自己修改自己的程序,BOOT程序可以从任何的通信口,或存储器中获取新的代码,然后将自己内部FLASH的程序更新。它的实质不在于“能在无上位PC机下重改程序”,你的概念糊度了。



    使用BOOT与ISP的一个重要区别是:ISP下载时,MCU不工作(不运行程序),而BOOT是MCU在工作,在运行自己的程序,由自己的程序(BOOT程序)读数据,并写入FLASH中。



注意:顶楼中的一段:

    IAP的本质就是,MCU可以灵活地运行一个常驻Flash的引导加载程序(Boot Loader Program),实现对用户应用程序的在线自编程更新。引导加载程序的设计可以使用任何的可用的数据接口和相关的协议读取代码,或者从程序存储器中读取代码,然后将代码写入(编程)到Flash存储器中。

   引导加载程序有能力读写整个Flash存储器,包括引导加载程序所在的引导加载区本身。

出0入0汤圆

发表于 2005-1-11 22:28:24 | 显示全部楼层
我明白马老师的意思.我对IAP的认识就是从您的M128一小结开始的.

  我感兴趣的是'从程序存储器中读取代码,然后将代码写入(编程)到Flash存储器中。'而这个程序存储器在不借助外电路和器件的情况下如何处理.并作到正确而有意义.

  可能我的描述还存在许多毛病,见凉.谢谢.

出0入0汤圆

 楼主| 发表于 2005-1-11 22:48:13 | 显示全部楼层
在《M128》中,这段文字有变化,为“或者从程序存储器以及其它存储器中读取代码,......”



    从程序存储器中夺取代码的应用如下:由于M128的FLASH比较大,我们可以设计不同的两套运行程序,通常运行第一套,在需要时,运行程序转入BOOT程序,由BOOT程序读取第二套程序,覆盖掉第一套程序,然后由BOOT返回,开始执行第二套程序。



    使用BOOT的功能,高手可以发挥更大的想象空间,比如写个时间炸_弹,当时间炸_弹(第二套程序)爆发后,将原程序“消灭”了,不留痕迹,不怕打官司了:)

出0入0汤圆

发表于 2005-1-11 23:22:16 | 显示全部楼层
谢谢马老师,我又学了新的知识,还需努力.以后再向您请教.

出0入0汤圆

发表于 2005-3-7 17:05:34 | 显示全部楼层
大家有没有注意到BOOT区不能放常量?



C编译的常量都定义在BOOT区以外,如果绝对地址寻址BOOT,则得到结果都0xFF!

出0入0汤圆

 楼主| 发表于 2005-3-7 18:02:21 | 显示全部楼层
“大家有没有注意到BOOT区不能放常量?”这是不对的,如果不使用BOOT,整个Flash空间都可以放常量。只不过M128的Flash为128K,使用时应使用ELMP指令,使用常规的LMP指令,只能读取前64K的内容。请仔细看M128的手册。

出0入0汤圆

发表于 2005-3-8 09:15:16 | 显示全部楼层
我使用ICC 6.30,使用绝对寻址。

代码:unsigned int  crc= *((const unsigned int *)(0xf189));

读出的是0xffff。看汇编代码

+0000F175:   E8E9        LDI     R30,0x89         Load immediate

+0000F176:   EFF1        LDI     R31,0xF1         Load immediate

+0000F177:   90A7        ELPM    R10,Z+           Extended load program memory

而且RAMPZ 是0x01。

别外我设了一个全局常量,结果常量被放在BOOT区外。

const unsigned int kkkk=0x1911;

unsigned int crc = kkkk;

编译后:

+0000F169:   EAEC        LDI     R30,0xAC         Load immediate

+0000F16A:   EEF0        LDI     R31,0xE0         Load immediate

+0000F16B:   90A7        ELPM    R10,Z+           Extended load program memory and postincrement

+0000F16C:   90B6        ELPM    R11,Z            Extended load program memory

常量被放在了0xe0ac。虽然不在BOOT区了,我的BOOT区是4k,是从0xf000开始。

出0入0汤圆

发表于 2005-3-8 09:20:25 | 显示全部楼层
+0000F187:   EC88        LDI     R24,0xC8         Load immediate

+0000F188:   1582        CP      R24,R2           Compare

+0000F189:   F408        BRCC    +0x01            Branch if carry cleared

+0000F18A:   DF9A        RCALL   -0x0066          Relative call subroutine

+0000F18B:   B786        IN      R24,0x36         In from I/O location



0xf189地址是代码。应该不会读出0xffff。

出0入0汤圆

 楼主| 发表于 2005-3-8 12:21:34 | 显示全部楼层
应该先编写一段简单的汇编代码测试一下。在BOOT区中定义一个常量,然后去读出。

出0入0汤圆

发表于 2005-3-8 17:27:59 | 显示全部楼层
应该ICC的问题,我用IAR就没问题,不过IAR也有上64K地址分配出错问题

出0入0汤圆

发表于 2005-3-9 11:48:54 | 显示全部楼层
问题找到,MEG128的datasheet都是用0000-FFFF来描述程度空间,单位是字。

在ICC,IAR中常量的访问都是以字节地址来定址的。

出0入0汤圆

发表于 2005-4-6 11:42:43 | 显示全部楼层
谢谢马老师上面的资料.



我自己做了个M128的bootloader,加上自己用VB做了个上位机软件.感觉用起来比JTAG或ISP方式都爽.

不占任何资源....不过要占点空间...

出0入0汤圆

发表于 2005-4-6 12:03:46 | 显示全部楼层
哦,

我将马老师的bootloader代码移植到我的m8515,没有成功

郁闷

出0入0汤圆

 楼主| 发表于 2005-4-8 12:23:06 | 显示全部楼层
你做了哪些必要的改动?能不能说一下。如果照搬肯定不行。

出0入0汤圆

发表于 2005-4-8 13:02:22 | 显示全部楼层
我做的时候是用到上面三个函数:

//擦除(code=0x03)和写入(code=0x05)一个Flash页

void boot_page_ew(long p_address,char code)

{

    asm("mov r30,r16
"

        "mov r31,r17
"

        "out 0x3b,r18
");            //将页地址放入Z寄存器和RAMPZ的Bit0中

    SPMCSR = code;                //寄存器SPMCSR中为操作码

    asm("spm
");                    //对指定Flash页进行操作

}         

//填充Flash缓冲页中的一个字

void boot_page_fill(unsigned int address,int data)

{

    asm("mov r30,r16
"

        "mov r31,r17
"             //Z寄存器中为填冲页内地址

        "mov r0,r18
"

        "mov r1,r19
");            //R0R1中为一个指令字

    SPMCSR = 0x01;

    asm("spm
");

}

//等待一个Flash页的写完成

void wait_page_rw_ok(void)

{

      while(SPMCSR & 0x40)

     {

         while(SPMCSR & 0x01);

         SPMCSR = 0x11;

         asm("spm
");

     }

}



然后自己编写,串口通讯,页写入等,就是每写一页就要写256个字节.

上位机用VB 做了一个读HEX的文件并一帧是256个字节就行了.



整个程序用了1K 的50%...晶振用7.3728M...波特率用115200bps..

下载64K的程序只需<= 9秒   128K也不用超过20秒



爽!!!不过我调试了一个晚才搞定的...

出0入0汤圆

 楼主| 发表于 2005-4-8 13:27:29 | 显示全部楼层
【31楼】的boy123,你已经掌握了BOOTLOAD的精华了,是否能做些详细的说明,贴上让大家共同学习?

出0入0汤圆

发表于 2005-4-8 16:05:16 | 显示全部楼层
马老师,其实我掌握的也不是什么精华,我只是东抄一些西抄一些,凑在一起就可以用了.

(我2002年接触了一下AVR,去双龙买了几块M8的芯片,并送了一本马老师编的书.那时候例程较少.后来由于项目中不要用到就没有玩了..现在要做一个项目 所以 又捡起来了.一到网上一搜还挺多东东的..)



我的程序主体结构抄了一下国外的  http://www.microsyl.com/megaload/megaload.html

也参照了 M8 ,M16的  http://www.527dz.com/circuit/BootLoad.htm

基本上没有什么区别,只是通讯的接口不同.其实主要的是马老师上面的那三个重要操作(如,写,擦,).

只要改一改就可以适合自己用的了..





参考上面的例程时要注意一下就是SPM寄存器的地址是不同的..不过在头文件里有了

SPMCR  SPMCSR  寄存器不同的芯片有所不同..::

MEGA64和MEGA128的

    SPMCSR = 0x68  

M8---M32的

    SPMCR = 0x57



M128要注意切换高64K和低64K  通过对RAMPZ置1和置0来实现..







PC端可能要自己做会比较好一些(因为自己想怎么样就怎么样)...



在应该程序中也要加入通讯,由PC发指令进入bootloader.

我不建义从bootloader启动..除非是自己调试用..

做成产品一般是从应用程序启动....







#define  UCSRA                  UCSR0A

#define  UCSRB                    UCSR0B

#define  UCSRC                  UCSR0C

#define  UBRRL                          UBRR0L

#define  UBRRH                  UBRR0H

#define  UDR                          UDR0









unsigned char IsChar(void)

{

if(UCSRA & 0x80) return 1;

else return 0;

}

void TxChar(unsigned char ch)

{

while(!(UCSRA & 0x20));  // wait for empty transmit buffer



UDR = ch;                               // write char



}

/*****************************************************************************/

/* Flash Programing Code                                                                                     */

/*****************************************************************************/

//

void FlashLoad(void)

{

TxChar('O')

TxChar('K');  //发送OK



while (1)

          {

          GetPageNumber();



          if (RealPageAddress == 0xffff) return;



          if (GetPage())

                 {

                 WriteFlash();

                         if (CheckFlash())

                         {

                         TxChar('O')

                        TxChar('K');  //发送OK

                        }

                         else

                         {

                          TxChar('R');                //出错

                         }

          else TxChar('R');        //出错

          }

}

}

/*****************************************************************************/



//获取写哪一页



void GetPageNumber(void)

{

unsigned char PageAddressHigh;

unsigned char PageAddressLow;



while(!IsChar());

PageAddressHigh = RxChar();



while(!IsChar());

PageAddressLow = RxChar();



RealPageAddress = (int)((PageAddressHigh << 8) + PageAddressLow);

PageAddress = RealPageAddress << NSHIFTPAGE;



//大于64K控制

if (PageAddressHigh) RAMPZ = 1;

else RAMPZ = 0;



}



/*****************************************************************************/

char GetPage(void)

//获取一页数据

{

unsigned int i;

unsigned char LocalCheckSum = 0;

unsigned char CheckSum = 0;



for (i=0;i<PageByte;i++)

        {

        while(!IsChar());

        PageBuffer=RxChar();

        LocalCheckSum += PageBuffer;

           }

                  

while(!IsChar());

CheckSum = RxChar();

if (LocalCheckSum == CheckSum) return 1;

else return 0;

}



/*****************************************************************************/

//写一页

void WriteFlash(void)

{

unsigned int i;

unsigned int TempInt;



for (i=0;i<PageByte;i+=2)

           {

           TempInt=PageBuffer+(PageBuffer[i+1]<<8);

           fill_temp_buffer(TempInt,i);               }

       

write_page(PageAddress,0x03);       //page ERASE

write_page(PageAddress,0x05);       //page write

   

enableRWW();

}



/*****************************************************************************/

//读取校验

char CheckFlash(void)

{

unsigned int i;                                                       

unsigned int TempInt;

unsigned int TempInt2;



for (i=0;i<PageByte;i+=2)

    {

        TempInt = read_program_memory(PageAddress + i,0x00);

        TempInt2 = PageBuffer +(PageBuffer[i+1]<<8);

        if (TempInt != TempInt2) return 0;

    }

return 1;

}





void main(void)

  {



//ini  M128的USART0

    UCSRC=0x86;                                         //8位数据+1位停止

    UCSRB = 0x18;       //允许串口发送和接收

    UBRRH = 0x00;

    UBRRL = BAUDRATE;

   

    SendChar('S');              //通知PC机,BOOT下载准备就绪

   

         while (1)

           {

             switch (RxChar())

               {

                 case 'W':

                                         FlashLoad();

                                                      TxChar('K');

                                            break;

                 case 'E':

                              SendChar('E');  //通知PC机,进入运用程序区

                              Delay_1ms();

                                                           ExecCode();          //退出bootloader

                             break;

                 case 'I': //读信息

                              TxChar(ChipType);  

                                           TxChar(BootSize);

                 TxChar(BootVer);

                             break;

                 case 'L': //写配置信息

                              

                             break;   

                 case 'R': //读配置信息

                              

                             break;                 

                case 'I': //读信息

                              

                             break;                

                 default :

                

                 break;

               }

           }



  }





实际通讯过程中建议要有接收超时标志,超时了就退出.继续等待命令.以保证通讯顺畅

否则以上的程序可能由于通讯出错,就死掉了..


-----此内容被boy123于2005-04-08,16:41:26编辑过

出0入0汤圆

发表于 2006-1-17 04:57:52 | 显示全部楼层
当我用马老师的程序以及icc上传了bootloader后,在串口只能得到两个字符“Ty", all the rest message from "Type 'd' download, Others run app.
\r\0" were missing, program lock up of course.



Can anyone help me explain why? Thanks in advance.

出0入0汤圆

发表于 2006-1-17 09:43:29 | 显示全部楼层
对不起,上面问题解决了。又有新问题。

不能退出bootloader, 总是下面信息,谢谢了。

Type 'd' download, Others run app.

OK

Type 'd' download, Others run app

出0入0汤圆

发表于 2006-1-23 11:58:22 | 显示全部楼层
CRC好像不通过!!!

不知是X^16 + X^12 + X^5 + 1是否为Xmodem协议的CRC



还是



//计算CRC

int calcrc(char *ptr, int count)

{

    int crc = 0;

    char i;

     

    while (--count >= 0)

    {

        crc = crc ^ (int) *ptr++ << 8;

        i = 8;

        do

        {

        if (crc & 0x8000)

            crc = crc << 1 ^ 0x1021;

        else

            crc = crc << 1;

        } while(--i);

    }

    return (crc);

}

这个函数有不对???

出0入0汤圆

发表于 2006-1-23 12:03:09 | 显示全部楼层
请问有什么不对?



这个程序经过实际使用,验证是没有问题的。



GCCAVR的内置CRC16函数也是一样。

static __inline__ uint16_t _crc_xmodem_update(uint16_t __crc, uint8_t __data);

        多项式Polynomial: x^16 + x^12 + x^5 + 1 (0x1021)

        crc初始值Initial value: 0x0

        专用于XMODEM通讯协议,等效于C写的

        uint16_t crc_xmodem_update (uint16_t crc, uint8_t data)

    {

        int i;

        crc = crc ^ ((uint16_t)data << 8);

        for (i=0; i<8; i++)

        {

            if (crc & 0x8000)

                crc = (crc << 1) ^ 0x1021;

            else

                crc <<= 1;

        }

        return crc;

    }

出0入0汤圆

 楼主| 发表于 2006-1-23 19:35:24 | 显示全部楼层
该问题已经解决,见yyiu002自己的帖子,是他的HEX2BIN程序的问题。问题不在我提供的例程。

出0入0汤圆

发表于 2006-1-24 14:47:51 | 显示全部楼层
我没用那个hex2bin程序,直接发的test.hex文件(不是编译代吗,只是为了验证能从PC机写APP区的FLASH)



用上面的例程CRC确实不通,可能和编译器有关系

我用的是cvavr,换了个标准函数就通过了!!


-----此内容被zuoxia于2006-01-24,15:21:49编辑过

出0入0汤圆

发表于 2006-1-24 15:15:53 | 显示全部楼层
照搬上面的程序,编译平台cvavr ,

只修改了下面3个函数,其他不变

//擦除(code=0x03)和写入(code=0x05)一个Flash页

void boot_page_ew(long p_address,char code)

{

    #asm

       mov r30,r16

       mov r31,r17

       out 0x3b,r18            ;将页地址放入Z寄存器和RAMPZ的Bit0中

    #endasm

    SPMCSR = code;                //寄存器SPMCSR中为操作码

    #asm("spm");                    //对指定Flash页进行操作

}         

//填充Flash缓冲页中的一个字

void boot_page_fill(unsigned int address,int data)

{

    #asm

        mov r30,r16

        mov r31,r17             ;Z寄存器中为填冲页内地址

        mov r0,r18

        mov r1,r19            ;R0R1中为一个指令字

    #endasm

    SPMCSR = 0x01;

    #asm("spm");

}

//等待一个Flash页的写完成

void wait_page_rw_ok(void)

{

      while(SPMCSR & 0x40)

     {

         while(SPMCSR & 0x01);

         SPMCSR = 0x11;

         #asm("spm");

     }

}





编译通过后注入,从boot区启动,Lockbit都设为不禁止,103容丝位勾去掉

用超级终端测试,在不使用HEX2BIN的情况下,发送一个test.hex文件,然后用avrstudio自带的

readflash读出来查看,boot的代码是对的,但app的的每页第一个字为0,后面全0xff!!!

仿真进去查看在调用boot_page_fill时data[]中的数据就是test.hex的数据,证明与PC的Xmodem通信正常.

但是FLASH就是写不进,是不是还有哪个地方需要注意或我遗漏的???

难道真的时编译器的不同?????

出0入0汤圆

 楼主| 发表于 2006-1-25 13:02:13 | 显示全部楼层
又是照搬程序!



我的例子是使用ICC+汇编,你改用CVAVR+汇编。那么你知道ICC和CVAVR它们调用函数时,其参数是如何传递的?有区别吗?请查看相关的帮助资料(在ICC和CVAVR的帮助中都有说明),弄明白后改写三个汇编的程序。



一般的C程序员是做不了好的单片机和嵌入式系统的软件工程师的。我们要求掌握的要多的多。

出0入0汤圆

发表于 2006-1-26 09:07:15 | 显示全部楼层
CVAVR参数是用y,y+1...传递的,但内容和是r16-r19一样!





问题在于CVAVR用R30作为中转寄存器

    #asm

        mov r30,r16  

        mov r31,r17             ;Z寄存器中为填冲页内地址  

        mov r0,r18  

        mov r1,r19            ;R0R1中为一个指令字  

    #endasm

    SPMCSR = 0x01;  

    #asm("spm");        --->>>>>>到这时 r30的值变为0x01了,而不是前面赋的r16的值



所以上面三个汇编程序的Z寄存器老被改掉!!





还有那个CRC,也是的.都是c代吗,汇编出来的结果也不一样.以后用CVAVR要当心了,吸取教训.



修改以后问题解决,感谢马老师指点!!!

出0入0汤圆

发表于 2006-2-16 11:12:17 | 显示全部楼层
参考马老师的M128 IAP后,我对M64 IAP的实现看法



如下图设置:



其它配置按M128进行。



请问马老师这样对不对?





另外我对hex文件转换是用SL的软件,另存为得到BIN文件的。在M128应用下来没问题,操作方便!



我在这感谢马老师的知识成果!!!

出0入0汤圆

发表于 2006-3-4 19:15:47 | 显示全部楼层
马老师你好,我用上面的那个例程,编译下载后,和PC通讯没问题,程序下载也提示成功,但不能运行,请问这是为什么啊?

出0入0汤圆

发表于 2006-5-4 20:02:08 | 显示全部楼层
根据马老师的程序,改写成了mega8的bootloader,未经马老师同意贴在这里,还请见谅。你的学生。

//meg8 bootloader

//BOOTSZ1 BOOTSZ0=00

//meg8 bootloader

//BOOTSZ1 BOOTSZ0=00

#include <iom8v.h>

#define SPM_PAGESIZE 64              //M8的一个Flash页为64字节(32字)



#define BAUD 38400                //波特率采用38400bps

#define CRYSTAL 8000000            //系统时钟8MHz

//计算和定义M128的波特率设置参数

#define BAUD_SETTING (unsigned char)((unsigned long)CRYSTAL/(16*(unsigned long)BAUD)-1)

#define BAUD_H (unsigned char)(BAUD_SETTING>>8)

#define BAUD_L (unsigned char)BAUD_SETTING



#define DATA_BUFFER_SIZE 128        //定义接收缓冲区长度

//定义Xmoden控制字符

#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'

//定义全局变量

const char startupString[]="Type 'd' download, Others run app.
\r\0";

const char loadString[]="download over
\r\0";

char data[DATA_BUFFER_SIZE];

unsigned int address = 0;

//擦除(code=0x03)和写入(code=0x05)一个Flash页

void boot_page_ew(unsigned int p_address,char code)

{

    asm("mov r30,r16
"

        "mov r31,r17
"

      //  "out 0x3b,r18
");     

          );       //将页地址放入Z寄存器和RAMPZ的Bit0中

    SPMCR = code;                   //寄存器SPMCSR中为操作码

    asm("spm
");                    //对指定Flash页进行操作

}         

//填充Flash缓冲页中的一个字

void boot_page_fill(unsigned int address,int data)

{

    asm("mov r30,r16
"

        "mov r31,r17
"             //Z寄存器中为填冲页内地址

        "mov r0,r18
"

        "mov r1,r19
");            //R0R1中为一个指令字

    SPMCR = 0x01;

    asm("spm
");

}

//等待一个Flash页的写完成

void wait_page_rw_ok(void)

{

      while(SPMCR & 0x40)

     {

         while(SPMCR & 0x01);

         SPMCR = 0x11;

         asm("spm
");

     }

}

//更新一个Flash页的完整处理

void write_two_page(void)

{

   //128字节分两页

    int i;

    boot_page_ew(address,0x03);                    //擦除一个Flash页

    wait_page_rw_ok();                            //等待擦除完成

    for(i=0;i<SPM_PAGESIZE;i+=2)                //将数据填入Flash缓冲页中

    {

        boot_page_fill(i, data+(data[i+1]<<8));

    }

    boot_page_ew(address,0x05);                    //将缓冲页数据写入一个Flash页

    wait_page_rw_ok();                            //等待写入完成

       

        //写第2页

        address += SPM_PAGESIZE;    //Flash页加1

         boot_page_ew(address,0x03);                    //擦除一个Flash页

    wait_page_rw_ok();                            //等待擦除完成

    for(i=0;i<SPM_PAGESIZE;i+=2)                //将数据填入Flash缓冲页中

    {

        boot_page_fill(i, data[SPM_PAGESIZE+i]+(data[SPM_PAGESIZE+i+1]<<8));

    }

       

    boot_page_ew(address,0x05);                    //将缓冲页数据写入一个Flash页

    wait_page_rw_ok();                            //等待写入完成

       

}         

//从RS232发送一个字节

void uart_putchar(char c)

{

    while(!(UCSRA & 0x20));

    UDR = c;

}

//从RS232接收一个字节

int uart_getchar(void)

{

    unsigned char status,res;

    if(!(UCSRA & 0x80)) return -1;        //no data to be received

    status = UCSRA;

    res = UDR;

    if (status & 0x1c) return -1;        // If error, return -1

    return res;

}

//等待从RS232接收一个有效的字节

char uart_waitchar(void)

{

    int c;

    while((c=uart_getchar())==-1);

    return (char)c;

}

//计算CRC

int calcrc(char *ptr, int count)

{

    int crc = 0;

    char i;

     

    while (--count >= 0)

    {

        crc = crc ^ (int) *ptr++ << 8;

        i = 8;

        do

        {

        if (crc & 0x8000)

            crc = crc << 1 ^ 0x1021;

        else

            crc = crc << 1;

        } while(--i);

    }

    return (crc);

}

//退出Bootloader程序,从0x0000处执行应用程序

void quit(void)

{

     uart_putchar('O');uart_putchar('K');

     uart_putchar(0x0d);uart_putchar(0x0a);

         uart_putchar(0x0d);uart_putchar(0x0a);

     while(!(UCSRA & 0x20));            //等待结束提示信息回送完成

     MCUCR = 0x01;

    MCUCR = 0x00;                    //将中断向量表迁移到应用程序区头部

   //  RAMPZ = 0x00;                    //RAMPZ清零初始化

     asm("jmp 0x0000
");                //跳转到Flash的0x0000处,执行用户的应用程序

}

//主程序

void main(void)

{

    int i = 0;

    unsigned char timercount = 0;

    unsigned char packNO = 1;

    int bufferPoint = 0;

    unsigned int crc;

//初始化M128的USART0

    UBRRH = BAUD_H;     

    UBRRL = BAUD_L;            //Set baud rate

    UCSRB = 0x18;            //Enable Receiver and Transmitter

  //  UCSRC = 0x06;            //Set frame format: 8data, 2stop bit

        UCSRC=0x8e;//8位数据+2位STOP位,m8与m128的URSEL不同

        //UCSRC=0x86;//8位数据+1位STOP位,m8与m128的URSEL不同

//初始化M8的T/C2,15ms自动重载

  OCR2 = 0xEA;  

  TCCR2 = 0x0F;     

//向PC机发送开始提示信息

    while(startupString!='\0')

    {

        uart_putchar(startupString);

        i++;

    }

//3秒种等待PC下发“d”,否则退出Bootloader程序,从0x0000处执行应用程序



   while(1)

    {

        if(uart_getchar()== 'd') break;

        if (TIFR & 0x80)                        //timer2 over flow

        {

              if (++timercount > 200) quit();        //200*15ms = 3s

            TIFR = TIFR|0x80;

        }

    }

    //每秒向PC机发送一个控制字符“C”,等待控制字〈soh〉

    while(uart_getchar()!=XMODEM_SOH)        //receive the start of Xmodem

    {

         if(TIFR & 0x80)                    //timer2 over flow

        {

            if(++timercount > 67)                        //wait about 1 second

            {

                uart_putchar(XMODEM_RECIEVING_WAIT_CHAR);    //send a "C"

                timercount=0;

            }

            TIFR=TIFR | 0x80;

        }

    }

    //开始接收数据块

    do

    {

        if ((packNO == uart_waitchar()) && (packNO ==(~uart_waitchar())))

        {    //核对数据块编号正确

                //SPM_PAGESIZEW

            for(i=0;i<128;i++)                //接收128个字节数据

            {

                data[bufferPoint]= uart_waitchar();

                bufferPoint++;     

            }

            crc = (uart_waitchar()<<8);

            crc += uart_waitchar();            //接收2个字节的CRC效验字

            if(calcrc(&data[bufferPoint-128],128)==crc)    //CRC校验验证

            {    //正确接收128个字节数据

             while(bufferPoint >= SPM_PAGESIZE)

                    //  while(bufferPoint >= 64) //(m8每页64字节)

                {    //正确接受128个字节的数据

                                       //收到128字节写入2页Flash中

                                        write_two_page();



                                         address += SPM_PAGESIZE;

                    bufferPoint = 0;

                }     

                uart_putchar(XMODEM_ACK);        //正确收到一个数据块

                packNO++;                        //数据块编号加1

            }

            else

            {

                uart_putchar(XMODEM_NAK);        //要求重发数据块

            }

        }

        else

        {

            uart_putchar(XMODEM_NAK);                //要求重发数据块

        }

    }while(uart_waitchar()!=XMODEM_EOT);                //循环接收,直到全部发完

    uart_putchar(XMODEM_ACK);                        //通知PC机全部收到

        i=0;

         while(loadString!='\0')

    {

        uart_putchar(loadString);

        i++;

    }



    quit();                      //退出Bootloader程序,从0x0000处执行应用程序

       

}

出0入0汤圆

发表于 2006-5-8 15:47:21 | 显示全部楼层
楼上的,你改成MEGA之后,能行吗,调试成功吗,我看到个错误啊

  在退出程序guit()中,mega8 和128的退出向量的设计不同啊,mega8是用GICR的头两位!

  有没有认真看啊

出0入0汤圆

发表于 2006-5-21 16:15:30 | 显示全部楼层
弱弱的问:

void boot_page_ew(long p_address,char code)

{

    asm("mov r30,r16
"

        "mov r31,r17
"

        "out 0x3b,r18
");            //将页地址放入Z寄存器和RAMPZ的Bit0中

    SPMCSR = code;                //寄存器SPMCSR中为操作码

    asm("spm
");                    //对指定Flash页进行操作

}         

//填充Flash缓冲页中的一个字

void boot_page_fill(unsigned int address,int data)

{

    asm("mov r30,r16
"

        "mov r31,r17
"             //Z寄存器中为填冲页内地址

        "mov r0,r18
"

        "mov r1,r19
");            //R0R1中为一个指令字

    SPMCSR = 0x01;

    asm("spm
");

}

这两个方法中怎么没有对address的处理呢,比如将address利用汇编语句写入r30,r31z寄存器呢?也没有将address赋值给r16,r17啊?那么z寄存器怎么就得到address的值呢!谢谢!
-----此内容被h2dog于2006-05-21,16:18:56编辑过

出0入0汤圆

发表于 2006-5-22 18:51:31 | 显示全部楼层
为什么没人回答呢?是这个问题太简单了么?(:-(

出0入0汤圆

发表于 2007-1-10 09:12:11 | 显示全部楼层
马老师 ,boy123 脸笑



能不能把

//擦除(code=0x03)和写入(code=0x05)一个Flash页

void boot_page_ew(long p_address,char code)

{

    asm("mov r30,r16
"

        "mov r31,r17
"

        "out 0x3b,r18
");            //将页地址放入Z寄存器和RAMPZ的Bit0中

    SPMCSR = code;                //寄存器SPMCSR中为操作码

    asm("spm
");                    //对指定Flash页进行操作

}   

中的



        "out 0x3b,r18
");            //将页地址放入Z寄存器和RAMPZ的Bit0中

语句说明一下,

还有如何操作超过64K的程序擦除与写入,

谢谢。

出0入0汤圆

发表于 2008-7-11 22:20:21 | 显示全部楼层
mark

出0入0汤圆

发表于 2008-7-11 22:34:30 | 显示全部楼层
mark

出0入0汤圆

 楼主| 发表于 2008-7-12 00:45:20 | 显示全部楼层
楼上2位把老贴翻上来,估计也是有些疑问。实际上我在LZ位的说明中已经谈到了:

一个Flash页的擦除、写入,以及填充Flash缓冲页的函数采用内嵌AVR汇编完成,在ICCAVR中,寄存器R16、R17、R18、R19用于传递一个C函数的第1、2个参数(int类型)或第1个参数(long类型),具体参考ICCAVR应用说明。

都要用BOOTLOAD功能,说明已经不是初学的了,还没有看帮助的习惯?不好。

下面仔细讲讲

//擦除(code=0x03)和写入(code=0x05)一个Flash页
void boot_page_ew(long p_address,char code)
{
    asm("mov r30,r16"
        "mov r31,r17"
        "out 0x3b,r18");            //将页地址放入Z寄存器和RAMPZ的Bit0中
    SPMCSR = code;                //寄存器SPMCSR中为操作码
    asm("spm");                    //对指定Flash页进行操作
}         

1。在ICCAVR中,寄存器R16、R17、R18、R19用于传递一个C函数的第1、2个参数(int类型)或第1个参数(long类型)。因此boot_page_ew(long p_address,char code)的第一个参数p_address(long型)就在R16、R17、R18、R19中。对与M128讲,其页地址为17位长度(128K),因此有效的数据在R16、R17、R18中。(如果其它的AVR,FLASH <64K,就只要使用int型,在R16和R17中)

2。R30和R31是Z寄存器,而Ox3B是RAMPZ寄存器(在I/O空间)。所以前3句嵌入的汇编,就是巧妙的将C函数中的参数放入了相应的寄存器中了。

3。第4句是C语言,直接将第2个参数赋给了SPMCSR,C会自己处理的。

4。第5句嵌入汇编,对对指定Flash页进行code指定的操作。

注意,不同的C,其子程序的参数传递不同,这是对于ICCAVR的,其它的C需要查看其帮助说明。

出0入0汤圆

发表于 2008-7-12 08:18:47 | 显示全部楼层
hehe,谢谢马老师
我只是现在对bootload感兴趣,现在对这个东西还不懂,在学习中....
你的这篇文章说的比较清楚.
所以做了个记号.

出0入0汤圆

发表于 2008-7-23 09:34:59 | 显示全部楼层
我参考的你的的程序,用ICC编译的,我尝试写6页数据均为0x0102,但是结果只写了一页(M16里的0x0000~0x0080地址),
for (page=0;page<6;page++)
  {
   boot_page_ew(page,0x03);//擦除页
   wait_page_rw_ok();                                    //等待擦除完成
         for(counter=0;counter<128;counter+=2)
         {
          boot_page_fill(counter,0x0102);
        }
        boot_page_ew(page,0x05);             //将缓冲页数据写入一个Flash页
        wait_page_rw_ok();              //等待写入完成
  }
这样可以不?能这样写不,我想一次写多页,可以不?

出0入0汤圆

发表于 2008-7-23 09:46:14 | 显示全部楼层
如果要写多页,怎么个修改法才行?

出0入0汤圆

发表于 2008-7-25 11:24:51 | 显示全部楼层
我看45楼已经把马老师的代码改成了mega8的bootloader,请问有谁能改成mega8的bootloader啊?希望大家帮忙,谢谢!

出0入0汤圆

发表于 2008-7-25 13:57:14 | 显示全部楼层
不好意思,刚才写错了,是把代码改成mega16的bootloader,谢谢!

出0入0汤圆

发表于 2008-7-25 15:48:18 | 显示全部楼层
我在45楼mega8的bootloader的基础上,把#include <iom8v.h>改为#include <iom16v.h>;
                                     #define SPM_PAGESIZE 64改为#define SPM_PAGESIZE 128;
之后又在ICCAVR中   在Device Configration栏中选定器件ATMega16;
                   Program Type选定为Boot Loader;
                   Boot Size选择1K Words。

想直接放到M16上,结果在ICC里编译的时候出现了错误,如下:

!W E:\AVR\bootloader\M16.c: [warning] in function 'boot_page_fill', argument 'address' has no use.
!W E:\AVR\bootloader\M16.c: [warning] in function 'boot_page_fill', argument 'data' has no use.
!E M16.s(72): Extraneous character 'r'
!E M16.s(104): Extraneous character 'r'
D:\PROGRA~1\ICCAVR~1.14C\bin\imakew.exe: Error code 1
D:\PROGRA~1\ICCAVR~1.14C\bin\imakew.exe: 'M16.o' removed.
Done: there are error(s). Exit code: 1. Fri Jul 25 15:35:32 2008        

请问是什么错误?麻烦高手帮我看一下,谢谢!

出0入0汤圆

发表于 2008-7-25 16:24:38 | 显示全部楼层
学习中

出0入0汤圆

发表于 2008-7-25 23:57:00 | 显示全部楼层
马老师:
#define BAUD_SETTING (unsigned char)((unsigned long)CRYSTAL/(16*(unsigned long)BAUD)-1)
#define BAUD_H (unsigned char)(BAUD_SETTING>>8)
#define BAUD_L (unsigned char)BAUD_SETTING
        
不理解#define BAUD_SETTING (unsigned char)((unsigned long)CRYSTAL/(16*(unsigned long)BAUD)-1)为什么是unsigned char?

出0入0汤圆

 楼主| 发表于 2008-7-26 02:31:04 | 显示全部楼层
正确应该为,在<M128>书中没有做勘误,后面的书中已经改正。

//计算和定义波特率设置参数
#define BAUD_SETTING (unsigned int)((unsigned long)CRYSTAL/(16*(unsigned long)BAUD)-1)
#define BAUD_H (unsigned char)(BAUD_SETTING>>8)
#define BAUD_L (unsigned char)(BAUD_SETTING)

出0入0汤圆

发表于 2008-7-26 10:34:55 | 显示全部楼层
马老师这么晚还回复我,太感动了

出0入0汤圆

发表于 2008-7-30 14:57:21 | 显示全部楼层
马老师:
这几天空余时间参考了你的M128 Bootloader,但是很遗憾没有成功
我用的是M16 ,4Hz晶振,ICCAVR6.31A编译的,源程序如下,请马老师抽空看下
/*****************************************************
Compiler:    ICCAVR 6.31a
Target:      Mega16
Crystal:     4Mhz
Used:        T/C0,USART0
*****************************************************/

/*
                XMODEM-CRC传输协议
                CRC16校验
l    Xmodem的控制字符:<soh> 01H、<eot> 04H、<ack> 06H、<nak> 15H、<can> 18H、<eof> 1AH。
2    Xmodem传输数据块格式:“<soh> <packNO> <255-packNO> <…128个字节的数据块…> <cksum>”。
     其中<soh>为起始字节;<packNO>为数据块编号字节,每次加一;<255-packNO>是前一字节的反码;
     接下来是长度为128字节的数据块;最后的<cksum>是128字节数据的CRC校验码,长度为2个字节。
3    接收端收到一个数据块并校验正确时,回送<ack>;接收错误回送<nak>;而回送<can>表示要发送端停止发送。
4    发送端收到<ack>后,可继续发送下一个数据块(packNO+1);而收到<nak>则可再次重发上一个数据块。
5    发送端发送<eot>表示全部数据发送完成。如果最后需要发送的数据不足128个字节,用<eof>填满一个数据块。
6    控制字符“C”有特殊的作用,当发送端收到“C”控制字符时,它回重新开始以CRC校验方式发送数据块(packNO = 1)。
7    每发送一个新的数据块<packNO>加1,加到OxFF后下一个数据块的<packNO>为零。
8    校验方式采用16位CRC校验(X^16 + X^12 + X^5 + 1)。
熔丝位设置
BOOTSZ1=0
BOOTSZ0=0  Boot区为1K字节大小。
BOOTRST=0  复位向量位于Boot区。*/
#include <iom16v.h>
//M16的一个Flash页为128字节(64字)
#define SPM_PAGESIZE 128
//波特率采用38400bps
#define BAUD 38400
//系统时钟16MHz
#define CRYSTAL 4000000
//计算和定义M16的波特率设置参数
#define BAUD_SETTING (unsigned int)((unsigned long)CRYSTAL/(16*(unsigned long)BAUD)-1)
#define BAUD_H (unsigned char)(BAUD_SETTING>>8)
#define BAUD_L (unsigned char)BAUD_SETTING

//定义接收缓冲区长度
#define DATA_BUFFER_SIZE SPM_PAGESIZE

//定义Xmoden控制字符
#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'

//定义全局变量
const char startupString[]="Type 'd' download, Others run app. \r\0";
char data[DATA_BUFFER_SIZE];
long address = 0;

//擦除(code=0x03)和写入(code=0x05)一个Flash页
void boot_page_ew(long p_address,char code)
{
    asm("mov r30,r16");
    asm("mov r31,r17");
    asm("out 0x3b,r18");            //将页地址放入Z寄存器和RAMPZ的Bit0中
    SPMCR = code;                //寄存器SPMCSR中为操作码
    asm("spm");                    //对指定Flash页进行操作
}         
//填充Flash缓冲页中的一个字
void boot_page_fill(unsigned int address,int data)
{
    asm("mov r30,r16");
    asm("mov r31,r17");            //Z寄存器中为填冲页内地址
    asm("mov r0,r18");
    asm("mov r1,r19");            //R0R1中为一个指令字
    SPMCR = 0x01;
    asm("spm");
}
//等待一个Flash页的写完成
void wait_page_rw_ok(void)
{
      while(SPMCR & 0x40)
     {
         while(SPMCR & 0x01);
         SPMCR = 0x11;
         asm("spm");
     }
}
//更新一个Flash页的完整处理
void write_one_page(void)
{
    int i;
    boot_page_ew(address,0x03);                    //擦除一个Flash页
    wait_page_rw_ok();                            //等待擦除完成
    for(i=0;i<SPM_PAGESIZE;i+=2)                //将数据填入Flash缓冲页中
    {
        boot_page_fill(i, data+(data[i+1]<<8));
    }
    boot_page_ew(address,0x05);                    //将缓冲页数据写入一个Flash页
    wait_page_rw_ok();                            //等待写入完成
}

//从RS232发送一个字节
void uart_putchar(char c)
{
    while(!(UCSRA & 0x20));
    UDR = c;
}

//从RS232接收一个字节
int uart_getchar(void)
{
    unsigned char status,res;
    if(!(UCSRA & 0x80)) return -1;
    status = UCSRA;
    res = UDR;
    if (status & 0x1c) return -1;        // 如果帧错误;数据溢出;奇偶校验错误, return -1
    return res;
}

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

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

//退出Bootloader程序,从0x0000处执行应用程序
void quit(void)
{
    uart_putchar('O');
        uart_putchar('K');
    uart_putchar(0x0d);
        uart_putchar(0x0a);
    while(!(UCSRA & 0x20));          //等待结束提示信息回送完成
    MCUCR = 0x01;
    MCUCR = 0x00;                     //将中断向量表迁移到应用程序区头部
    //RAMPZ = 0x00;                     //RAMPZ清零初始化
    asm("jmp 0x0000");                //跳转到Flash的0x0000处,执行用户的应用程序
}

//主程序
void main(void)
{
    int i = 0;
    unsigned char timercount = 0;
    unsigned char packNO = 1;
    int bufferPoint = 0;
    unsigned int crc;
//初始化M16的USART0
    UBRRH = BAUD_H;     
    UBRRL = BAUD_L;        //设置波特率
    UCSRB = 0x18;          //接收器与发送器使能
    UCSRC = 0x8E;          //设置帧格式: 8 个数据位, 2 个停止位
//初始化M16的T/C0,15ms自动重载   (CTC模式)
    OCR0 = 0xEA;
    TCCR0 = 0x0D;     
//向PC机发送开始提示信息
    while(startupString!='\0')
    {
        uart_putchar(startupString);
        i++;
    }
//3秒种等待PC下发“d”,否则退出Bootloader程序,从0x0000处执行应用程序
    while(1)
    {
        if(uart_getchar()== 'd') break;
        if (TIFR & 0x02)                        //timer0 over flow
        {
            if (++timercount > 200) quit();     //200*15ms = 3s
            TIFR = TIFR|0x02;
        }
    }
    //每秒向PC机发送一个控制字符“C”,等待控制字〈soh〉
    while(uart_getchar()!=XMODEM_SOH)        //receive the start of Xmodem
    {
         if(TIFR & 0x02)                    //timer0 over flow
        {
            if(++timercount > 67)                        //wait about 1 second
            {
                uart_putchar(XMODEM_RECIEVING_WAIT_CHAR);    //send a "C"
                timercount=0;
            }
            TIFR=TIFR | 0x02;
        }
    }
    //开始接收数据块
    do
    {
        if ((packNO == uart_waitchar()) && (packNO ==(~uart_waitchar())))
        {    //核对数据块编号正确
            for(i=0;i<128;i++)                //接收128个字节数据
            {
                data[bufferPoint]= uart_waitchar();
                bufferPoint++;
            }
            crc = (uart_waitchar()<<8);
            crc += uart_waitchar();            //接收2个字节的CRC效验字
            if(calcrc(&data[bufferPoint-128],128)==crc)    //CRC校验验证
            {    //正确接收128个字节数据
                while(bufferPoint >= SPM_PAGESIZE)
                {    //正确接收128个字节的数据
                    write_one_page();            //收到128字节写入一页Flash中
                    address += SPM_PAGESIZE;    //Flash页加1
                    bufferPoint = 0;
                }     
                uart_putchar(XMODEM_ACK);        //正确收到一个数据块
                packNO++;                        //数据块编号加1
            }
            else
            {
                uart_putchar(XMODEM_NAK);        //要求重发数据块
            }
        }
        else
        {
            uart_putchar(XMODEM_NAK);                //要求重发数据块
        }
    }while(uart_waitchar()!=XMODEM_EOT);                //循环接收,直到全部发完
    uart_putchar(XMODEM_ACK);                        //通知PC机全部收到
     
    if(bufferPoint) write_one_page();                //把剩余的数据写入Flash中
    quit();                //退出Bootloader程序,从0x0000处执行应用程序
}

出0入0汤圆

发表于 2008-7-30 14:59:31 | 显示全部楼层
超级终端显示的是乱码,会是波特率问题吗?

出0入0汤圆

发表于 2008-8-3 02:43:45 | 显示全部楼层
马老师很忙,知道的高手也请指点下,先谢了

出0入0汤圆

发表于 2008-8-3 09:28:58 | 显示全部楼层
我很想帮你,可惜我不会,
密切关注中~~

出0入0汤圆

发表于 2008-8-20 16:16:15 | 显示全部楼层
mark,对ARM的bootload是否有同样的启示?

出0入0汤圆

发表于 2008-8-22 11:34:24 | 显示全部楼层
参考一下,最近在做。

出0入0汤圆

发表于 2008-8-26 17:47:26 | 显示全部楼层
【63楼】 tangliangbo  
参考这个程序不可能成功!
仔细分析一下就会发现其中的原因了!

出0入0汤圆

发表于 2008-9-3 14:31:19 | 显示全部楼层
【69楼】 smallsnail 燕 青
我看了很久,但还是没有发现其中的原因了,请问你成功了没有?
能否指点下,谢谢!!!

出0入0汤圆

发表于 2008-9-3 17:01:08 | 显示全部楼层
【70楼】 tangliangbo
收回那句话,时间长了改不了了!

出0入0汤圆

发表于 2008-9-3 19:02:52 | 显示全部楼层
【71楼】 smallsnail 燕 青
不懂你的意思?
能否讲下你是怎么实现的,非常感谢!

出10入95汤圆

发表于 2008-9-3 21:48:25 | 显示全部楼层
路过,回头再看!

出0入0汤圆

发表于 2008-9-11 21:06:13 | 显示全部楼层
记号

出0入0汤圆

发表于 2008-9-11 23:50:35 | 显示全部楼层
mark

出0入0汤圆

 楼主| 发表于 2008-10-9 16:54:11 | 显示全部楼层
一般不提倡在C里使用这样的方法,不容易掌握,除非你对汇编、C、以及AVR都非常熟悉。

当然,跳转实现可以使用内嵌汇编,但跳过去并不一定你的程序能正常,因为涉及到好多的东西,如中断向量、堆栈等。

出0入0汤圆

发表于 2008-10-13 17:41:07 | 显示全部楼层
马老师:
1、我现在是这样的,我用的是AT91SAM7X128&nbsp;ARM7。建立两个工程:一个bootloader,一个app。代码是一样的(只是跳转的地址不同),在链接的时候把bootloader定位在0x00处,app定位在0x6600处,他们之间相互跳转可以运行,也没涉及到向量表的移动。为什么avr在跳转是要移动向量表呢,这个不是在编译链接的时候就固定了吗?比如:在avr中,做bootloader程序时,向量表会被定位在boot的开头,而在做app时编译链接后向量表会定位在app的开头,app区升级完成后直接跳转不就可以了吗?
2、还有一个就是:在两程序跳转的过程中,SP指针是不是要重新初始化,否则就造成ram的浪费?
3、在做iap的时候还有什么事件要注意的呢

出0入0汤圆

 楼主| 发表于 2008-10-13 19:38:04 | 显示全部楼层
如果BOOTLOAD和APP中都使用某个中断资源,功能相同使用中断服务相同就不需要转移中断向量的。但功能不同时,你就要写两个中断服务了。那么一种方式是中断服务中可以将两个功能的代码都写好,通过判断进行选择。这样也可以不需要转移中断向量。另外就是写两段代码,使用转移中断向量的方法。另外注意:中断响应后跳转到中断向量处不是软件指令实现的,是通过硬件实现的转移。向量表本身是固定的,编译系统只是将中断代码进行定位,然后将转入入口的指令添加中断向量的位置中。

使用BOOTLOAD,向量表的迁移等都是一些高级的功能,需要具备相当的功力才能正确和使用这些功能。

我在《M128》书中提到,希望在BOOTLOAD中尽量不要使用中断,这样就不涉及到中断向量迁移的问题。因为如果你在BOOTLOAD中使用了中断,那么通常必须使用中断向量迁移了。因此你可以看到,书中BOOTLOAD的例子中使用的USART是采用软件查询的方式,没有使用中断。尽管我一直强调,使用USART最好使用中断加缓冲的方式。但在BOOTLOAD里最好还是不使用中断。

因为BOOTLOAD的功能主要是将APP代码写入APP区,这样中断向量及代码就必须在BOOTLOAD区中。而APP中使用的中断的话,服务代码应该同APP在一起的。当然你放在BOOTLOAD区中也可以,但问题是,一旦你需要更新中断服务的代码就麻烦了。正常的BOOTLOAT就是完成一个APP更新的功能,它第一次写入BOOTLOAD中就不改变了。而APP包括APP中使用的中断需要更新的。因此一定要把APP和BOOTLOAD完全独立开。

如果APP和BOOTLOAD代码是完全独立的,那么两部分代码的开始都要做初始化工作,包括重新定义SP的指针。

出0入0汤圆

发表于 2008-12-9 20:17:53 | 显示全部楼层
好资料,学习中

出0入0汤圆

发表于 2008-12-10 08:59:38 | 显示全部楼层
mark

出0入0汤圆

发表于 2008-12-10 13:27:13 | 显示全部楼层
看了很久,有很多的了解
谢谢马朝老师&nbsp;、
但我有一个地方不明白:我第一次下载应用程序成功,然后跳到应用程序处运行,它还会运行自己写的BOOT程序吗?如果不运行了第二次就不能下载应用程序了!
我想是不是在写应用程序时在程序里加一个跳指令到BOOT程序处!??

现在很想知道,望高人指点,

出0入0汤圆

发表于 2008-12-10 15:37:00 | 显示全部楼层
明白了,原来是起动时首先运行BOOT程序,在BOOT程序做了一个判断:如果不想更新应用程序就运行原来的应用程序,不然就接收新的HEX文件来升级应用程序!

真好!再次谢谢马朝老师&nbsp;不然我还不知道从哪里学呢!

出0入0汤圆

发表于 2008-12-11 12:23:05 | 显示全部楼层
从不了解到今天试用成功,很高兴
感谢马朝老师,

出0入0汤圆

发表于 2008-12-26 19:15:36 | 显示全部楼层
简单说一句,IAP就是首先将一个程序通过ISP下载到AVR后面指定的位置,然后用熔丝位设置MCU开始到这个位置执行这个程序,那就可以用串口将新的程序加载到MCU的0X00位置后再跳转到这个位置执行,另外还可以用IAP将数据打到flash空间里,让这个程序可以充分利用flash空间,之于远程不远程就见人见之啦,哈哈!

出0入0汤圆

发表于 2008-12-28 23:55:08 | 显示全部楼层
记号

出0入0汤圆

发表于 2009-5-18 10:26:30 | 显示全部楼层
支持!!

出0入0汤圆

发表于 2009-5-26 21:59:42 | 显示全部楼层
马老师,您好

想请教一下,你的嵌入汇编参数传递是如何确认用哪个寄存器的,您写的按照ICC的编译标准,如果是GCC或者是其他的呢,有没有一种通用参数传递方式呢???

出0入20汤圆

发表于 2009-5-26 22:07:38 | 显示全部楼层
经典帖~

出0入0汤圆

发表于 2009-5-27 17:30:13 | 显示全部楼层
马老师没在??????????

出0入0汤圆

发表于 2009-5-28 18:37:56 | 显示全部楼层
学习了

出0入0汤圆

发表于 2009-8-13 16:15:47 | 显示全部楼层
mark

出0入0汤圆

发表于 2009-8-13 17:53:01 | 显示全部楼层
日期怎么不对??

出0入0汤圆

发表于 2009-8-14 09:06:08 | 显示全部楼层
我使用的编译环境是avr-gcc,bootloader下进去后能传应用程序,但是应用程序启动不起来,熔丝位选择的bootloader首地址是0xF000,这个是字地址,我想问哈各位大侠,我的bootloader下进去的真正地址是0xF000还是0xF000*2呢?

出0入0汤圆

发表于 2009-8-14 11:53:14 | 显示全部楼层
MARK

出0入0汤圆

发表于 2009-8-14 15:11:33 | 显示全部楼层
大家好啊,我QQ328900549,想在线请教个位IAP功能,我头都大了

出0入0汤圆

发表于 2009-10-10 14:43:00 | 显示全部楼层
下载程序不能下载到指定的bootloader 区,原因不知道,有遇到过的吗?帮帮小弟
具体过程如下:

1.在ICCAVR中   在Device Configration栏中选定器件ATMega128;  
                   Program Type选定为Boot Loader;  
                   Boot Size选择1K Words。  
照搬源程序,编译通过。
2.在AVR studio 中,利用其自带的-->program avr ,之后弹出选择下载类型,采用AVRISP MK II ,USB 端口,选择器件atmega128,Fuse  
配置:
1    使芯片工作于ATmega128方式;
l    配置BOOTLOADER区的大小为1024个字,起始首地址为0xFC00;
l    配置BOOTRST熔丝位,设定芯片上电起动从BOOTLOADER区的起始地址处开始
1    检查锁定位默认,均为11111111
l    下载Bootloader程序的HEX文件;

3.显示 program OK!

但是, 我再采用AVR studio,read flash ,读出来的怎么是FFFFFFF呢?判断应该是没有烧录到指定的位置,但是所有的地址中都是FFFF,之后怀疑是下载线的问题,进行了如下验证:

1.在ICCAVR中   在Device Configration栏中选定器件ATMega128;  
                   Program Type选定为Appilication;  
                   Boot Size选择1K Words。
  编译通过。
2.修改熔丝位BOOTRST后,下载成功后再读出来发现与编译生成的是一样的,那就排除了是下载线的问题了。

出0入0汤圆

 楼主| 发表于 2009-10-10 16:38:39 | 显示全部楼层
仔细看LZ位的说明:

1.    Bootloader程序的编译与下载
首先在ICCAVR中新建一个工程项目,并按照生成Bootloader程序代码的要求进行正确的设置。打开Project –> Options的Compiler Options设置选项窗口,见图5.1:
    在Device Configration栏中选定器件ATMega128;
    选定Use RAMPZ/ELPM项(ATMega128的Flash > 64K字节); ====》这个设置了吗?
    Program Type选定为Boot Loader;
    Boot Size选择1K Words。

出0入0汤圆

发表于 2009-10-10 16:48:02 | 显示全部楼层
很好,很好,移植移植~~o~~

出0入0汤圆

发表于 2009-10-10 17:31:36 | 显示全部楼层
回复:选定Use RAMPZ/ELPM项(ATMega128的Flash > 64K字节); ====》这个设置了吗?

已经设置了,可是读出来的所有的地址中都是FFFF,找了一下午原因都不知道怎么回事;
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

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

GMT+8, 2024-4-18 21:39

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

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