2009-10-24 10:26AVR端口位操作头文件
AVR位操作的实现一般有两种 一种是利用移位 或与
比如DDRA&=(1<<5); PORTA|=(1<<5); 这种的话估计大家都会的
还有一种就是利用位段实现(位段的更多知识可以参照有关C语言
籍 我就不多说啦)
看不懂代码也不要紧 会用就可以!
#ifndef _bit_h
#define _bit_h
typedef struct //定义一个8字节的位段 bit0~7是每个位段名称 1代
一位 PBIT就是整个位段的名称
{
unsigned bit0 : 1 ;
unsigned bit1 : 1 ;
unsigned bit2 : 1 ;
unsigned bit3 : 1 ;
unsigned bit4 : 1 ;
unsigned bit5 : 1 ;
unsigned bit6 : 1 ;
unsigned bit7 : 1 ;
}PBIT;
#define PORTABIT (*(volatile PBIT *)0x3B)
/*0x3B是PORTA的地址 (volatile PBIT *)0x3B 就是将这个地址强制类型装换为我们前面定义过的位段
volatile关键字还不懂的话去查点
~~前面加个*号 不用我说也知道什么意思吧*/
#define DDRABIT (*(volatile PBIT *)0x3A)
#define PINABIT (*(volatile PBIT *)0x39)
#define PORTBBIT (*(volatile PBIT *)0x38)
#define DDRBBIT (*(volatile PBIT *)0x37)
#define PINBBIT (*(volatile PBIT *)0x36)
#define PORTCBIT (*(volatile PBIT *)0x35)
#define DDRCBIT (*(volatile PBIT *)0x34)
#define PINCBIT (*(volatile PBIT *)0x33)
#define PORTDBIT (*(volatile PBIT *)0x32)
#define DDRDBIT (*(volatile PBIT *)0x31)
#define PINDBIT (*(volatile PBIT *)0x30)
/*使用方法:要将PORTA的第一位输出为高电平
DDRABIT.bit0=1;
PORTABIT.bit0=1;
或者为了便于程序的移植我们一般用宏定义
#define power PORTABIT.bit0
power=1;
#define power_out() DDRABIT.bit0=1
*/
//当然PORTABIT.bit0敲起来有点长 可以用宏定义把他改小一点
#define PORTABIT0 PORTABIT.bit0
//可以改成自己看的习惯的 但不要与系统头文件中定义过的冲突 比如PA0 PORTA0
#define PORTABIT1 PORTABIT.bit1
#define PORTABIT2 PORTABIT.bit2
#define PORTABIT3 PORTABIT.bit3
#define PORTABIT4 PORTABIT.bit4
#define PORTABIT5 PORTABIT.bit5
#define PORTABIT6 PORTABIT.bit6
#define PORTABIT7 PORTABIT.bit7
#define PORTBBIT0 PORTBBIT.bit0
#define PORTBBIT1 PORTBBIT.bit1
#define PORTBBIT2 PORTBBIT.bit2
#define PORTBBIT3 PORTBBIT.bit3
#define PORTBBIT4 PORTBBIT.bit4
#define PORTBBIT5 PORTBBIT.bit5
#define PORTBBIT6 PORTBBIT.bit6
#define PORTBBIT7 PORTBBIT.bit7
#define PORTCBIT0 PORTCBIT.bit0
#define PORTCBIT1 PORTCBIT.bit1
#define PORTCBIT2 PORTCBIT.bit2
#define PORTCBIT3 PORTCBIT.bit3
#define PORTCBIT4 PORTCBIT.bit4
#define PORTCBIT5 PORTCBIT.bit5
#define PORTCBIT6 PORTCBIT.bit6
#define PORTCBIT7 PORTCBIT.bit7
#define PORTDBIT0 PORTDBIT.bit0
#define PORTDBIT1 PORTDBIT.bit1
#define PORTDBIT2 PORTDBIT.bit2
#define PORTDBIT3 PORTDBIT.bit3
#define PORTDBIT4 PORTDBIT.bit4
#define PORTDBIT5 PORTDBIT.bit5
#define PORTDBIT6 PORTDBIT.bit6
#define PORTDBIT7 PORTDBIT.bit7
//**********************
#define DDRABIT0 DDRABIT.bit0
#define DDRABIT1 DDRABIT.bit1
#define DDRABIT2 DDRABIT.bit2
#define DDRABIT3 DDRABIT.bit3
#define DDRABIT4 DDRABIT.bit4
#define DDRABIT5 DDRABIT.bit5
#define DDRABIT6 DDRABIT.bit6
#define DDRABIT7 DDRABIT.bit7
#define DDRBBIT0 DDRBBIT.bit0
#define DDRBBIT1 DDRBBIT.bit1
#define DDRBBIT2 DDRBBIT.bit2
#define DDRBBIT3 DDRBBIT.bit3
#define DDRBBIT4 DDRBBIT.bit4
#define DDRBBIT5 DDRBBIT.bit5
#define DDRBBIT6 DDRBBIT.bit6
#define DDRBBIT7 DDRBBIT.bit7
#define DDRCBIT0 DDRCBIT.bit0
#define DDRCBIT1 DDRCBIT.bit1
#define DDRCBIT2 DDRCBIT.bit2
#define DDRCBIT3 DDRCBIT.bit3
#define DDRCBIT4 DDRCBIT.bit4
#define DDRCBIT5 DDRCBIT.bit5
#define DDRCBIT6 DDRCBIT.bit6
#define DDRCBIT7 DDRCBIT.bit7
#define DDRDBIT0 DDRDBIT.bit0
#define DDRDBIT1 DDRDBIT.bit1
#define DDRDBIT2 DDRDBIT.bit2
#define DDRDBIT3 DDRDBIT.bit3
#define DDRDBIT4 DDRDBIT.bit4
#define DDRDBIT5 DDRDBIT.bit5
#define DDRDBIT6 DDRDBIT.bit6
#define DDRDBIT7 DDRDBIT.bit7
//*****************
#define PINABIT0 PINABIT.bit0
#define PINABIT1 PINABIT.bit1
#define PINABIT2 PINABIT.bit2
#define PINABIT3 PINABIT.bit3
#define PINABIT4 PINABIT.bit4
#define PINABIT5 PINABIT.bit5
#define PINABIT6 PINABIT.bit6
#define PINABIT7 PINABIT.bit7
#define PINBBIT0 PINBBIT.bit0
#define PINBBIT1 PINBBIT.bit1
#define PINBBIT2 PINBBIT.bit2
#define PINBBIT3 PINBBIT.bit3
#define PINBBIT4 PINBBIT.bit4
#define PINBBIT5 PINBBIT.bit5
#define PINBBIT6 PINBBIT.bit6
#define PINBBIT7 PINBBIT.bit7
#define PINCBIT0 PINCBIT.bit0
#define PINCBIT1 PINCBIT.bit1
#define PINCBIT2 PINCBIT.bit2
#define PINCBIT3 PINCBIT.bit3
#define PINCBIT4 PINCBIT.bit4
#define PINCBIT5 PINCBIT.bit5
#define PINCBIT6 PINCBIT.bit6
#define PINCBIT7 PINCBIT.bit7
#define PINDBIT0 PINDBIT.bit0
#define PINDBIT1 PINDBIT.bit1
#define PINDBIT2 PINDBIT.bit2
#define PINDBIT3 PINDBIT.bit3
#define PINDBIT4 PINDBIT.bit4
#define PINDBIT5 PINDBIT.bit5
#define PINDBIT6 PINDBIT.bit6
#define PINDBIT7 PINDBIT.bit7
#endif
/*--------------------------------------------------------------*/
//ICC-AVR application builder :
// Target : M8
// Crystal: 1.000Mhz
/*--------------------------------------------------------------*/
/*--------------------------------------------------------------*/
//防止重复定义
#ifndef __ICCAVRIO_H__
#define __ICCAVRIO_H__
/*--------------------------------------------------------------*/
//定义新的数据类型,方便进行IO端口操作。
//定义一个8字节的位域 bit0~7是每个位域名称, 1代表一位, bit_field就是整个位域的名称
typedef struct
{
unsigned bit0: 1 ;
unsigned bit1: 1 ;
unsigned bit2: 1 ;
unsigned bit3: 1 ;
unsigned bit4: 1 ;
unsigned bit5: 1 ;
unsigned bit6: 1 ;
unsigned bit7: 1 ;
}bit_field;
#define GET_BIT(adr) (*(( volatile bit_field * )(&adr)))
/*--------------------------------------------------------------*/
//设置是否自动初始化IO方向寄存器
//自动初始化IO方向寄存器无需在初始化程序中用PORTA=0X..;形式来初始化io控制寄存器,
//同时也不争的证明了avr单片机的端口输入/出切换功能
#define AUTOINIT 1
// 0 //手动配置IO方向寄存器
// 1 //自动配置IO方向寄存器
/*--------------------------------------------------------------*/
//输出端口设置
#if (AUTOINIT == 1) //自动初始化IO方向寄存器
#define PORT(m, n) GET_BIT(DDR##m).bit##n = 1;\
GET_BIT(PORT##m).bit##n
#define P_OUT(m, n) DDR##m |= n;\
PORT##m
#else //需手动配置IO方向寄存器
#define PORT(m, n) GET_BIT(PORT##m).bit##n
#endif
/*--------------------------------------------------------------*/
//输入端口设置(
输入端口)
#if (AUTOINIT == 1) //自动初始化IO方向寄存器
#define PIN(m, n) (!(GET_BIT(DDR##m).bit##n = 0) &&\
(GET_BIT(PORT##m).bit##n = 1) &&\
GET_BIT(PIN##m).bit##n)
#define P_IN(m, n) (DDR##m &= ~n, PORT##m = n, PIN##m & n)
#else //需手动配置IO方向寄存器
#define PIN(m, n) GET_BIT(PIN##m).bit##n
#endif
/*-------------------------------------------------------------*/
//端口输入输出方向设置
//方便直观操作 自由设定单个io口的方向
#define DRA(n) GET_BIT(DDRA).bit##n
#define DRB(n) GET_BIT(DDRB).bit##n
#define DRC(n) GET_BIT(DDRC).bit##n
#define DRD(n) GET_BIT(DDRD).bit##n
#define DDR(m, n) GET_BIT(DDR##m).bit##n
/*--------------------------------------------------------------*/
#endif
/*-------------------------------------------------------------*/
/*
//在我们实际项目中,需要用到按键输入,继电器,SPI器件输出,
//两者分别为输入,和输出之用,这时候可以方面的在各自c文件对应的.h文件中写下如下语句:
#define KEY1 PIN(C, 3) //定义三个按键,使能上拉
#define KEY2 PIN(C, 4)
#define KEY3 PIN(C, 5)
#define SCLK_SPI PORT(B, 5) //定义spi口的两个控制引脚
#define CS_SPI PORT(C, 0)
//多位定义(数据总线端口)
#define KEY_IN P_IN(D, 0x0f) //按键手动输入 低四位设置为输入,其他位方向不变
#define LED_OUT P_OUT(C, 0x0f) //LED显示输出 低四位设置为输出,其他位方向不变
定义后直接赋值:
LED_OUT = ~KEY_IN; //高电平点亮LED, 按键低电平有效
#define LCD_DATA_IN P_IN(D, 0xff) //LCD数据总线设置为输入
#define LCD_DATA_OUT P_OUT(D, 0xff) //LCD数据总线设置为输出
定义后直接赋值:
TEMP = LCD_DATA_IN; //读入数据
LCD_DATA_OUT = TEMP; //输出数据
//上述PIN PORT 自动化定义的方法中,有些不足,
//如:在DS18B20这样的应用中,需要切换引脚的输入输出,就必须为18B20的引脚安排两套定义,
//类似于:
#define DS18B20_IN PIN(A, 1)
#define DS18B20_OUT PORT(A, 1)
//此外:PORT和PIN的自动化定义中,含有DDR的操作,
//凡是用到PIN和PORT定义过的端口的地方都需要重复DDR操作,带来冗余代码。
//呵呵,虽然代码有点繁琐,但却很方便的,便于程序的移植。
//希望朋友们能够用得上!
/*-------------------------------------------------------------*/
AVR的两种位操作的比较(位域方式和移位宏方式)
测试环境如下:
硬件:AT90S2313
软件: WiinAVR gcc3.3 -Os级优化(最小size)。
说明:
由于AVR不支持位操作,所以必须通过软件来实现。下面对我所知道的两种方法进行一个简单的比较。
1、位域方式。先定义一个位域,
typedef struct _bit_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 bit7 : 1 ;
unsigned char bit6 : 1 ;
}bit_field;
再用一个宏 ,来指向要操作的位。
#define LED GET_BITFIELD(PORTB).bit0
#define BUTTON GET_BITFIELD(PINB).bit7
使用时只需要直接赋值即可:如LED = 0 ,LED = 1, 或者直接判断 LED==0 , LED ==1.
这种方法类似C51中的位操作。直接。
2、位移宏方式。主要有三个.
#define Set_Bit(val, bitn) (val |=(1<<(bitn)))
#define Clr_Bit(val, bitn) (val&=~(1<<(bitn)))
#define Get_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;如是其它编译器,请修改。
#include
// 定义一个寄存器(Register)或端口(Port)的八个位
typedef struct _bit_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 bit7 : 1 ;
unsigned char bit6 : 1 ;
}bit_field;
//定义一个宏,用来得到每一位的值
#define GET_BITFIELD(addr) (*((volatile bit_field *) (addr)))
//定义每一个位
#define LED GET_BITFIELD(PORTB).bit0
#define BUTTON GET_BITFIELD(PINB).bit7
#define Set_Bit(val, bitn) (val |=(1<<(bitn)))
#define Clr_Bit(val, bitn) (val&=~(1<<(bitn)))
#define Get_Bit(val, bitn) (val &(1<<(bitn)) )
int main( void )
{
DDRB = 0x41; //配置PB0为输出,PB7为输入
if ( BUTTON==0 ) LED = 1; else LED = 0;
//if(!Get_Bit(PINB,7) ) Set_Bit(PORTB,0); else Clr_Bit(PORTB,0);
while(1);
}
// ---------------------- end -----------------------------
4、测试过程。
a.先使用位域方式。
主程序中使用 if ( BUTTON==0 ) LED = 1; else LED = 0;
结果如下:
int main( void )
{
4a: cf ed ldi r28, 0xDF ; 223
4c: d0 e0 ldi r29, 0x00 ; 0
4e: de bf out 0x3e, r29 ; 62
50: cd bf out 0x3d, r28 ; 61
DDRB = 0x41; //配置PB0为输出,PB7为输入
52: 81 e4 ldi r24, 0x41 ; 65
54: 87 bb out 0x17, r24 ; 23
if ( BUTTON==0 ) LED = 1; else LED = 0;
56: 86 b3 in r24, 0x16 ; 22
58: e8 2f mov r30, r24
5a: ff 27 eor r31, r31
5c: 80 81 ld r24, Z
5e: 86 fd sbrc r24, 6
60: 07 c0 rjmp .+14 ; 0x70
62: 88 b3 in r24, 0x18 ; 24
64: e8 2f mov r30, r24
66: ff 27 eor r31, r31
68: 80 81 ld r24, Z
6a: 81 60 ori r24, 0x01 ; 1
6c: 80 83 st Z, r24
6e: 06 c0 rjmp .+12 ; 0x7c
70: 88 b3 in r24, 0x18 ; 24
72: e8 2f mov r30, r24
74: ff 27 eor r31, r31
76: 80 81 ld r24, Z
78: 8e 7f andi r24, 0xFE ; 254
7a: 80 83 st Z, r24
while(1);
7c: ff cf rjmp .-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; else LED = 0; 这句用了19条指令38Bytes. (居然运用了3个寄存器白r24,r30,r31,和一个Z,代码真是苦涩,,我看不懂,准备以后作代码加密用:). )
b.使用移位宏方式。
将 if ( BUTTON==0 ) LED = 1; else LED = 0; 换为等效的 if(!Get_Bit(PINB,7) ) Set_Bit(PORTB,0); else Clr_Bit(PORTB,0);
结果,main函数仅24Bytes.其它代码一样,略去. 所以,上面这句代码仅用了24-14=10Bytes ,5条指令。生成的代码如下:
56: b7 99 sbic 0x16, 7 ; 22
58: 02 c0 rjmp .+4 ; 0x5e
5a: c0 9a sbi 0x18, 0 ; 24
5c: 01 c0 rjmp .+2 ; 0x60
5e: c0 98 cbi 0x18, 0 ; 24
5. 菜论:鱼和熊掌。
由于AVR可以对I/O脚进行sbic,sbi,cbi,这样的位操作,所以使用I/O脚操作时,移位宏可以产生高效的代码。
例如,要实现上面的几个简单的指令,为了实现LED=1这样的类似C51的sbit的效果,我必须多付出(38-10=28Bytes)的代价。
6......
对于I/O脚,可以产生这样高效的代码,是因为有sbi和cbi这样的指令,那么对于一般的变量,又如何呢?................
AVR的位操作只能对地址小于$1F的寄存器使用
大于$1F的寄存器,AVR 芯片本身就不支持位操作,什么语言都一样,只能用操作RAM的方式来处理
几个位操作的小函数,经过了CVAVR的编译(其他编译器也是一样的),AVRStudio的模拟调试。结果是正确的,但运行时间约10uS,有兴趣的网友可以来交流。
1 测试指定的位
unsigned char BitTst(unsigned char *Pa,char b) //Pa为被测试的对象的地址,b值为指定的位
{
return *Pa & (1<