【实验目的】
用定时器实现数码管从10到0的递减,步长为1
【原理图】
【数码管实现原理】
数码管,其原理就是把发光二极管封装起来,组合成数字,或者图形,同时为了节约驱动IO的开销,把阳极或者阴极连在一起,组成共阳或者共阴数据码,同时利用视觉保留的原理,实现动态扫描来实现图像的显示。
【74HC595】
为了更加节省IO的开销,74HC595可以实现由串口通信转来并口输出的功能,他只需要3个IO输入,可实现8个IO的并行输出,同时可以通过集联,来实现多个74HC595串联来驱动多个数码管。
【实现步骤】
1、首先定义用于驱动数码管的数组
// 16进制数字显示信史段码(0-9,A-F),段码位=1时,数码管的对应的笔段不会被点亮
uint8_t Disp_DX[] = { 0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8, 0x80, 0x90, 0x77, 0x83, 0xC6, 0xA1, 0x86, 0x8E, 0xFF, 0xBF };
这样我们在需要显示数字或者字母时就可以根据编号来拿到对应的驱动信号。
2、驱动74HC595的IO选择,根据原理图,先需要配置GPIO为输出模式,打开syscfg来配置PA13,PA26,PA27为输出模式,其配置如下图所示:
同理,配置好PA13,PA27为SCK、RCLK
3、为了实现准确的计时,我这里开启定时器,配置一个500ms的定时器。
如上图所示,我创建一个定时器名称为TIMER_0的定时器,配置好时钟源、分频系数、向下计数、每500ms溢出,在计数器到达0时产生中断。
配置好后保存并生成生代码。
4、代码实现
1)为了代码的可阅读性,这里先用宏定义,实现三个IO写入高低电平的简写:
#define HC595_DAT(x) ((x)?(DL_GPIO_setPins(HC595_PORT, HC595_DIO_PIN)) : (DL_GPIO_clearPins(HC595_PORT, HC595_DIO_PIN))) #define HC595_CLK(x) ((x)?(DL_GPIO_setPins(HC595_PORT, HC595_SCLK_PIN)) : (DL_GPIO_clearPins(HC595_PORT, HC595_SCLK_PIN))) #define HC595_RCK(x) ((x)?(DL_GPIO_setPins(HC595_PORT, HC595_RCLK_PIN)) : (DL_GPIO_clearPins(HC595_PORT, HC595_RCLK_PIN)))
2)使能锁存代码如下:
void Display_Out() { HC595_RCK(0); HC595_RCK(1); }
3)向数据总线写入一个byte
void HC595_WriteData(uint8_t data) { uint8_t i; for(i = 0; i < 8; i++) { if((data & 0x80)>0){ HC595_DAT(1); } else { HC595_DAT(0); } data <<= 1; HC595_CLK(0); HC595_CLK(1); } }
4) 向数据总线写入一个字符
void HC595_SEND_DATA(uint8_t disp_num, uint8_t disp_bit) { HC595_WriteData(disp_num); HC595_WriteData(1<<disp_bit); Display_Out(); }
5)向数据总线写入8个字符
void Disp_Data(uint16_t dataH, uint16_t dataL) { uint16_t tempH,tempL; uint8_t num_q, num_b,num_s,num_g; tempL = dataL; num_q = tempL/1000; num_b = tempL/100%10; num_s = tempL/10%10; num_g = tempL%10; HC595_SEND_DATA(Disp_DX[num_q],3); HC595_SEND_DATA(Disp_DX[num_b],2); HC595_SEND_DATA(Disp_DX[num_s],1); HC595_SEND_DATA(Disp_DX[num_g],0); tempH = dataH; num_q = tempH/1000; num_b = tempH/100%10; num_s = tempH/10%10; num_g = tempH%10; HC595_SEND_DATA(Disp_DX[num_q],7); HC595_SEND_DATA(Disp_DX[num_b],6); HC595_SEND_DATA(Disp_DX[num_s],5); HC595_SEND_DATA(Disp_DX[num_g],4); }
6)定时器中断回调函数,syscfg不能自动生成中断回调函数,需要自行添加,在中断回调函数中,每次中断进行即是500ms,让变量自增一次,当到达2时,即为1秒,这时更新需要秒的变量,进行自减,当到达0时让显示秒的变量恢复为10。代码如下:
void TIMER_0_INST_IRQHandler(void) { switch(DL_Timer_getPendingInterrupt(TIMER_0_INST)){ case DL_TIMER_IIDX_ZERO: TimerCnt ++; if(TimerCnt >= 2) { TimerCnt = 0; TmpVal --; if(TmpVal == 0) { TmpVal = 10; } } break; default: break; } }
7)、在main中,我首先定义显示秒的初始值为10,然后开启定时器,在while循环中显示变量,即可实现相应的功能,其代码如下:
int main(void) { TmpVal = 10; SYSCFG_DL_init(); NVIC_EnableIRQ(TIMER_0_INST_INT_IRQN); while (1) { Disp_Data(0000,TmpVal); } }
5、程序实现的效果如下: