微波EDA网,见证研发工程师的成长!
首页 > 硬件设计 > 嵌入式设计 > AVR单片机C语言程序设计中的位操作

AVR单片机C语言程序设计中的位操作

时间:11-20 来源:互联网 点击:
在标准C语言的的教材中,对于位运算的操作是基本不涉及的,但是在单片机系统的程序中,需要经常操作各类以字节为单位的寄存器,而这些寄存器通常都是以二进制中的位为控制单位的数据组合。往往一个8位寄存器中的每一位都有各自的控制对象,例如端口B的方向寄存器DDRB,如下图所示

它实际上控制着PB口的8个端口PB0-PB7的方向,也就是说它的每一位都控制一个端口的方向,如果我们要把端口PB0-PB3设置为输出口,而把PB4-PB7设置为输入口,在不用位运算符的情况下,我们可以直接使用赋值语句DDRB=0x0f来实现,这样是完全可以实现的。
但是如果出现下面的情况:在程序中PB口的8位端口的状态本来是1、3、5、7为输入。0、2、4、6为输出(即DDRB=0x55),接下来要将PB口的第1位设置为输出,其它端口的状态不变,然后又要将第2位设置为输入,其它端口的状态不变。该怎么实现?也许我们仍然可以使用赋值语句来实现,比如DDRB=0x55;接下来设置DDRB=0x57;然后再设置DDRB=0x53;首先要肯定的是,这种做法是绝对正确的。但是我们可能有没有注意到,在改变其中一位的值的时候,我们同时还要考虑其它7位的状态,并且要小心翼翼的避免不小心改变了其它位的值。
那么有没有一种方法,可以简单的实现修改某一位的状态,同时不会改变其它位的状态呢?
这就牵出了单片机C语言程序设计中的位运算的概念。
我们来看这个语句:DDRE |= (1 < PE5); 这个语句实现的功能是将PE口的第5位设置为输出口,其余口的状态不变。它是怎么实现的?首先我们来看1 < PE5这个表达式,我们前面已经介绍了,AVR各寄存器的宏定义是在头文件io.h中定义好的,我们可以直接调用,现在

我们就来看看在io.h中PE5是如何定义的(在WINAVR的安装目录下查找iom64.h),我们可以看到PE口的8位分别定义如下:
/* Port E Data Register - PORTE */
#define PE7 7
#define PE6 6
#define PE5 5
#define PE4 4
#define PE3 3
#define PE2 2
#define PE1 1
#define PE0 0
可以看出,实际上PE5=5;那么1 < PE5,就很容易理解了,它的作用是把1左移5位,最后的结果按照二进制表示就是0b00100000,而DDRE|= (1 < PE5);实际上是DDRE= DDRE | (1 < PE5);首先“|”表示的是“或”操作,DDRE是端口E的方向寄存器,在iom64.h中定义为:

#define DDRE _SFR_IO8(0x02);它实际上是定义了一个标识符,这个标识符对应数据存储区RAM中的某个地址,这个我们暂且不去深究。我们还是回过头来看DDRE= DDRE | (1 < PE5);这句话实现的功能,这句话实际上是将寄存器DDRE中的内容(数据)跟二进制数0b00100000进行或操作,我们知道两个数的或操作的结果是:只要有一个是1,结果就是1.那么假如DDRE中本来的值是0b10001010(0x8a),它和0b00100000进行“或”操作以后的结果变成了0b10101010(0xaa)。我们可以看出,相或以后DDRE中的第5位以外的各位的值都没有改变,而第5位变成了1,我们的目的就是要将第5位设为输出口(即将第5位设置为1)。
现在我们来看一下从语言中有几种位运算符:
移位运算符:左移<,右移>>
与运算符:&
或运算符:|
取反运算符:~
异或运算符:^
就这些了,总共只有6种位运算符。现在我们来看一下这些运算符都起什么作用;
左移运算符:表达形式为x例如,x是一个unsigned char类型的数据(即x是一个单字节数据,共有8位),设x的初值为0x00000001,执行xn=0时,xn=1时,xn=2时,x...
n=7时,xn=8时,x从结果来看,当n在1-7之间取值时,运算的结果总是1依次向左移动一位,但是当n>=8以后,x的值就一直是0了,这是因为x是一个只有8位的整形变量,它的最大二进制长度是8位,当n超过8以后,移位操作的结果已经超出8位的范围了,产生了溢出现象。
右移的原理跟左移相似,只是它的运算结果跟左移刚好相反。
在单片机C语言编程中,经常使用移位操作来实现将数据乘以(左移)或除以(右移)2的n次方的乘除运算,利用移位操作实现乘除运算可以显著提高单片机的运算速度和效率。其详细原理我们可以翻阅相关的C语言书籍来进行更深了解。
“取反”、“与”、“或”、“非”运算经常用于对寄存器的某一位进行操作,
例如,使端口B的第二位输出高电平,同时不改变其余端口的状态,我们可以采用如下方法:
PORTB |= (1实际上,想将一个8位寄存器的某一位设置为1,可以采用这样的语句:寄存器名(如PORTB) |= (1 < X),式中X表示第X位。
相反的,如果想将一个8位寄存器的某一位设置为0,可以采用这样的语句:寄存器名(如PORTB) &= ~(1 < X),式中X表示第X位。

Copyright © 2017-2020 微波EDA网 版权所有

网站地图

Top