日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

AVR之BOOTLOADER技術(shù)詳解

 堆泄露棧溢出 2015-05-07

ATmega128具備引導(dǎo)加載支持的用戶程序自編程功能(In-System Programming by On-chipBoot Program),它提供了一個(gè)真正的由MCU本身自動(dòng)下載和更新(采用讀/寫同時(shí)"Read-While-Write"進(jìn)行的方式)程序代碼的系統(tǒng)程序自編程更新的機(jī)制。利用AVR的這個(gè)功能,可以實(shí)現(xiàn)在應(yīng)用編程(IAP)以及實(shí)現(xiàn)系統(tǒng)程序的遠(yuǎn)程自動(dòng)更新的應(yīng)用。

IAP的本質(zhì)就是,MCU可以靈活地運(yùn)行一個(gè)常駐Flash的引導(dǎo)加載程序(Boot Loader Program),實(shí)現(xiàn)對(duì)用戶應(yīng)用程序的在線自編程更新。引導(dǎo)加載程序的設(shè)計(jì)可以使用任何的可用的數(shù)據(jù)接口和相關(guān)的協(xié)議讀取代碼,或者從程序存儲(chǔ)器中讀取代碼,然后將代碼寫入(編程)到Flash存儲(chǔ)器中。

引導(dǎo)加載程序有能力讀寫整個(gè)Flash存儲(chǔ)器,包括引導(dǎo)加載程序所在的引導(dǎo)加載區(qū)本身。引導(dǎo)加載程序還可以對(duì)自身進(jìn)行更新修改,甚至可以將自身刪除,使系統(tǒng)的自編程能力消失。引導(dǎo)加載程序區(qū)的大小可以由芯片的熔絲位設(shè)置,該段程序區(qū)還提供兩組鎖定位,以便用戶選擇對(duì)該段程序區(qū)的不同級(jí)別的保護(hù)。

本節(jié)將給出一個(gè)實(shí)際的的Boot Loader程序,它可以配合Windows中的超級(jí)終端程序,采用Xmodem傳輸協(xié)議,通過RS232接口下載更新用戶的應(yīng)用程序。

5.2.1 基本設(shè)計(jì)思想

1    Boot Loader程序的設(shè)計(jì)要點(diǎn)

Boot Loader程序的設(shè)計(jì)是實(shí)現(xiàn)IAP的關(guān)鍵,它必須能過通過一個(gè)通信接口,采用某種協(xié)議正確的接收數(shù)據(jù),再將完整的數(shù)據(jù)寫入到用戶程序區(qū)中。本例Boot Loader程序的設(shè)計(jì)要點(diǎn)有:

1)采用ATmega128USART口實(shí)現(xiàn)與PC之間的簡易RS232三線通信;

2 采用Xmodem通信協(xié)議完成與PC機(jī)之間的數(shù)據(jù)交換;

3)用戶程序更新完成后自動(dòng)轉(zhuǎn)入用戶程序執(zhí)行;

4 Boot Loader程序采用C語言內(nèi)嵌AVR匯編方式編寫,閱讀理解方便,可移植性強(qiáng),代碼小于1K字。

2    Xmodem通信協(xié)議

Xmodem協(xié)議是一種使用撥號(hào)調(diào)制解調(diào)器的個(gè)人計(jì)算機(jī)通信中廣泛使用的異步文件運(yùn)輸協(xié)議。這種協(xié)議以128字節(jié)塊的形式傳輸數(shù)據(jù),并且每個(gè)塊都使用一個(gè)校驗(yàn)和過程來進(jìn)行錯(cuò)誤檢測(cè)。如果接收方關(guān)于一個(gè)塊的校驗(yàn)和與它在發(fā)送方的校驗(yàn)和相同時(shí),接收方就向發(fā)送方發(fā)送一個(gè)認(rèn)可字節(jié)。為了便于讀者閱讀程序,下面簡要說明該協(xié)議的主要特點(diǎn),有關(guān)Xmoden的完整的協(xié)議請(qǐng)參考其它相關(guān)的資料。

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

