[GD32L233C-START 评测] 【GD32L233C-START评测】第5篇 动态扫描实现直驱4位时钟数码管

yang_alex   2022-2-20 17:22 楼主

        单片机驱动多位数码管时有共阳和共阴两种接法,我们这里使用的是共阳4位时钟数码管。下面是他的外观图、段位图和引脚图:

0-1.PNG 0-2-1.PNG 0-2.PNG

       先来说明一下我们的硬件电路:数码管的位引脚DIG1、DIG2、DIG3、DIG4分别接评估板的PB9、PB6、PB5、PB1,数码管的段引脚A、B、C、D、E、F、G、DP分别接评估板的PB8、PB4、PB12、PB14、PB13、PB7、PB0、PB15。这里没有接限流电阻,主要是考虑到:都是用杜邦线连接的,加电阻不方便(实际做板子的时候最好在每个位引脚和MCU的GPIO之间加个限流电阻),另外就是因为采用了动态扫描以及3.3V电压本来就不高。注意,如果LED的驱动电流比较大的时候就不能直接用MCU的GPIO去驱动数码管的位引脚了,而是需要去驱动一个PNP三极管,通过控制这个PNP三极管的导通与否去控制数码管的位引脚是否接到正电源。

       软件方面:驱动方法是评估板上接数码管位引脚的GPIO输出高电平去推数码管中的LED,评估板上接数码管段引脚的GPIO输出低电平去拉数码管中的LED,这样对应的LED就会亮。动态扫描的原理是:评估板上接数码管位引脚的GPIO轮流输出扫描信号,使每一瞬间只有一个数码管被选通(输出高电平,数码管该位共阳极被高电平驱动),然后由评估板上接数码管段引脚的7个GPIO(这个4位时钟数码管上每个数码右下角的点是空的,没有LED,不需要驱动)送入该位要显示的字形码(输出低电平),点亮该位字形段显示字形。这样在的段码驱动和位选线配合控制下,就可以使各数码管轮流点亮显示各自的字符。DP1和DP2另外控制。扫描频率恰当的情况下,由于人眼的暂留效应,4位时钟数码是同时显示的。

      工作原理和电路说清楚了,我们来看一下代码:

      首先,对用到的GPIO做初始化,这里利用了评估板所带的gd32l233c_start.c程序,所以项目重要添加这个程序,并且在相应的头文件gd32l233c_start.h中增加相应引脚的定义:

#define LED_A_PIN                         GPIO_PIN_8
#define LED_A_GPIO_PORT                   GPIOB
#define LED_A_GPIO_CLK                    RCU_GPIOB

#define LED_B_PIN                         GPIO_PIN_4
#define LED_B_GPIO_PORT                   GPIOB
#define LED_B_GPIO_CLK                    RCU_GPIOB

#define LED_C_PIN                         GPIO_PIN_12
#define LED_C_GPIO_PORT                   GPIOB
#define LED_C_GPIO_CLK                    RCU_GPIOB

#define LED_D_PIN                         GPIO_PIN_14
#define LED_D_GPIO_PORT                   GPIOB
#define LED_D_GPIO_CLK                    RCU_GPIOB

#define LED_E_PIN                         GPIO_PIN_13
#define LED_E_GPIO_PORT                   GPIOB
#define LED_E_GPIO_CLK                    RCU_GPIOB

#define LED_F_PIN                         GPIO_PIN_7
#define LED_F_GPIO_PORT                   GPIOB
#define LED_F_GPIO_CLK                    RCU_GPIOB

#define LED_G_PIN                         GPIO_PIN_0
#define LED_G_GPIO_PORT                   GPIOB
#define LED_G_GPIO_CLK                    RCU_GPIOB

#define LED_1_PIN                         GPIO_PIN_9
#define LED_1_GPIO_PORT                   GPIOB
#define LED_1_GPIO_CLK                    RCU_GPIOB

#define LED_2_PIN                         GPIO_PIN_6
#define LED_2_GPIO_PORT                   GPIOB
#define LED_2_GPIO_CLK                    RCU_GPIOB

#define LED_3_PIN                         GPIO_PIN_5
#define LED_3_GPIO_PORT                   GPIOB
#define LED_3_GPIO_CLK                    RCU_GPIOB

#define LED_4_PIN                         GPIO_PIN_1
#define LED_4_GPIO_PORT                   GPIOB
#define LED_4_GPIO_CLK                    RCU_GPIOB

#define LED_DP_PIN                         GPIO_PIN_15
#define LED_DP_GPIO_PORT                   GPIOB
#define LED_DP_GPIO_CLK                    RCU_GPIOB

       void gd_eval_led_init(led_typedef_enum lednum)在gd32l233c_start.c程序中有定义,我们直接拿来初始化数码管相关驱动引脚:

