一、先前知识
使用到的74HC138译码器和74HC573锁存器,在前面三次基础模块的介绍中都有简单介绍,这里就不再介绍了。
单个数码管的介绍(在基础模块03的文章中有介绍,此处再次介绍是为了加深我们的印象)
蓝桥杯板子上使用的数码管是共阳极连接的,即组成数码管的八个段码中的八个发光二极管的正极公共端接在一个高电平上。当单片机引脚输出低电平给发光二极管的负极时发光二极管点亮,反之则发光二极管熄灭。就是通过控制每个段码的点亮与熄灭,来显示我们想要的数字。
图一、单个数码管
图二、数码管段码共阳连接
数码管模块介绍(在基础模块03的文章中有介绍,此处再次介绍是为了加深我们的印象)
由下图可知,573锁存器输入第六个通道时输出的八个引脚经过U22、U23再给到数码管;573锁存器输入第七个通道输出时八个引脚各自经过一个1K的电阻之后再经过U22、U23给到数码管。
首先要控制数码管模块点亮,分为两步。第一步就需要写入二进制数先控制让某一个(或者全部)数码管处于准备状态,也就是说单片机引脚一输出数码管就能立即点亮;第二步就需要写入二进制数来控制数码管显示某个数字。
图三、共阳数码管模块
图四、输入数码管前经过的模块
数码管动态显示说明
这里介绍一下上一篇文章中的数码管静态显示和此篇文章的动态显示的概念。
静态显示:静态显示用同时显示各个字符。位码始终有效,显示内容完全跟数据线上的值一致。并且由于静态显示不用不断变换位码,占用的CPU时间也就更短。
动态显示:动态显示实质上就是轮流点亮各个数码管实现多位数码管整体显示的效果。在轮流显示的过程中每位数码管点亮一定的时间,由于人眼的视觉暂留现象以及发光二极管的余辉效应,尽管实际上各位数码管并非同时点亮,但是只要数码管之间扫描的速度够快,那么给人的感觉就是一组稳定显示的数据,只要数码管之间切换的延时不是很大,就不会有闪烁感,但是一旦给的延时时间过大,数码管就会有闪烁感。
注意:数码管的动态显示十分重要!!!后面用到的全部都是数码管的动态显示!!!
部分代码解释(在基础模块03的文章中有介绍,此处再次介绍是为了加深我们的印象)
在此次的代码中我们不再是像前两次选择138译码器通道一样,去定义引脚。而是采用了一种全新的方式。
就case 4部分的代码而言(其他通道选择情况类似):
case 4:
P2 = ( P2 & 0x1f ) | 0x80; // Y4C
break;
首先我们先看“P2&0x1f” 。我们可以先假设P2=10101010,那么P2&0x1f=10101010 & 00011111,根据“&”的用法我们知道,这里是八位二进制数按位“与”,则P2&0x1f=000 01010。显而易见的是该步骤只是改变了P2的高三位,而后面几位都没有改变。
其次我们看“( P2 & 0x1f ) | 0x80” ,还是按照上面的假设,那么就是 00001010|10000000,根据“|”的用法我们知道,这里是八位二进制数按位“或”,则结果为 100 01010,这就是最终的结果。从头到尾的两步操作,都只是改变了P2初始值中的前三位。
下面,我们做出解释:
由138译码器芯片的引脚图我们知道,它的输入只需要P2口的高三位,不需要其他的引脚,那么我们的在操作时最好的情况就是,我只改变P2口的高三位,然后输入给138译码器。首先不管P2初始的高三位是否满足要求,我都先把它置成0,也就是“P2&0x1f”干的事,然后把高三位置成我想要的数,也就是“|0x80”干的事,再输入给138译码器。
图五、74HC138译码器模块
二、模块控制实现过程简述
先往74HC138译码器中写入二进制数110(十进制数6),输出就是Y6为低电平,经过一个或非门输出的Y6C就为高电平,再输入74HC573锁存器。然后输入二进制数来让某一个(或者全部)数码管进入准备状态。
再控数码管的显示,也就是控制段码的点亮,就需要先往74HC138译码器中写入二进制数111(十进制数7),输出就是Y7为低电平,经过一个或非门输出的Y7C就为高电平,再输入74HC573锁存器。然后输入二进制数来让数码管显示数字。
三、所要实现的功能
实现数码管的动态显示,在8位数码管中,前四位显示“2023”年,接着两位显示字符“--”,后面两位显示月份“03”。并且实现让月份以一定的时间延时后加一,月份在01~12之间轮流显示。程序循环实现上述功能。
四、代码实现
①共阳数码管段码数组
// 不加“code”会影响单片机的运行速度,会更改RAM,加了code之后这一部分内容就不变了,也不能更改,就不会过多占用RAM
unsigned char code SMG_Duanma[18] =
{
0xc0 , 0xf9 , 0xa4 , 0xb0 , 0x99 , 0x92 , 0x82 , 0xf8 , 0x80 ,
0x90 , 0x88 , 0x80 , 0xc6 , 0xc0 , 0x86 , 0x8e , 0xbf , 0x7f
}; // 0~F - .
②138译码器通道选择函数
// 通道选择
void HC138_Init( unsigned char channel )
{
switch( channel )
{
case 0:
P2 = ( P2 & 0x1f ) | 0x00; // 0
break;
case 4:
P2 = ( P2 & 0x1f ) | 0x80; // Y4C
break;
case 5:
P2 = ( P2 & 0x1f ) | 0xa0; // Y5C
break;
case 6:
P2 = ( P2 & 0x1f ) | 0xc0; // Y6C
break;
case 7:
P2 = ( P2 & 0x1f ) | 0xe0; // Y7C
break;
}
}
③初始化系统-关闭不需要用到的LED灯、蜂鸣器和继电器
// 初始化系统
void Init_System(void)
{
// 关闭LED灯
HC138_Init( 4 );
P0 = 0xff;
// 关闭蜂鸣器和继电器
HC138_Init( 5 );
P0 = 0xaf; // 1010 1111
HC138_Init( 0 ); // 关闭所有通道选择
}
④控制单个数码管点亮函数
// 单个数码管点亮
void SMG_Light( unsigned char pos , unsigned char dat )
{
// 数码管的位置
HC138_Init( 6 ); // com口 Y6C
P0 = 0x01 << pos; // com口 依次高电平
// 数码管的内容
HC138_Init( 7 );
P0 = dat; // 显示数字
}
⑤数码管动态显示
// 数码管动态显示
void SMG_DT(void)
{
SMG_Light( 0 , SMG_Duanma[2] ); // 2
Delay_tms( 10 );
SMG_Light( 1 , SMG_Duanma[0] ); // 0
Delay_tms( 10 );
SMG_Light( 2 , SMG_Duanma[2] ); // 2
Delay_tms( 10 );
SMG_Light( 3 , SMG_Duanma[3] ); // 3
Delay_tms( 10 );
SMG_Light( 4 , SMG_Duanma[16] ); // -
Delay_tms( 10 );
SMG_Light( 5 , SMG_Duanma[16] ); // -
Delay_tms( 10 );
SMG_Light( 6 , SMG_Duanma[yue / 10] ); // 0
Delay_tms( 10 );
SMG_Light( 7 , SMG_Duanma[yue % 10] ); // 3
Delay_tms( 10 );
}
⑥延时函数
void Delay_tms( unsigned int t )
{
int i;
while( t-- )
{
for( i=115 ; i>0 ; i-- )
{
}
}
}
⑦新延时函数
// 延时有两种方法,一种是通过外部函数进行延时,第二种就是把要延时的內容放在循环里,不满足循环条件了再跳出
void Delay_New( unsigned int t ) // 当t减小到0时退出循环,开始 yue 增加
{
while( t-- )
{
SMG_DT(); // 数码管动态显示
}
}
⑧整个函数展示
#include <STC15F2K60S2.H>
#include "Delay_ms.h"
// 不加“code”会影响单片机的运行速度,会更改RAM,加了code之后这一部分内容就不变了,也不能更改,就不会过多占用RAM
unsigned char code SMG_Duanma[18] =
{
0xc0 , 0xf9 , 0xa4 , 0xb0 , 0x99 , 0x92 , 0x82 , 0xf8 , 0x80 ,
0x90 , 0x88 , 0x80 , 0xc6 , 0xc0 , 0x86 , 0x8e , 0xbf , 0x7f
}; // 0~F - .
// 月份
int yue = 2;
// 通道选择
void HC138_Init( unsigned char channel )
{
switch( channel )
{
case 0:
P2 = ( P2 & 0x1f ) | 0x00; // 0
break;
case 4:
P2 = ( P2 & 0x1f ) | 0x80; // Y4C
break;
case 5:
P2 = ( P2 & 0x1f ) | 0xa0; // Y5C
break;
case 6:
P2 = ( P2 & 0x1f ) | 0xc0; // Y6C
break;
case 7:
P2 = ( P2 & 0x1f ) | 0xe0; // Y7C
break;
}
}
// 初始化系统
void Init_System(void)
{
// 关闭LED灯
HC138_Init( 4 );
P0 = 0xff;
// 关闭蜂鸣器和继电器
HC138_Init( 5 );
P0 = 0xaf; // 1010 1111
HC138_Init( 0 ); // 关闭所有通道选择
}
// 单个数码管点亮
void SMG_Light( unsigned char pos , unsigned char dat )
{
// 数码管的位置
HC138_Init( 6 ); // com口 Y6C
P0 = 0x01 << pos; // com口 依次高电平
// 数码管的内容
HC138_Init( 7 );
P0 = dat; // 显示数字
}
// 数码管动态显示
void SMG_DT(void)
{
SMG_Light( 0 , SMG_Duanma[2] ); // 2
Delay_tms( 10 );
SMG_Light( 1 , SMG_Duanma[0] ); // 0
Delay_tms( 10 );
SMG_Light( 2 , SMG_Duanma[2] ); // 2
Delay_tms( 10 );
SMG_Light( 3 , SMG_Duanma[3] ); // 3
Delay_tms( 10 );
SMG_Light( 4 , SMG_Duanma[16] ); // -
Delay_tms( 10 );
SMG_Light( 5 , SMG_Duanma[16] ); // -
Delay_tms( 10 );
SMG_Light( 6 , SMG_Duanma[yue / 10] ); // 0
Delay_tms( 10 );
SMG_Light( 7 , SMG_Duanma[yue % 10] ); // 3
Delay_tms( 10 );
}
// 延时有两种方法,一种是通过外部函数进行延时,第二种就是把要延时的內容放在循环里,不满足循环条件了再跳出
void Delay_New( unsigned int t ) // 当t减小到0时退出循环,开始 yue 增加
{
while( t-- )
{
SMG_DT(); // 数码管动态显示
}
}
int main(void)
{
Init_System(); // 初始化系统
while(1)
{
yue++; // 月份
if( yue > 12)
{
yue = 1;
}
Delay_New( 100 );
}
}