2 Xmodem傳輸數(shù)據(jù)塊格式:"<soh> <packNO> <255-packNO> <...128個(gè)字節(jié)的數(shù)據(jù)塊...> <cksum>"。其中<soh>為起始字節(jié);<packNO>為數(shù)據(jù)塊編號(hào)字節(jié),每次加一;<255-packNO>是前一字節(jié)的反碼;接下來是長度為128字節(jié)的數(shù)據(jù)塊;最后的<cksum>128字節(jié)數(shù)據(jù)的CRC校驗(yàn)碼,長度為2個(gè)字節(jié)。

3)接收端收到一個(gè)數(shù)據(jù)塊并校驗(yàn)正確時(shí),回送<ack>;接收錯(cuò)誤回送<nak>;而回送<can>表示要發(fā)送端停止發(fā)送。

4 發(fā)送端收到<ack>后,可繼續(xù)發(fā)送下一個(gè)數(shù)據(jù)塊(packNO+1);而收到<nak>則可再次重發(fā)上一個(gè)數(shù)據(jù)塊。

5)發(fā)送端發(fā)送<eot>表示全部數(shù)據(jù)發(fā)送完成。如果最后需要發(fā)送的數(shù)據(jù)不足128個(gè)字節(jié),用<eof>填滿一個(gè)數(shù)據(jù)塊。

6 控制字符"C"有特殊的作用,當(dāng)發(fā)送端收到"C"控制字符時(shí),它回重新開始以CRC校驗(yàn)方式發(fā)送數(shù)據(jù)塊(packNO = 1)。

7 每發(fā)送一個(gè)新的數(shù)據(jù)塊<packNO>1,加到OxFF后下一個(gè)數(shù)據(jù)塊的<packNO>為零。

8 校驗(yàn)方式采用16CRC校驗(yàn)(X^16 + X^12 + X^5 + 1)

5.2.2 源程序代碼

下面給出的源程序是在ICCAVR中實(shí)現(xiàn)的。

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

采用串行接口實(shí)現(xiàn)Boot_load應(yīng)用的實(shí)例

華東師大電子系 2004.07

Compiler:    ICC-AVR 6.31

Target:    Mega128

Crystal:    16Mhz

Used:        T/C0,USART0

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

#include <iom128v.h>

#define SPM_PAGESIZE 256              //M128的一個(gè)Flash頁為256字節(jié)(128)

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

#define CRYSTAL 16000000            //系統(tǒng)時(shí)鐘16MHz

//計(jì)算和定義M128的波特率設(shè)置參數(shù)

#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        //定義接收緩沖區(qū)長度

//定義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./n/r/0";

char data[DATA_BUFFER_SIZE];

long address = 0;

//擦除(code=0x03)和寫入(code=0x05)一個(gè)Flash

void boot_page_ew(long p_address,char code)

{

    asm("mov r30,r16/n"

        "mov r31,r17/n"

        "out 0x3b,r18/n");            //將頁地址放入Z寄存器和RAMPZBit0

    SPMCSR = code;                //寄存器SPMCSR中為操作碼

    asm("spm/n");                    //對(duì)指定Flash頁進(jìn)行操作

}        

//填充Flash緩沖頁中的一個(gè)字

void boot_page_fill(unsigned int address,int data)

{

    asm("mov r30,r16/n"

        "mov r31,r17/n"             //Z寄存器中為填沖頁內(nèi)陸址

        "mov r0,r18/n"

        "mov r1,r19/n");            //R0R1中為一個(gè)指令字

    SPMCSR = 0x01;

    asm("spm/n");

}

//等待一個(gè)Flash頁的寫完成

void wait_page_rw_ok(void)

{

      while(SPMCSR & 0x40)

     {

         while(SPMCSR & 0x01);

         SPMCSR = 0x11;

         asm("spm/n");

     }

}

//更新一個(gè)Flash頁的完整處理

void write_one_page(void)

{

    int i;

    boot_page_ew(address,0x03);                    //擦除一個(gè)Flash

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

    for(i=0;i<SPM_PAGESIZE;i+=2)                //將數(shù)據(jù)填入Flash緩沖頁中

    {

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

    }

    boot_page_ew(address,0x05);                    //將緩沖頁數(shù)據(jù)寫入一個(gè)Flash

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

}        