void gpio_config(void)
{
		/* initilize the LEDs*/	
		gd_eval_led_init(LED1);	
		gd_eval_led_init(LED_A);	
		gd_eval_led_init(LED_B);	
		gd_eval_led_init(LED_C);	
		gd_eval_led_init(LED_D);	
		gd_eval_led_init(LED_E);	
		gd_eval_led_init(LED_F);	
		gd_eval_led_init(LED_G);	
		gd_eval_led_init(LED_1);	
		gd_eval_led_init(LED_2);	
		gd_eval_led_init(LED_3);	
		gd_eval_led_init(LED_4);	
		gd_eval_led_init(LED_DP);	
}

        我们利用一个定时器实现4位时钟数码管的动态扫描,所以要先配置这个定时器(定时3毫秒):

void timer_config(void)
{
    timer_oc_parameter_struct timer_ocinitpara;
    timer_parameter_struct timer_initpara;

  	/* enable the peripherals clock */
    rcu_periph_clock_enable(RCU_TIMER5);

    /* deinit a TIMER */
    timer_deinit(TIMER5);
    /* initialize TIMER init parameter struct */
    timer_struct_para_init(&timer_initpara);
    /* TIMER5 configuration */
    timer_initpara.prescaler        = 63;
    timer_initpara.alignedmode      = TIMER_COUNTER_EDGE;
    timer_initpara.counterdirection = TIMER_COUNTER_UP;
    timer_initpara.period           = 3000;  //3ms
    timer_initpara.clockdivision    = TIMER_CKDIV_DIV1;
    timer_init(TIMER5, &timer_initpara);

    /* auto-reload preload enable */
    timer_auto_reload_shadow_enable(TIMER5);
    /* clear channel 0 interrupt bit */
    timer_interrupt_flag_clear(TIMER5, TIMER_INT_FLAG_UP);
    /* channel 0 interrupt enable */
    timer_interrupt_enable(TIMER5, TIMER_INT_UP);

    /* enable a TIMER */
    timer_enable(TIMER5);
}

        再在定时器的中断程序中更新4位时钟数码管各位要显示的数:

void TIMER5_IRQHandler(void)
{
  uint16_t seg6,seg5,seg1,seg3,seg4,seg2 = 0x7191;// seg2段处理 0xf191不显示DP,DP置1 0x7191显示DP DP单独控制  //	seg3 位处理 //	seg4 段数据 //	seg5 输出数据	
  if(SET == timer_interrupt_flag_get(TIMER5, TIMER_INT_FLAG_UP)) 
  {
	/* clear channel 1 interrupt bit */
	timer_interrupt_flag_clear(TIMER5, TIMER_INT_FLAG_UP);
	seg2 = 0x7191;
	if(Times < 4)
	{
	  switch(Times)
	  {
	    case 0:     seg4 = display_clock[hour1]|0x8000 ;   //显示第一位
	                seg3 = 0x0200; //第一位 0x0200  														
	                break;
	    case 1:     seg4 =  display_clock[hour2]|0x8000 ;  //显示第二位   
	                seg3 = 0x0040; //第二位 0x0040 														
	                break;					
	    case 2:     seg4 =  display_clock[min1]|0x8000  ;  //显示第三位	
	                seg3 = 0x0020; //第三位 0x0020  												
	                break;					
	    case 3:     seg4 =  display_clock[min2]|0x8000 ;   //显示第四位	
	                seg3 = 0x0002; //第四位 0x0002 														
	                break;
	    default :   break;
	  }
	  seg1 = gpio_output_port_get(GPIOB);
	  seg5 = (seg2 | seg1 )& 0xfd9d;
	  gpio_port_write(GPIOB,seg5 );  //去拖影
	  seg5 = (seg4 & seg5 )| seg3;
	  gpio_port_write(GPIOB,seg5 );	
	  Times++;
	}
	else
	{
	  Times = 0;
	}
  }
}

        接下来就是主程序中给定要时钟数码管要显示的数,已经秒脉冲的显示(500毫秒翻转一次)

#include "gd32l23x.h"
#include "gd32l233c_start.h"
#include "systick.h"

uint16_t hour1,hour2,min1,min2,sec1,sec2;
uint32_t display_clock[] = {0x8263, 0xE3E3,0x92E2,0xA2E2,0xE362,0xA272,0x8272,0xE081,0x8262,0xA000};
uint16_t Times = 0,pwm_pulse_value = 20;

void gpio_config(void);
void nvic_config(void);
void timer_config(void);

int main(void)
{
	systick_config();
	gpio_config();
	nvic_config();
	timer_config();
	
	hour1 =  1;					 
	hour2 =  8;	              
	min1  =  5;					
	min2  =  6;	

	while(1) 
	{
	  gd_eval_led_toggle(LED_DP); //秒闪烁
	  delay_1ms(500);		
	}
}

         好了。编译下载,跑起来看看我们的成果吧:

SEG.gif

本帖最后由 yang_alex 于 2022-2-21 17:56 编辑

回复评论

暂无评论,赶紧抢沙发吧
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复