AVR 單片機(jī)與GCC 編程----之二
2.5 EEPROM 數(shù)據(jù)存儲(chǔ)器操作
#include EEPROM.h>
頭文件聲明了avr-libc 提供的操作EEPROM 存儲(chǔ)器的API 函數(shù)。
這些函數(shù)有:
EEPROM_is_ready() //EEPROM 忙檢測(返回EEWE 位)
EEPROM_busy_wait() //查詢等待EEPROM 準(zhǔn)備就緒
uint8_t EEPROM_read_byte (const uint8_t *addr) //從指定地址讀一字節(jié)
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) //寫一字節(jié)至指定地址
void EEPROM_write_word (uint16_t *addr, uint16_t val) //寫一字到指定地址
void EEPROM_write_block (const void *buf, void *addr, size_t n)//寫塊
在程序中對(duì)EEPROM 操作有兩種方式
方式一:直接指定EERPOM 地址
示例:
/*此程序?qū)?xaa 寫入到EEPROM 存儲(chǔ)器 0 地址處,
再從0 地址處讀一字節(jié)賦給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 地址處讀取一字節(jié)賦給RAM 變量val
while(1);
}
方式二:先定義EEPROM 區(qū)變量法
示例:
#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 存儲(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è)段代表一無縫隙的數(shù)據(jù)塊(地址范圍),一個(gè)段里存儲(chǔ)的數(shù)據(jù)都為同一性質(zhì),如“只讀”數(shù)據(jù)。as (匯編器)在編譯局部程序時(shí)總假設(shè)從0 地址開始,并生成目標(biāo)文件。最后ld(鏈接器)在連接多個(gè)目標(biāo)文件時(shí)為每一個(gè)段分配運(yùn)行時(shí)(run-time)統(tǒng)一地址。這雖然是個(gè)簡單的解釋,卻足以說明我門為為什么用段.
ld 將這些數(shù)據(jù)塊正確移動(dòng)到它們運(yùn)行時(shí)的地址。 此過程非常嚴(yán)格,數(shù)據(jù)的內(nèi)部順序與長度均不能發(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ù)開始執(zhí)行之間的啟動(dòng)(startup)代碼。
此類段共定義10 個(gè)分別是.init0 到.init9。執(zhí)行順序是從.init0 到.init9。
.init0:
此段綁定到函數(shù)__init()。用戶可重載__init(),復(fù)位后立即跳到該函數(shù)。
.init1:
未用,用戶可定義
.init2:
初始化堆棧的代碼分配到此段
.init3:
未用,用戶可定義
.init4:
初始化.data 段(從FLASH 復(fù)制全局或靜態(tài)變量初始值到.data),清零.bss 段。
像UNIX 一樣.data 段直接從可執(zhí)行文件中裝入。Avr-gcc 將.data 段的初始值存儲(chǔ)到flash
rom 里.text 段后,.init4 代碼則負(fù)責(zé)將這些數(shù)據(jù)復(fù)制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è)無限循環(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)的開始地址指定給連接器,這是通過給avr-gcc 命令行添加
-Wl,-Tdata,addr 選項(xiàng)來實(shí)現(xiàn)的,其中addr 必須是0X800000 加SRAM 實(shí)際地址。例如 要將.data 段從0x1100 開始,則addr 要給出0X801100。
三..bss 段
沒有被初始化的RAM 區(qū)全局或靜態(tài)變量被分配到此段,在應(yīng)用程序被執(zhí)行前的startup過程中這些變量被清零。
另外,.bss 段有一個(gè)子段 .noinit , 若變量被指定到.noinit 段中則在startup 過程中不會(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)中可指定段的開始地址,如下的選項(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 地址開始處,RAM 存儲(chǔ)器被定位到0x800000 開始處,EEPROM 存儲(chǔ)器被定位到0X810000 處。所以在指定段開始地址時(shí)若是RAM 內(nèi)的段或EEPROM 內(nèi)的段時(shí)要在實(shí)際存儲(chǔ)器地址前分別加上0x800000 和0X810000。
除上述四個(gè)段外,自定義段因需要而可被定義。由于編譯器不知道這類段的開始地址,又稱它們?yōu)槲炊x段。必需在鏈接選項(xiàng)中指定自定義段的開始地址。如下例:
void MySection(void) __attribute__((section(".mysection")));
void MySection(void)
{
printf("hello avr!");
}
鏈接選項(xiàng):
avr-gcc ... -Wl,--section-start=.mysection=0x001c00
關(guān)鍵詞:
AVR單片機(jī)GCC編
相關(guān)推薦
技術(shù)專區(qū)
- FPGA
- DSP
- MCU
- 示波器
- 步進(jìn)電機(jī)
- Zigbee
- LabVIEW
- Arduino
- RFID
- NFC
- STM32
- Protel
- GPS
- MSP430
- Multisim
- 濾波器
- CAN總線
- 開關(guān)電源
- 單片機(jī)
- PCB
- USB
- ARM
- CPLD
- 連接器
- MEMS
- CMOS
- MIPS
- EMC
- EDA
- ROM
- 陀螺儀
- VHDL
- 比較器
- Verilog
- 穩(wěn)壓電源
- RAM
- AVR
- 傳感器
- 可控硅
- IGBT
- 嵌入式開發(fā)
- 逆變器
- Quartus
- RS-232
- Cyclone
- 電位器
- 電機(jī)控制
- 藍(lán)牙
- PLC
- PWM
- 汽車電子
- 轉(zhuǎn)換器
- 電源管理
- 信號(hào)放大器
評(píng)論