AutoLeaders控制组—51单片机学习笔记(一)

本笔记基于STC89C52RC进行学习
链接 51单片机入门教程-

单片机

单片机内部集成了 CPU,RAM,ROM,定时器,中断系统,通讯接口等一系列电脑的常用硬件
功能单片机的任务是信息采集,处理,和硬件设备的控制

使用工具

  • keil5 程序的编写。
  • STC-ISP 下载(烧入)程序到单片机,以及一些额外的工具如串口助手,延迟代码生成。

发光二极管

发光二极管单向导通,导通且电流合适时发光。
开发板中的二极管正极始终为高电平,而负极与单片机的P20-P27引脚连接。(串联了2k欧的限流电阻来防止电流过大)
要控制二极管的亮灭就需要控制引脚输出的高低电平。
单片机中的P2八位寄存器,每一位都连了驱动器,使其的值反映为引脚的高低电平。
所以问题转化为控制P2寄存器中的数值来控制发光二极管的状态。
—————————————
后续章节指出能用P2_0=0;
来实现单个二极管的控制
(也可以推测出各位默认为1)

点亮一个LED

若需要点亮D1二极管
则P2应该为 1111 1110B
代码如下

#include <REGX52.h> 
//头文件中引入了P2口对应寄存器的地址
void main()
{
	P2=0xFE;//1111 1110
	while(1){
	
	}//使得赋值只执行了一次
}

LED的闪烁

亮灭的操作看上去只有两步:
点亮与熄灭
但实际上,还有一步很重要的延时
毕竟晶振主频很高,单纯循环两步操作
肉眼是不可见其闪烁的
延时0.2秒的代码如下

#include <REGX52.h> 
#include <INTRINS.h>
void Delay200ms(void)
{
	unsigned char data i, j, k;

	_nop_();//在intrins.h头文件中,什么都不做执行一步
	i = 2;
	j = 103;
	k = 147;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}

void main()
{
	while(1){
		P2=0xFE;//点亮
		Delay200ms();//调用函数,来延时
		P2=0xFF;//熄灭
		Delay200ms();
	}
}

延时函数的优化

使其可以延时任意秒~~(但我估摸着精度会有所降低)~~

void sleep(int a)	//Delayams
{
	
	unsigned char data i, j;
	do
	{
		_nop_();
		i = 2;
		j = 199;
		do
		{
			while (--j);
		} while (--i);
	}while (--a);
}

LED流水灯

可以按照视频中的顺序赋值来完成流水灯
也可以利用<<,>>,~ 等运算符来完成目标~~(不能白学了C)~~
以下代码展示用乘法来替代移位运算

#include <REGX52.h> 
#include <INTRINS.h> 
void sleep(int a)   //延时函数
{
	
	unsigned char data i, j;
	do
	{
		_nop_();
		i = 2;
		j = 199;
		do
		{
			while (--j);
		} while (--i);
	}while (--a);
}
void main()
{
	short a=128;
	while(1){
		if(a==128){
			a=1;
		}else{
			a*=2;
		}
		P2=~a;//按位取反
		sleep(500);
	}
}

独立按键

介绍

按键按下时左右两边导通,
一边接引脚一边接地
按下时会使引脚变为低电平。
对应的置零的的位为

  • K1-P3_1
  • K2-P3_0
  • K3-P3_2
  • K4-P3_3

按键的抖动&消抖&检测松手

对于机械开关,当机械触点断开闭合时,由于机械出点的弹性作用,一个开关在闭合时不会马上稳定地接通,在断开时也不会以下子断开,会伴随一连串的抖动
在这里插入图片描述

可以通过软件层面延迟20ms来进行消抖
部分代码如下

		if(P3_0==0){
			sleep(20);
			P2_1=~P2_1;
		}

若松手后执行,部分代码如下

		if(P3_0==0){
			sleep(20);
			while(P3_0==0);
			sleep(20);
			P2_1=~P2_1;
		}

控制LED状态

通过K2来实现D2的亮灭
代码如下

#include <REGX52.h> 
#include <INTRINS.h> 
void sleep(unsigned int a)	
{
	
	unsigned char data i, j;
	do
	{
		_nop_();
		i = 2;
		j = 199;
		do
		{
			while (--j);
		} while (--i);
	}while (--a);
}
void main()
{
	while(1){
		
		if(P3_0==0){
			sleep(20);
			while(P3_0==0);
			sleep(20);
			P2_1=~P2_1;
		}
	}	
}

控制LED显示二进制

代码如下(相比于视频,没有引入新的变量且有减法运算且长按能一直进行运算)

