C51单片机
- 开发工具
- 新建工程
- LED灯
- 原理
- 点亮一个LED灯
- 闪烁的LED灯
- LED流水灯
- 独立按键控制LED灯暗灭
- 独立按键控制LED灯位移
- 数码管
- 原理
- 显示一个数字
- 显示多个数字
- 矩阵键盘
- 原理
- 输出数字
开发工具
普中C51单片机:
新建工程
1. 打开Keil uVision5,面板选择project新建工程
2. 保存工程名字后会出现下面这个界面,我所用的是STC89C52,这个软件上虽然没有,但可以选用Atmel里的AT89C52,操作方式和功能基本一样。
3. 然后添加一个C语言程序文件就可以开始写程序了
LED灯
原理
LED灯正极连接VCC端是高电平,所以想要LED灯亮,需要在另一个端口给低电平,才能有电流。P2_端口起寄存器作用,CPU通过给寄存器赋值控制电路,用1 / 0表示高低电平,若想D1亮,理论上来讲程序可以写成P2 = 1111 1110,但在c语言它会被默认为十进制,所以要转化成16进制P2 = 0xFE更为方便。
点亮一个LED灯
写好程序烧录到单片机里,需要先生成程序下载的文件。
然后编译,再在STC-ISP中打开程序文件下载程序,打开单片机开关即可成功点亮D1灯,想要其他的灯亮同理,亮的为0,不亮为1,然后转化成16进制。
闪烁的LED灯
让LED灯闪烁无非亮一下暗一下,写个while循环让零一零一交替变换。但当程序只是写成P2 = 0xFE; P2 = 0xFF;时会发现灯几乎没有变化,这是因为LED速度特别快,中间那短短的间隔我们基本分辨不出来,因此看上去就还是一直亮的,所以需要写个函数让LED灯亮与暗之间的时间延长。
在STL-ISP有软件延时计算器,它可以生成延时函数。
系统频率看单片机晶振上数值
将函数复制到程序,即可实现LED闪烁,改变延时时间就可以改变LED闪烁的周期。
如果每次改变时间都要生成一次函数也有点麻烦,可以先生成一个1毫秒的函数,然后增加变量x,把原本函数的内容循环x次,这样就相当于一个延时1*x毫秒的函数,每次要改变时间就只用改变参数x。
void Delay(unsigned int x)
{
unsigned char i, j;
while(x--)
{
_nop_();//需要头文件#include <Intrins.h>
i = 2;
j = 199;
do
{
while (--j);
} while (--i);
}
}
LED流水灯
可以通过位运算用循环实现流水灯
void main()
{
while(1)
{
int i=8;
P2 = 0xFF;//一开始都不亮
while(i--)
{
Delay500ms();
P2 = P2>>1;//逐一开始一个个亮起,到全部亮重新下一个循环
Delay500ms();
}
}
}
稍微改变一下代码可以让LED灯单独一个一个流水亮起
void main()
{
while(1)
{
int i=8;
P2 = 0xFF;
while(i--)
{
if(P2==0xFF) P2 = P2<<1;
else P2 = (P2<<1) + 0x1;//左移低位补0,加1去除尾部的0可以保证只有一个0,这样LED灯就可以一个一个流水亮起
Delay500ms();
}
}
}
还可以让其以二进制形式从1到255亮
void main()
{
while(1)
{
int i=256;
while(i--)
{
P2 -= 0x1;//或者num=0; num++; P2 = ~num;
Delay(300);
}
}
}
独立按键控制LED灯暗灭
独立按键一头接地 ,一头接 I/O口上,单片机通电时,I/O口默认高电平。寄存器可以检测I/O口电平,再读回寄存器,当没有按下独立按键时,检测I/O口为高电平,按下独立按键时,检测I/O口为低电平,由此可以来判断按键是否按下。
P2控制的是整个8个LED灯,若要控制一个,可以写成P2_0、P2_1等形式,单独控制一个灯。
void main()
{
while(1)
{
if(P3_1==0) P2_0 = 0;//K1键按下P3_1=0,此时P2_0这个灯亮
else P2_0 = 1;//松开按键灯不亮
}
}
对于机械开关,在机械触点因弹性作用下在开关闭合、断开时会产生一连串抖动,所以可以在按键按下松开时延时一段时间进行消抖。
改一下可以实现跟日常开关灯一样,按一次开关后一直亮或一直暗。
void main()
{
while(1)
{
if(P3_1==0)//按下按键
{
Delay(20);//按下开关瞬间的延时
while(P3_1==0);//没松手灯的状态不变,松手后变
Delay(20);//松开开关瞬间的延时
P2_0 = ~P2_0;//让灯一次亮一次暗
}
}
}
独立按键控制LED灯位移
通过两个按键控制LED左移右移
void main()
{
int num=0;
while(1)
{
if(P3_1==0)//控制LED灯从第一个到第八个方向移动
{
Delay(20);
while(P3_1==0);
Delay(20);
num++;
if(num==8) num = 0;//此时说明LED灯已经亮到第八个了,继续往下亮应该从第一个重新开始
P2 = ~(0x01<<num);//1左移的位置取反就是要亮的那个灯
}
if(P3_0==0)//控制LED灯反方向移动
{
Delay(20);
while(P3_0==0);
Delay(20);
if(num==0) num = 7;
else num--;
P2 = ~(0x01<<num);
}
}
}
数码管
原理
common ground即公共端,一般指接地点,所以每一个 “ 8 ” 有一个共阴极,“ 8 ” 的每一个边对应一个引脚,给它的共阴极赋值0(位选),边的引脚赋值1,就可以让 “ 8 ” 对应边亮起(段选),呈现出想要的数字。
LED1 、 LED2…LED8端接在74HC138译码器输出端,它可以控制输出0还是1,还可以通过P2三个端口控制这八个端口。其按CBA顺序看,如果是000,转化为十进制即0,所以就是Y0为0,其他为1,剩下同理。
显示一个数字
void main()
{
P2_4 = 0;
P2_3 = 1;
P2_2 = 1;//Y3对应LED4
P0 = 0x7D;//0111 1101对应gfedca边亮
}
显示多个数字
void main()
{
while(1){
P2_4=1;
P2_3=0;
P2_2=1;
P0=0x7D;//LED6显示6
Delay(1);
P2_4=1;
P2_3=1;
P2_2=0;
P0=0x4F;//LED7显示3
Delay(1);
}
}
可以看到两个LED灯显示间加了延时函数,如果不加,会出现以下现象,它的显示位置错乱。因为数码管显示首先位选然后段选,然后再下一个位选段选,中间特别快这样上一个段选可能就跟下一个的位选结合显示,这样数据就错位了。所以让其中间延时一点可以避免这个现象。
但同时也不能延时太长,太长肉眼会很明显的看到两个数字的跳动,延时1毫秒数字依然是跳动的但频率过快人眼看上去就是常亮了。
矩阵键盘
原理
与独立按键类似,按下为0,从其接线可以看出每个按键一个脚和其同一行相连,另一个与其同一列相连,这样可以减少I/O口占用,如果像独立按键每个按键接入一个I/O口将需要16个I/O口,现在就只需要8个。要确定是哪个按键按下,就跟看坐标一样,可以先从行看,也可以先从列看。例如先从行看给P1_7赋0,然后检测P1_3是不是等于0,如果是代表S1被按下。
输出数字
我是跟着江科大自动协51单片机视频学的,利用LCD1602显示数字,LCD1602的程序从他那获取的。
写每个按键代表数字的函数:
unsigned char MatrixKey()
{
unsigned char ch=0;
P1 = 0xFF;//开始时全部置1
P1_7 = 0;//从第一行开始判断
if(P1_3==0){Delay(20);while(P1_3==0);Delay(20);ch = 1;}
if(P1_2==0){Delay(20);while(P1_2==0);Delay(20);ch = 2;}
if(P1_1==0){Delay(20);while(P1_1==0);Delay(20);ch = 3;}
if(P1_0==0){Delay(20);while(P1_0==0);Delay(20);ch = 4;}
P1 = 0xFF;
P1_6 = 0;
if(P1_3==0){Delay(20);while(P1_3==0);Delay(20);ch = 5;}
if(P1_2==0){Delay(20);while(P1_2==0);Delay(20);ch = 6;}
if(P1_1==0){Delay(20);while(P1_1==0);Delay(20);ch = 7;}
if(P1_0==0){Delay(20);while(P1_0==0);Delay(20);ch = 8;}
P1 = 0xFF;
P1_5 = 0;
if(P1_3==0){Delay(20);while(P1_3==0);Delay(20);ch = 9;}
if(P1_2==0){Delay(20);while(P1_2==0);Delay(20);ch = 10;}
if(P1_1==0){Delay(20);while(P1_1==0);Delay(20);ch = 11;}
if(P1_0==0){Delay(20);while(P1_0==0);Delay(20);ch = 12;}
P1 = 0xFF;
P1_4 = 0;
if(P1_3==0){Delay(20);while(P1_3==0);Delay(20);ch = 13;}
if(P1_2==0){Delay(20);while(P1_2==0);Delay(20);ch = 14;}
if(P1_1==0){Delay(20);while(P1_1==0);Delay(20);ch = 15;}
if(P1_0==0){Delay(20);while(P1_0==0);Delay(20);ch = 16;}
return ch;
}
主函数:
void main()
{
unsigned char x;
LCD_Init();
while(1)
{
x = MatrixKey();
if(x)//x!=0说明有按键按下并显示那个按键代表数字,可以防止其循环过快看不到数字变化
{
LCD_ShowNum(1,1,x,2);
}
}
}