51单片机学习总结(一)LED灯(流水灯),蜂鸣器和数码管(鬼影消除)(内含模块代码)

写在最最最前面

疫情期间在家真是效率低下啊
返校之后,会有51单片机的考核测试
在博客写下各个模块的总结
方便以后查看
如有错误,欢迎大佬指出


数码管又学到了一个新的能显示千位的代码


主控芯片原理图

51单片机学习总结(一)LED灯(流水灯),蜂鸣器和数码管(鬼影消除)(内含模块代码)_第1张图片
常用延时方法
①软件延时
②定时器延时

LED灯模块

原理图
51单片机学习总结(一)LED灯(流水灯),蜂鸣器和数码管(鬼影消除)(内含模块代码)_第2张图片使用:
这是共阳极,也就是当上电之后,串口如果输出高电压,LED灯两侧不会有电压差,不会形成电流,LED灯不会发亮。只有输出低电压,才能够亮灯。
有8个串口,从上到下根据二进制来控制串口信号。

简单的例子

#include //包含51头文件

unsigned int i;//0~65535

void main()//main函数自身会循环
{
	while(1)//大循环
	{
		P1 = 0;	//点亮P1口8个LED
		i = 65535;
		while(i--);//软件延时
		P1 = 0xff;//1111 1111 熄灭P1口8个LED
		i = 65535;
		while(i--);//软件延时	
	}	
}

2.如果需要实现流水灯效果,可以借助下列函数

#include//包含下面函数的标准库
_crol_(a,1);//字符型循环左移函数,_crol(char,左移位数)
_cror_(a,1);//字符型循环右移函数,_crol(char,右移位数)
//此外,还可以使用<<和>>移位函数来实现
//但是<<和>>移位之后会直接清0
//比如a = 11110000,<<之后会变成11100000
//而使用_crol_(a,1),会变成11100001

最简单的流水灯代码

#include 
#include 

#define uchar unsigned char
#define uint unsigned int

void delay(uint z) //最简单的延时函数,里面的x,y是测试得来,大概是延时z微秒
{
	uint x,y;
	for(x=z; x>0; x--)
		for(y = 144; y>0; y--);
}
/*刚学单片机的时候写的一个有点花样的流水灯,有以下不足*/
//1.变量没有使用能够简单易懂的名字,而采用了a这样的名字,这样是及其不好的
//2.后面一截使用了太多的for循环,逻辑上有些复杂
//3.没有使用定时器,可以采用定时器来使得代码更加美观
void main()
{
	uchar temp,a;
	temp = 0X3f;
	P1 = temp;
	for(a = 1; a < 5; a++){
		delay(1000);
		temp = _cror_(temp, 2);
		P1 = temp;
	}
	P1 = 0XFF;
	delay(1000);
	temp = 0Xfe;
	P1 = 0XFe;
	for(a = 1; a < 9; a++){		
		delay(80);
		temp = _crol_(temp, 1);
		P1 = temp;						
	}
	for(a = 1; a < 9; a++){		
		temp = _cror_(temp, 1);
		P1 = temp;	
		delay(80);
	}
	temp = 0Xfc;
	P1 = 0XFc;
	for(a = 1; a < 8; a++){		
		delay(80);
		temp = _crol_(temp, 1);
		P1 = temp;						
	}
	for(a = 1; a < 8; a++){		
		temp = _cror_(temp, 1);
		P1 = temp;	
		delay(80);
	}
	for(a = 0; a <=1; a++){  
	P1 = 0XFF;
	delay(80);
	P1 = 0X7E;
	delay(80);
	P1 = 0XBD;
	delay(80);
	P1 = 0XDB;
	delay(80);
	P1 = 0XE7;
	delay(80);
	P1 = 0XDB;
	delay(80);
	P1 = 0XBD;
	delay(80);
	P1 = 0X7E;
	delay(80);
	}
	delay(500);
}

大家可以自行设计有花样的流水灯噢

蜂鸣器

原理图
(红箭头的地方是PNP管,还有一种类似的是NPN管,可以自行百度原理)
51单片机学习总结(一)LED灯(流水灯),蜂鸣器和数码管(鬼影消除)(内含模块代码)_第3张图片使用:
蜂鸣器使用起来非常的简单,只需要控制蜂鸣器输出电压为1或者0即可

因为这学期上课还学了一些Arduino,Arduino里面的蜂鸣器可以根据数字信号0~255来控制音调的高低,但是我还没有太搞清楚这两者的区别,欢迎懂的朋友评论一下,教教蒟蒻 ☺️

下面附上蜂鸣器的模块化代码

/*pbdata.h*/
#ifndef __PBDATA_H__
#define __PBDATA_H__
#define uchar unsigned char
#define uint unsigned int

#include 
#include "buzzer.h"
/*蜂鸣器*/
sbit BEEP = P2^3;

#endif
/*buzzer.h*/
#ifndef __BUZZER_H__
#define __BUZZER_H__

void BUZ_ON();
void BUZ_OFF();

#endif
/*buzzer.c*/
#include"pbdata.h"
//pbdata.h是所有需要的头文件
void BUZ_OFF()
{
	BEEP = 1;
}
 
void BUZ_ON()
{
	BEEP = 0;
}

数码管

原理图
51单片机学习总结(一)LED灯(流水灯),蜂鸣器和数码管(鬼影消除)(内含模块代码)_第4张图片使用:
因为单片机上面的串口数量是有限制的,所以我们必须要做到以最少的串口来完成数码管的控制,这就需要使用到74H35锁存器。
51单片机学习总结(一)LED灯(流水灯),蜂鸣器和数码管(鬼影消除)(内含模块代码)_第5张图片首先我们要先了解,锁存器如何控制数码管
51单片机学习总结(一)LED灯(流水灯),蜂鸣器和数码管(鬼影消除)(内含模块代码)_第6张图片我们使用的是共阴极数码管,也就是串口输出1的时候,对应的那一个数码管才会亮起来
而上图一个数码管上面a-g即对应下面那个锁存器右侧的A-G,即若A-G全部输出1,则数码管全亮。
而靠上方的那个锁存器是用来选8个数码管中的哪一个的,一次只能选择一个数码管,然后再通过靠下面的锁存器来控制哪几个灯亮起来。从而达到实现亮数字。
但是两个锁存器都是用一个P0口来控制,所以我们需要P26和P27来控制使用哪一个锁存器,只需要一个给1/0,即可控制锁存器开关。
注:一次只能使用一个锁存器。
问题紧接着又出来了,我们记不住那么多显示数码管的2进制代码,也没办法记住选择对应的数码管的2进制代码,自然而然,我们可以使用数组来实现选择数码管和数码管输出的数字。

unsigned char code SMGduan[]={//共阴极数码管段选表
//0	  1    2    3    4    5    6    7    8	  9
0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f
};
unsigned char code SMGwei[8]={//共阴极数码管位选表
//1    2    3    4    5    6    7    8
0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f};

有了以上的基础,我们就可以让一个数码管显示一个数字了,但是因为我们每次都只能选择一个输出,这被称为静态扫描,实用性极低。这时候,我们利用视觉暂留效果来实现多个数码管同时亮起,这被称作为动态扫描,以下是模块代码

/*pbdata.h*/
#ifndef __PBDATA_H__
#define __PBDATA_H__
#define uchar unsigned char
#define uint unsigned int

#include 
#include "SMG.h"
/*SMG.h*/
#ifndef __SMG_H__
#define __SMG_H__

sbit WE=P2^7;//位选锁存器
sbit DU=P2^6;//段选锁存器

