C51单片机

  • 开发工具
  • 新建工程
  • LED灯
  • 原理
  • 点亮一个LED灯
  • 闪烁的LED灯
  • LED流水灯
  • 独立按键控制LED灯暗灭
  • 独立按键控制LED灯位移
  • 数码管
  • 原理
  • 显示一个数字
  • 显示多个数字
  • 矩阵键盘
  • 原理
  • 输出数字


开发工具

普中C51单片机:

c51 运行lua c51怎么用_寄存器


c51 运行lua c51怎么用_单片机_02


c51 运行lua c51怎么用_单片机_03

新建工程

1. 打开Keil uVision5,面板选择project新建工程

c51 运行lua c51怎么用_寄存器_04

2. 保存工程名字后会出现下面这个界面,我所用的是STC89C52,这个软件上虽然没有,但可以选用Atmel里的AT89C52,操作方式和功能基本一样。

c51 运行lua c51怎么用_单片机_05


3. 然后添加一个C语言程序文件就可以开始写程序了

c51 运行lua c51怎么用_学习_06

LED灯

原理

c51 运行lua c51怎么用_赋值_07


LED灯正极连接VCC端是高电平,所以想要LED灯亮,需要在另一个端口给低电平,才能有电流。P2_端口起寄存器作用,CPU通过给寄存器赋值控制电路,用1 / 0表示高低电平,若想D1亮,理论上来讲程序可以写成P2 = 1111 1110,但在c语言它会被默认为十进制,所以要转化成16进制P2 = 0xFE更为方便。

点亮一个LED灯

写好程序烧录到单片机里,需要先生成程序下载的文件。

c51 运行lua c51怎么用_学习_08


然后编译,再在STC-ISP中打开程序文件下载程序,打开单片机开关即可成功点亮D1灯,想要其他的灯亮同理,亮的为0,不亮为1,然后转化成16进制。

c51 运行lua c51怎么用_赋值_09


c51 运行lua c51怎么用_c51 运行lua_10

闪烁的LED灯

让LED灯闪烁无非亮一下暗一下,写个while循环让零一零一交替变换。但当程序只是写成P2 = 0xFE; P2 = 0xFF;时会发现灯几乎没有变化,这是因为LED速度特别快,中间那短短的间隔我们基本分辨不出来,因此看上去就还是一直亮的,所以需要写个函数让LED灯亮与暗之间的时间延长。

在STL-ISP有软件延时计算器,它可以生成延时函数。

c51 运行lua c51怎么用_赋值_11


系统频率看单片机晶振上数值

c51 运行lua c51怎么用_学习_12

将函数复制到程序,即可实现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();
		}
	}
}

c51 运行lua c51怎么用_寄存器_13

稍微改变一下代码可以让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();
		}
	}
}

c51 运行lua c51怎么用_学习_14


还可以让其以二进制形式从1到255亮

void main()
{
	while(1)
	{
		int i=256;
		while(i--)
		{
			P2 -= 0x1;//或者num=0; num++; P2 = ~num;
			Delay(300);
		}
	}
}

c51 运行lua c51怎么用_c51 运行lua_15

独立按键控制LED灯暗灭

c51 运行lua c51怎么用_赋值_16


独立按键一头接地 ,一头接 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;//松开按键灯不亮
	}
}

对于机械开关,在机械触点因弹性作用下在开关闭合、断开时会产生一连串抖动,所以可以在按键按下松开时延时一段时间进行消抖。

c51 运行lua c51怎么用_c51 运行lua_17


改一下可以实现跟日常开关灯一样,按一次开关后一直亮或一直暗。

void main()
{
	while(1)
	{
		if(P3_1==0)//按下按键
		{
			Delay(20);//按下开关瞬间的延时
			while(P3_1==0);//没松手灯的状态不变,松手后变
			Delay(20);//松开开关瞬间的延时
			P2_0 = ~P2_0;//让灯一次亮一次暗
		}
	}
}

独立按键控制LED灯位移

通过两个按键控制LED左移右移

c51 运行lua c51怎么用_赋值_18

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);
		}
	}
}

数码管

原理

c51 运行lua c51怎么用_学习_19


common ground即公共端,一般指接地点,所以每一个 “ 8 ” 有一个共阴极,“ 8 ” 的每一个边对应一个引脚,给它的共阴极赋值0(位选),边的引脚赋值1,就可以让 “ 8 ” 对应边亮起(段选),呈现出想要的数字。

c51 运行lua c51怎么用_学习_20


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边亮
}

c51 运行lua c51怎么用_学习_21

显示多个数字

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灯显示间加了延时函数,如果不加,会出现以下现象,它的显示位置错乱。因为数码管显示首先位选然后段选,然后再下一个位选段选,中间特别快这样上一个段选可能就跟下一个的位选结合显示,这样数据就错位了。所以让其中间延时一点可以避免这个现象。

c51 运行lua c51怎么用_学习_22


但同时也不能延时太长,太长肉眼会很明显的看到两个数字的跳动,延时1毫秒数字依然是跳动的但频率过快人眼看上去就是常亮了。

c51 运行lua c51怎么用_c51 运行lua_23

矩阵键盘

原理

c51 运行lua c51怎么用_学习_24


与独立按键类似,按下为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);
		}
	}
}

c51 运行lua c51怎么用_学习_25