//RS232發(fā)送一個(gè)字節(jié)

void uart_putchar(char c)

{

    while(!(UCSR0A & 0x20));

    UDR0 = c;

}

//RS232接收一個(gè)字節(jié)

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接收一個(gè)有效的字節(jié)

char uart_waitchar(void)

{

    int c;

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

    return (char)c;

}

//計(jì)算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處執(zhí)行應(yīng)用程序

void quit(void)

{

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

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

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

     MCUCR = 0x01;

     MCUCR = 0x00;                    //將中斷向量表遷移到應(yīng)用程序區(qū)頭部

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

     asm("jmp 0x0000/n");        //跳轉(zhuǎn)到Flash0x0000處,執(zhí)行用戶的應(yīng)用程序

}

//主程序

void main(void)

{

    int i = 0;

    unsigned char timercount = 0;

    unsigned char packNO = 1;

    int bufferPoint = 0;

    unsigned int crc;

//初始化M128USART0

    UBRR0H = BAUD_H;    

    UBRR0L = BAUD_L;            //Set baud rate

    UCSR0B = 0x18;            //Enable Receiver and Transmitter

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

//初始化M128T/C0,15ms自動(dòng)重載

  OCR0 = 0xEA;

  TCCR0 = 0x0F;    

//PC機(jī)發(fā)送開始提示信息

    while(startupString[i]!='/0')

    {

        uart_putchar(startupString[i]);

        i++;

    }

//3秒種等待PC下發(fā)"d",否則退出Bootloader程序,從0x0000處執(zhí)行應(yīng)用程序

    while(1)

    {

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

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

        {

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

            TIFR = TIFR|0x02;

        }

    }

    //每秒向PC機(jī)發(fā)送一個(gè)控制字符"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;

        }

    }

    //開始接收數(shù)據(jù)塊

    do

    {

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

        {    //核對(duì)數(shù)據(jù)塊編號(hào)正確

            for(i=0;i<128;i++)             //接收128個(gè)字節(jié)數(shù)據(jù)

            {

                data[bufferPoint]= uart_waitchar();

                bufferPoint++;    

            }

            crc = (uart_waitchar()<<8);

            crc += uart_waitchar();        //接收2個(gè)字節(jié)的CRC效驗(yàn)字

            if(calcrc(&data[bufferPoint-128],128)==crc)    //CRC校驗(yàn)驗(yàn)證

            {    //正確接收128個(gè)字節(jié)數(shù)據(jù)

                while(bufferPoint >= SPM_PAGESIZE)

                {    //正確接受256個(gè)字節(jié)的數(shù)據(jù)

                    write_one_page();         //收到256字節(jié)寫入一頁Flash

                    address += SPM_PAGESIZE;    //Flash頁加1

                    bufferPoint = 0;

                }    

                uart_putchar(XMODEM_ACK);      //正確收到一個(gè)數(shù)據(jù)塊

                packNO++;                      //數(shù)據(jù)塊編號(hào)加1

            }

            else

            {

                uart_putchar(XMODEM_NAK);     //要求重發(fā)數(shù)據(jù)塊

            }

        }

        else

        {

            uart_putchar(XMODEM_NAK);           //要求重發(fā)數(shù)據(jù)塊

        }

    }while(uart_waitchar()!=XMODEM_EOT);          //循環(huán)接收,直到全部發(fā)完

    uart_putchar(XMODEM_ACK);                    //通知PC機(jī)全部收到

    

    if(bufferPoint) write_one_page();        //把剩余的數(shù)據(jù)寫入Flash

    quit();                //退出Bootloader程序,從0x0000處執(zhí)行應(yīng)用程序

}

    程序的主體部分采用C高級(jí)編寫,結(jié)構(gòu)性好,程序的相應(yīng)部分都給出了比較詳細(xì)的注釋說明,讀者非常容易讀懂和理解。下面再對(duì)程序做進(jìn)一步的說明。

