▪ 基本简介
一个1位数码管共有8段:ABCDEFG+小数点,如下图:
按照我们常规的设计方案一个 IO 口控制一个段,一个1位数码管那么就需要8个IO。当然我们也可以使用扩展 IO 芯片(例如 74HC164、74HC595),那么这种方案就占用单片机2~3个IO和一个扩展IO芯片。
现在我们需要控制一个3位的数码管:
- 采用IO口直控:那么需要单片机 IO 口24个
- 采用扩展 IO 芯片控制:那么需要单片机 IO 口 2~3 个
但是市面上有些 3 位的数码管只有 6 个接口,如下图:
这种元件就不是我们常规的控制方法了,这种是基于 查理复用算法 封装的数码管,只需要 6 个IO就能控制 3位数码管(3*8 个LED灯)。所以在使用这个元件时,你需要先了解查理复用算法,具体请参考我的《复用 IO 口:查理复用算法(Charlieplexing)》文章,或者直接百度搜索查看:查理复用算法(Charlieplexing)。
▪ 原理分析
不同厂家对于3位6脚的数码管可能设计不同的电路和针脚定义,具体咨询购买的厂家或其数据手册。这里我们取其中一种作为例子讲解,其数据手册图如下:
根据上面的数据手册,我们画出其对应的原理图,如下:
如上图所示,同时基于查理复用算法,我们可以分析出:
- 数码管2脚高电平,3脚低电平,其余脚高阻态;那么 1A 会被点亮(Dig.1 数码管的第一个二极管灯亮)
- 数码管1脚高电平,4脚低电平,其余脚高阻态;那么 DP2 会被点亮(Dig.2 数码管的小数点二极管灯亮)
- .....
▪ 实现代码
以上代码基于 STM8 库函数:
/* -------------------------------------------------- */
// ● 全局变量
/* -------------------------------------------------- */
/**
* 数码管基础变量
*/
// 数字和数码管段映射表
// 数组下标即为数字(0~9),数组的值即为数码管8个段的状态值(ABCDEFG+DP 八个段用采用一个字节表示)
// 例如要显示数字 0,那么需要点亮 ABCDEF 这七个段,那么对应的就是 1111110+0 = 0xFC
short GV_DigitronNumsMapSegs[10] = {0xFC, 0x60, 0xDA, 0xF2, 0x66, 0xB6, 0xBE, 0xE0, 0xFE, 0xE6};
/* -------------------------------------------------- */
// ● 函数主体
/* -------------------------------------------------- */
/**
* 取数码管针脚编号对应芯片上的针脚号
*
* @param short number 数码管针脚编号
* @return GPIO_Pin_TypeDef 无效参数时返回 GPIO_PIN_ALL
*/
GPIO_Pin_TypeDef DIGITRON_GetPin( short number )
{
// 适配针脚
if( number == 1 ) return GPIO_PIN_1;
if( number == 2 ) return GPIO_PIN_3;
if( number == 3 ) return GPIO_PIN_5;
if( number == 4 ) return GPIO_PIN_0;
if( number == 5 ) return GPIO_PIN_2;
if( number == 6 ) return GPIO_PIN_7;
// 无效返回
return GPIO_PIN_ALL;
}
/**
* 获取数码管针脚编号对应芯片上的端口号
*
* @param short number 数码管针脚编号
* @return GPIO_TypeDef* 无效参数时返回 0
*/
GPIO_TypeDef* DIGITRON_GetPort( short number )
{
// 适配端口
if( number == 1 ) return GPIOA;
if( number == 2 ) return GPIOA;
if( number == 3 ) return GPIOA;
if( number == 4 ) return GPIOH;
if( number == 5 ) return GPIOH;
if( number == 6 ) return GPIOF;
// 无效返回
return (GPIO_TypeDef*)0x0000;
}
/**
* 显示数码管重置
*/
void DIGITRON_ShowReset()
{
// 初始化
int num;
// 全部拉低电平
// 必须拉低,不然会有拖影
for( num = 1; num <= 6; num++ ){
GPIO_WriteLow(DIGITRON_GetPort(num), DIGITRON_GetPin(num));
}
// 全部初始化为高阻态
for( num = 1; num <= 6; num++ ){
GPIO_Init(DIGITRON_GetPort(num), DIGITRON_GetPin(num), GPIO_MODE_OUT_OD_LOW_SLOW);
GPIO_DeInit(DIGITRON_GetPort(num));
}
}
/**
* 显示数码管的段
*
* @param short unit 数码管位号(即第几个数码管)
* @param char seg 数码管对应 A~H 二极管(8段+小数点)
*/
void DIGITRON_ShowUnitSeg( short unit, char seg )
{
if( unit == 1 ){
if( seg == 'A' ) DIGITRON_ShowUnitSegCore(3,2);
if( seg == 'B' ) DIGITRON_ShowUnitSegCore(4,2);
if( seg == 'C' ) DIGITRON_ShowUnitSegCore(2,5);
if( seg == 'D' ) DIGITRON_ShowUnitSegCore(6,2);
if( seg == 'E' ) DIGITRON_ShowUnitSegCore(5,2);
if( seg == 'F' ) DIGITRON_ShowUnitSegCore(2,3);
if( seg == 'G' ) DIGITRON_ShowUnitSegCore(2,4);
}
if( unit == 2 ){
if( seg == 'A' ) DIGITRON_ShowUnitSegCore(4,5);
if( seg == 'B' ) DIGITRON_ShowUnitSegCore(5,3);
if( seg == 'C' ) DIGITRON_ShowUnitSegCore(5,4);
if( seg == 'D' ) DIGITRON_ShowUnitSegCore(4,3);
if( seg == 'E' ) DIGITRON_ShowUnitSegCore(3,6);
if( seg == 'F' ) DIGITRON_ShowUnitSegCore(3,4);
if( seg == 'G' ) DIGITRON_ShowUnitSegCore(3,5);
if( seg == 'H' ) DIGITRON_ShowUnitSegCore(1,4); // 数据手册中的 DP2
}
if( unit == 3 ){
if( seg == 'A' ) DIGITRON_ShowUnitSegCore(6,1);
if( seg == 'B' ) DIGITRON_ShowUnitSegCore(6,3);
if( seg == 'C' ) DIGITRON_ShowUnitSegCore(6,5);
if( seg == 'D' ) DIGITRON_ShowUnitSegCore(4,6);
if( seg == 'E' ) DIGITRON_ShowUnitSegCore(6,4);
if( seg == 'F' ) DIGITRON_ShowUnitSegCore(5,6);
if( seg == 'G' ) DIGITRON_ShowUnitSegCore(5,1);
}
}
/**
* 显示数码管的段 - 核心(设置针脚)
* 该函数主要设置针脚的高低电平,以便使数码管的段显示
*
* @param short highPinNum 数码管针脚编号(设置为高电平的针脚)
* @param short lowPinNum 数码管针脚编号(设置为低电平的针脚)
*/
void DIGITRON_ShowUnitSegCore( short highPinNum, short lowPinNum )
{
// 重置
DIGITRON_ShowReset();
// 初始化为推挽输出
GPIO_Init(DIGITRON_GetPort(lowPinNum), DIGITRON_GetPin(lowPinNum), GPIO_MODE_OUT_PP_LOW_SLOW);
GPIO_Init(DIGITRON_GetPort(highPinNum), DIGITRON_GetPin(highPinNum), GPIO_MODE_OUT_PP_LOW_SLOW);
// 设置高低电平
GPIO_WriteLow(DIGITRON_GetPort(lowPinNum), DIGITRON_GetPin(lowPinNum));
GPIO_WriteHigh(DIGITRON_GetPort(highPinNum), DIGITRON_GetPin(highPinNum));
}
/**
* 显示数码管值(单管的值)
*
* @param short unit 数码管位号(即第几个数码管)
* @param short num 0-9 数字
* @param short dot 小数点
*/
void DIGITRON_ShowUnitNum( short unit, short number, short dot )
{
// 初始化
short numSegs = GV_DigitronNumsMapSegs[number];
// 显示小数点
if( dot > 0 ) DIGITRON_ShowUnitSeg(unit, 'H');
// 显示数字
if( (numSegs&0x80) == 0x80 ) DIGITRON_ShowUnitSeg(unit, 'A'); // & 1000 0000
if( (numSegs&0x40) == 0x40 ) DIGITRON_ShowUnitSeg(unit, 'B'); // & 0100 0000
if( (numSegs&0x20) == 0x20 ) DIGITRON_ShowUnitSeg(unit, 'C'); // & 0010 0000
if( (numSegs&0x10) == 0x10 ) DIGITRON_ShowUnitSeg(unit, 'D'); // & 0001 0000
if( (numSegs&0x08) == 0x08 ) DIGITRON_ShowUnitSeg(unit, 'E'); // & 0000 1000
if( (numSegs&0x04) == 0x04 ) DIGITRON_ShowUnitSeg(unit, 'F'); // & 0000 0100
if( (numSegs&0x02) == 0x02 ) DIGITRON_ShowUnitSeg(unit, 'G'); // & 0000 0010
}
/**
* 显示数码管值
*
* @param float numval 要显示的数值
*/
void DIGITRON_Show( float numval )
{
// 初始化
float num;
// 数字范围:0~99
if( numval >= 0 && numval <= 99 ){
num = numval/10;
DIGITRON_ShowUnitNum(1, (int)num, 0);
num = (int)numval%10;
DIGITRON_ShowUnitNum(2, (int)num, 1);
num = (int)(numval*10)%10;
DIGITRON_ShowUnitNum(3, (int)num, 0);
}
// 数字范围:其他
else{
DIGITRON_ShowUnitNum(1, 9, 0);
DIGITRON_ShowUnitNum(2, 9, 0);
DIGITRON_ShowUnitNum(3, 9, 0);
}
}