如何使用AVR-GCC API
嵌入式編程的代碼可以簡單地分為兩部分,一是與硬件無關(guān)的算法部分,對其編程與普通C編程沒有區(qū)別;二是與硬件相關(guān)的寄存器/端口操作部 分。不同的MCU實現(xiàn)方法各有不同。在AVR-GCC里則通過一系列的API來解決。當(dāng)然,用戶也可以定義自己的API。在此簡單地介紹目前AVR- GCC里定義的API,以及AVR-GCC的工作過程。
一.存儲器API
AVR具有三種存儲器:FLASH,SRAM和EEPROM。 AVR-GCC將程序代碼放在FLASH,數(shù)據(jù)放在SRAM。
I.程序存儲器
如果要將數(shù)據(jù)(如常量,字符串,等等)放在FLASH里,用 戶需要指明數(shù)據(jù)類型__attribute__((progmem))。為了方便使用,AVR-GCC定義了一些更直觀的符號,如下表所示。
類 型定義
prog_voidvoid __attribute__((progmem))
prog_charchar __attribute__((progmem))
prog_intint __attribute__((progmem))
prog_longlong __attribute__((progmem))
prog_long_longlong long __attribute__((progmem))
PGM_Pprog_char const*
PGM_VOID_Pprog_void const*
提供的庫函數(shù)有:
1.__elpm_inline
用法:uint8_t __elpm_inline(uint32_t addr);
說明:執(zhí)行ELPM指令從FLASH里取數(shù)。參數(shù)為32位地址,返回一個8位數(shù)據(jù)。
2.__lpm_inline
用 法:uint8_t __elpm_inline(uint16_t addr);
說明:執(zhí)行LPM指令從FLASH里取數(shù)。參數(shù)為16位地址, 返回一個8位數(shù)據(jù)。
3.memcpy_P
用法:void* memcpy_P(void* dst, PGM_VOID_P src, size_t n);
說明:memcpy的特殊版本。完成從FLASH取n個字節(jié)的任務(wù)。
4.PRG_RDB
用 法:uint8_t PGR_RDB(uint16_t addr);
說明:此函數(shù)簡單地調(diào)用__lpm_inline
5.PSTR
用 法:PSTR(s);
說明:參數(shù)為字符串。功能是將其放在FLASH里并返回地址。
6.strcmp_P
用法:int strcmp(char const*, PGM_P);
說明:功能與strcmp()類似。第二個參數(shù)指向程序存儲器內(nèi)的字符串。
7.strcpy_P
用 法:char* strcpy_P(char*, PGM_P);
說明:功能與strcpy()類似。第二個參數(shù)指向程序存儲器內(nèi)的字符串。
8.strlen_P
用 法:size_t strlen_P(PGM_P);
說明:功能與strlen()類似。第二個參數(shù)指向程序存儲器內(nèi)的字符串。
9.strncmp_P
用 法:size_t strncmp_P(char const*, PGM_P, size_t);
說明:功能與strncmp()類似。第二個 參數(shù)指向程序存儲器內(nèi)的字符串。
10.strncpy_P
用法:size_t strncpy_P(char*, PGM_P, size_t);
說明:功能與strncpy()類似。第二個參數(shù)指向程序存儲器內(nèi)的字符串。
II.EEPROM
AVR內(nèi)部有 EEPROM,但地址空間與SRAM的不相同。在訪問時必須通過I/O寄存器來進行。EEPROM API封裝了這些功能,為用戶提供了高級接口。使用時要包含eeprom.h。在程序里定義EEPROM數(shù)據(jù)的例子如下:
static uint8_t variable_x __attribute__((section(".eeprom"))) = 0;
不同的AVR器件具 有不同數(shù)目的EEPROM。鏈接器將針對不同的器件分配存儲器空間。
1.eeprom_is_ready
用法:int eeprom_is_ready(void);
說明:此函數(shù)用于指示是否可以訪問EEPROM。如果EEPROM正在執(zhí)行寫操作,則在4ms內(nèi)無 法訪問。此函數(shù)查詢相應(yīng)的狀態(tài)位來指示現(xiàn)在是否可以訪問EEPROM。
2.eeprom_rb
用法:uint8_t eeprom_rb(uint16_t addr);
說明:從EEPROM里讀出一個字節(jié)的內(nèi)容。參數(shù)addr用于指示要讀出的地址。 _EEGET(addr)調(diào)用此函數(shù)。
3.eeprom_read_block
用法:void eeprom_read_block(void* buf, uint16_t addr, size_t n);
說明:讀出一塊EEROM的內(nèi) 容。參數(shù)addr為起始地址,n表明要讀取的字節(jié)數(shù)。數(shù)據(jù)被讀到SRAM的buf里。
4.eeprom_rw
用 法:unint16_t eeprom_rw(uint16_t addr);
說明:從EEPROM里讀出一個16位的數(shù)據(jù)。低字節(jié)為低8位,高 字節(jié)為高8位。參數(shù)addr為地址。
5.eeprom_wb
用法:void eeprom_wb(uint16_t addr, uint8_t val);
說明:將8位數(shù)據(jù)val寫入地址為addr的EEPROM存儲器里。_EEPUT(addr,val)調(diào) 用此函數(shù)。
二.中斷API
由于C語言設(shè)計目標(biāo)為硬件無關(guān),因此各種編譯器在處理中斷時使用的方法都是編譯器設(shè)計者自己的方法。
在 AVR-GCC環(huán)境里,向量表已經(jīng)預(yù)先定義,并指向具有預(yù)定義名稱的中斷例程。通過使用合適的名稱,用戶例程就可以由相應(yīng)的中斷所調(diào)用。如果用戶沒有定義 自己的中斷例程,則器件庫的缺省例程被加入。
除了中斷向量表的問題,編譯器還必須處理相關(guān)寄存器保護的問題。中斷API解決了細(xì)節(jié)問題。用戶只要 將中斷例程定義為INTERRUPT()或SIGAL()即可。而對于用戶沒有定義的中斷,缺省例程的處理是reti指令。
函數(shù)定義可參見 interrupt.h,中斷信號符號表參見sig-avr.h。
1.cli
用法:void cli(void);
說 明:通過置位全局中斷屏蔽位來禁止中斷。其編譯結(jié)果僅為一條匯編指令。
2.enable_external_int
用 法:void enable_external_int(uint8_t ints);
說明:此函數(shù)訪問GIMSK寄存器(對于MEGA器件則是 EIMSK寄存器)。功能與宏outp()一樣。
3.INTERRUPT
用法:INTERRUPT(signame)
說 明:定義中斷源signame對應(yīng)的中斷例程。在執(zhí)行時,全局屏蔽位將清零,其他中斷被使能。ADC結(jié)束中斷例程的例子如下所示:
INTERRUPT(SIG_ADC)
{
}
4.sei
用 法:void sei(void);
說明:通過清零全局中斷屏蔽位來使能中斷。其編譯結(jié)果僅為一條匯編指令。
5.SIGNAL
用 法:SIGNAL(signame)
說明:定義中斷源signame對應(yīng)的中斷例程。在執(zhí)行時,全局屏蔽位保持置位,其他中斷被禁止。ADC結(jié)束 中斷例程的例子如下所示:
SIGNAL(SIG_ADC)
{
}
6.timer_enable_int
用 法:void timer_enable_int(uint8_t ints);
說明:此函數(shù)操作TIMSK寄存器。也可以通過outp()來設(shè) 置。
四.I/O API
I.I/O端口API
1.BV
用法:BV(pos);
說明:將位定義轉(zhuǎn)換成屏 蔽碼(MASK)。與頭文件io.h里的位定義一起使用。例如,置位WDTOE和WDE可表示為"BV(WDTOE) | BV(WDE)"
2.bit_is_clear
用 法:uint8_t bit_is_clear(uint8_t port, uint8_t bit);
描述:如果port的bit位清零則返 回1。此函數(shù)調(diào)用sbic指令,故port應(yīng)為有效地址。
3.bit_is_set
用法:uint8_t bit_is_set(uint8_t port, uint8_t bit);
描述:如果port的bit位置位則返回1。此函數(shù)調(diào)用sbis 指令,故port應(yīng)為有效地址。
評論