【普中STM32精灵开发攻略】--第 14 章 动态数码管实验
(1)实验平台:
普中STM32精灵开发板https://item.taobao.com/item.htm?id=739076227953(2)资料下载:普中科技-各型号产品资料下载链接
前面章节我们已经介绍了如何使用 STM32 单片机控制IO 输出。本章将介绍数码管显示,在实际应用中通常需要显示多位数值,即数码管动态显示。开发板上板载 2 个四位一体的共阴数码管,本章我们就来介绍下如何使用STM32 单片机控制动态数码管显示。本章所要实现的功能是:控制动态数码管从左至右显示数字 0-7。学习本章可以参考前面的实验章节内容。本章分为如下几部分内容:
14.1 数码管介绍
14.1.1 数码管简介
数码管是一种半导体发光器件,其基本单元是发光二极管。数码管也称LED数码管,不同行业人士对数码管的称呼不一样,其实都是同样的产品。数码管按段数可分为七段数码管和八段数码管,八段数码管比七段数码管多一个发光二极管单元,也就是多一个小数点(DP),这个小数点可以更精确的表示数码管想要显示的内容;按能显示多少个(8)可分为 1 位、 2 位、3 位、4 位、5位、6 位、7 位等数码管。按发光二极管单元连接方式可分为共阳极数码管和共阴极数码管。
共阳数码管是指将所有发光二极管的阳极接到一起形成公共阳极(COM)的数码管,共阳数码管在应用时应将公共极 COM 接到电源正极,当某一字段发光二极管的阴极为低电平时,相应字段就点亮,当某一字段的阴极为高电平时,相应字段就不亮。
共阴数码管是指将所有发光二极管的阴极接到一起形成公共阴极(COM)的数码管,共阴数码管在应用时应将公共极 COM 接到地线GND 上,当某一字段发光二极管的阳极为高电平时,相应字段就点亮,当某一字段的阳极为低电平时,相应字段就不亮。
不同位数的数码管实物图如下所示:
14.1.2 数码管显示原理
不管将几位数码管连在一起,数码管的显示原理都是一样的,都是靠点亮内部的发光二极管来发光,下面我们就来讲解一个数码管是如何亮起来的。数码管内部电路如下图所示:
从上图可看出,一位数码管的引脚是 10 个,显示一个8 字需要7 个小段,另外还有一个小数点,所以其内部一共有 8 个小的发光二极管,最后还有一个公共端,多数生产商为了封装统一,单位数码管都封装10 个引脚,其中第3和第 8 引脚是连接在一起的。而它们的公共端又可分为共阳极和共阴极,图中间为共阳极内部原理图,右图为共阴极内部原理图。
对共阴极数码来说,其 8 个发光二极管的阴极在数码管内部全部连接在一起,所以称“共阴”,而它们的阳极是独立的,通常在设计电路时一般把阴极接地。当我们给数码管的任意一个阳极加一个高电平时,对应的这个发光二极管就点亮了。如果想要显示出一个 8 字,并且把右下角的小数点也点亮的话,可以给8个阳极全部送高电平,如果想让它显示出一个 0 字,那么我们可以除了给第“g,dp” 这两位送低电平外,其余引脚全部都送高电平,这样它就显示出0 字了。
如果使用共阴数码管,需要注意增加单片机 IO 口驱动电流,因为共阴数码管是要靠单片机 IO 口输出电流来点亮的,但单片机 I/O 口难以输出稳定的、如此大的电流,所以数码管与单片机连接时需要加驱动电路,可以用上拉电阻的方法或使用专门的数码管驱动芯片,比如 74HC573、74HC245 等,其输出电流较大,电路接口简单。
共阳极数码管其内部 8 个发光二极管的所有阳极全部连接在一起,电路连接时,公共端接高电平,因此我们要点亮哪个发光管二极管就需要给阴极送低电平,此时显示数字的编码与共阴极编码是相反的关系,数码管内部发光二极管点亮时,也需要 5mA 以上的电流,而且电流不可过大,否则会烧坏发光二极管。因此不仅要防止数码管电流过大,同时要防止流经数码管的电流集中到单片机时电流不能过大,否则会损坏主芯片。
一般共阳极数码管更为常用,为什么呢? 这是因为数码管的非公共端往往接在 IC 芯片的 I/O 上,而 IC 芯片的驱动能力往往是比较小的,如果采用共阴极数码管,它的驱动端在非公共端, 就有可能受限于 IC 芯片输出电流不够而显示昏暗,要外加上拉电阻或者是增加三极管加大驱动能力。但是IC 芯片的灌电流,即输入电流范围比较大。所以使用共阳极数码管的好处是:将驱动数码管的工作交到公共端(一般接驱动电源),加大驱动电源的功率自然要比加大IC 芯片I/O口的驱动电流简单许多。另一方面,这样也能减轻主芯片的负担。
我们开发板上使用的数码管是 2 个四位一体的共阴极数码管(即8 个LED的阳极全部并联一起引出,阴极分别引出如 A、 B...DP),本章实验也是在该数码管上实现动态显示。如果要让共阴数码管显示数字0,即对应的段ABCDEF要点亮即给它高电平,其他的段熄灭即给它低电平。其他的数字显示方式一样,这里就不多说。下面给出共阴和共阳数码管的 0-F 段码数据表,如下所示:
①共阴数码管码表
0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d,
0 1 2 3 4 5
0x7d, 0x07, 0x7f, 0x6f, 0x77, 0x7c,
6 7 8 9 A B
0x39, 0x5e, 0x79, 0x71, 0x00,
C D E F 无显示
②共阳数码管码表
0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92,
0 1 2 3 4 5
0x82, 0xF8, 0x80, 0x90, 0x88, 0x83,
6 7 8 9 A B
0xC6, 0xA1, 0x86, 0x8E, 0xFF,
C D E F 无显示
从上述共阳和共阴码表中不难发现,它们的数据正好是相互取反的值。比如共阴数码管数字 0 段码:0x3f,其二进制是:0011 1111,取反后为:1100 0000,转换成 16 进制即为 0XC0。其他段码依此类推。该段码数据由来,是将a 段作为最低位,b 段作为次低位,其他按顺序类推,dp 段为最高位,共8 位,可使用STM32 某端口的高或低八位控制数码管的段选数据口,比如PA0-PA7 口。
14.1.3 多位数码管简介
多位数码管,即两个或两个以上单个数码管并列集中在一起形成一体的数码管。当多位一体时,它们内部的公共端是独立的,而负责显示什么数字的段线(a-dp)全部是连接在一起的,独立的公共端可以控制多位一体中的哪一位数码管点亮,而连接在一起的段线可以控制这个能点亮数码管亮什么数字,通常我们把公共端叫做“位选线”,连接在一起的段线叫做“段选线”,有了这两个线后,通过单片机及外部驱动电路就可以控制任意的数码管显示任意的数字了。
一般一位数码管有 10 个引脚, 二位数码管也是 10 个引脚,四位数码管是12 个引脚,关于具体的引脚及段、位标号大家可以查询相关资料,最简单的办法就是用数字万用表测量,若没有数字万用表也可用 5V 直流电源串接1k 电阻后测量,将测量结果记录,通过统计便可绘制出引脚标号。多位数码管有许多是按一定要求设计的,引脚不完全按照一般规则设定,所以需要在使用时查找手册,最直接的办法就是按照数码管上的标示向生产商要求。
我们开发板上使用了 2 个四位一体的共阴数码管,这样可在上面同时显示8个数值。
14.1.4 数码管动态显示原理
多位数码管依然可以静态显示,但是显示时要么只显示一位数码管,要么多位同时显示相同内容。当多位数码管应用于某一系统时,它们的“位选”是可独立控制的,而“段选”是连接在一起的,我们可以通过位选信号控制哪几个数码管亮,而在同一时刻,位选选通的所有数码管上显示的数字始终都是一样的,因为它们的段选是连接在一起的,送入所有数码管的段选信号都是相同的,所以它们显示的数字必定一样,数码管的这种显示方法叫做静态显示。
而动态显示,就是利用减少段选线,分开位选线,利用位选线不同时选择通断,改变段选数据来实现的。比如在第一次选中第一位数码管时,给段选数据0,下一次位选中第二位数码管时显示 1。为了在显示 1 的时候,0 不会消失(当然实际上是消失了),必须在人肉眼观察不到的时间里再次点亮第一次点亮的0。而这时就需要记住,人的肉眼正常情况下只能分辨变化超过24ms 间隔的运动。也就是说,在下一次点亮 0 这个数字的时间差不得大于24ms。这时就会发现,数码管点亮是在向右或者向左一位一位点亮,形成了动态效果。如果把间隔时间改长就能直接展现这一现象。
数码管动态显示的应用非常多,所以大家一要认真学好数码管的动态显示方法。
14.2 74HC245 和 74HC138 芯片介绍
通过前面内容的介绍我们知道,要使单片机能控制开发板上4 位一体的共阴数码管显示,仅靠单片机 IO 口来驱动是不行的,这里就需要增加外部驱动芯片,开发板上使用的是 74HC245 芯片。2 个 4 位一体的共阴数码管的位选线有8 根,直接让单片机 IO 口控制是没有任何问题的,但考虑到单片机IO 口资源的限制,通常我们会使用一种 IO 扩展芯片,比如 74HC138、74HC164、74HC595 芯片等,只需要很少的单片机 IO 口就可以扩展出 8 个控制口,通过级联方式甚至可扩展出更多的控制口(这个在后面 LED 点阵实验章节中会有详细介绍)。我们开发板上使用的是 74HC138 译码器芯片,只需单片机 3 个 IO 口就可以实现8 个位选管脚的控制,节省了芯片的 IO 资源。
下面就来看看这两个芯片功能及使用方法。
14.2.1 74HC245 芯片简介
74HC245 是一种三态输出、八路信号收发器,主要应用于大屏显示,以及其它的消费类电子产品中增加驱动。
(1)主要特性
①采用 CMOS 工艺
②宽电压工作范围:3.0V-5.0V
③双向三态输出
④八线双向收发器
⑤封装形式:SOP20、SOP20-2、TSSOP20、DIP20
(2)管脚功能定义
从上面的管脚功能定义说明及真值表可以知道该芯片使用方法很简单,给OE使能管脚低电平,DIR 管脚为高电平传输方向是 A->B 输出,DIR 为低电平传输方向是 B->A,至于输出高电平还是输出低电平取决于输入端的状态,如果输入为低电平,输出即为低;输入为高电平,输出即为高。如果OE 使能管脚为高电平,不论 DIR 管脚是高还是低,输出是高组态。
通常我们使用 74HC245 芯片用作驱动只会让其在一个方向输出,即DIR 管脚为高电平,传输方向是 A->B。
14.2.2 74HC138 芯片简介
74HC138D 是一种三通道输入、八通道输出译码器,主要应用于消费类电子产品。
(1)主要特性
①采用 CMOS 工艺
②低功耗
③工作电压:3.0V-5.0V
④封装形式:SOP16
(2)管脚功能定义
从上面的管脚功能定义说明及真值表可以知道该芯片使用方法很简单,给E1、E2 使能管脚低电平,E3 管脚为高电平,至于哪个管脚输出有效电平(低电平),要看 A0,A1,A2 输入管脚的电平状态。如果 A0,A1,A2 都为低电平,则Y0 输出有效电平(低电平),其他管脚均输出高电平。如果A0 为高电平,A1,A2 都为低电平,则 Y1 输出有效电平(低电平),其他管脚均输出高电平。其他几种输出大家可以对照真值表查看。如果 E1、E2 使能管脚任意一个为高电平或者 E3 为低电平,不论输入是什么,输出都为高电平。
这里给大家总结一个方法:A0、A1、A2 输入就相当于3 位2 进制数,A0是低位,A1 是次高位,A2 是高位。而 Y0-Y7 具体哪一个输出有效电平,就看输入二进制对应的十进制数值。比如输入是 101(A2,A1,A0),其对应的十进制数是 5,所以 Y5 输出有效电平(低电平)。
有关 74HC245 和 74HC138 芯片的更多详细介绍,大家可以在资料“\6--芯片资料\开发板芯片数据手册”内查看。
14.3 硬件设计
本实验使用到硬件资源如下:
(1)动态数码管模块
(2)74HC138
开发板上的动态数码管模块电路如下图所示:
上图电路使用的是 2 个四位一体的共阴数码管组成,即8 位数码管的段选数据 a-dp 全部并联一起引出,每位数码管的位选即公共端引出。数码管的段选a-dp连接在 74HC245 驱动芯片输出口,由 PA0-PA7 引脚控制。由PB5、PB4、PB3 管脚控制 74HC138 译码器输入从而控制数码管位选。
14.4 软件设计
本章所要实现的功能是:控制动态数码管从左至右显示数字0-7。
我们打开“\4--实验程序\1--基础实验\8-动态数码管实验”工程,在APP文件夹内可以看到又新建了一个 smg 文件夹,管理数码管相关驱动代码。
14.4.1 数码管初始化及显示函数
我们打开工程中 smg.c 文件,里面代码如下:
#include "smg.h"
#include "SysTick.h"//共阴极数码管显示0~F的段码数据
u8 gsmg_code[17]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};//数码管端口初始化
void SMG_Init(void)
{GPIO_InitTypeDef GPIO_InitStructure;//定义结构体变量RCC_APB2PeriphClockCmd(LSA_PORT_RCC,ENABLE);RCC_APB2PeriphClockCmd(LSB_PORT_RCC,ENABLE);RCC_APB2PeriphClockCmd(LSC_PORT_RCC,ENABLE);RCC_APB2PeriphClockCmd(SMG_A_DP_PORT_RCC,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);GPIO_PinRemapConfig(GPIO_Remap_SWJ_Disable,ENABLE);GPIO_InitStructure.GPIO_Pin=LSA_PIN; //选择你要设置的IO口GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP; //设置推挽输出模式GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; //设置传输速率GPIO_Init(LSA_PORT,&GPIO_InitStructure); /* 初始化GPIO */GPIO_InitStructure.GPIO_Pin=LSB_PIN; GPIO_Init(LSB_PORT,&GPIO_InitStructure);GPIO_InitStructure.GPIO_Pin=LSC_PIN; GPIO_Init(LSC_PORT,&GPIO_InitStructure);GPIO_InitStructure.GPIO_Pin=SMG_A_DP_PIN; GPIO_Init(SMG_A_DP_PORT,&GPIO_InitStructure);
}//数码管端口8位数据同时操作,不影响高位
//写入数据到8位端口,数据低位对应端口低引脚
//GPIO_Pin:8位端口低位引脚
//data:写入数据
void SMG_Write_Data(u16 GPIO_Pin,u8 data)
{u8 i,j=GPIO_Pin; for(i=0;i<8;i++){if(data&0x01)GPIO_WriteBit(SMG_A_DP_PORT, j<<i ,Bit_SET); elseGPIO_WriteBit(SMG_A_DP_PORT, j<<i ,Bit_RESET); data = data >> 1 ; }
}//数码管显示
void SMG_Display(u8 dat[],u8 pos)
{u8 i=0;u8 pos_temp=pos-1;for(i=pos_temp;i<8;i++){switch(i)//位选{case 0: LSC=1;LSB=1;LSA=1;break;case 1: LSC=1;LSB=1;LSA=0;break;case 2: LSC=1;LSB=0;LSA=1;break;case 3: LSC=1;LSB=0;LSA=0;break;case 4: LSC=0;LSB=1;LSA=1;break;case 5: LSC=0;LSB=1;LSA=0;break;case 6: LSC=0;LSB=0;LSA=1;break;case 7: LSC=0;LSB=0;LSA=0;break;}SMG_Write_Data(GPIO_Pin_0,dat[i-pos_temp]);//传送段选数据delay_ms(1);//延时一段时间,等待显示稳定SMG_Write_Data(GPIO_Pin_0,0x00);//消隐}
}
SMG_Init 函数为数码管初始化功能,主要实现对数码管使用的IO 时钟进行配置,由于位选控制管脚在 PB3 和 PB4,而这两个引脚初始为JTAG 功能,要使其作为 IO 口使用,需要关闭 JTAG 功能,因此使能 AFIO 端口时钟,调用GPIO_PinRemapConfig 函数关闭 JTAG 功能。
SMG_Write_Data 函数的功能与前面学习 LED 跑马灯完全一样,此处不再叙述。
SMG_Display 函数为数码管动态显示功能,该函数有2 个参数,dat 为数组首地址,可将要显示的数据转换为段码传入;pos 为显示的位置,即从左往右数第几个开始显示。其内部按照动态扫描原理实现。
在该文件的开头也定义了一个全局数组变量 gsmg_code,用于存储共阴数码管 0-F 的段码数据。如果需要在数码管显示对应数字,直接换算到这个数组内数据即可。
14.4.2 主函数
我们打开工程中 main.c 文件,里面代码如下:
#include "system.h"
#include "SysTick.h"
#include "led.h"
#include "smg.h"int main()
{u8 i=0;u8 j=0;u8 smgbuf[8];SysTick_Init(72);LED_Init();SMG_Init();while(1){i++;if(i%10==0)LED0=!LED0;for(j=0;j<8;j++)smgbuf[j]=gsmg_code[j];SMG_Display(smgbuf,1);delay_ms(10);}
}
主函数内容比较简单,首先初始化相关外设,如 LED、数码管等;然后进入while 循环,在循环体内将 0-7 段码数据存储在数组 smgbuf 内,并调用SMG_Display 函数实现动态显示 0-7。
14.5 实验现象
使用 USB 线将开发板和电脑连接成功后(电脑能识别开发板上CH340 串口),把编译后产生的.hex 文件烧入到芯片内,实现现象如下:动态数码管显示01234567。
注意:如果在数码管显示同时不想让 LED 模块亮,可将旁边的J11 黄色跳线帽拿掉即可。