前言:51系列单片机硬件驱动
入门基础
流水灯是第一个单片机程序,或者说是最简单的单片机程序,但是里面蕴含了一个最基本的原理:用数据直接控制外设,小小的led灯即是数据的最直接体现。
目录
1.C语言移位指令实现连续点亮;
2.C51库函数实现循环点亮;
3.万能方法—硬写入数值并延时;
4.C语言掩码实现连续点亮;
5.C语言实现循环点亮
6.连续亮灭
实验准备:
实验板P1口连接8个led小灯的阴极,共阳极接法,有限流电阻10kΩ。图略
1.C语言移位指令实现连续点亮
#includetypedef unsigned int u16; //typedef定义数据类型void delay(u16 i);int main(void) //主函数{int j;while(1){for(j=0;j<7;j++){P1=0xfe<<j;delay(50000);}}}void delay(u16 i) //延时函数{while(i--);}
说明:把输出口赋值和移位指令放一起,每次都从头开始。
效果:点亮第一个led;点亮1,2号led;点亮1,2,3号led。。。依次进行,每次延时约0.5ms
自动循环点亮。
2.C51库函数实现循环点亮
#include //添加库函数支持,可以调用左循环移位函数_crol_#include //因为要用到左右移函数,所以加入这个头文?typedef unsigned int u16;void delay(u16 i);void delay(u16 i){while(i--);}int main(void){int j;P1=0xfe;delay(50000); //给初值添加延时,不然只闪一下while(1){for(j=0;j<7;j++) //循环移位,和j值已经无关{P1=_crol_(P1,1); //循环移位delay(50000);}}}
说明:每次延时后只点亮一个led灯,依次是1号led,2号led。。。。和方法1有区别
3.万能方法—硬写入数值并延时
#includetypedef unsigned int u16;void delay(u16 i);void delay(u16 i){while(i--);}int main(void){P1=0xfe;delay(50000);P1=0xfd;delay(50000);P1=0xfb;delay(50000);P1=0xf7;delay(50000);P1=0xef;delay(50000);P1=0xdf;delay(50000);P1=0xbf;delay(50000);P1=0x7f;delay(50000);while(1);}
说明:这种办法当然看起来最笨拙了。但是您有没有发现,不管你想让灯亮几个,用什么顺序亮,都可以用这个最笨拙的办法去实现(如果想不到移位循环什么的)。为什么要写得这么麻烦,原因有二,一是因为突出了单片机的核心思想,给端口一个1或者0,就会按照人的思路去走。二是因为看见有个同学在问,怎么让流水灯从外侧向内侧亮,如果把main()函数改一改就会有所发现了
int main(void){P1=0x7e;delay(50000);P1=0xbd;delay(50000);P1=0xdb;delay(50000);P1=0xe7;delay(50000);P1=0xdb;delay(50000);P1=0xbd;delay(50000);}
是不是想玩什么花样的led灯,都能写出来(虽然很麻烦。。。)
4.C语言掩码实现连续点亮
#includetypedef unsigned int u16;void delay(u16 i);int main(void){int j;while(1){for(j=0;j<7;j++){ P1&=~(1<<j);delay(50000);} P1=0xfe;}}void delay(u16 i){while(i--);}
说明:和方法1含义一样,用掩码给字节写入数据,是典型的C语言方法。
led流水灯是51单片机的起点,从这里认识到通过数据操作控制硬件的概念
5.C语言实现循环点亮
int main(void) //C语言自身的循环移位{P1=0xfe;delay(50000);while(1){P1=P1<>7; //位或符号不是序列点,P1中途不发生变化 delay(50000);}}
6.连续亮灭
方法1的加强版,反复的移位实现不同效果
#includetypedef unsigned int u16;void delay(u16 i);void delay(u16 i){while(i--);}int main(void){int j;while(1){for(j=0;j<8;j++){P1=0xfe<<j; delay(50000);}for(j=0;j>j; delay(50000);}}}
——————–2022.3.20update————————————————————————————-
抽象1:流水灯的实现包括写入数值和延时两个方面(方法3是最直接体现),要实现灯光移动的视觉效果,则加上移位。为什么要这样解释到最细,显得啰嗦,因为对初学编程的人来说,能像搭积木一样理解程序,会比较友好。比如P1=0xfe<<j;表示写入数值,外面加上for(j=0;j<8;j++)表示移位,或者写成
while(1){P1=0xfe;delay(50000);for(j=1;j<=7;j++){P1=0xfe<<j; delay(50000);}for(j=0;j>j; delay(50000);}}
更直观,赋值,移位,延时一目了然。会写了之后,改成下面那段代码更简洁。
抽象2:流水灯效果常通过移位来实现。移位写法不同,效果不同。
一.其中‘连续亮起’的含义是每次写入0或者1(由led灯接法确定,本例共阳极接法写入0),并且移动1位(或n位)。具体代码由循环(for循环或者while循环都可以)加每次移动1位(或n位)完成。这种方法每次给出的初值是确定的,例如移动1位初值是0xfe和0x80。以上述方法5为例,如果main函数改成
int main(void){int j;while(1){for(j=0;j<8;j+=2){P1=0xfc<<j; delay(50000);}for(j=0;j>j; delay(50000);}}}
可以发现如果每次移动2位,初值需要修改成0xfc和0xc0。
循环灭led灯有一个小bug,修改如下:
int main(void){int j;while(1){for(j=0;j<8;j+=2){P1=0xfc<<j; delay(50000);}for(j=0;j>j; //修改后达到逐渐灭灯的效果 delay(50000);}}}
产生的原因:右移时补0或者补1是有可能不同的,如果需要逐渐灭灯的目的,需要持续写入1,所以做了修改,前面的代码做了相应修改。
二.“循环点亮”的含义是每次只有一个灯的亮灭发生变化,用循环移位实现,本例中对应方法2,直接调用库函数。(C语言也可以写循环移位,但俺写了以后没成功实现)。因为直接参加循环移位,所以可能把初状态单独列出来写并延时。
另:谈到C语言的移位,在后面的接收字节数据和发送字节数据中也常常用到,被称为“移位或”。在硬件通信中,最基本的单位是“位”,而对于计算机来说,最基本的单位是“字节”。所以需要将位整合成字节实现通信。思路是接收变量所在的字节移动1位(左移或右移根据情况而定),与将要接收或发送的位相或,外部加循环(for和while都可以)8次,得到一个字节。具体代码见后续
———————————————————–2022.4.6Update————————————————
再次发现用C语言移位指令实现连续移位最方便,下面第j位
形如:P1&=~(1<<j); //写0
P1|=1<<j; //写1
加上循环指令,则可以实现连续写入。
一个一个写入的,就用循环移位指令