IAR for AVR 學習筆記(1)--資料類型 資料類型(編譯器支援 ISO/ANSI C 基本資料類型和一些附加資料類型) 1.1. 整型資料 bool 資料類型在C++語言裡是預設支援的。如果你在C代碼的頭檔案裡包含stdbool.h, bool資料類型也可以使用在C語言裡。也可以使用布爾值 false和 true。 1.2.浮點資料類型: 1.3.指針類型:指針有資料指針和函數指針。 1、資料指針: 資料指針的大小為8位,16位,24位。定義為:在整型資料類型後加”*”符号。 例如:char * p; 整型資料沒有24位,具體定義指針見後面擴充關鍵字章節。 2、函數指針:函數指針的大小為16位,24位。 指針定義:在函數類型後加”*”符号 IAR for AVR 學習筆記(2)--擴充關鍵字 可以用來解決資料,函數的存放等。有了它我們就可以定義變量存放在EEPROM,FLASH空間。定義中斷函數,指針等等。IAR關鍵字很多,這裡隻列舉常用的。 2.1.擴充關鍵字:用于控制資料和指針。 __eeprom 用于EEPROM 存儲空間, 控制資料存放,控制指針類型和存放 __tinyflash, __flash, __farflash, __hugeflash 用于flash 存儲空間, 控制資料存放,控制指針類型和存放: __ext_io, __io 用于I/O存儲空間, 控制資料存放,控制指針類型和存放 __regvar 放置一個變量在工作寄存器中 2.2.函數擴充關鍵字:。 __nearfunc __farfunc 用于控制資料存放,這組關鍵字必須在函數聲明和定義的時候指定: __interrupt. 關鍵字控制函數的類型。這組關鍵字必須在函數聲明和定義的時候指定 __root. 關鍵字僅僅控制有定義的函數: 2.3.其它特别的關鍵字: @ 用于變量的絕對位址定位。也可以用#pragma location 指令 #pragma vector 提供中斷函數的入口位址。 __root 保證沒有使用的函數或者變量也能夠包含在目标代碼中 __no_init 禁止系統啟動的時候初始化變量. asm, __asm **彙編代碼 IAR for AVR 學習筆記(3)--位操作 3.1.在c語言裡對位的操作如一般如下: PORTB|=(1<<2);//置PORTB的第2位=1 PORTB&=~(1<<2);//置PORTB的第2位=0 PORTB^|=(1<<2);//取反PORTB的第2位 While(PORTB&(1<<2));//判斷1 While(!(PORTB&(1<<2)));//判斷為0 3.2.IAR編譯器對位的支援更強大,除了上面的方法外還有以下更簡單的操作方法: PORTB_ Bit2=1; //置PORTB的第2位=1 PORTB_ Bit2=0; //置PORTB的第2位=0 PORTB_ Bit2=~ PORTB_ Bit2;//取反PORTB的第2位 While(PORTB_ Bit2);或者while(PORTB_Bit2==1);//判斷1 while(PORTB_ Bit2==0);//判斷0 PORTC_Bit4=PORTB_Bit2;//把PORTB的第2位傳送到PORTC的第4位 3.3.位變量定義: 由于iar使用了擴充語言,它對位域的支援變為最小為char類型,我們可以很友善地用來定義位變量。 采用結構體來定義位變量: struct { unsigned char bit0:1; unsigned char bit1:1; unsigned char bit2:1; unsigned char bit3:1; unsigned char bit4:1; unsigned char bit5:1; unsigned char bit6:1; unsigned char bit7:1; }t; 然後就可以用以下位變量了。 t.bit0=1; t.bit0=~t.bit0; 但是采用以上結構體做出來的位變量隻可以通路t的位,不能夠直接通路變量t,和标準的IAR位操作也不一樣。采用聯合體來定義效果更佳。 #i nclude<iom8.h> union { unsigned char t; struct {unsigned char t_bit0:1, t_bit1:1, t_bit2:1, t_bit3:1, t_bit4:1, t_bit5:1, t_bit6:1, t_bit7:1; }; }; void main(void) { t_bit0=1;//通路變量t的位 t_bit0=~t_bit0; PORTB=t;//直接通路變量t } 位變量也可以直接定義在工作寄存器裡。 3.4 bool 資料類型在C++語言裡是預設支援的。 如果你在C代碼的頭檔案裡包含stdbool.h, bool資料類型也可以使用在C語言裡。也可以使用布爾值 false和 true。不過是占用8位1個位元組。 #i nclude<iom8.h> #i nclude<stdbool.h> bool y=0;//定義位變量 void main(void) { y=!y;//取反位變量 PORTB_Bit3=y;//傳遞位變量 } IAR for AVR 學習筆記(4)--Flash操作 FLASH常用類型的具體操作方法 4.1.FLASH 區域資料存儲。 用關鍵字 __flash 控制來存放, __ flash 關鍵字寫在資料類型前後效果一樣 __flash unsigned char a;//定義一個變量存放在flash空間 unsigned char __flash a;//效果同上 __flash unsigned char p[];//定義一個數組存放在flash空間 對于flash空間的變量的讀操作同SRAM資料空間的操作方法一樣,編譯器會自動用 LPM,ELPM 指令來操作。 例: #i nclude<iom8.h> __flash unsigned char p[]; __flash unsigned char a; void main(void) {PORTB=p[1];// 讀flash 數組變量的操作 PORTB=a;// 讀flash 變量的操作 } 由于在正常的程式中,flash 空間是隻讀的,是以沒有指派的變量是沒有意義的。定義常數在flash 空間,隻要給變量賦與初值就可以了。由于常數在flash空間的位址是随機配置設定的,讀取變量才可以讀取到常數值。 10 IAR-AVR –C 編譯器簡要指南 __flash unsigned char a=9;//定義一個常數存放在EEPROM空間。 __flash unsigned char p[]={1,2,3,4,5,6,7,8}; //定義一個組常數存放在flash 空間。 例: #i nclude<iom8.h> __flash unsigned char p[]={1,2,3,4,5,6,7,8}; __flash unsigned char a=9; void main(void) { PORTB=a;//讀取flash 空間值9 PORTC=p[0]; //讀取flash 空間值 } 4.1.2flash 空間絕對位址定位: __flash unsigned char a @ 0x8;//定義變量存放在flash 空間0X08單元__flash unsigned char p[] @ 0x22//定義數組存放在flash 空間,開始位址為0X22單元 __flash unsigned char a @ 0x08=9;//定義常數存放在flash 空間0X08單元 __flash unsigned char p[] @ 0x22={1,2,3,4,5,6,7,8}; //定義一個組常數存放在EEPROM空間開始位址為0X22單元 由于常數在flash 空間的位址是已經配置設定的,讀取flash 空間值可以用變量和位址。 4.2.與 __flash 有關的指針操作。 __flash 關鍵字控制指針的存放和類型。 4.2.1指向flash 空間的指針flash 指針(控制類型屬*) unsigned char __flash * p;//定義指向flash 空間位址的指針,8位。 unsigned int __flash * p;//定義個指向flash 空間位址的指針,16位。 unsigned int __farflash * p;//定義指向flash 空間位址的指針,24位。 unsigned int __hugeflash * p;//定義指向flash 空間位址的指針,24位。 unsigned char __flash * p;//定義一個指向flash 空間位址的指針,指針本身存放在SARM中。P的值代表flash 空間的某一位址。*p表示flash 空間該位址單元存放的内容。例:假定p=10,表示flash空間位址10單元,而flash M空間10單元的内容就用*p來讀取。 例: #i nclude<iom8.h> char __flash t @ 0x10 ; char __flash *p ; void main(void) { PORTB=*p;//讀取flash 空間10單元的值 PORTB=*(p+3);//讀取flash 空間0x13單元的值 } 4.2.2.存儲于flash 空間的指針資料指針 就象存儲與flash 空間的資料一樣控制存儲屬* __flash unsigned char * p; //定義指向SARMM空間位址的指針,指針本身存放在flash 中。 4.3.控制資料和指針存放的__flash 定義必須是全局變量,控制類型屬*(好像隻有指針)可以是局部變量。 #i nclude<iom8.h> __flash unsigned char p;//控制存放 void main(void) { unsigned char __flash* t;//控制屬* PORTB=p; PORTB=*t; } 4.4. __root 關鍵字保證沒有使用的函數或者變量也能夠包含在目标代碼中. 定義存放在__flash 空間的資料在程式編譯時會自動生成代碼嵌入到flash代碼中,對于程式沒有使用也要求編譯的資料(比如可以在代碼中嵌入你的版本号,時間等)必須加關鍵字__root 限制。 例: #i nclude<iom8.h> __root __flash unsigned char p @ 0x10 =0x56; void main(void) {} 程式沒有使用P變量,編譯也會生成該代碼。 :020000020000FC :1000000016C018951895189518951895189518955F :10001000569518951895189518951895189518953A :10002000189518951895089500008895FECF0FE94A :100030000DBF00E00EBFC0E8D0E003D0F4DFF4DF76 :06004000F3CF01E008957A :0400000300000000F9 :00000001FF 4.5.flash 操作宏函數:在comp_a90.h intrinsics.h頭檔案裡有詳細說明。flash 空間具正常情況下有隻讀性能,對于讀flash 資料編譯器會自動編譯對應的LPM,ELPM指令,但對于flash 空間的自程式設計寫指令SPM就沒有對應的C指令了,這裡不講解詳細的自程式設計方法,隻是講解一下對flash 的讀寫函數。 直接在程式中讀取flash 空間位址資料:要包含intrinsics.h頭檔案 __load_program_memory(const unsigned char __flash *);//64K空間 //從指定flash 空間位址讀資料。該函數在intrinsics.h頭檔案裡有詳細說明。 在comp_a90.h檔案有它的簡化書寫_LPM(ADDR)。注意彙編指令LPM Rd ,Z中的Z是一個指針。是以用(const unsigned char __flash *)來強制轉換為指向flash空間位址指針。故該條宏函數的正确寫法應該如下: __load_program_memory((const unsigned char __flash *)ADDR); 例: #i nclude<iom8.h> #i nclude <intrinsics.h> void main(void) {PORTB=__load_program_memory((const unsigned char __flash *)0x12); } 該條函數書寫不友善,在comp_a90.h檔案有簡化: #define _LPM(ADDR) __load_program_memory (ADDR)稍微友善一點。改為 #define _LPM(ADDR) __load_program_memory ((const unsigned char __flash *)ADDR)就更友善了,直接使用資料就可以了。 例: #i nclude<iom8.h> #i nclude<comp_a90.h> #i nclude<intrinsics.h> void main(void) { PORTB=__LPM(0x12);// 從指定flash 空間位址單元0x12中讀資料 } __extended_load_program_memory(const unsigned char __farflash *); //128K空間_ELPM(ADDR); //128K空間 參照上面的了解修改可以書寫更簡單。 4.6.自程式設計函數: _SPM_GET_LOCKBITS();//讀取縮定位 _SPM_GET_FUSEBITS();//讀取熔絲位 _SPM_ERASE(Addr);//16位頁擦除 _SPM_FILLTEMP(Addr,Word);//16位頁緩沖 _SPM_PAGEWRITE(Addr;)//16位頁寫入 _SPM_24_ERASE(Addr); //24位頁擦除 _SPM_24_FILLTEMP(Addr,Data); //24位頁緩沖 _SPM_24_PAGEWRITE(Addr) //24位頁寫入 IAR for AVR 學習筆記(5)--SRAM操作 SARM資料類型的具體操作方法 SARM空間是AVR單片機最重要的部分,所有的操作必須依賴該部分來完成。變量在SARM空間的存儲模式有tiny ,small large 三種,也就是對應于__tiny, __near __far三中存儲屬性。一旦選擇為哪種存儲模式,對應的資料預設屬性也就确定了,但可以采用__tiny, __near __far關鍵字來更改。 對于程式中的局部變量,編譯器會自動處理的,我們也不可能加什麼儲存屬*,但IAR提供了強大的外部變量定義。 5.1.定義變量在工作寄存器 IAR編譯器内部使用了部分工作寄存器,留給使用者的隻有R4-R15供12個寄存器供使用者使用,要使用工作寄存器必須在工程選項裡打開鎖定選項。 例: 定義兩個變量使用工作寄存器R14,R15。 #i nclude<iom8.h> __regvar __no_init char g @ 15; __regvar __no_init char P @ 14; void main(void) { g++; P++; } 在工程選項裡c/c++ complier>code裡打開要使用的寄存器R14-R15。 編譯結果就如下,看看是不是直接使用了寄存器做為資料應用 // 4 void main(void) main: CFI Block cfiBlock0 Using cfiCommon0 CFI Function main // 5 { g++; REQUIRE ?Register_R14_is_global_regvar REQUIRE ?Register_R15_is_global_regvar INC R15 // 6 P++; } INC R14 RET 注意:定義在寄存器裡變量不能帶有初始值。最好不要使用超過9個寄存器變量,不然可能引起潛在的危險,因為建立庫的時候沒有鎖定任何寄存器。 5.2.定義變量的絕對位址.沒有特性的變量是随機配置設定的,要給變量配置設定位址必須加以特性修飾注意在定義位址的時候千萬不要和片内寄存器位址重合了。 5.2.1定義沒有存儲特性的絕對位址變量必須加__no_init 或者const對象特* __no_init char t @ 0x65;//定義在I/O位址以外 const char t @ 0x65;//定義隻讀變量的位址 例: #i nclude<iom8.h> __no_init char u @ 0x65 ; void main(void) {u++;} 對應彙編: void main(void) \ main: {u++;} \ 00000000 E6E5 LDI R30, 101 \ 00000002 E0F0 LDI R31, 0 \ 00000004 8100 LD R16, Z \ 00000006 9503 INC R16 \ 00000008 8300 ST Z, R16 \ 0000000A 9508 RET 5.2.2帶存儲特性的關鍵字定義變量的絕對位址__io,__ext_io定義變量在i/o空間 #i nclude<iom8.h> __io char u @ 0x65 ; void main(void) {u++;} 對應彙編: void main(void) \ main: {u++;} \ 00000000 91000065 LDS R16, 101 \ 00000004 9503 INC R16 \ 00000006 93000065 STS 101, R16 \ 0000000A 9508 RET 從5.2.1和5.2.2對比,發現用5.2.2方法定義代碼小多了。 5.3.關鍵字volatile保證從最原始的位置讀取變量。在IAR編譯器裡,除了__no_init和__root定義的變量外,其他的類型的變量都包含有volatile和__no_init特* IAR for AVR 學習筆記(6)--中斷及相關函數操作 6.1.中斷函數: 在IAR編譯器裡用關鍵字來__interrupt來定義一個中斷函數。用#pragma vector來提供中斷函數的入口位址 #pragma vector=0x12//定時器0溢出中斷入口位址 __interrupt void time0(void) { ; } 上面的入口位址寫成#pragma vector=TIMER0_OVF_vect更直覺,每種中斷的入口位址在頭檔案裡有描述。函數名稱time0可以為任意名稱。中斷函數會自動保護局部變量,但不會保護全局變量。 6.2.内在函數也可以稱為本征函數 編譯器自己編寫的能夠直接通路處理器底層特征的函數。在intrinsics.h中有描述完整類型在comp_a90.h裡有進一步的簡化書寫方式 6.2.1延時函數,以周期為标準 __delay_cycles(unsigned long ); 如果處理器頻率為1M,延時100us,如下: __delay_cycles(100 ); 當然你也可以對該函數進行修改: #define CPU_F 1000000 #define delay_us (unsigned long) __delay_cycles((unsigned long )*CPU_F) #define delay_ms (unsigned long) __delay_cycles((unsigned long )*CPU_F/1000) 6.2.2中斷指令 __disable_interrupt( );//**CLI指令, 也可以用_CLI();也可以SREG_Bit7=0; __enable_interrupt( );// **SEI指令,也可以用_SEI();也可以SREG_Bit7=1; 其實對于狀态字的置位和清零隻有BSET S 和BCLR S兩條指令。像SEI不過是BSET 7;的另一個名字而已。AVR指令中還有很多類似的現象,如:ORI 和 SBR 指令完全一樣,号稱130多條指令的AVR其實沒有那麼多指令的。 6.2.3從FLASH空間指定位址讀取資料 __extended_load_program_memory(unsigned char __farflash *); __load_program_memory(unsigned char __flash *); 該條指令以及正确的使用方法在4.5.flash 操作宏函數裡詳細講解,這裡不再重複 6.2.4乘法函數 __fracdtional_multiply_signed(signed char, signed char); __fractional_multiply_signed_with_unsigned(signed char, unsigned char); __fractional_multiply_unsigned(unsigned char, unsigned char); //以上為定點小數乘法 __multiply_signed(signed char, signed char);//有符号數乘法 __multiply_signed_with_unsigned(signed char, unsigned char); //有符号數和無符号數乘法 __multiply_unsigned(unsigned char, unsigned char);//無符号數乘法 6.2.4 半位元組交換指令 __swap_nibbles(unsigned char); 6.2.5 MCU控制指令 __no_operation();//空操作指令 _NOP(); __sleep();//休眠指令 _SLEEP(); __watchdog_reset();//看門狗清零 _WDR(); IAR for AVR 學習筆記(7)--頭檔案含義 avr_macros.h裡面包含了讀寫16位寄存器的簡化書寫,和幾個位操作函數 comp_a90.h對大量的内在函數做了簡要書寫 ina90.h包含"inavr.h" "comp_A90.h"檔案 intrinsics.h内在函數提供最簡單的操作處理器底層特征。休眠,看門狗,FLASH函數。 iomacro.H I/O寄存器定義檔案樣本。 iom8.h 包含I/O等寄存器定義 IAR for AVR 學習筆記(8)--彙編嵌入方式 嵌入彙編語言 線上彙編:使用asm或者__asm,推薦使用__asm。 #i nclude<iom8.h> void main() { asm("NOP \n" "CLH \n" "OR R16,R17 \n"); } 不過IAR提供了完全可以通路底層的函數,建議不要頻繁使用彙編 |