疫情期间在家真是效率低下啊
返校之后,会有51单片机的考核测试
在博客写下各个模块的总结
方便以后查看
如有错误,欢迎大佬指出
数码管又学到了一个新的能显示千位的代码
原理图
使用:
这是共阳极,也就是当上电之后,串口如果输出高电压,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管,可以自行百度原理)
使用:
蜂鸣器使用起来非常的简单,只需要控制蜂鸣器输出电压为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;
}
原理图
使用:
因为单片机上面的串口数量是有限制的,所以我们必须要做到以最少的串口来完成数码管的控制,这就需要使用到74H35锁存器。
首先我们要先了解,锁存器如何控制数码管
我们使用的是共阴极数码管,也就是串口输出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
以上,如有错误,欢迎大家指出,我也是小白初学,开始总结笔记,希望能和大家一起学习,一起讨论。