STM32F103 与 TM1650 驱动4位数码管:为智能交通灯添加倒计时显示

📅 2026/7/11 8:12:06
STM32F103 与 TM1650 驱动4位数码管:为智能交通灯添加倒计时显示
STM32F103 与 TM1650 驱动4位数码管为智能交通灯添加倒计时显示在嵌入式系统开发中为交通信号灯系统添加直观的倒计时显示是提升用户体验的关键环节。本文将详细介绍如何利用STM32F103微控制器通过GPIO模拟I2C协议驱动TM1650芯片实现4位共阴极数码管的稳定控制为智能交通灯系统增加专业级的倒计时功能。1. 硬件架构设计1.1 核心器件选型STM32F103C8T6作为主控芯片其丰富的外设资源和适中的价格使其成为嵌入式开发的理想选择。该芯片具备72MHz主频的Cortex-M3内核64KB Flash存储和20KB SRAM多达37个快速GPIO端口内置定时器支持精确时序控制TM1650是一款专为LED显示设计的驱动芯片主要特性包括支持8段×16位LED显示内置按键扫描电路可调节显示亮度8级典型工作电流仅5mA1.2 硬件连接原理系统连接示意图如下STM32F103C8T6 TM1650 4位共阴数码管 GPIOB6 (SCL) ------- SCLK GPIOB7 (SDA) ------- DIO 3.3V --------------- VCC GND ---------------- GND SEG1~8 ------- 数码管段选(a~dp) GRID1~4 ------ 数码管位选(DIG1~4)注意实际连接时需在STM32与TM1650之间的I2C线上添加2.2KΩ上拉电阻确保信号稳定性。2. GPIO模拟I2C驱动实现2.1 I2C时序模拟基础TM1650采用类I2C通信协议但时序要求相对宽松。关键时序参数如下时序参数典型值说明SCL周期5μs约200kHz时钟频率起始条件保持时间4μsSDA下降沿前SCL高电平时间数据建立时间2μsSCL上升沿前数据稳定时间停止条件建立时间4μsSCL高电平时SDA上升沿实现GPIO模拟的底层驱动函数// 引脚定义 #define TM1650_SCL_PIN GPIO_Pin_6 #define TM1650_SDA_PIN GPIO_Pin_7 #define TM1650_PORT GPIOB // 微秒级延时函数 void delay_us(uint32_t us) { uint32_t ticks us * (SystemCoreClock / 1000000) / 5; while(ticks--); } // 产生起始条件 void I2C_Start(void) { GPIO_SetBits(TM1650_PORT, TM1650_SDA_PIN); GPIO_SetBits(TM1650_PORT, TM1650_SCL_PIN); delay_us(5); GPIO_ResetBits(TM1650_PORT, TM1650_SDA_PIN); delay_us(5); GPIO_ResetBits(TM1650_PORT, TM1650_SCL_PIN); } // 发送一个字节 void I2C_SendByte(uint8_t byte) { for(uint8_t i0; i8; i) { (byte 0x80) ? GPIO_SetBits(TM1650_PORT, TM1650_SDA_PIN) : GPIO_ResetBits(TM1650_PORT, TM1650_SDA_PIN); byte 1; delay_us(2); GPIO_SetBits(TM1650_PORT, TM1650_SCL_PIN); delay_us(3); GPIO_ResetBits(TM1650_PORT, TM1650_SCL_PIN); } // 伪ACK检测 GPIO_SetBits(TM1650_PORT, TM1650_SDA_PIN); delay_us(2); GPIO_SetBits(TM1650_PORT, TM1650_SCL_PIN); delay_us(3); GPIO_ResetBits(TM1650_PORT, TM1650_SCL_PIN); } // 产生停止条件 void I2C_Stop(void) { GPIO_ResetBits(TM1650_PORT, TM1650_SDA_PIN); delay_us(2); GPIO_SetBits(TM1650_PORT, TM1650_SCL_PIN); delay_us(4); GPIO_SetBits(TM1650_PORT, TM1650_SDA_PIN); delay_us(5); }2.2 TM1650初始化配置TM1650需要配置显示模式和亮度参数void TM1650_Init(void) { // 显示模式设置4位8段普通显示模式 I2C_Start(); I2C_SendByte(0x48); // 显示模式命令地址 I2C_SendByte(0x01); // 开启显示4位8段 I2C_Stop(); // 亮度设置PWM控制亮度级别5 I2C_Start(); I2C_SendByte(0x68); // 亮度/按键地址 I2C_SendByte(0x44); // PWM亮度控制级别5 I2C_Stop(); }3. 数码管显示驱动实现3.1 数字编码与显示缓存共阴极数码管段码表0-9const uint8_t digitCode[] { 0x3F, // 0 0x06, // 1 0x5B, // 2 0x4F, // 3 0x66, // 4 0x6D, // 5 0x7D, // 6 0x07, // 7 0x7F, // 8 0x6F // 9 }; uint8_t displayBuffer[4] {0}; // 显示缓存3.2 动态扫描刷新函数采用动态扫描方式实现4位数码管显示void TM1650_Refresh(void) { for(uint8_t i0; i4; i) { I2C_Start(); I2C_SendByte(0x60 (i1)); // 数码管地址命令 I2C_SendByte(displayBuffer[i]); // 段码数据 I2C_Stop(); delay_us(500); // 位间延时 } }3.3 倒计时显示功能实现将倒计时数值转换为显示缓存void UpdateCountdown(uint16_t seconds) { uint8_t minutes seconds / 60; uint8_t secs seconds % 60; // 分钟十位 displayBuffer[0] (minutes 10) ? digitCode[minutes/10] : 0; // 分钟个位 displayBuffer[1] digitCode[minutes%10] | 0x80; // 添加小数点 // 秒钟十位 displayBuffer[2] digitCode[secs/10]; // 秒钟个位 displayBuffer[3] digitCode[secs%10]; TM1650_Refresh(); }4. 系统集成与优化4.1 与交通灯主程序协同工作在交通灯状态机中集成倒计时显示typedef enum { RED_STATE, GREEN_STATE, YELLOW_STATE } TrafficLightState; void TrafficLight_Update(void) { static TrafficLightState state RED_STATE; static uint16_t countdown 30; // 默认30秒 switch(state) { case RED_STATE: if(countdown 0) { UpdateCountdown(countdown--); SetLED(RED_ON); } else { state GREEN_STATE; countdown GetGreenDuration(); // 获取绿灯时长 } break; case GREEN_STATE: if(countdown 0) { UpdateCountdown(countdown--); SetLED(GREEN_ON); } else { state YELLOW_STATE; countdown 3; // 黄灯固定3秒 } break; case YELLOW_STATE: if(countdown 0) { UpdateCountdown(countdown--); SetLED(YELLOW_ON); } else { state RED_STATE; countdown GetRedDuration(); // 获取红灯时长 } break; } }4.2 显示亮度自动调节根据环境光线自动调节显示亮度void AutoAdjustBrightness(void) { uint16_t lightSensor ADC_Read(LIGHT_SENSOR_CH); uint8_t brightness; if(lightSensor 800) brightness 7; // 强光环境 else if(lightSensor 400) brightness 5; else brightness 3; // 弱光环境 I2C_Start(); I2C_SendByte(0x68); // 亮度命令地址 I2C_SendByte(0x40 | (brightness 0x07)); // 设置亮度 I2C_Stop(); }4.3 低功耗优化策略在夜间模式降低系统功耗void EnterNightMode(void) { // 关闭数码管显示 I2C_Start(); I2C_SendByte(0x48); I2C_SendByte(0x00); // 关闭显示 I2C_Stop(); // 设置MCU为低功耗模式 PWR_EnterSTOPMode(PWR_Regulator_LowPower, PWR_STOPEntry_WFI); }5. 常见问题与调试技巧5.1 显示异常排查流程当数码管显示异常时可按以下步骤排查电源检查确认3.3V电源稳定测量TM1650 VCC引脚电压检查所有GND连接是否可靠信号线检查用示波器观察SCL/SDA波形确认上拉电阻值合适2.2KΩ-4.7KΩ检查线路是否有短路/断路软件调试简化测试代码单独测试每个数码管验证I2C时序是否符合规格检查段码表是否正确5.2 抗干扰设计建议提高系统稳定性的关键措施硬件方面在电源引脚添加100nF去耦电容信号线走线尽量短避免平行走线使用屏蔽线连接较长的外设软件方面增加I2C通信重试机制定期刷新显示防止残影添加看门狗定时器复位功能// 带重试的发送函数 uint8_t Safe_I2C_Send(uint8_t addr, uint8_t data, uint8_t retry) { while(retry--) { I2C_Start(); I2C_SendByte(addr); I2C_SendByte(data); I2C_Stop(); if(Check_ACK()) return 1; // 成功 delay_ms(1); } return 0; // 失败 }5.3 性能优化技巧提升显示效果的实用方法动态扫描优化调整位间延时时间通常300-800μs采用非对称占空比减少闪烁显示效果增强添加小数点闪烁效果提示状态变化在状态切换时添加过渡动画资源占用优化使用DMA释放CPU资源将段码表存储在Flash而非RAM// 使用const将段码表存储在Flash __attribute__((section(.rodata))) const uint8_t digitCode[] {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F};