出处:http://blog.sina.com.cn/s/blog_72266aca0100mjnp.html
IAR学习笔记(摘抄)
2008年03月25日 星期二 18:03
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提供了完全可以访问底层的函数,建议不要频繁使用汇编
|
IAR优化选项 #pragma optimize 指令使用
要注意的是:设置优化等级要小于初始options默认设置的优化等级
#pragmaoptimize= none //one of none, low,medium, high, size, or speed放在被优化函数前
#pragma optimize 指令格式:
#pragma optimize=token1 token2 token3
where token isone or more of the following:
token1、
speed Optimizes forspeed。
size Optimizes for size。
token2 、
2|3|6|9 Specifies level of optimization。
token3、
no_cse Turns off common sub-expression elimination。
no_inline Turns offfunction inlining。
no_unroll Turns offloop unrolling。
no_code_motion Turns off code motion.
Example
#pragma optimize=speed 9
int small_and_used_often()
{
...
}
#pragma optimize= size 9
int big_and_seldom_used()
{
...
}
======================================================================================
使用好处:
1、 可以使用这个方便优化或者不优化部分程序。
2、 可用于查找优化等级过高导致的程序错误,查找优化错误的函数。
获得相对地址:
和MPLAB、keil不同,可以直接用“../”这样的用相对路径,IAR用$PROJ_DIR$\linker\lnkr5f100fe.xcl和$TOOLKIT_DIR$\LIB\
IAR中常用的 #pragma 命令和扩展关键字
pragma 命令
1、#pragma message(“ ”)
编译器编译到此处,在Build窗口中打印相应文本信息。
2、#pragma error “”
编译器编译到此处,在Build窗口中产生错误并打印其文本信息。
3、#pragma inline [=forced | never]
用这个指令是建议编译将这条指令后面的函数内联到调用它的函数的函数体中去。
当#pragma inline = forced,则强制让编译器对函数内联,如果内联不成功,会发出警告消息。
4、#pragma location = {address | register | NAME}
该指令用处
-
定位该指令之后的全局或静态变量到指定的absolute address上。其中变量必须定义为__no_init。
pragma location = address 等价于 @ address,其中变量也必须定义为__no_init 。
示例:
__no_init volatile char alpha @ 0xFF2000
运用绝对地址定位,还需注意地址的对齐问题。
-
定位该指令之后的变量到指定寄存器中,其中该变量必须声明为__no_init,同时该变量的作用域为整个文件。
===========register(寄存器R4-R11)
pragma location = register 等价于 @ register,其中变量也必须定义为__no_init
-
将该指令之后的函数或变量放置到一个指定的section中。其中,不要试图将那些通常放在不同section的变量放置在同一section中。
=============NAME(A user-defined section name; cannot be a section name predefined for use by the compiler and linker.
pragma location = section 等价于 @ section
示例
变量放置在自定义的section中。
__no_init int alpha @ “MY_NOINIT”; /* OK */
#pragma location=”MY_CONSTANTS”
const int beta; /* OK */
函数放置在自定义section中。
void f(void) @ “MY_FUNCTIONS”;
void g(void) @ “MY_FUNCTIONS”
{
}
#pragma location=”MY_FUNCTIONS”
void h(void);
- #pragma required = symbol
#pragma required确保链接输出中包括某个符号所需的另一个符号。该指令必须放在紧邻第二个符号的前边。如果符号在应用中不可见,使用该指令。例如,如果仅通过某个变量所在的段对其进行间接引用,必须使用#pragma required。
eg:
const char copyright[] = “Copyright by me”;
#pragma required=copyright
int main()
{
/* Do something here. */
}
Even if the copyright string is not used by the application, it will still be included by the
linker and available in the output.
5、扩展关键字
__no_init
正常情况下,应用程序启动时,IAR运行时环境将全部全局和静态变量初始化为0。IAR C编译器支持声明不初始化的变量,使用__no_init类型限定符。声明__no_init的变量不需初始化。一些关键数据在系统复位(如看门狗复位或其他原因造成的复位)时的数值是不能改变的!在这种情况下可用__no_init限定。
__root
__root限定的函数和变量在即是没有被任何函数引用的情况下,它依然存在于目标代码中而不会被优化掉。
__task
被该关键字限定的函数在被调用时不会做寄存器保护,即没有寄存器入栈出栈操作。通常用在RTOS的启动函数中。
By default, functions save the contents of used preserved registers on the stack upon entry, and restore them at exit. Functions that are declared __taskdo not save all registers, and therefore require less stack space.
Because a function declared __taskcan corrupt registers that are needed by the calling function, you should only use __taskon functions that do not return or call such a function from assembler code.
The function maincan be declared __task, unless it is explicitly called from the
application. In real-time applications with more than one task, the root function of each task can be declared __task.
在IAR中实验了一下,发现编译器还是将寄存器进行了压栈操作,不知为何?
__weak
在链接阶段,一个symbol可以有多个weak定义和最多一个non-weak定义。如果symbol需要被程序引用,存在non-weak,则non-weak被引用。如果不在在non-weak,则weak中的一个会被应用。