单片机驱动多位数码管时有共阳和共阴两种接法,我们这里使用的是共阳4位时钟数码管。下面是他的外观图、段位图和引脚图:
先来说明一下我们的硬件电路:数码管的位引脚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);
}
}
好了。编译下载,跑起来看看我们的成果吧:
本帖最后由 yang_alex 于 2022-2-21 17:56 编辑