本文的主要开发内容是对8位数码管的编程驱动以及实现数码管从10到0的递减显示,首先需要设置数码管与MCU的引脚连接。下图为8位数码管的电路原理图、引脚功能定义,所用到的数码管驱动芯片是HC595,这是一颗高速串行数据转并行输出芯片。
从以上电路原理图知,8位数码管由两片HC595芯片组成,其中第一片HC595芯片负责接收串行数据输入,并将低八位串行数据用于驱动单个数码管,高八位数据串行输入到第二片HC595芯片,用于驱动第几位数码管显示。当低八位数据和高八位串行数据传输完毕,在RCLK引脚上给予不小于2us的上升沿,此时将有一位数码管被点亮。
按照以上说明,那么每次只能点亮一位数码管,这可无法实现多位数码管同时点亮呢。然而非也非也,由于人眼都会存在一个视觉暂留性,在极短的时间内,快速循环点亮多位数码管,那么我们看到的就是多位数码管"同时点亮"了。这个和手机屏幕显示也是类似的,手机屏幕的各个显示点也并非同时点亮,而是以超快的频率循环点亮来实现整个屏幕内容的显示。
一位数码管示意图
数码管模块由八段LED组成,均为共阳极,那么驱动LED显示就需要锁存器中的数据为低电平数据,LED段驱动顺序为a-b-c-d-e-f-g-h,然而对应的HC595内部锁存器是数据存储顺序为LED段驱动顺序的倒序。
例如,数码管驱动显示数字6(小数点不显示),对应LED段驱动数据为01000001,而锁存器存储数据为10000010。对于一位数码管来说,它能够显示的数字范围是0~9,因此可以将0~9的锁存器数据封装在一个数组中,方便数码管显示0~9中任一位数据时直接调用。
根据以上原理,在程序开发中实现了数码管从10到0递减显示,并且额外增加了一个秒表计时器功能,开机默认运行倒计时模式,循环从10递减到0。按下按键1实现功能切换,切换至秒表计时器模式,数码管显示0,按下按键1实现计算功能,理论上可以计时的时间为2^32秒,实际上应该用不了计时这么久的。再一次按下按键实现计时终止,当前数码管显示最终的计时时间,再次按下按键1,实现计时清零重新返回计时启动模式。
无论是倒计时模式还是计时器模式,数码管数字刷新显示操作均由定时器0的中断回调函数完成,而精确延时操作由定时器1完成。定时器中断的机制是定时器在每隔设定的间隔内触发一次中断,中断可以暂停当前CPU的执行任务,转向执行定时器中断回调函数的任务,执行完毕后返回执行上一次CPU暂停执行的任务。因此在程序中需要指定中断优先级别,中断级别是数字越小优先级越高,程序中设定按键中断完成功能模式切换,将按键中断优先级设为最高,其次是定时器1精确延时中断优先级为次高,定时器0数码管刷新中断优先级最低。
syscfg文件下载链接:syscfg.zip
源代码:
#include "ti_msp_dl_config.h" #include <math.h> #define NUM_MAXLENGTH 8 #define delay_us 32 typedef struct{ uint32_t Num_start; uint8_t Num_polarity; uint8_t polarity_flag; uint8_t func_mode;//0表示倒计时模式,1表示秒表模式 uint8_t stopwatch_status;//0表示等待启动秒表,1表示正在计时,2表示停止计时 }LED_Segment_Mode; LED_Segment_Mode LS_Mode; uint8_t Num_List[]={0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90,0x8C,0xBF,0xC6,0xA1,0x86,0xFF,0xbf}; int32_t Num_Decrease=1; /** * @brief 时钟上升沿配置 * * @param[in] 外设端口PORTx、t1、t2 * */ void HC595_CLKRisingEdge_Config(GPIO_Regs* PORTx,uint32_t PINx,uint32_t t1,uint32_t t2){ DL_GPIO_clearPins(PORTx, PINx); delay_cycles(delay_us*t1); DL_GPIO_setPins(PORTx, PINx); delay_cycles(delay_us*t2); } /** * @brief HC595输入1字节数据 * * @param[in] byte字节数据 * */ void LED_Segment_WriteByte(uint8_t byte){ for(uint8_t i=1;i<=8;i++){ if(byte&0x80){ DL_GPIO_setPins(PORT_HC595_PORT,PORT_HC595_PIN_HC595_DIO_PIN); } else { DL_GPIO_clearPins(PORT_HC595_PORT,PORT_HC595_PIN_HC595_DIO_PIN); } HC595_CLKRisingEdge_Config(PORT_HC595_PORT,PORT_HC595_PIN_HC595_SCLK_PIN,2,2); byte<<=1; } } /** * @brief 数码管显示一位数字 * * @param[in] Num_one数字,index显示位置 * */ void LED_Segment_Display_Num_One(uint8_t Num_one,uint8_t index){ // switch(Num_one){ // case 0: // break; // } LED_Segment_WriteByte(Num_List[Num_one]); LED_Segment_WriteByte(1<<index); HC595_CLKRisingEdge_Config(PORT_HC595_PORT,PORT_HC595_PIN_HC595_RCLK_PIN,2,2); } /** * @brief 数码管显示小数点 * * @param[in] Num_one小数点,index显示位置 * */ void LED_Segment_Display_point(uint8_t Num_one,uint8_t index){ // switch(Num_one){ // case 0: // break; // } LED_Segment_WriteByte(Num_one); LED_Segment_WriteByte(1<<index); HC595_CLKRisingEdge_Config(PORT_HC595_PORT,PORT_HC595_PIN_HC595_RCLK_PIN,2,2); } /** * @brief 数码管显示处理 * * @param[in] Num数字 * */ void Num_LED_Segment_Process(uint32_t Num){ uint8_t num_lsb=0; uint8_t i; for(i=0;i<NUM_MAXLENGTH;i++) { num_lsb=Num%10; Num/=10; LED_Segment_Display_Num_One(num_lsb,i); if(Num==0)break; } if(LS_Mode.Num_polarity==0){ LED_Segment_WriteByte(0xbf); LED_Segment_WriteByte(0x01<<(i+1)); HC595_CLKRisingEdge_Config(PORT_HC595_PORT,PORT_HC595_PIN_HC595_RCLK_PIN,2,2); } } int main(void) { SYSCFG_DL_init(); NVIC_SetPriority(TIMER_0_INST_INT_IRQN, 2); NVIC_EnableIRQ(TIMER_0_INST_INT_IRQN); DL_TimerG_startCounter(TIMER_0_INST); // NVIC_SetPriority(TIMER_1_INST_INT_IRQN, 1); NVIC_EnableIRQ(TIMER_1_INST_INT_IRQN); DL_TimerG_startCounter(TIMER_1_INST); NVIC_SetPriority(GPIO_MULTIPLE_GPIOA_INT_IRQN, 0); NVIC_EnableIRQ(GPIO_MULTIPLE_GPIOA_INT_IRQN); LS_Mode.Num_start=10; LS_Mode.polarity_flag=1; LS_Mode.Num_polarity=1;//默认显示正数 LS_Mode.func_mode=0; LS_Mode.stopwatch_status=0; while (1) { delay_cycles(10); // LED_Segment_Display_Num_One(0x7f,0x01<<5); // LED_Segment_Display_point(0x7f,5); // Num_LED_Segment_Process_Float(0.356,2); } } void TIMER_0_INST_IRQHandler(void) { switch (DL_TimerG_getPendingInterrupt(TIMER_0_INST)) { case DL_TIMER_IIDX_ZERO: Num_LED_Segment_Process(Num_Decrease); break; default: break; } } void TIMER_1_INST_IRQHandler(void) { switch (DL_TimerG_getPendingInterrupt(TIMER_1_INST)) { case DL_TIMER_IIDX_ZERO: // DL_GPIO_togglePins(GPIO_LEDS_PORT,GPIO_LEDS_USER_LED_1_PIN | GPIO_LEDS_USER_TEST_PIN); if(LS_Mode.func_mode==0){ if(LS_Mode.polarity_flag==1){ Num_Decrease--; if(Num_Decrease<0) Num_Decrease=LS_Mode.Num_start; } // else // { // // if(LS_Mode.Num_polarity==1){ // Num_Decrease--; // if(Num_Decrease<0) // LS_Mode.Num_polarity=0; // } // else{ // Num_Decrease++; // if(Num_Decrease==LS_Mode.Num_start) // LS_Mode.Num_polarity=1; // } // } } else { if(LS_Mode.stopwatch_status==0) Num_Decrease=0; else if(LS_Mode.stopwatch_status==1) Num_Decrease++; else { } } break; default: break; } } void GROUP1_IRQHandler(void) { switch (DL_GPIO_getPendingInterrupt(PORT_Key2_PORT)) { case PORT_Key2_PIN_Key2_IIDX: switch(LS_Mode.stopwatch_status){ case 0: LS_Mode.stopwatch_status++; break; case 1: LS_Mode.stopwatch_status++; break; case 2: LS_Mode.stopwatch_status=0; break; default: LS_Mode.stopwatch_status=0; break; } break; case PORT_Key1_PIN_Key1_IIDX: DL_GPIO_togglePins(GPIO_LEDS_PORT, GPIO_LEDS_USER_LED_1_PIN); if(LS_Mode.func_mode==0) { Num_Decrease=0; LS_Mode.stopwatch_status=0; LS_Mode.func_mode++; } else if(LS_Mode.func_mode==1) { LS_Mode.func_mode--; Num_Decrease=LS_Mode.Num_start; } else { LS_Mode.func_mode=0; Num_Decrease=LS_Mode.Num_start; } break; default: // DL_GPIO_togglePins(GPIO_LEDS_PORT, GPIO_LEDS_USER_LED_1_PIN); break; } }
效果图: