3位6脚的数码管分析和程序编写

1、基本介绍

        一般数码管的位和段分别采用不同的IO进行控制,即每个位均采用不同的IO,段则共用IO,通过扫描切换位来显示。那么3位的数码管,最起码需要的IO数量=3(dig)+7(seg)。
        在特殊情况下,如做小型化或IO资源比较紧张的产品时,IO的数量则显示得非常珍贵。在数码管显示时如何保证IO能够合理利用也非常重要,故市场上出现一种3位6引脚的数码管,仅需要6个IO即可实现其显示,如下图所示。

 2、工作原理

        每个厂家对3位6引脚的数码管针脚的定义可能会有所区别,但其使用的原理是一样的。在这里只选择其中的一种定义进行分析其工作原理。

        如上图所示,单片机若要点亮DIG1的A段仅需要将2引脚设置为高电平,3引脚设置为低电平即可;若要点高DIG2的B段仅需要将3引脚设置为高电平,5引脚设置为低电平。 当然在画电路时记得在每个引脚添加限流电阻如220R/330R或其他,通过不同的限流电阻值可控制数码管的亮度。

        按上述点亮,那此时出现3引脚又是低电平又是高电平情况,如何处理?其实原理非常简单,采用扫描方式点亮,由于人的眼睛视觉暂留,只要扫描的速度足够快,即可认为其一直在显示。

        那么3位6引脚的数码管采用每段扫描,按1ms刷新1段的速率,那么3位8段(带DP)则需要扫描24次才能够刷新完毕。在刷新的过程中,其余未点亮时则将其设定为输入浮空状态,以保证其没电流输出。

3、程序编写

        在这里以小华的HC32F072的MCU为例,对代码部分进行编写。

3.1IO初始化

        在应用前初始化相应的IO接口是最基本的,其初化如下:

void LED_ShowInit(void)
{
    stc_gpio_cfg_t stcGpioCfg;
    
    ///< 打开GPIO外设时钟门控
    Sysctrl_SetPeripheralGate(SysctrlPeripheralGpio, TRUE); 
    
    ///< 端口方向配置->输出(其它参数与以上(输入)配置参数一致)
    stcGpioCfg.enDir = GpioDirOut;
    ///< 端口上下拉配置->下拉
    stcGpioCfg.enPu = GpioPuEnable;
    stcGpioCfg.enPd = GpioPdDisable;
//    stcGpioCfg.enDrv = GpioDrvH;
    ///< GPIO IO LED端口初始化
    Gpio_Init(LED1_DISP_Port, LED1_DISP_Pin, &stcGpioCfg);
    Gpio_Init(LED2_DISP_Port, LED2_DISP_Pin, &stcGpioCfg);
    Gpio_Init(LED3_DISP_Port, LED3_DISP_Pin, &stcGpioCfg);
    Gpio_Init(LED4_DISP_Port, LED4_DISP_Pin, &stcGpioCfg);
    Gpio_Init(LED5_DISP_Port, LED5_DISP_Pin, &stcGpioCfg);
    Gpio_Init(LED6_DISP_Port, LED6_DISP_Pin, &stcGpioCfg);

}

3.2获取数码管的引脚

        为方便获取各数码管的引脚编号和端口号,故做为通用函数方便后续调用。

en_gpio_pin_t LED_GetPin(uint8_t PinIndex)
{
    en_gpio_pin_t PinValue;
    
    switch(PinIndex)
    {
        case 1:
            PinValue = LED1_DISP_Pin;
            break;
        case 2:
            PinValue = LED2_DISP_Pin;
            break;
        case 3:
            PinValue = LED3_DISP_Pin;
            break;
        case 4:
            PinValue = LED4_DISP_Pin;
            break;
        case 5:
            PinValue = LED5_DISP_Pin;
            break;
        case 6:
            PinValue = LED6_DISP_Pin;
            break;
        default:
            PinValue = LED1_DISP_Pin;
            break;
    }
    return PinValue;
}

en_gpio_port_t LED_GetPort(uint8_t PinIndex)
{
    en_gpio_port_t PortValue;
    
    switch(PinIndex)
    {
        case 1:
            PortValue = LED1_DISP_Port;
            break;
        case 2:
            PortValue = LED2_DISP_Port;
            break;
        case 3:
            PortValue = LED3_DISP_Port;
            break;
        case 4:
            PortValue = LED4_DISP_Port;
            break;
        case 5:
            PortValue = LED5_DISP_Port;
            break;
        case 6:
            PortValue = LED6_DISP_Port;
            break;
        default:
            PortValue = LED1_DISP_Port;
            break;
    }
    return PortValue;
}

3.3数码管IO重置复位

        在扫描过程中,为了保证未扫描到的段不被点亮,故需要在扫描新的段前将其复位,其代码如下:

void LED_ShowReset(void)
{
    uint8_t num;
    stc_gpio_cfg_t stcGpioCfg;
    
    stcGpioCfg.enDir = GpioDirIn;
    stcGpioCfg.enPu = GpioPuDisable;
    stcGpioCfg.enPd = GpioPdDisable;
    stcGpioCfg.enDrv = GpioDrvH;
    stcGpioCfg.enOD = GpioOdDisable;
    //stcGpioCfg.enCtrlMode = GpioAHB;
    
    for(num=1;num<=6;num++)
    {
        Gpio_ClrIO(LED_GetPort(num), LED_GetPin(num));     // 全部拉低电平 必须拉低,不然会有拖影
    }
    for(num=1;num<=6;num++)
    {
        Gpio_Init(LED_GetPort(num), LED_GetPin(num), &stcGpioCfg);
    }
}

3.4数码管段点亮

        此代码为显示数码管的核心部分,其中highPinNum表示需要设定为高电平输出的引脚,lowPinNum表示需要设定为低电平输出的引脚。并且在每次点亮新的段时必须调用LED_ShowReset()函数将其余的IO恢复为默认状态。

void LED_ShowUnitSegCore(uint8_t highPinNum, uint8_t lowPinNum)
{
    stc_gpio_cfg_t stcGpioCfg;
    
    stcGpioCfg.enDir = GpioDirOut;
    stcGpioCfg.enPu = GpioPuDisable;
    stcGpioCfg.enPd = GpioPdDisable;
    stcGpioCfg.enDrv = GpioDrvH;
    stcGpioCfg.enOD = GpioOdDisable;
    
    // 重置
    LED_ShowReset();
    
    // 初始化为推挽输出
    Gpio_Init(LED_GetPort(lowPinNum),  LED_GetPin(lowPinNum), &stcGpioCfg);
    Gpio_Init(LED_GetPort(highPinNum), LED_GetPin(highPinNum), &stcGpioCfg);
    
    // 设置高低电平
    Gpio_ClrIO(LED_GetPort(lowPinNum),  LED_GetPin(lowPinNum)); 
    Gpio_SetIO(LED_GetPort(highPinNum), LED_GetPin(highPinNum)); 
}

3.5显示相应的IO引脚

        根据上面图片的定义,点亮DIG1的A段则需要将2引脚设定为高电平,3引脚设定为低电平即可,以此类推故其函数编写如下:

