AVR-GCC里定義的API

admin @ 2014-03-25 , reply:0

        嵌入式編程的代碼可以簡單地分為兩部分,一是與硬體無關的演算法部分,對其編程與普通C編程沒有區別;二是與硬體相關的寄存器/埠操作部分。不同的MCU實現方法各有不同。在AVR-GCC里則通過一系列的API來解決。當然,用戶也可以定義自己的API。在此簡單地介紹目前AVR-GCC里定義的API,以及AVR-GCC的工作過程。

一.應用程序啟動過程(Start Up)
標準庫文件包含一個啟動模塊(Start Up Module),用於為真正執行用戶程序做環境設置。
啟動模塊完成的任務如下:
1. 提供預設向量表
2. 提供預設中斷程序入口
3. 初始化全局變數
4. 初始化看門狗
5. 初始化寄存器MCUCR
6. 初始化數據段
7. 將數據段.bss的內容清零
8. 跳轉到main()。(不用調用方式,因為main()不用返回)
啟動模塊包含預設中斷向量表,其內容為預先定義好的函數名稱。這些函數名稱可以由程序員重載。中斷向量表的第一個內容為複位向量,執行結果是將程序跳轉到_init_。在啟動模塊里,_init_表示的地址與_real_init_指向的地址相同。如果要加入客戶代碼,則需要在程序里定義一個_init_函數。在此函數的末尾跳轉到_real_init_。具體實現如下:
void _real_init_(void);
void _init_(void) __attribute__((naked));
void _init_(void)
{
 // 用戶代碼
 
 // 最後的代碼必須為:
 asm ("rjmp _real_init_");
}

在_real_init_部分,系統將設置看門狗和MCUCR寄存器。啟動模塊並沒有真正取用相應寄存器的設置數值(以符號_init_wdctr_,_init_mcucr_,_init_emcucr_表示),而是通過地址來取得其值。因而用戶可以通過鏈接器的--defsym選項來設置這些符號的地址。如果用戶沒有定義,則啟動模塊將使用預設值。
接下來系統將從程序存儲器里把具有初值的全局變數載入到數據存儲器SRAM。然後是將數據段.bbs清零。此數據段包含所有沒有的初值的非AUTO變數。
最後,系統跳轉到main()函數,用戶代碼開始執行。系統對此特殊函數加入一些特殊的處理。進入此函數后,堆棧指向SRAM的末尾。

二.存儲器API
AVR具有三種存儲器:FLASH,SRAM和EEPROM。AVR-GCC將程序代碼放在FLASH,數據放在SRAM。
I.程序存儲器
如果要將數據(如常量,字元串,等等)放在FLASH里,用戶需要指明數據類型__attribute__((progmem))。為了方便使用,AVR-GCC定義了一些更直觀的符號,如下表所示。
類型 定義
prog_void void __attribute__((progmem))
prog_char char __attribute__((progmem))
prog_int int __attribute__((progmem))
prog_long long __attribute__((progmem))
prog_long_long long long __attribute__((progmem))
PGM_P prog_char const*
PGM_VOID_P prog_void const*
提供的庫函數有:
1.__elpm_inline
用法:uint8_t __elpm_inline(uint32_t addr);
說明:執行ELPM指令從FLASH里取數。參數為32位地址,返回一個8位數據。
2.__lpm_inline
用法:uint8_t __elpm_inline(uint16_t addr);
說明:執行LPM指令從FLASH里取數。參數為16位地址,返回一個8位數據。
3.memcpy_P
用法:void* memcpy_P(void* dst, PGM_VOID_P src, size_t n);
說明:memcpy的特殊版本。完成從FLASH取n個位元組的任務。
4.PRG_RDB
用法:uint8_t PGR_RDB(uint16_t addr);
說明:此函數簡單地調用__lpm_inline
5.PSTR
用法:PSTR(s);
說明:參數為字元串。功能是將其放在FLASH里並返回地址。
6.strcmp_P
用法:int strcmp(char const*, PGM_P);
說明:功能與strcmp()類似。第二個參數指向程序存儲器內的字元串。
7.strcpy_P
用法:char* strcpy_P(char*, PGM_P);
說明:功能與strcpy()類似。第二個參數指向程序存儲器內的字元串。
8.strlen_P
用法:size_t strlen_P(PGM_P);
說明:功能與strlen()類似。第二個參數指向程序存儲器內的字元串。
9.strncmp_P
用法:size_t strncmp_P(char const*, PGM_P, size_t);
說明:功能與strncmp()類似。第二個參數指向程序存儲器內的字元串。
10.strncpy_P
用法:size_t strncpy_P(char*, PGM_P, size_t);
說明:功能與strncpy()類似。第二個參數指向程序存儲器內的字元串。
II.EEPROM
AVR內部有EEPROM,但地址空間與SRAM的不相同。在訪問時必須通過I/O寄存器來進行。EEPROM API封裝了這些功能,為用戶提供了高級介面。使用時要包含eeprom.h。在程序里定義EEPROM數據的例子如下:
static uint8_t variable_x __attribute__((section(".eeprom"))) = 0;
不同的AVR器件具有不同數目的EEPROM。鏈接器將針對不同的器件分配存儲器空間。
1. eeprom_is_ready
用法:int eeprom_is_ready(void);
說明:此函數用於指示是否可以訪問EEPROM。如果EEPROM正在執行寫操作,則在4ms內無法訪問。此函數查詢相應的狀態位來指示現在是否可以訪問EEPROM。
2. eeprom_rb
用法:uint8_t eeprom_rb(uint16_t addr);
說明:從EEPROM里讀出一個位元組的內容。參數addr用於指示要讀出的地址。_EEGET(addr)調用此函數。
3. Eeprom_read_block
用法:void eeprom_read_block(void* buf, uint16_t addr, size_t n);
說明:讀出一塊EEROM的內容。參數addr為起始地址,n表明要讀取的位元組數。數據被讀到SRAM的buf里。
4. eeprom_rw
用法:unint16_t eeprom_rw(uint16_t addr);
說明:從EEPROM里讀出一個16位的數據。低位元組為低8位,高位元組為高8位。參數addr為地址。
5. eeprom_wb
用法:void eeprom_wb(uint16_t addr, uint8_t val);
說明:將8位數據val寫入地址為addr的EEPROM存儲器里。_EEPUT(addr,val)調用此函數。

三.中斷API
由於C語言設計目標為硬體無關,因此各種編譯器在處理中斷時使用的方法都是編譯器設計者自己的方法。
在AVR-GCC環境里,向量表已經預先定義,並指向具有預定義名稱的中斷常式。通過使用合適的名稱,用戶常式就可以由相應的中斷所調用。如果用戶沒有定義自己的中斷常式,則器件庫的預設常式被加入。
除了中斷向量表的問題,編譯器還必須處理相關寄存器保護的問題。中斷API解決了細節問題。用戶只要將中斷常式定義為INTERRUPT()或SIGAL()即可。而對於用戶沒有定義的中斷,預設常式的處理是reti指令。
函數定義可參見interrupt.h,中斷信號符號表參見sig-avr.h。
1. cli
用法:void cli(void);
說明:通過置位全局中斷屏蔽位來禁止中斷。其編譯結果僅為一條彙編指令。
2. enable_external_int
用法:void enable_external_int(uint8_t ints);
說明:此函數訪問GIMSK寄存器(對於MEGA器件則是EIMSK寄存器)。功能與宏outp()一樣。
3. INTERRUPT
用法:INTERRUPT(signame)
說明:定義中斷源signame對應的中斷常式。在執行時,全局屏蔽位將清零,其他中斷被使能。ADC結束中斷常式的例子如下所示:
INTERRUPT(SIG_ADC)
{
}
4. sei
用法:void sei(void);
說明:通過清零全局中斷屏蔽位來使能中斷。其編譯結果僅為一條彙編指令。
5. SIGNAL
用法:SIGNAL(signame)
說明:定義中斷源signame對應的中斷常式。在執行時,全局屏蔽位保持置位,其他中斷被禁止。ADC結束中斷常式的例子如下所示:
SIGNAL(SIG_ADC)
{
}
6. timer_enable_int
用法:void timer_enable_int(uint8_t ints);
說明:此函數操作TIMSK寄存器。也可以通過outp()來設置。
四.I/O API
I.I/O埠API
1. BV
用法:BV(pos);
說明:將位定義轉換成屏蔽碼(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。此函數調用sbic指令,故port應為有效地址。
3.bit_is_set
用法:uint8_t bit_is_set(uint8_t port, uint8_t bit);
描述:如果port的bit位置位則返回1。此函數調用sbis指令,故port應為有效地址。
4.cbi
用法:void cbi(uint8_t port, uint8_t bit);
說明:清零port的bit位。bit的值為0~7。如果port為實際I/O寄存器,則此函數生成一條cbi指令;否則,函數生成相應的優化代碼。
5.inp
用法:uint8_t inp(uint8_t port);
說明:從埠port讀入8比特的數值。如果port為常數,則函數生成一條in指令;若為變數,則函數用直接定址指令。
6.__inw
用法:uint16_t __inw(uint8_t port);
說明:從I/O寄存器讀入16位的數值。此函數用於讀取16位寄存器(ADC,ICR1,OCR1,TCNT1)的值,因為讀取這些寄存器需要合適的步驟。由於此函數只產生兩條彙編指令,因此要在中斷禁止時使用,否則有可能由於中斷插入到指令之間造成讀取錯誤。
7.__inw_atomic
用法:uint16_t __inw_atomic(uint8_t port);
說明:以原子語句方式讀取16位I/O寄存器的數值。此函數首先禁止中斷,讀取數據之後再恢復中斷狀態,因此可以安全地應用在各種系統狀態。
8.loop_until_bit_is_clear
用法:oidoid loop_until_bit_is_clear (uint8_t port, uint8_t bit);
說明:此函數簡單地調用sbic指令來測試埠port的bit位是否清零。port必須為有效埠。
9.loop_until_bit_is_set
用法:oidoid loop_until_bit_is_set (uint8_t port, uint8_t bit);
說明:此函數簡單地調用sbis指令來測試埠port的bit位是否置位。port必須為有效埠。
10.outp
用法:void outp(uint8_t val, uint8_t port);
說明:將val寫入埠port。如果port為常數,則函數生成一條out指令;若為變數,則函數用直接定址指令。
11.__outw
用法:void __outw(uint16_t val, uint8_t port);
說明:將16位的val寫入埠port。此函數適合於操作16位寄存器,如ADC,ICR1,OCR1,TCNT1。由於此函數只產生兩條彙編指令,因此要在中斷禁止時使用,否則有可能由於中斷插入到指令之間造成讀取錯誤。
12.__outw_atomic
用法:void __outw_atomic(uint16_t val, uint8_t port);
說明:將16位的val寫入埠port。此函數適合於操作16位寄存器,如ADC,ICR1,OCR1,TCNT1。此函數首先禁止中斷,讀取數據之後再恢復中斷狀態,因此可以安全地應用在各種系統狀態。
13.sbi
用法:void sbi(uint8_t port, uint8_t bit);
說明:置位port的bit位。bit的值為0~7。如果port為實際I/O寄存器,則此函數生成一條 sbi指令;否則,函數生成相應的優化代碼。
五.看門狗API
以下函數操作看門狗。宏定義參見wdt.h。
用戶可以通過起動代碼初始化看門狗。WDTCR的預設值為0。如果你希望將其設置為其他值,則需要在鏈接命令里加入相應的命令。使用的符號為__init_wdtcr__。如下為將WDTCR設置為0x1f的例子:
avr-ld –defsym __init_wdtcr__=0x1f
1. wdt_disable
用法:void wdt_disable(void);
說明:關閉看門狗。
2. wdt_enable
用法:void wdt_disable(unit8_t timeout);
說明:使能看門狗。看門狗溢出時間為timeout。
timeout 周期
0 16K CLK
1 32K CLK
2 64K CLK
3 128K CLK
4 256K CLK
5 512K CLK
6 1024K CLK
7 2048K CLK

3. wdt_reset
用法:void wdt_reset(void);
說明:產生喂狗指令wdr。
 
附錄:AVR-GCC配置
彙編選項
選項 描述
-mmcu=name 指定目標器件
name可以為:at90s1200,at90s2313,at90s2323,at90s2333,at90s2343,at904433,at90s8515,at90s8535,atmega103,atmega161
寄存器使用
如果用戶需要進行彙編與C的混合編程,必須了解寄存器的使用。
1.寄存器使用
r0  可用做暫時寄存器。如果用戶彙編代碼使用了r0,且要調用C代碼,則在調用之前必須保存r0。C中斷常式會自動保存和恢復r0。
r1  C編譯器假定此寄存器內容為“0”。如果用戶使用了此寄存器,則在彙編代碼返回之前須將其清零。C中斷常式會自動保存和恢復r1。
r2-r17,r28,r29  C編譯器使用這些寄存器。如果用戶彙編代碼需要使用這些寄存器,則必須保存並恢復這些寄存器。
R18-r27,r30,r31 如果用戶彙編代碼不調用C代碼則無需保存和恢復這些寄存器。如果用戶要調用C代碼,則在調用之前須保存。
2.函數調用規則
參數表:函數的參數由左至右分別分配給r25到r8。每個參數佔據偶數個寄存器。若參數太多以至r25到r8無法容納,則多出來的參數將放入堆棧。
返回值:8位返回值存放在r24。16位返回值存放在r25:r24。32位返回值存放在r25:r24:r23:r22。64位返回值存放在r25:r24:r23:r22:r21:r20:r19:r18。




[admin via 研發互助社區 ] AVR-GCC里定義的API已經有2069次圍觀

http://cocdig.com/docs/show-post-42471.html