目的
让数码管在多个指定位置显示指定信息。
我们接着《51单片机STC89C52RC——3.1 数码管静态展示-CSDN博客》
一,STC单片机模块
二,数码管
2.1 数码管位置
2.2 生活中用到的数目管
红绿灯
LED数码管在生活中随处可见,洗衣机、电饭煲、热水器、微波炉、冰箱、这些最基本的家用电器上基本都用到了这种7段LED数码管(段
为LED灯的个数)。
2.3 数码管的分类
按显示段数分:分为七段、八段、九段、十四段和十六段
七段:七段数码管由7个LED构成
八段:八段数码管比七段多了一个小数点
九段:九段数码管由9个LED构成
十四段:十四段数码管由14个LED构成
十六段:十六段数码管由16个LED构成
按显示位数分:分为1位、2位、3位及多位数码管(大于1位),
我们的板子上是8段4位。
2.4 数码管电路图
单个数码管的电路图设计如下
内部接线图如下两种
第一种:共阴极(8个LED的负极接一起)
第二种:共阳极(8个LED的正极接一起)
可以看到,这个‘8’有A、B、C、D、E、F、G和DP8个LED构成。我们在程序上只需要将对应位置的LED点亮就可以了。ABCDEFG‘DP’8个位置刚好对应上一个byte的8个Bit位。要注意这里是与byte的低位对齐。
比如:
数字‘0’就是A、B、C、D、E、F位置的LED亮起。0011 1111 ,十六进制0x3F;
数字‘1’就是B、C 两个位置LED亮起。二进制0000 0110 ,十六进制0x06;
数字‘2’就是A、B、G、E、D位置的LED亮起、
……
以此类推,我们可以用HEX罗列出数码管能展示的所有可供我们认识的字符,要显示对应的字符,则P0值如下表
数码管显示 | 点亮位置的LED | 十六进制 (P0值) | 二进制 | |||||||
高 | 低 | |||||||||
DP | G | F | E | D | C | B | A | |||
0 | ABCDEF | 0x3F | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 |
1 | BC | 0x06 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 0 |
2 | ABGED | 0x5B | 0 | 1 | 0 | 1 | 1 | 0 | 1 | 1 |
3 | ABCDG | 0x4F | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 1 |
4 | FGBC | 0x66 | 0 | 1 | 1 | 0 | 0 | 1 | 1 | 0 |
5 | AFGCD | 0x6D | 0 | 1 | 1 | 0 | 1 | 1 | 0 | 1 |
6 | AFEDCG | 0x7D | 0 | 1 | 1 | 1 | 1 | 1 | 0 | 1 |
7 | ABC | 0x07 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 |
8 | ABCDEFG | 0x7F | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
9 | GFABCD | 0x6F | 0 | 1 | 1 | 0 | 1 | 1 | 1 | 1 |
A | ABCEFG | 0x77 | 0 | 1 | 1 | 1 | 0 | 1 | 1 | 1 |
C | AFED | 0x39 | 0 | 0 | 1 | 1 | 1 | 0 | 0 | 1 |
E | AFEDG | 0x79 | 0 | 1 | 1 | 1 | 1 | 0 | 0 | 1 |
F | AFEG | 0x71 | 0 | 1 | 1 | 1 | 0 | 0 | 0 | 1 |
H | BCEFG | 0x76 | 0 | 1 | 1 | 1 | 0 | 1 | 1 | 0 |
J | BCDE | 0x1E | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 0 |
L | FED | 0x38 | 0 | 0 | 1 | 1 | 1 | 0 | 0 | 0 |
p | ABGFE | 0x73 | 0 | 1 | 1 | 1 | 0 | 0 | 1 | 1 |
q | ABGFC | 0x67 | 0 | 1 | 1 | 0 | 0 | 1 | 1 | 1 |
U | BCDEF | 0x3E | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 0 |
. | 'DP' | 0x80 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
b | FEGDC | 0x7C | 0 | 1 | 1 | 1 | 1 | 1 | 0 | 0 |
d | BCDEG | 0x5E | 0 | 1 | 0 | 1 | 1 | 1 | 1 | 0 |
u | CDE | 0x1C | 0 | 0 | 0 | 1 | 1 | 1 | 0 | 0 |
这里我们在程序中用两个数组表示;
/*数码管可显示值*/
unsigned int ShuMaGuan_EnableDisplayValue[]={0,1,2,3,4,5,6,7,8,9,
'A','C','E','F','H','J','L','P','q','U','.','b','d','u'};
/*数码管显示值对应的HEX*/
unsigned int ShuMaGuan_EnableDisplayHEX[]={
0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,
0x77,0x39,0x79,0x71,0x76,0x1E,0x38,0x73,0x67,0x3E,0x80,0x7C,0x5E,0x1C};
/**
* 函 数:获取可显值对应的HEX
* 参 数:数码管显示的值
* 返 回 值:数码管显示时对应的HEX
*/
unsigned int ShuMaGuan_GetEnableDisplayValueOfHEX(unsigned int Value)
{
unsigned int index=0;
for(index=0;index<24;index++)//遍历显示值的数组
{
if(ShuMaGuan_EnableDisplayValue[index]==Value)//找到对应值的索引
{
break;
}
}
return ShuMaGuan_EnableDisplayHEX[index];
}
/**
* 函 数:设置数码管要显示的值
* 参 数:显示值
* 返 回 值:无
*/
void ShuMaGuan_SetDisplayValue(unsigned int Value)
{
/*将要显示的值 复制给P0*/
P0=ShuMaGuan_GetEnableDisplayValueOfHEX(Value);
}
数码管中的8个LED的一端已经通过上面P0设置成功,另一端我们也要设置接通LED才会连接到电路中导通。所以我们还需要了解74HC138译码器。
2.5 74HC138 译码器
译码器的作用:简单理解就是尽量让程序设置少的针脚通断去控制逻辑电路通断和数据传输。
假如在这里不用译码器,我们全部用单一导线和开关将LED的两端与电源正负极接通。那么线路就比较多,引脚也较多,我们程序中要控制的就增加。
在74HC138译码器上,有三个引脚P22、P23、P24,每个引脚都可以有通断(0、1)那么我们用三位的二进制就可以满足8个位置上通断。
C | B | A | |
P24 | P23 | P22 | |
LED1 | 1 | 1 | 1 |
LED2 | 1 | 1 | 0 |
LED3 | 1 | 0 | 1 |
LED4 | 1 | 0 | 0 |
LED5 | 0 | 1 | 1 |
LED6 | 0 | 1 | 0 |
LED7 | 0 | 0 | 1 |
LED8 | 0 | 0 | 0 |
我们程序想让那个位置上的LED亮起,就设置对应值就导通了!
/**
* 函 数:设置要显示的数码管 位置
* 参 数:位置(左起1-8)
* 返 回 值:无
*/
void ShuMaGuan_SetDisplayLocation(unsigned int Location)
{
switch(Location) //位码输出
{
case 1:P2_4=1;P2_3=1;P2_2=1;break;
case 2:P2_4=1;P2_3=1;P2_2=0;break;
case 3:P2_4=1;P2_3=0;P2_2=1;break;
case 4:P2_4=1;P2_3=0;P2_2=0;break;
case 5:P2_4=0;P2_3=1;P2_2=1;break;
case 6:P2_4=0;P2_3=1;P2_2=0;break;
case 7:P2_4=0;P2_3=0;P2_2=1;break;
case 8:P2_4=0;P2_3=0;P2_2=0;break;
}
}
有了上面两步,我们在主函数就只需要设置在数码管的哪个位置显示什么值就好。
/**
* 函 数:数码管展示
* 参 数:Location 显示的位置,Value 显示的值
* 返 回 值:无
*/
void ShuMaGuan_Display(unsigned int Location,unsigned int Value)
{
/*显示位置*/
ShuMaGuan_SetDisplayLocation(Location);
/*显示值*/
ShuMaGuan_SetDisplayValue(Value);
}
2.6 数码管动态展示
有了上面的分析逻辑,我们只能在某一位上显示一个字符。
为什么???????????????????????
我们变动下主函数的代码,在第一位设置显示2,然后我们将译码器的位置改到2.
#include <REGX52.H>
#include "ShuMaGuan.h"
#include "Delay.h"
/**
* 函 数:主函数
* 参 数:无
* 返 回 值:无
*/
void main()
{
/*在数码管的第一个位置显示2*/
ShuMaGuan_Display(1,2);
Delay_ms(2000);//延时2秒
P2_4=1;P2_3=1;P2_2=0;//设置74HC138译码器,在第二位展示
while(1)
{
}
}
效果如下
我们程序只移位没有变动值,
第一位显示2,第二位显示1.
#include <REGX52.H>
#include "ShuMaGuan.h"
#include "Delay.h"
/**
* 函 数:主函数
* 参 数:无
* 返 回 值:无
*/
void main()
{
/*在数码管的第一个位置显示2*/
ShuMaGuan_Display(1,2);
Delay_ms(2000);//延时2秒
//P2_4=1;P2_3=1;P2_2=0;//设置74HC138译码器,在第二位展示
ShuMaGuan_Display(2,1);
while(1)
{
}
}
效果如下
不能同时显示21。这个问题怎么解决???
动态显示:动态显示轮流显示各个字符。利用人眼视觉暂留的特点,循环顺序变更位码,同时数据线上发送相应的显示内容。
静态显示:静态显示用同时显示各个字符。位码始终有效,显示内容完全跟数据线上的值一致。
#include <REGX52.H>
#include "ShuMaGuan.h"
#include "Delay.h"
/**
* 函 数:主函数
* 参 数:无
* 返 回 值:无
*/
void main()
{
while(1)
{
/*在数码管的第一个位置显示2*/
ShuMaGuan_Display(1,2);
/*在数码管的第二个位置显示1*/
ShuMaGuan_Display(2,1);
}
}
效果如下。这是啥??2和1同时显示在两个位置上????? 怎么解决这个问题???
那就延时2毫秒试试
#include <REGX52.H>
#include "ShuMaGuan.h"
#include "Delay.h"
/**
* 函 数:主函数
* 参 数:无
* 返 回 值:无
*/
void main()
{
while(1)
{
/*在数码管的第一个位置显示2*/
ShuMaGuan_Display(1,2);
/*延时1毫秒*/
Delay_ms(2);
/*在数码管的第二个位置显示1*/
ShuMaGuan_Display(2,1);
/*延时1毫秒*/
Delay_ms(2);
}
}
还是有阴影,比较明显,那就把延时加长 ,20毫秒试试
眼睛受不了,那多长时间合适????我们可以计算下,人眼每秒大于24帧连续就区分不出来1000/25=40毫秒,程序运行的这个while循环要是能小雨40毫秒。就不会看到闪烁。
那么我们控制 延时时长+数码显示时长<40毫秒。机器没执行一语句大概10微秒。
所以我们这里把程序延时时长设置成(7-10)毫秒。
#include <REGX52.H>
#include "ShuMaGuan.h"
#include "Delay.h"
/**
* 函 数:主函数
* 参 数:无
* 返 回 值:无
*/
void main()
{
while(1)
{
/*在数码管的第一个位置显示2*/
ShuMaGuan_Display(1,2);
/*延时9毫秒*/
Delay_ms(9);
/*在数码管的第二个位置显示1*/
ShuMaGuan_Display(2,1);
/*延时9毫秒*/
Delay_ms(9);
}
}
阴影变弱了,肉眼看不见闪烁了。 【技术不好,不能完全消失】
要消除阴影,有另外一种办法,显示完成1位后暂停100微秒,然后将所有LED的值清零
/**
* 函 数:数码管展示
* 参 数:Location 显示的位置,Value 显示的值
* 返 回 值:无
*/
void ShuMaGuan_Display(unsigned int Location,unsigned int Value)
{
/*显示位置*/
ShuMaGuan_SetDisplayLocation(Location);
/*显示值*/
ShuMaGuan_SetDisplayValue(Value);
/*等待显示稳定*/
Delay_10us(10);
/*消除阴影*/
P0=0x00;
}
三,创建Keil项目
详细参考:51单片机STC89C52RC——创建Keil项目-CSDN博客
四,代码
main.c 如下
#include <REGX52.H>
#include "ShuMaGuan.h"
#include "Delay.h"
/**
* 函 数:主函数
* 参 数:无
* 返 回 值:无
*/
void main()
{
while(1)
{
ShuMaGuan_Display(1,2);
ShuMaGuan_Display(2,1);
}
}
ShuMaGuan.h如下
#ifndef __SHUMAGUAN_H_
#defind __SHUMAGUAN_H_
void ShuMaGuan_Display(unsigned int Location,unsigned int Value);
#endif
ShuMaGuan.c如下
#include <REGX52.H>
/*数码管可显示值*/
unsigned int ShuMaGuan_EnableDisplayValue[]={0,1,2,3,4,5,6,7,8,9,
'A','C','E','F','H','J','L','P','q','U','.','b','d','u'};
/*数码管显示值对应的HEX*/
unsigned int ShuMaGuan_EnableDisplayHEX[]={
0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,
0x77,0x39,0x79,0x71,0x76,0x1E,0x38,0x73,0x67,0x3E,0x80,0x7C,0x5E,0x1C};
/**
* 函 数:获取可显值对应的HEX
* 参 数:数码管显示的值
* 返 回 值:数码管显示时对应的HEX
*/
unsigned int ShuMaGuan_GetEnableDisplayValueOfHEX(unsigned int Value)
{
unsigned int index=0;
for(index=0;index<24;index++)//遍历显示值的数组
{
if(ShuMaGuan_EnableDisplayValue[index]==Value)//找到对应值的索引
{
break;
}
}
return ShuMaGuan_EnableDisplayHEX[index];
}
/**
* 函 数:设置数码管要显示的值
* 参 数:显示值
* 返 回 值:无
*/
void ShuMaGuan_SetDisplayValue(unsigned int Value)
{
/*将要显示的值 复制给P0*/
P0=ShuMaGuan_GetEnableDisplayValueOfHEX(Value);
}
/**
* 函 数:设置要显示的数码管 位置
* 参 数:位置(左起1-8)
* 返 回 值:无
*/
void ShuMaGuan_SetDisplayLocation(unsigned int Location)
{
switch(Location) //位码输出
{
case 1:P2_4=1;P2_3=1;P2_2=1;break;
case 2:P2_4=1;P2_3=1;P2_2=0;break;
case 3:P2_4=1;P2_3=0;P2_2=1;break;
case 4:P2_4=1;P2_3=0;P2_2=0;break;
case 5:P2_4=0;P2_3=1;P2_2=1;break;
case 6:P2_4=0;P2_3=1;P2_2=0;break;
case 7:P2_4=0;P2_3=0;P2_2=1;break;
case 8:P2_4=0;P2_3=0;P2_2=0;break;
}
}
/**
* 函 数:数码管展示
* 参 数:Location 显示的位置,Value 显示的值
* 返 回 值:无
*/
void ShuMaGuan_Display(unsigned int Location,unsigned int Value)
{
/*显示位置*/
ShuMaGuan_SetDisplayLocation(Location);
/*显示值*/
ShuMaGuan_SetDisplayValue(Value);
}
Dealy.c
/**
* 函 数:延时函数 毫秒
* 参 数:ms 延时多少毫秒
* 返 回 值:无
*/
void Delay_ms(int ms) //@12.000MHz
{
unsigned char data i, j;
while(ms--)
{
i = 2;
j = 239;
do
{
while (--j);
} while (--i);
}
}
Delay.h
#ifndef __DELAY_H_
#defind __DELAY_H_
void Delay_ms(int ms);
#endif
五,代码编译、下载到51单片机
代码编译请参考
《51单片机STC89C52RC——代码编译-CSDN博客》
代码下载请参考
《51单片机STC89C52RC——STCAI-ISP代码下载-CSDN博客》