uchar code SMGduan[]={//共阴极数码管段选表
//0	  1    2    3    4    5    6    7    8	  9
0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f
};
uchar code SMGwei[8]={//共阴极数码管位选表
//1    2    3    4    5    6    7    8
0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f};
uchar LedBuff[8]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};//数码管显示缓冲区

void ShowNumber(unsigned long num);
void LedScan();

#endif
/*SMG.c*/
#include"pbdata.h"
/*该代码有一定的鬼影,无法根除*/
/*一个简单的动态扫描模块代码*/
/*功能:实现一个数字的显示,最多8位数*/
void ShowNumber(unsigned long num)
{
	uchar i,j=0;
	uchar buf[8];

	for(i=0;i<8;i++)//把每一个数字都先放到缓存数组中
	{
		buf[i]=num%10;
		num=num/10;
	}
	for(i=7;i>=1;i--)//从前开始扫描,比如100,那么前面5位应该是不良的,此时要把LedBuff[]中对应的2进制改为灭
	{
		if(buf[i]==0)
			LedBuff[i]=0x00;
		else 
			break;
	}
	for(;i>=0;i--)//显示数字
	{
		LedBuff[j]=SMGduan[buf[i]];
		j++;
		if(i==0)break;	
	}
}

void LedScan()//动态扫描数码管
{
	static uchar i=0;
	
	P0=0xff;//清0,有消除鬼影的作用
	WE=1;
	P0=SMGwei[i];
	WE=0;

	DU=1;
	P0=LedBuff[i];
	DU=0;
	 
	i++;
	if(i==8)i=0;
}
/*另一个代码*/
/*====================================
函数	:display(uchar i)
参数	:i 显示变量取值0-9999 
返回值	:无
描述	:数码管动态显示函数
====================================*/
#include 
#include 

sbit DU = P2^6;//数码管段选
sbit WE = P2^7;//数码管段选

//共阴数码管段选表0-9
uchar code SMGduan[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};
//数码管位选码
uchar code SMGwei[] = {0xfe, 0xfd, 0xfb, 0xf7};

void display(uint i)
{
	uchar q, b, s, g;
	static uchar wei;
	q = i / 1000;
	b = i % 1000 / 100;
	s = i % 100 / 10;
	g = i % 10;		
	P0 = 0XFF;//清除断码
	WE = 1;//打开位选锁存器
	P0 = SMGwei[wei];
	WE = 0;//锁存位选数据
	P0 = 0XFF;//清除断码
	switch(wei)
	{
		case 0: DU = 1; P0 = SMGduan[q] | 0x80;	DU = 0; break;//加上个| 0x80 是加上小数点
		case 1: DU = 1; P0 = SMGduan[b]; 	DU = 0; break;	
		case 2: DU = 1; P0 = SMGduan[s]; 	DU = 0; break;
		case 3: DU = 1; P0 = SMGduan[g]; 	DU = 0; break;		
	}
	wei++;
	if(wei == 4)
		wei = 0;
}

数码管的扫描最好放到中断里面去进行,中断我会总结到下一篇博客中

鬼影的消除

学习了一段时间的单片机,有些情况下的鬼影,是真没办法消除,但是有些情况的鬼影,可以通过以下的方法来消除。
①P0 = 0XFF;在位选之前使用,达到消除鬼影的作用。
②更改延时,减少延时的时间或者中断里面的时间。

ps:更多的方法需要大家去了解鬼影的产生是什么原因,知道原因之后再去消除就会变的十分简单
这里推荐一本书《手把手教你学 51 单片机-C 语言版》,里面各个方法讲的十分仔细,也有鬼影的教程,并且该书作者也进行了免费开源
在 6.4.2 数码管显示消隐 讲解了鬼影的原理和消除的方法。
电子版下载地址:www.kingst.org

以上,如有错误,欢迎大家指出,我也是小白初学,开始总结笔记,希望能和大家一起学习,一起讨论。

你可能感兴趣的:(单片机学习)