void LED_ShowUnitSeg(uint8_t unit,uint8_t seg)
{
    if(unit==LED_DIG1)
    {
        if( seg == 'A' )      LED_ShowUnitSegCore(2,3);
        else if( seg == 'B' ) LED_ShowUnitSegCore(2,4);
        else if( seg == 'C' ) LED_ShowUnitSegCore(5,2);
        else if( seg == 'D' ) LED_ShowUnitSegCore(2,6);
        else if( seg == 'E' ) LED_ShowUnitSegCore(2,5);
        else if( seg == 'F' ) LED_ShowUnitSegCore(3,2);
        else if( seg == 'G' ) LED_ShowUnitSegCore(4,2);
        else if( seg == 'H' ) LED_ShowUnitSegCore(2,1);  //DP1
    }
    else if(unit==LED_DIG2)
    {
        if( seg == 'A' )       LED_ShowUnitSegCore(5,4);      
        else if( seg == 'B' ) LED_ShowUnitSegCore(3,5);
        else if( seg == 'C' ) LED_ShowUnitSegCore(4,5);
        else if( seg == 'D' ) LED_ShowUnitSegCore(3,4);
        else if( seg == 'E' ) LED_ShowUnitSegCore(6,3);
        else if( seg == 'F' ) LED_ShowUnitSegCore(4,3);
        else if( seg == 'G' ) LED_ShowUnitSegCore(5,3);
        else if( seg == 'H' ) LED_ShowUnitSegCore(3,1);  //DP2
    }
    else if(unit==LED_DIG3)
    {
        if( seg == 'A' )      LED_ShowUnitSegCore(1,6);      
        else if( seg == 'B' ) LED_ShowUnitSegCore(3,6);
        else if( seg == 'C' ) LED_ShowUnitSegCore(5,6);
        else if( seg == 'D' ) LED_ShowUnitSegCore(6,4);
        else if( seg == 'E' ) LED_ShowUnitSegCore(4,6);
        else if( seg == 'F' ) LED_ShowUnitSegCore(6,5);
        else if( seg == 'G' ) LED_ShowUnitSegCore(1,5);
    }
}

3.6定时刷新函数

        此函数为LED显示的第二个核心部分,采用定时1ms刷新一次,动态刷新各输出接口,其定义如下:

void LED_ShowUpdate(void)
{
	uint8_t Value;
	
	if(Led_Disp_Struct.Timer!=NowTime_1ms)		//NowTime_1ms为1ms增加1
	{
		Led_Disp_Struct.Timer = NowTime_1ms;
		
		Led_Disp_Struct.Seg_Cnt++;
		if(Led_Disp_Struct.Seg_Cnt>7)
		{
			Led_Disp_Struct.Seg_Cnt = 0;
			Led_Disp_Struct.Dig_Cnt++;
		}
		
		if(Led_Disp_Struct.Dig_Cnt >= LED_DIG_MAX)
		{
			Led_Disp_Struct.Dig_Cnt = 0;
		}
		//DIGITRON_ShowUnitSeg(Led_Disp_Struct.Dig_Cnt+1,Led_Disp_Struct.Seg_Cnt+'A');
		Value = Led_Disp_Struct.Seg_Value[Led_Disp_Struct.Dig_Cnt][Led_Disp_Struct.Seg_Cnt];
		if(Value!='0')
		{
			LED_ShowUnitSeg(Led_Disp_Struct.Dig_Cnt,Value);
		}
		else
		{
			LED_ShowReset();			//必须要重置,否则有残影
		}
	}
}

3.7其余的赋值函数

        剩余的函数相对比较容易,就是赋值并保存相应的数值以便定时刷新函数进行定时刷新。

//保存状态值
void LED_SaveStatusValue(uint8_t unit,uint8_t Index,uint8_t seg)
{
	Led_Disp_Struct.Seg_Value[unit][Index] = seg;
}
/**
 * 显示数码管值(单管的值)
 *
 * @param uint8_t unit  数码管位号(即第几个数码管)
 * @param uint8_t num   0-9 数字
 * @param uint8_t dot   小数点
 */
