新聞中心

        EEPW首頁 > 嵌入式系統 > 設計應用 > AVR BootLoader應用范例

        AVR BootLoader應用范例

        作者: 時間:2016-11-13 來源:網絡 收藏

        /***********************************************
        **** AVR BootLoader應用范例 ***
        **** ***
        **** 作者: HJJourAVR ***
        **** 編譯器:WINAVR20050214 ***
        **** ***
        ****www.OurAVR.com2005.10.17 ***
        ***********************************************/
        //程序參考 馬潮老師的M128 Boot_load應用的實例,ICCAVR版本
        //Stephen更改:9600bps, MEGA16, 8M INTERNAL RC,BOOTSZ1=0, BOOTSZ0=0, BOOTRST=1
        /*
        本程序簡單的示范了AVR ATMEGA16的IAP應用,實現智能升級
        Boot Loader
        XMODEM-CRC傳輸協議
        CRC16校驗

        出于簡化程序考慮,各種數據沒有對外輸出,學習時建議使用JTAG ICE硬件仿真器。
        熔絲位設置
        BOOTSZ1=0
        BOOTSZ0=0 Boot區為1K字(2K字節)大小。
        BOOTRST=0 復位向量位于Boot區。//Stephen: 設BOOTRST=1,允許啟動

        makefile中的程序基地址偏移
        LDFLAGS += -Wl,--section-start=.text=0x3800 //0x3800字節=0x1C00字

        移植程序時,可根據實際大小設定Boot區,但要注意更改makefile和更改BootAdd常數,以及頁寫的大小分配;

        本文引用地址:http://www.104case.com/article/201611/316385.htm

        采用115200bps的通訊速率,升級14KB程序需要耗時約5秒[上位機是WINDOWS 2000的超級終端]

        疑問:
        1 用HEX文件燒錄工作正常,但elf仿真有問題。
        用AVRstudio仿真elf(熔絲設定BOOTRST=0,程序基地址偏移=0x3800)時,所有SRAM變量丟失初始化,
        表現為put_s()的都是亂碼或不可見字符。
        但如果改成應用程序(熔絲設定BOOTRST=1,沒有程序基地址偏移),則put_s()可以正常顯示

        2 XMODEM的結束應答(EOT/CAN)后需加 delay_ms(500)的延時(程序優化,統一寫在跳轉到用戶程序前),
        否則在下面的情況將會無法正常結束XMODEM的傳輸,但其實程序已經升級成功
        特殊情況:用戶程序里面使用了串口,而且波特率較低(如9600bps)且開機即發送大量數據

        */

        #include <avr/io.h>
        #include
        //時鐘定為外部晶體7.3728MHz,F_CPU=7372800 使用USART,115200bps
        #include
        /*
        boot_page_erase ( address )
        擦除FLASH 指定頁,其中address 是以字節為單位的FLASH 地址
        boot_page_fill ( address, data )
        填充BootLoader 緩沖頁,address 為以字節為單位的緩沖頁地址(對mega16 :0~128),
        而data 是長度為兩個字節的字數據,因此調用前address 的增量應為2。
        此時data 的高字節寫入到高地址,低字節寫入到低地址。
        boot_page_write ( address )
        boot_page_write 執行一次的SPM 指令,將緩沖頁數據寫入到FLASH 指定頁。
        boot_rww_enable ( )
        RWW 區讀使能

        根據自編程的同時是否允許讀FLASH 存儲器,FLASH 存儲器可分為兩種類型:
        可同時讀寫區( RWW Read-While-Write ) 和 非同時讀寫區( NRWW NotRead-While-Write)。
        對于MEGA16 RWW 為前14K 字節 NRWW 為后2K 字節。
        引導加載程序對RWW 區編程時MCU 仍可以從NRWW 區讀取指令并執行,而對NRWW 區編程時MCU 處于掛起暫停狀態。
        在對RWW 區自編程(頁寫入或頁擦除)時,由硬件鎖定RWW 區 , RWW 區的讀操作被禁止
        在對RWW 區的編程結束后應當調用boot_rww_enable() 使RWW 區開放。
        */
        #include
        /*
        GCCAVR內置函數,可以不用頭痛CRC16了
        關于CRC的詳細說明,可以查看一下網站:
        http://www.nongnu.org/avr-libc/user-manual/group__avr__crc.html
        函數原形
        static __inline__ uint16_t _crc16_update(uint16_t __crc, uint8_t __data);
        多項式Polynomial: x^16 + x^15 + x^2 + 1 (0xa001)
        crc初始值Initial value: 0xffff
        通常用于磁盤控制器(disk-drive controllers)
        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;
        }
        static __inline__ uint16_t _crc_ccitt_update (uint16_t __crc, uint8_t __data)
        多項式Polynomial: x^16 + x^12 + x^5 + 1 (0x8408)
        crc初始值Initial value: 0xffff
        專用于PPP和IrDA通訊協議
        */

        //管腳定義
        #define PIN_RXD 0 //PD0
        #define PIN_TXD 1 //PD1

        //常數定義
        #define SPM_PAGESIZE 128 //M16的一個Flash頁為128字節(64字)
        #define DATA_BUFFER_SIZE SPM_PAGESIZE //定義接收緩沖區長度
        #define BAUDRATE 9600 //115200 //波特率采用115200bps
        //#define F_CPU 7372800 //系統時鐘7.3728MHz

        //定義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_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; //XMODEM的接收數據結構

        unsigned long FlashAddress; //FLASH地址
        #define BootAdd 0x3800 //Boot區的首地址(應用區的最高地址)
        /* GCC里面地址使用32位長度,適應所有AVR的容量*/

        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 //完成


        //長延時 max 65536ms
        void delay_ms(unsigned int t)
        {
        while(t--)
        {
        _delay_ms(1);
        }
        }

        //更新一個Flash頁的完整處理
        void write_one_page(void)
        {
        unsigned char i;
        unsigned char *buf;
        unsigned int w;
        boot_page_erase(FlashAddress); //擦除一個Flash頁
        boot_spm_busy_wait(); //等待頁擦除完成
        buf=&strXMODEM.Xdata[0];
        for(i=0;i {
        w =*buf++;
        w+=(*buf++)<<8;
        //boot_page_fill(FlashAddress+i, w); //原句
        boot_page_fill(i, w); //只是低7位(128字節/頁)有效
        }
        boot_page_write(FlashAddress); //將緩沖頁數據寫入一個Flash頁
        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); //結尾發送回車換行
        }


        //接收指定字節數據(帶超時控制,Timer0的1ms時基)
        // *ptr 數據緩沖區
        // len 數據長度
        // timeout 超時設定,最長65.536S
        // 返回值 已接收字節數目
        unsigned char get_data(unsigned char *ptr,unsigned char len,unsigned int timeout)
        {
        unsigned count=0;
        do
        {
        if (UCSRA & (1< {
        *ptr++=UDR; //如果接收到數據,讀出
        count++;
        if (count>=len)
        {
        break; //夠了?退出
        }
        }
        if(TIFR & (1< {
        TIFR|=(1< timeout--; //倒計時
        }
        }
        while (timeout);
        return count;
        }

        //計算CRC16
        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() __attribute__((section(".stephen_bootloader"))); //stephen_bootloader
        int main()
        {
        unsigned char c;
        unsigned char i;
        unsigned int crc;
        //考慮到BootLoader可能由應用程序中跳轉過來,所以所用到的模塊需要全面初始化
        DDRA=0x00;
        DDRB=0x00;
        DDRC=0x00;
        PORTA=0xFF; //不用的管腳使能內部上拉電阻
        PORTB=0xFF;
        PORTC=0xFF;
        PORTD=0xFF;
        DDRD=(1< GICR = (1< GICR = (0<asm volatile("cli": : ); //關全局中斷
        //這個BootLoader沒有使用中斷。

        //初始化USART 115200 8, n,1 PC上位機軟件(超級終端)也要設成同樣的設置才能通訊
        UCSRC = (1< UBRRL = (F_CPU/BAUDRATE/16-1)%256; //設定波特率
        UBRRH = (F_CPU/BAUDRATE/16-1)/256;
        UCSRA = 0x00;
        UCSRB = (1< //初始化T/C0,CTC模式,256分頻,1ms自動重載
        OCR0 = 28;
        TCCR0 = (1< //CTC模式下,溢出標志是輸出比較匹配OCF0,對應的中斷是輸出比較匹配中斷;

        //向PC機發送開始提示信息
        put_s("************************************************************");
        //put_s(" ");
        put_s("IC ATMega16 Firmware 智能升級引導程序(Bootloader)VER20070107");
        put_s(" 使用Windows2000/xp 超級終端 串口發送 9600bps,8-N-1 ");
        put_s("如需更新用戶程序,請在3秒鐘內按下[d]鍵,否則3秒后運行用戶程序 ");
        put_s(">");

        //3秒種等待PC下發“d”,否則退出Bootloader程序,從0x0000處執行應用程序
        c=0;
        get_data(&c,1,3000); //限時3秒,接收一個數據
        if ((c==d)||(c==D))
        {
        STATUS=ST_WAIT_START; //并且數據=d或D,進入XMODEM
        put_s("請選擇BIN文件,使用XMODEM協議傳輸,最大14KB");
        }
        else
        {
        STATUS=ST_OK; //退出Bootloader程序
        }

        //進入XMODEM模式
        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); //限時1秒,接收133字節數據
        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(" 用戶程序升級成功!");
        break;
        case XMODEM_CAN: //收到取消符CAN
        put_c(XMODEM_ACK); //回應PC機
        STATUS=ST_OK;
        put_s("警告:用戶取消升級,用戶程序可能不完整");
        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;
        //AVR的16位整數是低位在先,XMODEM的CRC16是高位在先
        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(" 程序已滿,取消傳送!");
        break;
        }
        put_c(XMODEM_ACK); //回應已正確收到一個數據塊
        BlockCount++; //數據塊累計加1
        }
        }

        //退出Bootloader程序,從0x0000處執行應用程序
        put_s("退出Bootloader升級程序!");
        delay_ms(500); //很奇怪,見頂部的說明
        loop_until_bit_is_set(UCSRA,UDRE); //等待結束提示信息回送完成
        GICR = (1< GICR = (0< /* 無論BootLoader是否使用中斷,將中斷向量表遷移到應用程序區頭部,會增強程序的健壯性*/
        boot_rww_enable (); //RWW區讀允許,否則無法馬上執行用戶的應用程序
        asm volatile("jmp 0x0000": : ); //跳轉到Flash的0x0000處,執行用戶的應用程序
        }

        /*
        FLASH程序存儲器的編程方法常見的有以下幾種:

        (1)傳統的并行編程方法;
        (2)通過串行口進行在線編程ISP(In System Programmability) 對器件或電路甚至整個系統進行現場升級或功能重構;
        (3)在運行中,應用程序控制下的應用在線編程IAP (In Applocation Programing) 簡單地說就是在某一個section中運行程序,同時對另一個section進行擦除、讀取、寫入等操作。
        ISP方式相對于傳統方式有了極大的進步,它不需要將芯片從電路板上卸下就可對芯片進行編程,減少了開發時間,簡化了產品制造流程,并大大降低了現場升級的困難。
        而IAP方式是對芯片的編程處于應用程序控制之下,對芯片的編程融入在通信系統當中,通過各種接口(UART/SPI/IIC 等)來升級指定目標芯片的軟件。

        BootLoader 功能介紹
        BootLoader 提供我們通常所說的IAP(In Applicaion Program)功能。
        多數Mega系列單片機具有片內引導程序自編程功能(BootLoader)。
        MCU 通過運行一個常駐FLASH 的BootLoader 程序,利用任何可用的數據接口讀取代碼后寫入自身FLASH存儲器中 ,實現自編程目的

        基本設計思想(參考了馬潮老師的文章)
        1. Boot Loader程序的設計要點
        Boot Loader程序的設計是實現IAP的關鍵,它必須能過通過一個通信接口,采用某種協議正確的接收數據,再將完整的數據寫入到用戶程序區中。本例Boot Loader程序的設計要點有:
        1 采用ATmega16的USART口實現與PC之間的簡易RS232三線通信;
        2 采用Xmodem通信協議完成與PC機之間的數據交換;
        3 用戶程序更新完成后自動轉入用戶程序執行;
        2. Xmodem通信協議
        Xmodem協議是一種使用撥號調制解調器的個人計算機通信中廣泛使用的異步文件運輸協議。
        這種協議以128字節塊的形式傳輸數據,并且每個塊都使用一個校驗和過程來進行錯誤檢測。
        如果接收方關于一個塊的校驗和與它在發送方的校驗和相同時,接收方就向發送方發送一個認可字節。
        為了便于讀者閱讀程序,下面簡要說明該協議的主要特點,有關Xmoden的完整的協議請參考其它相關的資料。
        1 Xmodem的控制字符: 01H、 04H、 06H、 15H、 18H、 1AH、c 43H。
        2 XMODEM有兩種校驗模式:
        一種是一字節的checksum校驗模式,不常用。
        另一種是2字節的CRC16校驗模式(X^16 + X^12 + X^5 + 1),糾錯率高達99.9984%。
        兩種模式的選擇由接收端發送的啟動控制符來決定,啟動發送后不能切換。
        當發送端收到“NAK”控制字符時,它將會開始以checksum校驗方式發送數據塊。
        當發送端收到“C”控制字符時,它將會開始以CRC校驗方式發送數據塊。
        3 Xmodem-CRC傳輸數據塊格式:“ <255-BlockNO> <…128個字節的數據塊…> ”。
        其中為起始字節;
        為數據塊編號字節,每次加一;
        <255-BlockNO>是前一字節的反碼;
        接下來是長度為128字節的數據塊;
        最后的是128字節數據的CRC校驗碼,長度為2個字節,crc16hi,crc16lo。
        5 接收端收到一個數據塊并校驗正確時,回送;接收錯誤回送;而回送表示要發送端停止發送。
        6 BlockNO的初值為0x01,每發送一個新的數據塊加1,加到OxFF后下一個數據塊的為零,即8位無符號數。
        7 發送端收到后,可繼續發送下一個數據塊(BlockNO+1);而收到則可再次重發上一個數據塊。
        8 發送端發送表示全部數據發送完成。如果最后需要發送的數據不足128個字節,用填滿一個數據塊。

        */

        makefile中的程序基地址偏移
        LDFLAGS += -Wl,--section-start=.text=0x3800 //0x3800字節=0x1C00字

        即增加下圖中的27行

        然后在options 中勾擇Use External Makefile 選中剛才改的Makefile


        這是,編譯完成的hex文件大約15k?? 好像是5k

        升級的程序,不能是HEX文件,因為HEX文件是內含格式且每行信息可以不等長的(下圖)。對于這個BOOTLOADER升級程序,只能接
        收原始的二進制文件信息并覆寫到相應的flash區內,因此只能使用BIN格式。將HEX轉為BIN有一個小軟件



        而BIN文件是連續且等長的



        關鍵詞: AVRBootloade

        評論


        技術專區

        關閉
        主站蜘蛛池模板: 太仆寺旗| 贵阳市| 蓝山县| 赤水市| 新丰县| 平定县| 新密市| 涞源县| 亳州市| 九龙县| 二连浩特市| 富蕴县| 奈曼旗| 上虞市| 黑龙江省| 万载县| 白城市| 科技| 涪陵区| 南郑县| 枣阳市| 邯郸县| 大新县| 虹口区| 泸西县| 榆社县| 台东县| 青州市| 布尔津县| 郧西县| 伊宁县| 桦南县| 河源市| 宣威市| 呼玛县| 广昌县| 淮北市| 原平市| 额济纳旗| 柳林县| 太和县|