AVR的两种
位操作的比较(wjc3k发于21ic)(位域与C位操作的比较)
AVR的两种位操作的比较(位域方式和移位宏方式)
测试环境如下:
硬件:AT90S2313
软件:WiinAVRgcc3.3-Os级优化(最小size)。
说明:
由于AVR不支持位操作,所以必须通过软件来实现。下面对我所知道的两种方法进行一个简单的比较。
1、位域方式。先定义一个位域,
typedefstruct_bit_struct
{
unsignedcharbit0:1;
unsignedcharbit1:1;
unsignedcharbit2:1;
unsignedcharbit3:1;
unsignedcharbit4:1;
unsignedcharbit5:1;
unsignedcharbit6 :1;
unsignedcharbit7:1;
}bit_field;
再用一个宏,来指向要操作的位。
#defineLEDGET_BITFIELD(PORTB).bit0
#defineBUTTONGET_BITFIELD(PINB).bit7
使用时只需要直接赋值即可:如LED=0,LED=1,或者直接判断LED==0,LED==1.
这种方法类似C51中的位操作。直接。
2、位移宏方式。主要有三个.
#defineSet_Bit(val,bitn)(val|=(1<(bitn)))
#defineClr_Bit(val,bitn)(val&=~(1<(bitn)))
#defineGet_Bit(val,bitn)(val&(1<(bitn)))
三个分别用来设置某一位,清除某一位,取某一位的值.
使用方法为.Set_Bit(PORTA,3);Clr_Bit(PORTB,2);Get_Bit(val,5);
3、测试程序.
说明,假设PORTB.7接按纽,PORTB.0接LED
测试程序完成如下操作。
当BUTTON==0时,LED输出1否则输出0,
这样的目的是即测试了输入,又测试了输出1和输出0,相对全面一点。C代码如下.
//testled.c测试AVR的位操作.
//这是gcc;如是其它编译器,请修改。
#i ncludeavr/io.h>
//定义一个寄存器(Register)或端口(Port)的八个位
typedefstruct_bit_struct
{
unsignedcharbit0:1;
unsignedcharbit1:1;
unsignedcharbit2:1;
unsignedcharbit3:1;
unsignedcharbit4:1;
unsignedcharbit5:1;
unsignedcharbit7:1;
unsignedcharbit6:1;
}bit_field;
//定义一个宏,用来得到每一位的值
#defineGET_BITFIELD(addr)(*((volatilebit_field*)(addr)))
//定义每一个位
#defineLEDGET_BITFIELD(PORTB).bit0
#defineBUTTONGET_BITFIELD(PINB).bit7
#defineSet_Bit(val,bitn)(val|=(1<(bitn)))
#defineClr_Bit(val,bitn)(val&=~(1<(bitn)))
#defineGet_Bit(val,bitn)(val&(1<(bitn)))
intmain(void)
{
DDRB=0x41;//配置PB0为输出,PB7为输入
if(BUTTON==0)LED=1;elseLED=0;
//if(!Get_Bit(PINB,7))Set_Bit(PORTB,0);elseClr_Bit(PORTB,0);
while(1);
}
//----------------------end-----------------------------
4、测试过程。
a.先使用位域方式。
主程序中使用if(BUTTON==0)LED=1;elseLED=0;
结果如下:
intmain(void)
{
4a:cfedldir28,0xDF;223
4c:d0e0ldir29,0x00;0
4e:debfout0x3e,r29;62
50:cdbfout0x3d,r28;61
DDRB=0x41;//配置PB0为输出,PB7为输入
52:81e4ldir24,0x41;65
54:87bbout0x17,r24;23
if(BUTTON==0)LED=1;elseLED=0;
56:86b3inr24,0x16;22
58:e82fmovr30,r24
5a:ff27eorr31,r31
5c:8081ldr24,Z
5e:86fdsbrcr24,6
60:07c0rjmp.+14;0x70
62:88b3inr24,0x18;24
64:e82fmovr30,r24
66:ff27eorr31,r31
68:8081ldr24,Z
6a:8160orir24,0x01;1
6c:8083stZ,r24
6e:06c0rjmp.+12;0x7c
70:88b3inr24,0x18;24
72:e82fmovr30,r24
74:ff27eorr31,r31
76:8081ldr24,Z
78:8e7fandir24,0xFE;254
7a:8083stZ,r24
while(1);
7c:ffcfrjmp.-2;0x7c
main函数共52Bytes.其中,从lst文件看得出:main函数的初始化用了4条指令,8Bytes.最后一句while(1);用了1条指令2Bytes.(for循环和do-while也是)
DDRB=0x41用了2条指令4Bytes.计算一下:52-8-4-2=38Bytes,即if(BUTTON==0)LED=1;elseLED=0;这句用了19条指令38Bytes.(居然运用了3个寄存器白r24,r30,r31,和一个Z,代码真是苦涩,,我看不懂,准备以后作代码加密用:).)
b.使用移位宏方式。
将if(BUTTON==0)LED=1;elseLED=0;换为等效的if(!Get_Bit(PINB,7))Set_Bit(PORTB,0);elseClr_Bit(PORTB,0);
结果,main函数仅24Bytes.其它代码一样,略去.所以,上面这句代码仅用了24-14=10Bytes,5条指令。生成的代码如下:
56:b799sbic0x16,7;22
58:02c0rjmp.+4;0x5e
5a:c09asbi0x18,0;24
5c:01c0rjmp.+2;0x60
5e:c098cbi0x18,0;24
5.菜论:鱼和熊掌。
由于AVR可以对I/O脚进行sbic,sbi,cbi,这样的位操作,所以使用I/O脚操作时,移位宏可以产生高效的代码。
例如,要实现上面的几个简单的指令,为了实现LED=1这样的类似C51的sbit的效果,我必须多付出(38-10=28Bytes)的代价。
6......
对于I/O脚,可以产生这样高效的代码,是因为有sbi和cbi这样的指令,那么对于一般的变量,又如何呢?................