void LED_ShowUnitNum(uint8_t unit,uint8_t number,uint8_t dot)
{
	// 初始化
    uint8_t numSegs = SEG_DIGIT_VALUE[number];

    // 显示数字
    if( (numSegs&0x01) == 0x01 )
	{
		LED_SaveStatusValue(unit,0,'A');  // & 1000 0000
	}
	else
	{
		LED_SaveStatusValue(unit,0,'0');
	}
    if( (numSegs&0x02) == 0x02 )
	{
		LED_SaveStatusValue(unit,1,'B');  // & 0100 0000
	}
	else
	{
		LED_SaveStatusValue(unit,1,'0');
	}
    if( (numSegs&0x04) == 0x04 )
	{
		LED_SaveStatusValue(unit,2,'C');  // & 0010 0000
	}
	else
	{
		LED_SaveStatusValue(unit,2,'0');
	}
    if( (numSegs&0x08) == 0x08 )
	{
		LED_SaveStatusValue(unit,3,'D');  // & 0001 0000
	}
	else
	{
		LED_SaveStatusValue(unit,3,'0');
	}
    if( (numSegs&0x10) == 0x10 )
	{
		LED_SaveStatusValue(unit,4,'E');  // & 0000 1000
	}
	else
	{
		LED_SaveStatusValue(unit,4,'0');
	}
    if( (numSegs&0x20) == 0x20 )
	{
		LED_SaveStatusValue(unit,5,'F');  // & 0000 0100
	}
	else
	{
		LED_SaveStatusValue(unit,5,'0');
	}
    if( (numSegs&0x40) == 0x40 )
	{
		LED_SaveStatusValue(unit,6,'G');  // & 0000 0010
	}
	else
	{
		LED_SaveStatusValue(unit,6,'0');
	}
	
    if( dot > 0 )							   // 显示小数点
	{
		LED_SaveStatusValue(unit,7,'H');
	}
	else
	{
		LED_SaveStatusValue(unit,7,'0');
	}
}


/**
 * 显示数码管值
 *
 * @param float numval  要显示的数值
 */
void LED_ShowDigit(uint16_t numval)
{
    // 初始化
    uint8_t num;

    // 数字范围:0~999
    if(numval < 1000)
	{
        num = numval % 1000 / 100;
        LED_ShowUnitNum(LED_DIG1,num, 0);
        
        num = numval % 100 / 10;
        LED_ShowUnitNum(LED_DIG2,num, 0);
        
        num = numval % 10;
        LED_ShowUnitNum(LED_DIG3,num, 0);
    }
    
// 数字范围:其他
//    else
//	{
//        LED_ShowUnitNum(LED_DIG1, 9, 0);
//        LED_ShowUnitNum(LED_DIG2, 9, 0);
//        LED_ShowUnitNum(LED_DIG3, 9, 0);
//    }
}

3.8主函数运行

int32_t main(void)
{    
	App_ClockCfg();
	LED_ShowInit();
	while (1)
	{
		LED_ShowUpdate();

		if(ElapsedTime(LedTimer)>1000)
		{
			LedTimer = NowTime_1ms;
			LED_ShowDigit(LedValue);
			LedValue++;
			if(LedValue>999) LedValue=0;
		}
	}
}

3.9数码管数值定义

        在上述函数中会调用一个常量,那就是const uint8_t SEG_DIGIT_VALUE[10],那其如何定义?我们可以采用《LED段码数据生成器.exe》软件,这个网上找下就可以了或者通过下面的链接进行下载可以的。

        其软件定义如下:

        根据上述的自动生成的数据,那么常量值如下:

//按a=0,b=1,c=2,d=3,e=4,f=5,g=6,h=7的共阴极,其显示0-9的数值如下
const uint8_t SEG_DIGIT_VALUE[10] = {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F};

        若要增加其他内容显示,采用同样的配置即可。

4、工程文件

        不藏私,文件直接上传供大家参考:

        1、数码管软件下载地址:https://download.csdn.net/download/shujian123/88098214

        2、工程文件下载地址:https://download.csdn.net/download/shujian123/88098216

        3、听说有些朋友需要STM32的版本,在此专门更新了STM32版本的2个LED文件,其使用方法请参考第2点的工程文件一样:https://download.csdn.net/download/shujian123/90477907

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

*参天大树

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值