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

来源:互联网 发布:mac版painter2015破解 编辑:程序博客网 时间:2024/04/29 18:50
 在标准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语言的知识逐步融合到实例中。

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 小学一年级学生厌学怎么办 小学二年级厌学怎么办 做题做的烦躁了怎么办 英语不会做题怎么办 孩子审题不细心怎么办 生孩子有恐惧症怎么办 小孩不喜欢上幼儿园怎么办 小孩不喜欢去幼儿园怎么办 小孩不喜欢学生字怎么办 学生不喜欢补课老师怎么办 小孩不喜欢上学逃课怎么办 一年级小朋友不爱写字怎么办 幼儿园小朋友不爱写字怎么办 孩子懒散不积极怎么办 孩子不肯上幼儿园怎么办 孩子不肯去幼儿园怎么办 小孩记不住字怎么办 小孩不会写字要怎么办 一年级孩子不爱写字怎么办 一年级小孩不爱写字怎么办 孩子不爱写字怎么办呢 幼儿园孩子不爱写字怎么办 孩子上学没学籍怎么办 孩子上学务工证怎么办 孩子上学被欺负怎么办 孩子害怕上幼儿园怎么办 孩子写字肩膀疼怎么办 5岁不会写字怎么办 上中班不爱写字怎么办 孩子性子太慢怎么办 13小孩特别懒怎么办 小孩不肯上幼儿园怎么办 宝宝不肯上幼儿园怎么办 宝宝不肯去幼儿园怎么办 小孩子不肯去幼儿园怎么办 上幼儿园不说话怎么办 小孩写字不认真怎么办 游戏打开是乱码怎么办 小孩不写字该怎么办 小孩不喜欢穿袜子怎么办 宝宝不喜欢穿袜子怎么办