<dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><small id="yhprb"></small><dfn id="yhprb"></dfn><small id="yhprb"><delect id="yhprb"></delect></small><small id="yhprb"></small><small id="yhprb"></small> <delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"></dfn><dfn id="yhprb"></dfn><s id="yhprb"><noframes id="yhprb"><small id="yhprb"><dfn id="yhprb"></dfn></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><small id="yhprb"></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn> <small id="yhprb"></small><delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn>

新聞中心

EEPW首頁(yè) > 嵌入式系統 > 設計應用 > AVR 單片機與GCC 編程之存儲器操作

AVR 單片機與GCC 編程之存儲器操作

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

由于A(yíng)VR 指令都為16 位或32 位,程序計數器對它按字進(jìn)行尋址,因此FLASH存儲器按字組織的,但在程序中訪(fǎng)問(wèn)FLASH 存儲區時(shí)專(zhuān)用指令LPM 可分別讀取指定地址的高低字節。

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

圖2-1 AVR SRAM 組織

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

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

2.2 I/O 寄存器操作

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

在器件對應的ioxxxx.h 文件中定義了器件SFR 的預處理宏,在程序中直接對它賦值或引用的方式讀寫(xiě)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 的地址
對SFR 的定義宏進(jìn)一步說(shuō)明了SFR 與SRAM 操作的相同點(diǎn)。
關(guān)鍵字volatile 確保本條指令不會(huì )因C 編譯器的優(yōu)化而被省略。

2.3 SRAM 內變量的使用

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

定義值 長(cháng)度(字節) 值范圍
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; 定義了一個(gè)SRAM 變量并初始化成8
val=10; 改變變量值
const uint8_t val=8; 定義SRAM 區常量
register uint8_t val=10; 定義寄存器變量

2.4 在程序中訪(fǎng)問(wèn)FLASH 程序存儲器

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

1. FLASH 區整數常量應用

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

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

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

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

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

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

全局定義形式:
const char flash_str[] PROGMEM = “Hello, world!”;
函數內定義形式:
const char *flash_str = PSTR(“Hello, world!”);
以下為一個(gè)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.5EEPROM數據存儲器操作

#include
頭文件聲明了avr-libc 提供的操作EEPROM存儲器的API 函數。
這些函數有:
EEPROM_is_ready() //EEPROM忙檢測(返回EEWE 位)
EEPROM_busy_wait() //查詢(xún)等待EEPROM準備就緒
uint8_tEEPROM_read_byte (const uint8_t *addr) //從指定地址讀一字節
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) //寫(xiě)一字節至指定地址
voidEEPROM_write_word (uint16_t *addr, uint16_t val) //寫(xiě)一字到指定地址
voidEEPROM_write_block (const void *buf, void *addr, size_t n)//寫(xiě)塊

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

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

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

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

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

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

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

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

一..text 段

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

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

二..data 段

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

三..bss 段

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

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


評論


技術(shù)專(zhuān)區

關(guān)閉
国产精品自在自线亚洲|国产精品无圣光一区二区|国产日产欧洲无码视频|久久久一本精品99久久K精品66|欧美人与动牲交片免费播放
<dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><small id="yhprb"></small><dfn id="yhprb"></dfn><small id="yhprb"><delect id="yhprb"></delect></small><small id="yhprb"></small><small id="yhprb"></small> <delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"></dfn><dfn id="yhprb"></dfn><s id="yhprb"><noframes id="yhprb"><small id="yhprb"><dfn id="yhprb"></dfn></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><small id="yhprb"></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn> <small id="yhprb"></small><delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn>