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

 原文地址::http://www.eefocus.com/lsm1989/blog/11-05/219293_343a9.html   maybe 具体不详

 

在标准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<<n,意思是将数据x向左移动n位,在这里x和n都必须是无符号整形数据(所有的位运算符的操作对象都是无符号整形);
      例如,x是一个unsigned char类型的数据(即x是一个单字节数据,共有8位),设x的初值为0x00000001,执行x<<n的操作:
n=0时,x<<n表示x左移0位,实际就是不移动,x的值不改变
n=1时,x<<n表示x左移1位,运算结果为0b00000010
n=2时,x<<n表示x左移2位,运算结果为0b00000100
...
n=7时,x<<n表示x左移7位,运算结果为0b10000000
n=8时,x<<n表示x左移8位,运算结果为0b00000000
       从结果来看,当n在1-7之间取值时,运算的结果总是1依次向左移动一位,但是当n>=8以后,x的值就一直是0了,这是因为x是一个只有8位的整形变量,它的最大二进制长度是8位,当n超过8以后,移位操作的结果已经超出8位的范围了,产生了溢出现象。
        右移的原理跟左移相似,只是它的运算结果跟左移刚好相反。
       在单片机C语言编程中,经常使用移位操作来实现将数据乘以(左移)或除以(右移)2的n次方的乘除运算,利用移位操作实现乘除运算可以显著提高单片机的运算速度和效率。其详细原理我们可以翻阅相关的C语言书籍来进行更深了解。
      “取反”、“与”、“或”、“非”运算经常用于对寄存器的某一位进行操作,
例如,使端口B的第二位输出高电平,同时不改变其余端口的状态,我们可以采用如下方法:
PORTB |= (1<<PB2),
 实际上,想将一个8位寄存器的某一位设置为1,可以采用这样的语句:寄存器名(如PORTB) |= (1 << X),式中X表示第X位。
        相反的,如果想将一个8位寄存器的某一位设置为0,可以采用这样的语句:寄存器名(如PORTB) &= ~(1 << X),式中X表示第X位。

        写着的时候,总感觉心里清楚,但是表达不出来,本来是想结合单片机来讲解如何用C语言来开发单片机程序的,但总是没有办法将两者的结合很直观的表达出来。有些困惑!
        这或许就是现在市面上相当多的讲解单片机C语言开发的书为什么总是将C语言的讲解和具体的程序设计分开来讲的原因吧。大家都没有更好的办法在讲解具体的单片机开发的同时把C语言的知识逐步融合到实例中

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值