STM32 基于 74HC595 驱动 4 位共阳极数码管(定时器刷新 + 字符串显示)完整版教程

张开发
2026/4/16 16:24:47 15 分钟阅读

分享文章

STM32 基于 74HC595 驱动 4 位共阳极数码管(定时器刷新 + 字符串显示)完整版教程
前言在嵌入式开发中数码管是最常用的显示外设之一本文基于STM32F103C8T6 74HC595 串转芯片 驱动 4 位共阳极数码管采用定时器中断动态刷新支持数字、负号、小数点、左右对齐显示代码模块化、可直接移植适合新手入门与项目实战。一、硬件介绍主控STM32F103C8T6驱动芯片74HC595串行输入转并行输出节约 IO 口显示器件4 位共阳极数码管引脚定义软件已固定SEG_SCLK - PA4SEG_RCLK - PA5SEG_DIO - PA7刷新方式定时器 1 中断动态扫描无闪烁二、STM32CubeMX 完整配置1. 基础工程创建打开 STM32CubeMX选择芯片STM32F103C8T6配置系统时钟为内部 8MHzHSI2. GPIO 配置3 个输出引脚配置 PA4、PA5、PA7 为推挽输出、上拉、低速PA4 — SEG_SCLK 移位时钟PA5 — SEG_RCLK 锁存时钟PA7 — SEG_DIO 数据输入3. 定时器 TIM1 配置刷新用选择TIM1时钟源选择Internal Clock无需配置通道仅开启全局中断Configuration - NVIC Settings - TIM1 update interrupt- 勾选 Enabled分频与重载值由代码自动计算CubeMX 保持默认即可4. 时钟树配置SYSCLK8MHz内部 HSIAHB/APB1/APB2 均不分频5. 工程生成选择 MDK-ARMV5勾选Generated peripheral files as pair生成代码三、程序代码实现1. 数码管驱动头文件 seg_display.h#ifndef__SEG_DISPLAY_H#define__SEG_DISPLAY_H#includemain.h#includestdbool.h// 硬件引脚定义#defineSEG_SCLK_PinGPIO_PIN_4#defineSEG_SCLK_GPIO_PortGPIOA#defineSEG_RCLK_PinGPIO_PIN_5#defineSEG_RCLK_GPIO_PortGPIOA#defineSEG_DIO_PinGPIO_PIN_7#defineSEG_DIO_GPIO_PortGPIOA#defineSEG_DIGITS4// 4位数码管#defineSEG_BLANK16// 熄灭码#defineSEG_DASH17// 负号-// 对齐方式typedefenum{SEG_ALIGN_LEFT,SEG_ALIGN_RIGHT}SEG_Align_t;// 显示结构体数字 小数点typedefstruct{uint8_tnum;bool show_dp;}SEG_Digit_t;// 驱动接口函数voidSEG_Init(TIM_HandleTypeDef*htim,uint32_trefresh_freq_hz);voidSEG_ShowString(constchar*str,SEG_Align_t alignment);voidSEG_Clear(void);voidSEG_Timer_ISR(TIM_HandleTypeDef*htim);#endif2. 数码管驱动源文件 seg_display.c#includeseg_display.h#includestring.h/* 共阳极数码管段码表0~9、A~F、熄灭、负号 */staticconstuint8_tLED_table[18]{0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90,0x88,0x83,0xC6,0xA1,0x86,0x8E,0xFF,/* 熄灭 */0xBF/* 负号 - */};/* 位选表第0~3位数码管 */staticconstuint8_twei_table[8]{0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};staticTIM_HandleTypeDef*p_display_timer;staticvolatileSEG_Digit_t display_data[SEG_DIGITS];staticvoidSEG_SendByte(uint8_tbyte);staticvoidSEG_Latch(void);staticvoidSEG_SetBuffer(constSEG_Digit_t*buffer);/** * brief 数码管初始化自动计算定时器分频 */voidSEG_Init(TIM_HandleTypeDef*htim,uint32_trefresh_freq_hz){p_display_timerhtim;uint32_ttimer_clockHAL_RCC_GetPCLK2Freq();if((RCC-CFGRRCC_CFGR_PPRE2)!RCC_CFGR_PPRE2_DIV1)timer_clock*2;uint32_tperiod19;uint32_tprescaler(timer_clock/(refresh_freq_hz*(period1)))-1;htim-Init.Prescalerprescaler;htim-Init.Periodperiod;htim-Init.CounterModeTIM_COUNTERMODE_UP;htim-Init.AutoReloadPreloadTIM_AUTORELOAD_PRELOAD_ENABLE;if(HAL_TIM_Base_Init(htim)!HAL_OK)Error_Handler();// 引脚初始化电平HAL_GPIO_WritePin(SEG_SCLK_GPIO_Port,SEG_SCLK_Pin,GPIO_PIN_RESET);HAL_GPIO_WritePin(SEG_RCLK_GPIO_Port,SEG_RCLK_Pin,GPIO_PIN_RESET);HAL_GPIO_WritePin(SEG_DIO_GPIO_Port,SEG_DIO_Pin,GPIO_PIN_RESET);SEG_Clear();HAL_TIM_Base_Start_IT(p_display_timer);// 开启定时器中断}/** * brief 清空显示 */voidSEG_Clear(void){for(inti0;iSEG_DIGITS;i){display_data[i].numSEG_BLANK;display_data[i].show_dpfalse;}}/** * brief 字符串显示支持负数、小数点、左右对齐 */voidSEG_ShowString(constchar*str,SEG_Align_t align){SEG_Digit_t parse[SEG_DIGITS]{0};SEG_Digit_t buf[SEG_DIGITS]{0};intdigits0;bool negfalse;constchar*pstr;for(inti0;iSEG_DIGITS;i){buf[i].numSEG_BLANK;buf[i].show_dpfalse;}if(*p-){negtrue;p;}while(*pdigitsSEG_DIGITS){if(*p0*p9){parse[digits].num*p-0;parse[digits].show_dp(*(p1).);if(parse[digits].show_dp)p;digits;}p;}inttotaldigits(neg?1:0);intpos(alignSEG_ALIGN_LEFT)?(SEG_DIGITS-1):(total-1);if(negpos0){buf[pos].numSEG_DASH;pos--;}for(inti0;idigits;i){if(pos0)buf[pos--]parse[i];}SEG_SetBuffer(buf);}/** * brief 定时器中断刷新函数 */voidSEG_Timer_ISR(TIM_HandleTypeDef*htim){if(htim-Instance!p_display_timer-Instance)return;staticuint8_tidx0;uint8_tsegLED_table[display_data[idx].num];if(display_data[idx].show_dp)seg0x7F;// 小数点SEG_SendByte(seg);SEG_SendByte(wei_table[idx]);SEG_Latch();idx(idx1)%SEG_DIGITS;}/** * brief 74HC595发送一个字节 */staticvoidSEG_SendByte(uint8_tbyte){for(inti0;i8;i){HAL_GPIO_WritePin(SEG_DIO_GPIO_Port,SEG_DIO_Pin,(byte0x80)?GPIO_PIN_SET:GPIO_PIN_RESET);byte1;HAL_GPIO_WritePin(SEG_SCLK_GPIO_Port,SEG_SCLK_Pin,GPIO_PIN_RESET);HAL_GPIO_WritePin(SEG_SCLK_GPIO_Port,SEG_SCLK_Pin,GPIO_PIN_SET);}}/** * brief 锁存输出 */staticvoidSEG_Latch(void){HAL_GPIO_WritePin(SEG_RCLK_GPIO_Port,SEG_RCLK_Pin,GPIO_PIN_RESET);HAL_GPIO_WritePin(SEG_RCLK_GPIO_Port,SEG_RCLK_Pin,GPIO_PIN_SET);}/** * brief 更新显示缓冲区 */staticvoidSEG_SetBuffer(constSEG_Digit_t*buffer){memcpy((void*)display_data,buffer,sizeof(display_data));}3. main.c 关键代码/* 包含头文件 */#includeseg_display.h#includestdio.hcharbuf[10];uint8_tnum10;uint8_tnum299;intmain(void){HAL_Init();SystemClock_Config();MX_GPIO_Init();MX_TIM1_Init();/* 数码管初始化定时器1 500Hz刷新 */SEG_Init(htim1,500);while(1){// 格式0000 ~ 9999 动态变化sprintf(buf,%02d%02d,num1,num2);SEG_ShowString(buf,SEG_ALIGN_RIGHT);HAL_Delay(500);num1;if(num199)num10;num2--;if(num20)num299;}}/* 定时器溢出回调调用数码管刷新 */voidHAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef*htim){if(htim-InstanceTIM1){SEG_Timer_ISR(htim);}}四、核心功能说明74HC595 驱动串行输入 3 线SCLK/RCLK/DIO并行输出段选 位选驱动 4 位数码管定时器动态扫描无阻塞、无闪烁刷新率可配置400~800Hz 最佳强大字符串显示支持 0~9999支持负数 -12.3支持小数点 12.34支持左 / 右对齐模块化驱动只需修改头文件引脚即可移植提供SEG_ShowString统一显示接口五、使用示例// 显示 1234SEG_ShowString(1234,SEG_ALIGN_RIGHT);// 显示 -12.3SEG_ShowString(-12.3,SEG_ALIGN_RIGHT);// 显示 8.8SEG_ShowString(8.8,SEG_ALIGN_LEFT);// 清空SEG_Clear();六、常见问题数码管不亮检查共阳 / 共阴本驱动为共阳极检查 74HC595 接线检查定时器中断是否开启显示闪烁提高刷新率600~800Hz检查中断优先级显示乱码段码表是否匹配位选顺序是否正确七、总结本驱动采用模块化设计基于 STM32CubeMX 快速生成工程使用 74HC595 节约 IO定时器中断实现稳定无闪烁刷新支持丰富的显示格式非常适合毕业设计、智能小车、温湿度显示等项目使用代码可直接移植到其他 STM32 型号。最后希望这篇文章能帮到有需要的朋友有用欢迎点赞、收藏

更多文章