#include <REGX52.h> 
#include <INTRINS.h> 
void sleep(unsigned int a)	
{
	
	unsigned char data i, j;
	do
	{
		_nop_();
		i = 2;
		j = 199;
		do
		{
			while (--j);
		} while (--i);
	}while (--a);
}
void main()
{
	while(1){
		//加法
		if(P3_0==0){
			sleep(20);
			while(P3_0==0)
			{
				sleep(250);
				if(P3_0==0){//还是零的话,就要开始猛猛加了
					while(P3_0==0)
					{
						P2=~P2;
						P2++;
						P2=~P2;
						sleep(100);
					}
				}
			}
			sleep(20);//按住没超过250ms
			P2=~P2;
			P2++;
			P2=~P2;
		} 
		//减法
		if(P3_1==0){
			sleep(20);
			while(P3_1==0)
			{
				sleep(250);
				if(P3_1==0){
					while(P3_1==0)
					{
						P2=~P2;
						P2--;
						P2=~P2;
						sleep(100);
					}
				}
			}
			sleep(20);
			P2=~P2;
			P2--;
			P2=~P2;	
		}
	}	
}

独立按键控制LED位移

代码如下

#include <REGX52.h> 
#include <INTRINS.h> 
void sleep(unsigned int a)	
{
	
	unsigned char data i, j;
	do
	{
		_nop_();
		i = 2;
		j = 199;
		do
		{
			while (--j);
		} while (--i);
	}while (--a);
}
void main()
{
	while(1){
		//go left
		if(P3_0==0){
			sleep(20);
			while(P3_0==0);
			sleep(20);
			P2=~P2;
			if(P2==0){
				P2=1;
			}else{
				P2=P2<<1;
			}
			P2=~P2;
		}
		if(P3_1==0){
			sleep(20);
			while(P3_1==0);
			sleep(20);
			P2=~P2;
			if(P2==0){
				P2=128;
			}else{
				P2=P2>>1;
			}
			P2=~P2;
		}
	}	
}

数码管

介绍

单个数字是由8个二极管的亮灭来显示的
A
F B
G
E C
D DP
dp g f e d c b a对应一个8位二进制。
U: 0 0 1 1 1 1 1 0
-: 0 1 0 0 0 0 0 0
开发板中能’‘同时’‘显示8个数字。
但实际上,在一瞬间里只有一个数字是被点亮的。
之所以看上去同时点亮了多个,是因为点亮的数字切的很快。
决定哪个数字亮,通过3个引脚(P22-P24)表示三位二进制数,再解码(74LS38芯片)得到哪组二极管发光。

三位解码

P24x4+P23x2+P22x1+1=Loc
Loc为亮的LED组编码

八位控制亮灭

从各个引脚到二极管的正极,有一个74HC245芯片,用来增强高电平的驱动能力。对应单片机引脚为P00-P07。

静态数码管显示

构建了函数来使得任意位置显示任意数字
代码如下(与视频条件判断不同,是基于二进制的运算来确定二极管组号)

#include <REGX52.h> 
#include <INTRINS.h> 
char diction[10]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F};
void Nixie(unsigned char loc,char num)
{
	loc--;
	P2_2=loc%2;
	P2_3=loc/2%2;
	P2_4=loc/4%2;
	P0=diction[num];
	
}
void main()
{
	while(1)
	{
		Nixie(1,1);
	}
}

动态数码管显示

如果调用上面的函数来同时显示多个数字会存在拖影的现象。
所以需要优化前面构建的函数

void Nixie(unsigned char loc,char num)
{
	char diction[10]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F};
	loc--;
	P2_2=loc%2;
	P2_3=loc/2%2;
	P2_4=loc/4%2;
	P0=diction[num];
	sleep(1);//使其不会迅速归为全灭状态
	P0=0x00;//归为全灭,之后再赋值时就不会拖影了
}

对应任务

代码中对数码管编号的定义与任务书上相反,
代码中从右到左是一到八。
代码如下