1 函數(shù)"void  write_one_page(void)" 實(shí)現(xiàn)了對(duì)ATmega128一個(gè)Flash頁的完整編程處理。當(dāng)程序從串口正確接收到256個(gè)字節(jié)后,(ATmega128一個(gè)Flash頁為128個(gè)字),便調(diào)用該函數(shù)將其寫入ATmega128一個(gè)Flash頁中。函數(shù)先將一個(gè)指定的Flash頁進(jìn)行擦除;然后將數(shù)據(jù)填入Flash的緩沖頁中,最后將Flash 緩沖頁的數(shù)據(jù)寫入到該指定的Flash頁中(詳細(xì)技術(shù)細(xì)節(jié)見第二章相關(guān)內(nèi)容的介紹)。

2 一個(gè)Flash頁的擦除、寫入,以及填充Flash緩沖頁的函數(shù)采用內(nèi)嵌AVR匯編完成,在ICCAVR中,寄存器R16R17、R18、R19用于傳遞一個(gè)C函數(shù)的第1、2個(gè)參數(shù)(int類型)或第1個(gè)乘數(shù)(long類型),具體參考ICCAVR應(yīng)用說明。

3 函數(shù)"void quit(void)"的用途是退出Bootloader程序,從Flash0x0000處執(zhí)行用戶的應(yīng)用程序。在執(zhí)行強(qiáng)行跳轉(zhuǎn)指令"jmp 0x0000"前,對(duì)寄存器MCUCR的操作是將中斷向量地址遷移回應(yīng)用程序區(qū)的頭部,因?yàn)樵?/span>ICCAVR環(huán)境中編譯Bootloader程序時(shí),其自動(dòng)把中斷向量地址遷移到了Bootloader區(qū)的頭部。為了保證能正確執(zhí)行用戶的程序,在跳轉(zhuǎn)前需要把中斷向量地址遷再移回應(yīng)用程序區(qū)的頭部。

4)在這段Bootloader程序中使用的硬件資源為T/C0USART0,用戶在編寫其應(yīng)用程序時(shí),應(yīng)首先對(duì)這兩個(gè)硬件資源相關(guān)的寄存器重新做初始化。

5 Bootloader程序占具并住留在Flash的最高1K字空間內(nèi),因此實(shí)際的應(yīng)用程序空間為63K字(126K字節(jié)),所以用戶編寫的應(yīng)用程序不得超出126K字節(jié)。同時(shí)應(yīng)將ATmega128的熔絲位BLB12、BLB11的狀態(tài)設(shè)置為"00",禁止SPMLPM指令對(duì)Bootloader區(qū)的讀寫操作,已確保Bootloader程序不被改寫和擦除。

523 IAP的實(shí)現(xiàn)與應(yīng)用

1  Bootloader程序的編譯與下載

首先在ICCAVR中新建一個(gè)工程項(xiàng)目,并按照生成Bootloader程序代碼的要求進(jìn)行正確的設(shè)置。打開Project -> OptionsCompiler Options設(shè)置選項(xiàng)窗口,見圖5.1

1 Device Configration欄中選定器件ATMega128;

2 選定Use RAMPZ/ELPM項(xiàng)(ATMega128Flash > 64K字節(jié));

3 Program Type選定為Boot Loader;

4Boot Size選擇1K Words

正確設(shè)置好編譯選項(xiàng)后輸入C的源代碼,然后編譯生成.HEX的下載代碼程序。

在下載HEX文件前還要對(duì)ATmega128芯片的熔絲位進(jìn)行正確的配置:

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

2 配置BOOTSZ1BOOTSZ0熔絲位,設(shè)定BOOTLOADER區(qū)的大小為1024個(gè)字,起始首地址為0xFC00;

3)配置BOOTRST熔絲位,設(shè)定芯片上電起動(dòng)從BOOTLOADER區(qū)的起始地址處開始,即每次RESET復(fù)位后從0xFC00處執(zhí)行Bootloader程序;

4)下載Bootloader程序的HEX文件;

5 配置LB2LB1熔絲位,加密程序;

6)配置BLB12BLB11熔絲位,對(duì)BOOTLOADER區(qū)進(jìn)行安全鎖定。

特別注意的是,以上對(duì)芯片熔絲位的配置以及Bootloader程序的下載,需要由ISP、或JTAG、或并行方式實(shí)現(xiàn),既要實(shí)現(xiàn)IAP,首先還需要使用一次非IAP的編程方式來建立IAP的應(yīng)用環(huán)境。

