<var id="fnfpo"><source id="fnfpo"></source></var>
<rp id="fnfpo"></rp>

<em id="fnfpo"><object id="fnfpo"><input id="fnfpo"></input></object></em>
<em id="fnfpo"><acronym id="fnfpo"></acronym></em>
  • <th id="fnfpo"><track id="fnfpo"></track></th>
  • <progress id="fnfpo"><track id="fnfpo"></track></progress>
  • <tbody id="fnfpo"><pre id="fnfpo"></pre></tbody>

  • x
    x

    【轉載】AVR 單片機與GCC 編程----之二

    發布時間:2009-8-3 20:22    發布者:原野之狼
    關鍵詞: AVR , GCC , 單片機
    第二章 存儲器操作


    2.1 AVR 單片機存儲器組織結構


    AVR 系列單片機內部有三種類型的被獨立編址的存儲器,它們分別為:Flash 程序存儲器、內部SRAM 數據存儲器EEPROM 數據存儲器。
    Flash 存儲器為1K~128K 字節,支持并行編程和串行下載,下載壽命通?蛇_10,000 次。


    由于AVR 指令都為16 位或32 位,程序計數器對它按字進行尋址,因此FLASH 存儲器按字組織的,但在程序中訪問FLASH 存儲區時專用指令LPM 可分別讀取指定地址的高低字節。


    寄存器堆(R0~R31)、I/O 寄存器和SRAM 被統一編址。所以對寄存器和I/O 口的操作使用與訪問內部SRAM 同樣的指令。其組織結構如圖2-1 所示。





    圖2-1 AVR SRAM 組織


    32 個通用寄存器被編址到最前,I/O 寄存器占用接下來的64 個地址。從0X0060 開始為內部SRAM。外部SRAM 被編址到內部SRAM 后。


    AVR 單片機的內部有64~4K 的EEPROM 數據存儲器,它們被獨立編址,按字節組織。擦寫壽命可達100,000 次。


    2.2 I/O 寄存器操作


    I/O 專用寄存器(SFR)被編址到與內部SRAM 同一個地址空間,為此對它的操作和SRAM 變量操作類似。
    SFR 定義文件的包含:
    #include
    io.h 文件在編譯器包含路徑下的avr 目錄下,由于AVR 各器件間存在同名寄存器地址有不同的問題,io.h 文件不直接定義SFR 寄存器宏,它根據在命令行給出的 –mmcu 選項再包含合適的 ioxxxx.h 文件。


    在器件對應的ioxxxx.h 文件中定義了器件SFR 的預處理宏,在程序中直接對它賦值或引用的方式讀寫SFR,如:


    PORTB=0XFF;
    Val=PINB;


    從io.h 和其總包含的頭文件sfr_defs.h 可以追溯宏PORTB 的原型
    在io2313.h 中定義:
    #define PORTB _SFR_IO8(0x18)
    在sfr_defs.h 中定義:


    #define _SFR_IO8(io_addr) _MMIO_BYTE((io_addr) + 0x20)
    #define _MMIO_BYTE(mem_addr) (*(volatile uint8_t *)(mem_addr))


    這樣PORTB=0XFF; 就等同于 *(volatile unsigned char *)(0x38)=0xff;
    0x38 在器件AT90S2313 中PORTB 的地址
    對SFR 的定義宏進一步說明了SFR 與SRAM 操作的相同點。
    關鍵字volatile 確保本條指令不會因C 編譯器的優化而被省略。


    2.3 SRAM 內變量的使用


    一個沒有其它屬性修飾的C 變量定義將被指定到內部SRAM,avr-libc 提供一個整數類型定義文件inttype.h,其中定義了常用的整數類型如下表:


    定義值 長度(字節) 值范圍
    int8_t 1 -128~127
    uint8_t 1 0~255
    int16_t 2 -32768~32767
    uint16_t 2 0~65535
    int32_t 4 -2147483648~2147483647
    uint32_t 4 0~4294967295
    int64_t 8 -9.22*10^18~-9.22*10^18
    uint64_t 8 0~1.844*10^19


    根據習慣,在程序中可使用以上的整數定義。
    定義、初始化和引用


    如下示例:


    uint8_t val=8; 定義了一個SRAM 變量并初始化成8
    val=10; 改變變量值
    const uint8_t val=8; 定義SRAM 區常量
    register uint8_t val=10; 定義寄存器變量


    2.4 在程序中訪問FLASH 程序存儲器


    avr-libc 支持頭文件:pgmspace.h
    #include < avr/pgmspace.h >
    在程序存儲器內的數據定義使用關鍵字 __attribute__((__progmem__))。在pgmspace.h
    中它被定義成符號 PROGMEM。


    1. FLASH 區整數常量應用


    定義格式:
    數據類型 常量名 PROGMEM = 值 ;
    如:
    char val8 PROGMEM = 1 ;
    int val16 PROGMEM = 1 ;
    long val32 PROGMEM =1 ;
    對于不同長度的整數類型 avr-libc 提供對應的讀取函數:
    pgm_read_byte(prog_void * addr)


    pgm_read-word(prg_void *addr)
    pgm_read_dword(prg_void* addr)
    另外在pgmspace.h 中定義的8 位整數類型 prog_char prog_uchar 分別指定在FLASH
    內的8 位有符號整數和8 位無符號整數。應用方式如下:


    char ram_val; //ram 內的變量
    const prog_char flash_val = 1; //flash 內常量
    ram_val=pgm_read_byte(&flash_val); //讀flash 常量值到RAM 變量


    對于應用程序FLASH 常量是不可改變的,因此定義時加關鍵字const 是個好的習慣。


    2. FLASH 區數組應用:


    定義:
    const prog_uchar flash_array[] = {0,1,2,3,4,5,6,7,8,9}; //定義
    另外一種形式
    const unsigned char flash_array[] RROGMEM = {0,1,2,3,4,5,6,7,8,9};
    讀取示例:
    unsigend char I, ram_val;
    for(I=0 ; I<10 ;I ++) // 循環讀取每一字節
    {
    ram_val = pgm_read_byte(flash_array + I);
    … … //處理
    }


    2. FLASH 區字符串常量的應用


    全局定義形式:
    const char flash_str[] PROGMEM = “Hello, world!”;
    函數內定義形式:
    const char *flash_str = PSTR(“Hello, world!”);
    以下為一個FLASH 字符串應用示例
    #include
    #include
    #include
    const char flash_str1[] PROGMEM = “全局定義字符串”;
    int main(void)
    {
    int I;
    char *flash_str2=PSTR(“函數內定義字符串”);
    while(1)
    {
    scanf(“%d”,&I);
    printf_P(flash_str1);
    printf(“\n”);
    printf_P(flash_str2);
    printf(“\n”);
    }
    }


    2.5 EEPROM 數據存儲器操作


    #include EEPROM.h>
    頭文件聲明了avr-libc 提供的操作EEPROM 存儲器的API 函數。
    這些函數有:
    EEPROM_is_ready() //EEPROM 忙檢測(返回EEWE 位)
    EEPROM_busy_wait() //查詢等待EEPROM 準備就緒
    uint8_t EEPROM_read_byte (const uint8_t *addr) //從指定地址讀一字節
    uint16_t EEPROM_read_word (const uint16_t *addr) //從指定地址一字
    void EEPROM_read_block (void *buf, const void *addr, size_t n) //讀塊
    void EEPROM_write_byte (uint8_t *addr, uint8_t val) //寫一字節至指定地址
    void EEPROM_write_word (uint16_t *addr, uint16_t val) //寫一字到指定地址
    void EEPROM_write_block (const void *buf, void *addr, size_t n)//寫塊


    在程序中對EEPROM 操作有兩種方式


    方式一:直接指定EERPOM 地址
    示例:
    /*此程序將0xaa 寫入到EEPROM 存儲器 0 地址處,
    再從0 地址處讀一字節賦給RAM 變量val */
    #include
    #include EEPROM.h>
    int main(void)
    {
    unsigned char val;
    EEPROM_busy_wait(); //等待EEPROM 讀寫就緒
    EEPROM_write_byte(0,0xaa); //將0xaa 寫入到EEPORM 0 地址處
    EEPROM_busy_wait();
    val=EEPROM_read_byte(0); //從EEPROM 0 地址處讀取一字節賦給RAM 變量val
    while(1);
    }


    方式二:先定義EEPROM 區變量法
    示例:
    #include
    #include EEPROM.h>
    unsigned char val1 __attribute__((section(".EEPROM")));//EEPROM 變量定義方式
    int main(void)
    {
    unsigned char val2;
    EEPROM_busy_wait();
    EEPROM_write_byte (&val1, 0xAA); /* 寫 val1 */
    EEPROM_busy_wait();
    val2 = EEPROM_read_byte(&val1); /* 讀 val1 */
    while(1);
    }
    在這種方式下變量在EEPROM 存儲器內的具體地址由編譯器自動分配。相對方式一,數據在EEPROM 中的具體位置是不透明的。
    EEPROM 變量賦的初始值,編譯時被分配到.EEPROM 段中,可用avr-objcopy 工具從.elf文件中提取并產生ihex 或binary 等格式的文件。


    2.6 avr-gcc 段(section)與再定位(relocation)


    粗略的講,一個段代表一無縫隙的數據塊(地址范圍),一個段里存儲的數據都為同一性質,如“只讀”數據。as (匯編器)在編譯局部程序時總假設從0 地址開始,并生成目標文件。最后ld(鏈接器)在連接多個目標文件時為每一個段分配運行時(run-time)統一地址。這雖然是個簡單的解釋,卻足以說明我門為為什么用段.


    ld 將這些數據塊正確移動到它們運行時的地址。 此過程非常嚴格,數據的內部順序與長度均不能發生變化.這樣的數據單元叫做段,為段分配運行時地址叫再定位,此任務根據目標文件內的參考地址將段數據調整到運行時地址。


    Avr-gcc 中匯編器生成的目標文件(object-file)至少包含四個段,分別為: .text 段、.data段 、 .bss 段和.EEPROM 段,它們包括了程序存儲器(FLASH)代碼,內部RAM 數據,和EEPROM 存儲器內的數據。這些段的大小決定了程序存儲器(FLASH)、數據存儲器(RAM)、EEPROM 存儲器的使用量,關系如下:


    程序存儲器(FLASH)使用量 = .text + .data
    數據存儲器(RAM)使用量 = .data + .bss [+ .noinit] + stack [+ heap]
    EEPROM 存儲器使用量 = .EEPROM


    一..text 段


    .text 段包含程序實際執行代碼。另外,此段還包含.initN 和.finiN 兩種段,下面詳細討論。
    段.initN 和段.finiN 是個程序塊,它不會象函數那樣返回,所以匯編或C 程序不能調用。
    .initN、.finN 和絕對段(absolute section 提供中斷向量)構成avr-libc 應用程序運行框架,用戶編寫的應用程序在此框架中運行。
    .initN 段
    此類段包含從復位到main()函數開始執行之間的啟動(startup)代碼。
    此類段共定義10 個分別是.init0 到.init9。執行順序是從.init0 到.init9。
    .init0:
    此段綁定到函數__init()。用戶可重載__init(),復位后立即跳到該函數。
    .init1:
    未用,用戶可定義
    .init2:
    初始化堆棧的代碼分配到此段
    .init3:
    未用,用戶可定義
    .init4:
    初始化.data 段(從FLASH 復制全局或靜態變量初始值到.data),清零.bss 段。
    像UNIX 一樣.data 段直接從可執行文件中裝入。Avr-gcc 將.data 段的初始值存儲到flash
    rom 里.text 段后,.init4 代碼則負責將這些數據復制SRAM 內.data 段。
    .init5:
    未用,用戶可定義
    .init6:
    C 代碼未用,C++程序的構造代碼
    .init7:
    未用,用戶可定義
    .init8:
    未用,用戶可定義
    .init9:
    跳到main()
    avr-libc 包含一個啟動模塊(startup module),用于應用程序執行前的環境設置,鏈接時它被分配到init2 和init4 中,負責提供缺省中斷程序和向量、初始化堆棧、初始化.data 段和清零.bss 段等任務,最后startup 跳轉到main 函數執行用戶程序。
    .finiN 段
    此類段包含main()函數退出后執行的代碼。
    此類段可有0 到9 個, 執行次序是從fini9 到 fini1。
    .fini9
    此段綁定到函數exit()。用戶可重載exit(),main 函數一旦退出exit 就會被執行。
    .fini8:
    未用,用戶可定義
    .fini7:
    未用,用戶可定義
    .fini6:
    C 代碼未用, C++程序的析構代碼
    .fini5:
    未用,用戶可定義
    .fini4:
    未用,用戶可定義
    .fini3:
    未用,用戶可定義
    .fini2:
    未用,用戶可定義
    .fini1:
    未用,用戶可定義
    .fini0:
    進入一個無限循環。


    用戶代碼插入到.initN 或.finiN
    示例如下:
    void my_init_portb (void) __attribute__ ((naked)) \
    __attribute__ ((section (".init1")));
    void my_init_portb (void)
    {
    outb (PORTB, 0xff);
    outb (DDRB, 0xff);
    }
    由于屬性section(“.init1”)的指定,編譯后函數my_init_portb 生成的代碼自動插入到.init1段中,在main 函數前就得到執行。naked 屬性確保編譯后該函數不生成返回指令,使下一個初始化段得以順序的執行。


    二..data 段


    .data 段包含程序中被初始化的RAM 區全局或靜態變量。而對于FLASH 存儲器此段包含在程序中定義變量的初始化數據。類似如下的代碼將生成.data 段數據。
    char err_str[]=”Your program has died a horrible death!”;
    struct point pt={1,1};
    可以將.data 在SRAM 內的開始地址指定給連接器,這是通過給avr-gcc 命令行添加
    -Wl,-Tdata,addr 選項來實現的,其中addr 必須是0X800000 加SRAM 實際地址。例如 要將.data 段從0x1100 開始,則addr 要給出0X801100。


    三..bss 段


    沒有被初始化的RAM 區全局或靜態變量被分配到此段,在應用程序被執行前的startup過程中這些變量被清零。
    另外,.bss 段有一個子段 .noinit , 若變量被指定到.noinit 段中則在startup 過程中不會被清零。將變量指定到.noinit 段的方法如下:
    int foo __attribute__ ((section (“.noinit”)));
    由于指定到了.noinit 段中,所以不能賦初值,如同以下代碼在編譯時產生錯誤:
    int fol __attribute__((section(“.noinit”)))=0x00ff;
    四..EEPROM 段
    此段存儲EEPROM 變量。
    Static unsigned char eep_buffer[3] __attribute__((section(“.EEPROM”)))={1,2,3};
    在鏈接選項中可指定段的開始地址,如下的選項將.noinit 段指定位到RAM 存儲器
    0X2000 地址處。
    avr-gcc ... -Wl,--section-start=.noinit=0x802000
    要注意的是,在編譯時Avr-gcc 將FLASH、RAM 和EEPROM 內的段在一個統一的地址空間內處理,flash 存儲器被定位到0 地址開始處,RAM 存儲器被定位到0x800000 開始處,EEPROM 存儲器被定位到0X810000 處。所以在指定段開始地址時若是RAM 內的段或EEPROM 內的段時要在實際存儲器地址前分別加上0x800000 和0X810000。


    除上述四個段外,自定義段因需要而可被定義。由于編譯器不知道這類段的開始地址,又稱它們為未定義段。必需在鏈接選項中指定自定義段的開始地址。如下例:
    void MySection(void) __attribute__((section(".mysection")));
    void MySection(void)
    {
    printf("hello avr!");
    }
    鏈接選項:
    avr-gcc ... -Wl,--section-start=.mysection=0x001c00
    這樣函數MySection 被定位到了FLASH 存儲器0X1C00 處。

    本文地址:http://www.portaltwn.com/thread-2964-1-1.html     【打印本頁】

    本站部分文章為轉載或網友發布,目的在于傳遞和分享信息,并不代表本網贊同其觀點和對其真實性負責;文章版權歸原作者及原出處所有,如涉及作品內容、版權和其它問題,我們將根據著作權人的要求,第一時間更正或刪除。
    您需要登錄后才可以發表評論 登錄 | 立即注冊

    廠商推薦

    • Microchip視頻專區
    • EtherCAT®和Microchip LAN925x從站控制器介紹培訓教程
    • MPLAB®模擬設計器——在線電源解決方案,加速設計
    • 讓您的模擬設計靈感,化為觸手可及的現實
    • 深度體驗Microchip自動輔助駕駛應用方案——2025巡展開啟報名!
    • 貿澤電子(Mouser)專區

    相關在線工具

    相關視頻

    關于我們  -  服務條款  -  使用指南  -  站點地圖  -  友情鏈接  -  聯系我們
    電子工程網 © 版權所有   京ICP備16069177號 | 京公網安備11010502021702
    快速回復 返回頂部 返回列表
    精品一区二区三区自拍图片区_国产成人亚洲精品_亚洲Va欧美va国产综合888_久久亚洲国产精品五月天婷