1)实验平台:ALIENTEK NANO STM32F411 V1开发板
2)摘自《正点原子STM32F4 开发指南(HAL 库版》关注官方微信号公众号,获取更多资料:正点原子
第十七章 数码管显示实验
前面几章的实例,均没涉及到显示,这一章,我们将向大家介绍数码管的使用。在本章中,
我们将使用 NANO STM32F4 V1 开发板上的数码管模块,来点亮数码管,并实现字符的显示。
本章分为如下几个部分:
17.1 数码管/74HC595D/74HC138D 简介
17.2 硬件设计
17.3 软件设计
17.4 下载验证
17.1 数码管/74HC595D/74HC138D 简介
1)数码管
数码管,也称 LED 数码管,按发光二极管单元连接方式可分为“共阳极数码管”和“共阴
极数码管”。而开发板板载的数码管为四位共阴极数码管,实物图与内部引脚图,如图 17.1.1
所示:
图 17.1.1 四位数码管实物与内部引脚结构图
共阳极数码管是指将所有发光二极管的阳极接到一起,而共阴极数码管则是发光二极管的
阴极连到一起,连接方式如图 17.1.2 所示:
图 17.1.2 数码管的连接方式
从上图可以看到,数码管为共阴时,当某一字段的发光二极管阳极为高电平时,响应字段
就点亮,为共阳时,当某一字段的发光二极管阴极为低电平时,相应字段则点亮。所以通过点
亮相对应的字段就可以显示我们需要的字符。
数码管要正常显示,就要用驱动电路来驱动数码管的各个端码,从而显示出我们要的字符,
因此根据数码管的驱动方式不同,可以分为“静态式”和“动态式”两类。
静态显示驱动,也称为直流驱动。静态驱动是指每个数码管的每一个端码都有一个单片机
的 I/O 端口进行驱动,或者使用如 BCD 码二-十进制译码进行驱动。静态驱动的优点是编程简
单,显示亮度高,缺点是占用 I/O 端口多,如驱动 5 个数码管静态显示则需要 5×8=40 个 I/O 端
口来驱动,要知道 STM32 单片机可用的 IO 端口是有限的,实际应用时必须增加译码器进行驱
动,但会增加电路的复杂性。
动态显示驱动,是单片机应用中最为广泛的显示方式,动态驱动是将所有数码管的 8 个显
示笔划“a,b,c,d,e,f,g,dp”的同名端连载一起,另外为每个数码管的公共级 COM 增
加位选通控制电路,位选通由各自独立 IO 线控制,当单片机输出字形码时,所有数码管都接
收到相同的字形码,但究竟是哪个数码管会显示出字形,取决于单片机对位选通 COM 端电路
的控制,所有我们只要将需要显示的数码管的选通控制打开,改为就显示出字形,没有选通的
数码管就不会亮。通过分时轮流控制各个数码管的 COM 端,就可以使各个数码管轮流显示。
在轮流显示过程中,每位数码管的点亮时间为 1~2ms,由于人的视觉暂留现象及发光二极管的
余辉效应,尽管实际上各位数码管并非同时点亮,只要扫描的速度够快,给人的印象就是一组
稳定的显示数据,不会有闪烁感,动态显示的效果和静止显示是一样的,能够节省大连的 IO
端口,功耗降低。
本实验我们将使用动态驱动的方式驱动数码管,为了节省 IO,使用到驱动电路,段选数据
使用 74HC595D 串行数据转并行数据输出芯片,位选数据使用 74HC138D 三-八译码器芯片。
下面将介绍这两款芯片。
2)74HC595D
74HC595D 是恩智浦 NXP 公司推出的一款低噪声、低功耗、高速的 CMOS 移位寄存器,
能够驱动 15 个 LS-TTL 的负载。该器件包含一个 8 位串行输入、并行输出的移位寄存器及带有
三态输出控制的 8 位 D 型存储器。74HC595D 的特点如下:
1 、带存储功能的 8 位串行输入,并行输出的移位寄存器
2 、工作电压范围宽:2V-6V
3 、可级联使用
74HC595D 广泛应用于点阵屏和主控 IO 较少数据串行转并行的场合,74HC595D 的框图如
图 17.1.1 所示:
图 17.1.1 74HC595D 框图
1,引脚说明
74HC595D 的引脚说明如表 17.1.2 所示:
表 17.1.2 74HC595 引脚说明
我们使用的 74HC595D 采用 SO16 封装,总共 16 个脚,其中包括 8 个三态输出 IO 口、1
个串行数据输出端,1 个移位寄存器清零端,1 个数据输入时钟,1 个输出存储器锁存时钟,1
个输出使能端,1 个串行数据输入端,等其他引脚。下面我们分别以数据端和控制端讲解下引
脚的作用:
数据端:
Q0-Q7:八位并行数据输出端,可以直接控制数码管的 8 个段。
Q7’:级联输出端。它可接入下一个 74HC595 的 DS 端。
DS:串行数据输入端,级联的话接上一级的 Q7’。
控制端:
/MR(10 脚):低电平时将移位寄存器的数据清零。一般情况下接 VCC。
SH_CP(11 脚):上升沿时数据寄存器的数据移位。传输方向:Q0->Q1->Q2->Q3->...->Q7;
下降沿移位寄存器数据不变。
ST_CP(12 脚):上升沿时移位寄存器的数据进入数据存储器,下降沿时存储器数据不变。
通常将 ST_CP 置为低电平,当移位结束后,在 ST_CP 端产生一个正脉
冲,更新显示数据。
/OE(13 脚):高电平时禁止输出(高组态)。可以用一个引脚控制它,可以方便地产生
闪烁和熄灭效果。
2,时序图
数据的串行输入需要按照规定的时间才能正常写入,根据上面讲解,我们要知道的是
SH_CP 上升沿高电平和 ST_CP 正脉冲保持的时间,如图 17.1.3 所示:
图 17.1.3 SHCP 和 STCP 时序图
从上图可以看到时序的时间都是 ns 级别的,在写软件时我们只需要几 us 的时间就可以了。
通过 74HC595D 的使用,我们就可以以串行数据输入,并行数据输出控制数码管的端选段,
从而显示对应的字符。
3)74HC138D
74HC138D 是恩智浦 NXP 公司推出的一款高速 CMOS、低功耗的 3-8 译码器。通过三个地
址的输入设置,可选择对应八种的状态输出。74HC138D 的特点如下:
1)低功耗。
2)工作电压范围宽:3V-5V。
74HC138D 广泛应用于消费类电子产品,74HC138D 的框图如图 17.1.4 所示:
图 17.1.4 74HC138D 引脚和功能框图
1,引脚说明
74HC138D 的引脚说明如表 17.1.5 所示:
图 17.1.6 74HC138D 真值表
从图可以看到,当/E1、/E2、E3 使能端使能后,设置 A0、A1、A2 的输入(高电平有效),
/Y0~/Y7 都会有对应的状态输出(低电平),例如 A0、A1、A2 设置的是(A2=0,A1=1,A2=1),
根据 BCD-421 码的换算,代表是 3,则 Y3 输出有效电平 L,其他的都输出为无效电平 H。设
置(A2=1,A1=1,A0=1),代表是 7,则 Y7 输出有效电平 L,其他的都输出为无效电平 H。
通过 74HC138D 的使用,我们就可以控制对应数码管的位选端了。
17.2 硬件设计
本实验用到的硬件资源有:
1) 指示灯 DS0
2) 74HC595D、74HC138D
3) 数码管
数码管、74HC595D、74HC138D 电路连接如图 17.2.1-1 和图 17.2.1-2 所示:
图 17.2.1-1 开发板连接示意图
图 17.2.1-2 开发板连接示意图
原理图中 74HC595D 芯片标的引脚标号名称可能与芯片手册标的有点不一样,以下是引脚
对应标号关系,如表 17.2.2 所示:
表 17.2.2 引脚标号关系
从图 17.2.1 原理图可以看到,74HC138D 的使能端引脚默认已上拉或下拉硬件使能,
74HC595D 的 RST 复位引脚与开发板的复位线相连接,当按下开发板的复位按键或首次上电即
可对 74HC595D 的芯片的数据清零,以到达复位的作用。由于没有做级联的功能,SDO 引脚上
做了悬空处理。而使能端引脚也同样做了默认硬件使能。
在数码管中,两个数码管的 A~DOT 段选段都与 74HC595D 芯片的 QA~QH 相连接,而数
码管的 CH1~CH4 位选端则单独分别与 74HC138D 芯片的 Y0~Y7 引脚相连接。通过控制
74HC595D 输出的段码和控制 74HC138D 输出的位选就可以实现选择点亮数码管。
本节,我们将实现 8 位数码管的动态显示,通过 TIMER3 定时器 2ms 时间来刷新显示,1S
时间更新显示字符,下面我们开始软件设计。
17.3 软件设计
我们直接打开数码管显示实验可以发现 HARDWARE 下面有一个 和 timer 文件,同
时包含了头文件 和 。
文件下的代码,是关于定时器的初始化配置,在第十三章定时器中断实验我们有比
较详细的讲解,这里就不做介绍了。这里我们主要讲解下 的代码,首先是 LED_SMG_Init
函数,该函数比较简单,对使用到的 IO 口初始化,LED_SMG_Init 函数代码如下:
//数码管驱动 IO 初始化
void LED_SMG_Init(void)
{
GPIO_InitTypeDef GPIO_Initure;
__HAL_RCC_GPIOB_CLK_ENABLE(); //开启 GPIOB 时钟
__HAL_RCC_GPIOC_CLK_ENABLE(); //开启 GPIOC 时钟
//74HC595 DS-PB3 LCLK-PB4 SCK-PB5
GPIO_Ini|GPIO_PIN_4|GPIO_PIN_5;//PB3~5
GPIO_Ini;
//推挽输出
GPIO_Ini;
//上拉
GPIO_Ini;
//高速
HAL_GPIO_Init(GPIOB,&GPIO_Initure);
//74HC138 A0-PC10 A1-PC11 A2-PC12
GPIO_Ini|GPIO_PIN_11|GPIO_PIN_12;//PC10~12
HAL_GPIO_Init(GPIOC,&GPIO_Initure);
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_3,GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_5,GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_4,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOC,GPIO_PIN_10,GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOC,GPIO_PIN_11,GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOC,GPIO_PIN_12,GPIO_PIN_RESET);
}
接着,要介绍的是 LED_Wei 函数。这函数实现控制 74HC138D 的 A0~A2 地址,将入口参
数通过转换设置 LED_A0~LED_A2 的宏定义 IO 位带操作,LED_Wei 函数代码如下:
//74HC138 驱动
//数码管位选
//num:要显示的数码管编号 0-7(共 8 个数码管)
void LED_Wei(u8 num)
{
LED_A0=num&0x01;
LED_A1=(num&0x02)>>1;
LED_A2=(num&0x04)>>2;
}
LED_Write_Data 函数,该函数实现了段码数据以 8 个上升沿的时钟先低位后高位的串行输
入 形 式 , 传 输 给 74HC595D, 然 后 调 用 LED_Wei 函 数 控 制 位 选 需 要 显 示 的 数 码 管 ,
LED_Write_Date 函数代码如下:
//74HC595 驱动
//数码管显示
//duan:显示的段码
//wei:要显示的数码管编号 0-7(共 8 个数码管)
void LED_Write_Data(u8 duan,u8 wei)
{
u8 i;
for( i=0;i<8;i++)//先送段
{
LED_DS=(duan>>i)&0x01;
LED_SCK=0;
delay_us(5);
LED_SCK=1;
}
LED_Wei(wei);//后选中位
}
最后要介绍的是 LED_Refresh 函数,该函数实现对 74HC595D 串行输入的数据,进行刷新
输出给数码管,LED_Refresh 函数如下:
//74HC595 驱动
//数码管刷新显示
void LED_Refresh(void)
{
LED_LCLK=1;
delay_us(5);
LED_LCLK=0;
}
要正确显示数码管,需要正确的段码,接下来,我们介绍一个数码管段码查询的软件,该
软件的界面如图 17.3.1 所示:
图 17.3.1 数码管段码查询软件界面
该软件设置很简单,只要设置段码的排列顺序、数码管的极性,通过选择点击区,就可以
输出段码了。该软件也提供了一些常用的字符给我们选择。
根据我们开发板 74HC595D 驱动数码管的方式,极性选择“共阴”,排列的顺序选择“a-dp”,
如图 17.3.1 所示:
图 17.3.2 软件配置
根据软件的配置,设置了 0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F,.(点号),全灭,共 18 个显示字符段
码,分别是 0xfc,0x60,0xda,0xf2,0x66,0xb6,0xbe,0xe0,0xfe,0xf6,0xee,0x3e,0x9c,0x7a,0x9e,0x8e,
0x01,0x00。
文件下的代码我们就介绍完了,接下来看看 ,代码如下:
#ifndef _LED_SMG_H
#define _LED_SMG_H
#include ";
////74HC138 操作线
#define LED_A0 PCout(10) //A0 地址线
#define LED_A1 PCout(11) //A1 地址线
#define LED_A2 PCout(12) //A2 地址线
////74HC595 操作线
#define LED_DS
PBout(3) //数据线
#define LED_LCLK PBout(4) //锁存时钟线
#define LED_SCK
PBout(5) //时钟线
void LED_SMG_Init(void);
void LED_Refresh(void);
void LED_Write_Data(u8 duan,u8 wei);
#endif
该部分比较简单,就是一些 IO 的宏定义和函数的定义。
最后说一下 main,c,该部分代码如下:
//共阴数字数组
//0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F, .,全灭
u8 smg_num[]={0xfc,0x60,0xda,0xf2,0x66,0xb6,0xbe,0xe0,0xfe,
0xf6,0xee,0x3e,0x9c,0x7a,0x9e,0x8e,0x01,0x00};
int main(void)
{
HAL_Init();
//初始化 HAL 库
Stm32_Clock_Init(96,4,2,4);
//设置时钟,96Mhz
delay_init(96);
//初始化延时函数
uart_init(115200);
//初始化串口 115200
LED_Init();
//初始化 LED
LED_SMG_Init();
//数码管初始化
TIM3_Init(20-1,9600-1);
//2ms 定时显示
while(1)
{
}
}
u8 smg_wei=0;//数码管位选
u8 smg_duan=0;//数码管段选
u16 t=0;
//回调函数,定时器中断服务函数调用
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim==(&TIM3_Handler))
{
LED_Write_Data(smg_num[smg_duan],smg_wei);//数码管显示
LED_Refresh();//刷新显示
smg_wei++;
if(smg_wei==8) smg_wei=0;
t++;
if(t==500)//数码管每秒更新
{
t=0;
LED0=!LED0;
smg_duan++;
if(smg_duan==18) smg_duan=0;
}
}
}
代码比较简单,对数码管等其他外设初始化,控制数码管动态显示的定时器设置为 2ms 中
断,在定时器中断中每 2ms 刷新显示 1 位数码管,每 1S 时间更新显示数码管段码,字符从 0
开始不停的循环显示,同时 DS0 闪烁。然后我们编译此工程,直到编译成功为止。
代码比较简单,对数码管等其他外设初始化,控制数码管动态显示的定时器设置为 2ms 中
断,在定时器中断中每 2ms 刷新显示 1 位数码管,每 1S 时间更新显示数码管段码,字符从 0
开始不停的循环显示,同时 DS0 闪烁。至此,我们的软件设计就完成了。
17.4 下载验证
将代码下载到开发板后,可以看到 8 位数码管以 1S 时间更新显示,同时 DS0 不停的闪烁,
数码管显示如图 17.4.1 所示:
图 17.4.1 数码管显示效果
通过这一章的学习,我们学会了数码管的使用,在以后需要显示程序的编写,大家可以好
好利用。