#include <REGX52.h> 
#include <INTRINS.h> 
void sleep(unsigned int a)	//延时函数
{
	unsigned char data i, j;
	do
	{
		_nop_();
		i = 2;
		j = 199;
		do
		{
			while (--j);
		} while (--i);
	}while (--a);
}
void Nixie(unsigned char loc,char num)//自定义显示函数 U=10 -=11 
{
	char diction[12]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x3E,0x40};
	loc--;
	P2_2=loc%2;
	P2_3=loc/2%2;
	P2_4=loc/4%2;
	P0=diction[num];
	sleep(1);
	P0=0x00;//防止拖影
}
void united_output(char p7,char p2,char p1,char flag)//根据要求 格式化输出
{
	Nixie(8,10);//U字符
	Nixie(7,p7); 
	Nixie(1,p1);
	Nixie(2,p2);
	if(flag){//是否显示中间短横
		Nixie(3,11);
		Nixie(4,11);
	}
}
int jud()  //因为是在消抖后执行的jud()函数,这一步里面没有延时。
{
	char a=0;
	if(P3_0==0){
		a=2;
	}
	if(P3_1==0){
		a=1;
	}
	if(P3_2==0){
		a=3;
	}
	if(P3_3==0){
		a=4;
	}
	return a;
}
void xiaodou_danxianshi(char p7,char p2,char p1,char flag)//消抖的同时保持显示
{
	short i,n=7;//因为united_output()函数的执行也需要一定时间,
	            //所以循环次数比20少。测试的时候如果n=10,按松的快一点就会没反应
	for(i=0;i<n;i++){
		sleep(1);
		united_output(p7,p2,p1,flag);
	}
}
//写了四个不同的函数来执行四个不同的模式
int mode1()
{
	char flag=1;
	while(1)
	{
		united_output(1,0,1,0);
		if(P3_0==0 || P3_2==0 || P3_3==0){//如果其他按钮被按下
			xiaodou_danxianshi(1,0,1,0);//消抖
			flag=jud();//改变flag
			while(P3_0==0 || P3_2==0 || P3_3==0)//在按住的时候也要保持显示
			{
				united_output(1,0,1,0);
			}
			xiaodou_danxianshi(1,0,1,0);
			break;
		}
	}
	return flag;//把新的flag传回到excute函数
}
int mode2()
{	
	char flag=2;
	int tick=0;//计时的问题,由新变量tick来解决,tick记录已经循环了多少次
	char num=0;//数字显示
	while(1)
	{
		united_output(2,num/10,num%10,0);
		if(tick==203){//具体tick多大再使其回到零,其实是拿秒表恰的x
			tick=0;
			num=(num+1)%11;
		}
		tick++;
		if(P3_1==0 || P3_2==0 || P3_3==0){
			xiaodou_danxianshi(2,num/10,num%10,0);
			flag=jud();
			while(P3_1==0 || P3_2==0 || P3_3==0)
			{
				united_output(2,num/10,num%10,0);
			}
			xiaodou_danxianshi(2,num/10,num%10,0);
			break;
		}
	}
	return flag;
	
}
int mode3()
{
	char flag=3;
	int tick=0;//同mode2用tick确定跑了几次了
	char flag2=0;//是否点亮--
	while(1)
	{
		united_output(3,0,3,flag2);
		if(tick==96){//没对半分开,没有-- 和 有-- 执行耗时比约为2:3,循环次数比则约为3:2
			flag2=1;
		}else if(tick==160){
			flag2=0;
			tick=0;
		}
		tick++;
		if(P3_1==0 || P3_0==0 || P3_3==0){
			xiaodou_danxianshi(3,0,3,flag2);
			flag=jud();
			while(P3_1==0 || P3_0==0 || P3_3==0)
			{
				united_output(3,0,3,flag);
			}
			xiaodou_danxianshi(3,0,3,flag2);
			break;
		}	
	}
	return flag;
}
int mode4()
{
	char flag=4;
	char a=0;
	while(1)
	{
		united_output(4,a,a,0);
		if(P3_3==0)
		{
			xiaodou_danxianshi(4,a,a,0);
			while(P3_3==0){united_output(4,a,a,0);}
			xiaodou_danxianshi(4,a,a,0);
			a=(a+1)%10;
		}
		if(P3_1==0 || P3_0==0 || P3_2==0){
			xiaodou_danxianshi(4,a,a,0);
			flag=jud();
			while(P3_1==0 || P3_0==0 || P3_2==0)
			{
				united_output(4,a,a,0);
			}
			xiaodou_danxianshi(4,a,a,0);
			break;
		}
	}
	return flag;
}
int excute(char flag)
{
	if(flag==1){
		flag=mode1();
	}else if(flag==2){
		flag=mode2();
	}else if(flag==3){
		flag=mode3();	
	}else if(flag==4){
		flag=mode4();
	}
	return flag;//传回给主函数
}

void main()
{
	int flag=0;//根据flag的值来决定执行哪个函数
	while(1)
	{
		P0=0x00;
		if(P3_1==0)
		{
			sleep(20);
			while(P3_1==0);
			sleep(20);
			flag=1;
		}
		if(P3_0==0)
		{
			sleep(20);
			while(P3_0==0);
			sleep(20);
			flag=2;
		}
		if(P3_2==0)
		{
			sleep(20);
			while(P3_2==0);
			sleep(20);
			flag=3;
		}
		if(P3_3==0)
		{
			sleep(20);
			while(P3_3==0);
			sleep(20);
			flag=4;
		} 
		flag=excute(flag);//将flag交给执行函数,进入对应mode函数,当mode切换时再将新的flag传回来。
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值