2   IAP應(yīng)用

當(dāng)你按照上面的方法將Bootloader程序下載完成后,就可以使用它來下載你的應(yīng)用程序了。具體操作如下。

1 編寫你的應(yīng)用程序,編譯生成HEX文件;

2)使用HEX2BIN.EXE轉(zhuǎn)換程序,將HEX文件轉(zhuǎn)換成BIN文件;

3)使用普通的RS232電纜將PC機(jī)的串口與ATmega128的串口連接;

4)打開WINDOWS中的超級(jí)終端軟件,正確設(shè)置COM口的參數(shù):384001,8,無,2,無(使用2位停止位提高通信可靠性);

5ATmega128上電,在PC超級(jí)終端收到"Type 'd' download, Others run app."Bootloader程序啟動(dòng)的提示詳細(xì);

63秒鐘內(nèi)在PC上按下"d"鍵,通知Bootloader程序轉(zhuǎn)入接收數(shù)據(jù)并更新應(yīng)用程序的處理。3秒鐘內(nèi)沒有按"d"鍵,PC超級(jí)終端收到"OK"提示,Bootloader程序退出,自動(dòng)轉(zhuǎn)入執(zhí)行芯片內(nèi)原有的用戶應(yīng)用程序(如果有的話,否則再次啟動(dòng)Bootloader程序);

7)當(dāng)PC超級(jí)終端收到"C"(一秒鐘一個(gè)),說明Bootloader程序轉(zhuǎn)入接收數(shù)據(jù)和更新應(yīng)用程序的處理流程,正在等待PC下發(fā)數(shù)據(jù);

8)在PC超級(jí)終端上的工具欄中選擇"傳送->發(fā)送文件",在發(fā)送文件窗口選擇協(xié)議"Xmodem",文件欄中選定要下載應(yīng)用程序的BIN文件,單擊發(fā)送按鈕;

9  此時(shí)出現(xiàn)文件發(fā)送窗口,顯示文件發(fā)送的過程和進(jìn)度,以及是否出錯(cuò);

10)當(dāng)文件全部正確發(fā)送完成后,PC超級(jí)終端收到"OK"提示,Bootloader程序退出,自動(dòng)轉(zhuǎn)入執(zhí)行剛更新的用戶應(yīng)用程序。

ATmega128中燒入這樣一個(gè)Bootloader程序,建立了IAP后,最基本的開發(fā)AVR的環(huán)境就簡化成"PC+RS232電纜+目標(biāo)板"。讀者在掌握了Bootloader程序編寫的原理后,可以編寫自己的Bootloader程序,實(shí)現(xiàn)系統(tǒng)程序的自動(dòng)遠(yuǎn)程網(wǎng)絡(luò)更新等應(yīng)用。

AVRBOOTLOADER功能同其它一些芯片不同,它的BOOTLOADER程序沒有固化(固定)在芯片內(nèi)部(出廠為空),而是需要由用戶設(shè)計(jì)實(shí)現(xiàn)(實(shí)際上,你第一次下載BOOTLOADER程序還必須使用其它的方式編程,如ISP、JTAG等),因此對(duì)一般的用戶掌握起來有一定的困難,不如一些其它芯片的固化IAP使用方便。但對(duì)高手來講,可以根據(jù)實(shí)際需要編寫高級(jí)、高效、專用的BOOTLOADER程序,如從一個(gè)U盤讀取數(shù)據(jù),更新用戶的應(yīng)用程序;編寫一個(gè)時(shí)間炸彈,或?qū)τ脩舻拿艽a進(jìn)行驗(yàn)證,10次不對(duì)則將系統(tǒng)程序銷毀等等。簡單意味著使用方便,但靈活和適應(yīng)性差,而靈活性需要你具備更高的能力去駕馭它??赡軙?huì)有一天,在單片機(jī)的系統(tǒng)上也出現(xiàn)了"病毒"程序,其原因就是使用了固化的BOOTLOADER程序。由于固化(固定)的程序采用規(guī)定公開(開放)的接口,那么用一個(gè)帶"病毒"的應(yīng)用程序更新原來的應(yīng)用程序也就輕而易舉了。

 

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多