<meter id="pryje"><nav id="pryje"><delect id="pryje"></delect></nav></meter>
          <label id="pryje"></label>

          新聞中心

          EEPW首頁(yè) > 嵌入式系統(tǒng) > 設(shè)計(jì)應(yīng)用 > AVR 單片機(jī)與GCC 編程之存儲(chǔ)器操作

          AVR 單片機(jī)與GCC 編程之存儲(chǔ)器操作

          作者: 時(shí)間:2016-11-19 來(lái)源:網(wǎng)絡(luò) 收藏
          AVR 系列單片機(jī)內(nèi)部有三種類型的被獨(dú)立編址的存儲(chǔ)器,它們分別為:Flash 程序存儲(chǔ)器、內(nèi)部SRAM 數(shù)據(jù)存儲(chǔ)器和EEPROM數(shù)據(jù)存儲(chǔ)器。
          Flash存儲(chǔ)器為1K~128K 字節(jié),支持并行編程和串行下載,下載壽命通??蛇_(dá)10,000 次。

          由于AVR 指令都為16 位或32 位,程序計(jì)數(shù)器對(duì)它按字進(jìn)行尋址,因此FLASH存儲(chǔ)器按字組織的,但在程序中訪問(wèn)FLASH 存儲(chǔ)區(qū)時(shí)專用指令LPM 可分別讀取指定地址的高低字節(jié)。

          寄存器堆(R0~R31)、I/O 寄存器和SRAM 被統(tǒng)一編址。所以對(duì)寄存器和I/O 口的操作使用與訪問(wèn)內(nèi)部SRAM 同樣的指令。其組織結(jié)構(gòu)如圖2-1 所示。

          圖2-1 AVR SRAM 組織

          32 個(gè)通用寄存器被編址到最前,I/O 寄存器占用接下來(lái)的64 個(gè)地址。從0X0060 開(kāi)始為內(nèi)部SRAM。外部SRAM 被編址到內(nèi)部SRAM 后。

          AVR單片機(jī)的內(nèi)部有64~4K 的EEPROM數(shù)據(jù)存儲(chǔ)器,它們被獨(dú)立編址,按字節(jié)組織。擦寫壽命可達(dá)100,000 次。

          2.2 I/O 寄存器操作

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

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

          PORTB=0XFF;
          Val=PINB;

          從io.h 和其總包含的頭文件sfr_defs.h 可以追溯宏P(guān)ORTB 的原型
          在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 的地址
          對(duì)SFR 的定義宏進(jìn)一步說(shuō)明了SFR 與SRAM 操作的相同點(diǎn)。
          關(guān)鍵字volatile 確保本條指令不會(huì)因C 編譯器的優(yōu)化而被省略。

          2.3 SRAM 內(nèi)變量的使用

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

          定義值 長(zhǎng)度(字節(jié)) 值范圍
          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

          根據(jù)習(xí)慣,在程序中可使用以上的整數(shù)定義。
          定義、初始化和引用

          如下示例:

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

          2.4 在程序中訪問(wèn)FLASH 程序存儲(chǔ)器

          avr-libc 支持頭文件:pgmspace.h
          #include < avr/pgmspace.h >
          在程序存儲(chǔ)器內(nèi)的數(shù)據(jù)定義使用關(guān)鍵字 __attribute__((__progmem__))。在pgmspace.h
          中它被定義成符號(hào) PROGMEM。

          1. FLASH 區(qū)整數(shù)常量應(yīng)用

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

          pgm_read-word(prg_void *addr)
          pgm_read_dword(prg_void* addr)
          另外在pgmspace.h 中定義的8 位整數(shù)類型 prog_char prog_uchar 分別指定在FLASH
          內(nèi)的8 位有符號(hào)整數(shù)和8 位無(wú)符號(hào)整數(shù)。應(yīng)用方式如下:

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

          對(duì)于應(yīng)用程序FLASH 常量是不可改變的,因此定義時(shí)加關(guān)鍵字const 是個(gè)好的習(xí)慣。

          2. FLASH 區(qū)數(shù)組應(yīng)用:

          定義:
          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 ++) // 循環(huán)讀取每一字節(jié)
          {
          ram_val = pgm_read_byte(flash_array + I);
          … … //處理
          }

          2. FLASH 區(qū)字符串常量的應(yīng)用

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

          2.5EEPROM數(shù)據(jù)存儲(chǔ)器操作

          #include
          頭文件聲明了avr-libc 提供的操作EEPROM存儲(chǔ)器的API 函數(shù)。
          這些函數(shù)有:
          EEPROM_is_ready() //EEPROM忙檢測(cè)(返回EEWE 位)
          EEPROM_busy_wait() //查詢等待EEPROM準(zhǔn)備就緒
          uint8_tEEPROM_read_byte (const uint8_t *addr) //從指定地址讀一字節(jié)
          uint16_tEEPROM_read_word (const uint16_t *addr) //從指定地址一字
          voidEEPROM_read_block (void *buf, const void *addr, size_t n) //讀塊
          voidEEPROM_write_byte (uint8_t *addr, uint8_t val) //寫一字節(jié)至指定地址
          voidEEPROM_write_word (uint16_t *addr, uint16_t val) //寫一字到指定地址
          voidEEPROM_write_block (const void *buf, void *addr, size_t n)//寫塊

          在程序中對(duì)EEPROM操作有兩種方式

          方式一:直接指定EERPOM 地址
          示例:
          /*此程序?qū)?xaa 寫入到EEPROM存儲(chǔ)器0 地址處,
          再?gòu)? 地址處讀一字節(jié)賦給RAM 變量val */
          #include
          #include
          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); //從EEPROM0 地址處讀取一字節(jié)賦給RAM 變量val
          while(1);
          }

          方式二:先定義EEPROM區(qū)變量法
          示例:
          #include
          #include
          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存儲(chǔ)器內(nèi)的具體地址由編譯器自動(dòng)分配。相對(duì)方式一,數(shù)據(jù)在EEPROM中的具體位置是不透明的。
          為EEPROM變量賦的初始值,編譯時(shí)被分配到.EEPROM段中,可用avr-objcopy 工具從.elf文件中提取并產(chǎn)生ihex 或binary 等格式的文件。

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

          粗略的講,一個(gè)段代表一無(wú)縫隙的數(shù)據(jù)塊(地址范圍),一個(gè)段里存儲(chǔ)的數(shù)據(jù)都為同一性質(zhì),如“只讀”數(shù)據(jù)。as (匯編器)在編譯局部程序時(shí)總假設(shè)從0 地址開(kāi)始,并生成目標(biāo)文件。最后ld(鏈接器)在連接多個(gè)目標(biāo)文件時(shí)為每一個(gè)段分配運(yùn)行時(shí)(run-time)統(tǒng)一地址。這雖然是個(gè)簡(jiǎn)單的解釋,卻足以說(shuō)明我門為為什么用段.

          ld 將這些數(shù)據(jù)塊正確移動(dòng)到它們運(yùn)行時(shí)的地址。 此過(guò)程非常嚴(yán)格,數(shù)據(jù)的內(nèi)部順序與長(zhǎng)度均不能發(fā)生變化.這樣的數(shù)據(jù)單元叫做段,為段分配運(yùn)行時(shí)地址叫再定位,此任務(wù)根據(jù)目標(biāo)文件內(nèi)的參考地址將段數(shù)據(jù)調(diào)整到運(yùn)行時(shí)地址。

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

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

          一..text 段

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

          用戶代碼插入到.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”)的指定,編譯后函數(shù)my_init_portb 生成的代碼自動(dòng)插入到.init1段中,在main 函數(shù)前就得到執(zhí)行。naked 屬性確保編譯后該函數(shù)不生成返回指令,使下一個(gè)初始化段得以順序的執(zhí)行。

          二..data 段

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

          三..bss 段

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

          除上述四個(gè)段外,自定義段因需要而可被定義。由于編譯器不知道這類段的開(kāi)始地址,又稱它們?yōu)槲炊x段。必需在鏈接選項(xiàng)中指定自定義段的開(kāi)始地址。如下例:
          void MySection(void) __attribute__((section(".mysection")));
          void MySection(void)
          {
          printf("hello avr!");
          }
          鏈接選項(xiàng):
          avr-gcc ... -Wl,--section-start=.mysection=0x001c00
          這樣函數(shù)MySection 被定位到了FLASH存儲(chǔ)器0X1C00 處。


          評(píng)論


          技術(shù)專區(qū)

          關(guān)閉
          看屁屁www成人影院,亚洲人妻成人图片,亚洲精品成人午夜在线,日韩在线 欧美成人 (function(){ var bp = document.createElement('script'); var curProtocol = window.location.protocol.split(':')[0]; if (curProtocol === 'https') { bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; } else { bp.src = 'http://push.zhanzhang.baidu.com/push.js'; } var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(bp, s); })();