一、任务目标
用定时器实现数码管从10到0的递减步长为1
二、功能分析
由上面的目标,我们可以拆分出本次实现的功能,有定时器的定时功能以及数码管的控制,其中定时器的定时功能用于控制递减的周期,其递减不长为1;由于我们已经实现了基本定时功能,在上一课程中,所以本节课程主要实现的是数码管的控制。具体实现的现象可以暂定为结合上一课程的rgb显示以及两个按键的操作来启动数码管的控制,例如我们按一下S2之后才能开启数码管的递减操作,S1依然是控制递减周期的变换,这个就和我们之前控制rgb的循环点亮速率是结合到一块的。
三、硬件原理分析
我们在本部分主要介绍的还是本次的八位LED控制模块的主要原理,以及控制方式。本次的数码管显示模块是一个八位的,以控制是通过两个74 hc 595d进行锁存控制的,因为目前还没有具体的模块资料,我在淘宝上大概找了一下,和当前模块非常相似的,如下图:
咱们就以这个目前找到的类似模块作为参考,大致上的设计应该都是差不多的,下面是该模块的一个参考原理图:
我们先了解一下74HC595D芯片的情况,74HC595是一个8位串行输入、并行输出的位移缓存器:并行输出为三态输出。在SCK 的上升沿,串行数据由SDL输入到内部的8位位移缓存器,并由Q7'输出,而并行输出则是在LCK的上升沿将在8位位移缓存器的数据存入到8位并行输出缓存器。当串行数据输入端OE的控制信号为低使能时,并行输出端的输出值等于并行输出缓存器所存储的值。
在这里我们就可以分析出对于串行数据的传输有三部分控制,一个就是数据口以及时钟口,还有一个就是锁存输出的控制,而对于多个数码管的控制,通过两个芯片的串联来实现,第一个74HC595(左侧的)用于选择数码管(位选),第二个74HC595用于显示数码管(段选),因为对于大于8位的数据会通过Q7继续往下传输。
那么如何显示多位数据呢?我们都是到该芯片虽然能够锁存住数据,可是这只是一种状态,不同的数码管可能需要显示不同的数据,在位选不能一致的情况下就需要循环刷新的方式,这里就需要定时器的帮忙了,可以定期对数据进行一次刷新,循环往复的进行,人眼的接收频率是相对固定的,刷新的快就可以认为是常量的。
四、软件分析
软件的部分设计到两方面,一个是数码管模块的驱动设计,另一方面是循环往复刷新相关的定时器设计。
咱们先进行数码管模块的驱动设计,让数码管亮起来。在硬件原理分析部分,我们对模块进行了充分的分析了解,得知实际的控制信号只需要三根线(串行),这里我们使用IO口的操作区模拟时序,大概的时序图如下:
为此我们初始化三个引脚:
SCLK对应PA15,PCLK对应PA16,DIO对应PA23,然后在程序中编写驱动代码:
#define HC595_SCLK_RESET DL_GPIO_clearPins(GPIO_595_PORT, GPIO_595_PIN_SCLK_15_PIN) #define HC595_SCLK_SET DL_GPIO_setPins(GPIO_595_PORT, GPIO_595_PIN_SCLK_15_PIN) #define HC595_PCLK_RESET DL_GPIO_clearPins(GPIO_595_PORT, GPIO_595_PIN_PCLK_16_PIN) #define HC595_PCLK_SET DL_GPIO_setPins(GPIO_595_PORT, GPIO_595_PIN_PCLK_16_PIN) #define HC595_DIO_RESET DL_GPIO_clearPins(GPIO_595_PORT, GPIO_595_PIN_DIO_23_PIN) #define HC595_DIO_SET DL_GPIO_setPins(GPIO_595_PORT, GPIO_595_PIN_DIO_23_PIN) uint8_t num[12] = {0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0xff,0x00}; void HC595_Send_Byte(uint8_t num) { uint8_t i; for(i = 0;i<8;i++) { if(num & 0x80)//取最高位 1000 0000 { HC595_DIO_SET; } else { HC595_DIO_RESET; } HC595_SCLK_SET; delay_cycles(32); HC595_SCLK_RESET; delay_cycles(32); num <<=1; } } void HC595_Send_Data(uint8_t num,uint8_t show_bit) { HC595_Send_Byte(num); HC595_Send_Byte(1 << show_bit);//高4位没有用 HC595_PCLK_RESET; delay_cycles(32); HC595_PCLK_SET; delay_cycles(32); }
数组num[12]存储的是共阳极显示数据的位码,包括0~9以及全量和全灭;HC595_Send_Byte(uint8_t num)是八位数据输送函数;HC595_Send_Data(uint8_t num,uint8_t show_bit)是对一个数码管的操作函数。
那么定时器中应该如何操作呢?我们本次课程显示的只有两位,对于递减的操作可以和课程1中使用的定时器为同一个,而刷新部分咱们这次重新使用一个新的定时器:
暂时先不启动定时器
定时器中刷新显示:
void TIMER_Digital_INST_IRQHandler(void) { switch (DL_TimerG_getPendingInterrupt(TIMER_Digital_INST)) { case DL_TIMER_IIDX_ZERO: Digital.cnt++; if(Digital.cnt >= Digital.cnt_limit) Digital.cnt = 0; if(Digital.Mode_On_Off == 1) { HC595_Send_Data(num[Digital_num[Digital.cnt]],Digital.cnt); } break; default: break; } }
在按键中断处理中增加启动和调速后:
void GROUP1_IRQHandler(void) { switch (DL_GPIO_getPendingInterrupt(GPIO_key_PORT)) { case GPIO_key_S1_IIDX: if(LED.Mode_On_Off == 1) { LED.Mode_speed++; LED.Mode_speed %= 3; } if(Digital.Mode_On_Off == 1) { show_data = 10; Data_Split(show_data); Digital.Mode_speed++; Digital.Mode_speed %= 3; } break; case GPIO_key_S2_IIDX: if(LED.Mode_On_Off == 0) { LED.Mode_On_Off = 1; LED1_SET; } else { LED.Mode_On_Off = 0; LED1_RESET; } if(Digital.Mode_On_Off == 0) { Digital.Mode_On_Off = 1; DL_TimerG_startCounter(TIMER_Digital_INST); show_data = 10; Data_Split(show_data); } else { Digital.Mode_On_Off = 0; DL_TimerG_stopCounter(TIMER_Digital_INST); for(uint8_t i=0;i<8;i++) { HC595_Send_Data(num[10],i); } } break; default: break; } }
五、实验结果