51单片机LED闪烁编程实战
一, LED的闪烁
本学习采用的华中科大开发板
首先需要学习的是LED模块和51单片机的主板模块:
从原理图可以每一个LED所连接的GPIO接口,由于这里需实现D1灯泡闪烁,观察原理图,可知写代码需要设置P20接口,由于这里需实现点亮第一个LED灯,那么就是对P20进行赋值1,给予高电平操作。然后,可以看到旁边是有一个VCC的,那么就是那边提供一个高平,低电平是GND,所以我们这里就要把这个接口设置为低电平,0就是低电平,1就是高电平。#include <STC89C5xRC.H>void main(){P20 = 0;while(1); }
1 代码解析
这个P20到底是一个什么东西?我们可以右击看这个头文件,查看这个变量的类型和含义
sfr P2 = 0xA0; sbit P20 = P2^0; sbit P21 = P2^1; sbit P22 = P2^2; sbit P23 = P2^3; sbit P24 = P2^4; sbit P25 = P2^5; sbit P26 = P2^6; sbit P27 = P2^7;
sfr为Keil C51编译器所提供的扩展数据类型,用于声明单片机的特殊功能的寄存器特殊功能的寄存器用于控制和配置单片机中的各种硬件操作,如:GPIO,定时器,串口等,每组GPIO(P0,P1,P2,P3,P4)均有一个对应的8位寄存器
sfr类型
(1) sfr的定义
sfr是 特殊功能寄存器 的声明关键字。它告诉编译器,声名的变量名
P2
并不是一个普通的内存变量,而是对应着单片机内部一个具有特殊功能的寄存器的地址。0xA0
是特殊功能寄存器P2
在单片机内存地址空间中的硬件地址。(2) 对应的作用
是单片机的一个8位并行输入/输出端口。它由8个物理引脚(P2.0 到 P2.7)组成,可以用于控制外部设备(如LED、按键、数码管等)或读取外部信号,这里每一个位对应着一个引脚的电平控制。
sbit类型
(1) sbit的定义
sbit是 位寻址 的声明关键字。它用于定义一个特殊功能寄存器中的某一位。
(2) 变量的的定义
P00为该位的名称,可以自己定义(3)
^的作用
^
左边必须是一个已经定义好的sfr
变量(比如P2
,它代表整个端口)。右边是一个0到7的数字,指定要访问这个8位寄存器中的哪一位(0是最低位LSB,7是最高位MSB)
通过sbit可变量赋值可以修改寄存器对应的某一个值,入P0^0表示为寄存器的0位后面while就是卡死,让LED持续点亮
2 GPIO内部的电路结构
(1) n形mos管的做作用
如上图的一个像n的一个玩意就是n形mos管,如果经过反门为高电平,此路就导通,如果为低电平,此路就不导通。
(2) GPIO的工作原理
端口寄存器就是用来存储0-1数据的,当端口寄存器为0,这个时候有一个反门,然后设置为1为高电平,这个时候mos管导通,引脚接地,此时引脚就变成了低电平,如果端口寄存器为0的时候,此时输出高电平1,则接上面的电压,就是一个电阻上面一个杠的就是一个电源,然后此时引脚就是高电平。
3 扩展其他LED模块
下面是一个其他51单片机的LED模块
这里主要讲述的为这个三极管的作用,这个三极管我们可以看到是连接P34端口的
这个三极管是npn类型,当上面出现高电平的时候,才可以导通,这个时候我们有了这个就可以控制这整个电路了
二, 流水灯
1 设计需求
从P20到P27的LED灯依次进行点亮,间隔为100ms,不断地进行循环
2 软件设计
首先我们要知道二进制和十六进制和移位运算符
#include<STC89C5xRC.H>void Delay100ms(void) //@11.0592MHz {unsigned char data i, j;i = 180;j = 73;do{while (--j);} while (--i); }int main(){while(1){//直接对P2进行操作,那么就是对P2全部进行操作了;//P2 = 1111 11110;//由于C语言不支持二进制写法,所以要转为16进制P2 = 0xFE;Delay100ms();P2 = 0xFD;Delay100ms();P2 = 0XFB;Delay100ms();} }
首先C语言是不允许存储二进制地,那么我们就要转换为十六进制
但是这压根是一个一个写的,十分的麻烦,所以我们就要进行简化代码#include <STC89C5xRC.H> #include <intrins.h>void Delay100ms(void) //@11.0592MHz {unsigned char data i, j, k;_nop_();_nop_();i = 5;j = 52;k = 195;do{do{while (--k);} while (--j);} while (--i); }void main(){// 流水灯代码unsigned char temp = 0x01;// 测试1:所有LED全亮P2 = 0x00; // 共阴接法,全亮Delay100ms();Delay100ms();Delay100ms();// 测试2:所有LED全灭 P2 = 0xFF; // 共阴接法,全灭Delay100ms();Delay100ms();Delay100ms();while(1){P2 = ~temp;Delay100ms();temp <<= 1;if(temp == 0x00){temp = 0x01;}} }
首先我们进行一个0000 0001的赋值,然后我们对这个进行取反,那么我们就可以获得1111 1110,这个就是第一个流水灯的点亮信号,然后我们在对这0000 0001进行左移变成0000 0010,然后再对这个进行取反就是第二个流水灯的信号...,以此类推就可以得到每一次都精准对每一位赋值。
3 左移的操作
左移的基本概念
左移操作用符号<<表示。
对于一个二进制数x,左移n位的操作可以表示为x << n。
左移时,左边的n位会被丢弃,右边会补上n个0。
三, 闪烁LED
1 基于点亮LED修改
思路:就是不断的为把这个P20的状态在0和1中进行反复调整
那么我们只需要进行调整就好了代码1
#include <STC89C5xRC.H> #include <INTRINS.H>void main(){unsigned char i;unsigned char j;//255为该类型的最大值while(1){P20 = 0;for(i = 1; i < 255; i++){for(j = 1; j < 255; j++){}}P20 = 1;for(i = 1; i < 255; i++){for(j = 1; j < 255; j++){}}} }
我们只现需要在while循环里面进行嵌套两个for循环就好了,unsigned char的最大值为255,所以我们只可以设置到255,要不然无法出循环,因为又进行从0开始了
如此反复就可以实现这个灯光的闪烁了。优化代码2
但是上面的代码还是不够号,我们要优化我们的代码才可以
void main(){unsigned char i;unsigned char j;//255为该类型的最大值while(1){P20 = ~P00;//取反for(i = 1; i < 255; i++){for(j = 1; j < 255; j++){}}} }
我们在此基础上取了一个取反操作,但是我们无法估计这个时间开销了多少,所以这个是ISP出了一个软件延迟计算机。
优化代码3
然后我们只需要把这个代码复制到里面就好了
void Delay500ms(void) //@11.0592MHz {unsigned char data i, j, k;_nop_();//这个为no operator---空操作//因为下面的操作无法精确这个时间//所以我们需要这个进行微调来精确时间i = 4;j = 129;k = 119;do{do{while (--k);} while (--j);} while (--i); }void main(){while(1){P20 = ~P00;Delay500ms();} }
这个_nop_( )函数是在#include <INTRINS.H>这个头文件里面的