告别裸机点灯:用STM32标准库+TM1638模块做个4位计数器(附按键扫描教程)

张开发
2026/4/13 10:05:18 15 分钟阅读

分享文章

告别裸机点灯:用STM32标准库+TM1638模块做个4位计数器(附按键扫描教程)
从零打造智能计数器STM32标准库与TM1638模块实战指南数码管作为经典的显示器件在工业控制、仪器仪表等领域有着广泛应用。而TM1638作为一款集成了数码管驱动和按键扫描功能的芯片极大简化了硬件设计。本文将带你用STM32标准库开发一个完整的4位计数器项目实现显示和按键交互功能。1. 项目准备与环境搭建1.1 硬件选型与连接本项目核心硬件包括主控芯片STM32F103ZET6正点原子战舰开发板显示模块TM1638驱动板带4位8段共阴极数码管下载调试器JLINK或ST-Link连接方式如下表所示STM32引脚TM1638引脚功能说明PC7STB片选信号PC8DIO数据输入输出PC9CLK时钟信号3.3V/5VVCC电源正极GNDGND电源地提示虽然TM1638规格书建议5V供电但实际测试3.3V也能工作只是亮度稍低。如果使用5V供电建议在DIO线上加电平转换电路。1.2 软件开发环境配置安装Keil MDK开发环境建议版本5.25以上创建新工程选择STM32F103ZE芯片添加标准外设库StdPeriph Library配置工程选项设置正确的晶振频率和调试接口// 系统时钟配置示例72MHz void RCC_Configuration(void) { RCC_DeInit(); RCC_HSEConfig(RCC_HSE_ON); while(RCC_GetFlagStatus(RCC_FLAG_HSERDY) RESET); RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9); RCC_PLLCmd(ENABLE); while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) RESET); RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK); while(RCC_GetSYSCLKSource() ! 0x08); }2. TM1638驱动原理深度解析2.1 通信时序与协议TM1638采用类似SPI的同步串行通信协议但有以下特点时钟极性上升沿有效数据采样在时钟上升沿采样数据片选信号STB低电平有效典型写时序流程拉低STB信号发送命令字节发送数据字节可选拉高STB信号// TM1638写数据函数实现 void TM1638_WriteByte(uint8_t data) { for(uint8_t i0; i8; i) { CLK 0; DIO (data 0x01) ? 1 : 0; data 1; CLK 1; // 上升沿锁存数据 } }2.2 关键指令集TM1638支持多种控制指令主要分为三类显示控制指令0x80-0x8F0x80关闭显示0x8F最大亮度显示中间值可调节亮度0x81-0x8E数据设置指令0x40-0x470x40地址自动增加模式0x44固定地址模式地址设置指令0xC0-0xC7设置显示数据的起始地址3. 数码管显示功能实现3.1 数码管编码原理共阴极数码管需要为每个段提供高电平才能点亮。TM1638采用特殊的编码方式每个数码管对应2个字节的显示内存字节位定义bit0段abit1段b...bit7小数点// 0-9数字的段码表共阴极 const uint8_t digitPattern[10] { 0x3F, // 0 0x06, // 1 0x5B, // 2 0x4F, // 3 0x66, // 4 0x6D, // 5 0x7D, // 6 0x07, // 7 0x7F, // 8 0x6F // 9 };3.2 动态显示实现实现4位计数器的显示功能void displayNumber(uint16_t num) { uint8_t digits[4]; // 分解数字到各位 digits[0] num % 10; // 个位 digits[1] (num / 10) % 10; // 十位 digits[2] (num / 100) % 10;// 百位 digits[3] num / 1000; // 千位 // 写入TM1638显示内存 for(uint8_t i0; i4; i) { Write_DATA(i*2, digitPattern[digits[i]]); } }4. 按键扫描与交互功能4.1 按键扫描原理TM1638集成了8个按键的扫描功能通过读取特定地址的数据可以获取按键状态发送读键指令0x42从DIO线读取4个字节数据每个字节的bit0-bit7对应不同按键组合uint8_t Read_key(void) { uint8_t key_value 0; STB 0; TM1638_WriteByte(0x42); // 发送读键指令 // 设置DIO为输入模式 GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Pin GPIO_Pin_8; GPIO_InitStructure.GPIO_Mode GPIO_Mode_IPU; GPIO_Init(GPIOC, GPIO_InitStructure); // 读取按键数据 for(uint8_t i0; i4; i) { uint8_t temp 0; for(uint8_t j0; j8; j) { CLK 0; if(DIO_INT) temp | (1j); CLK 1; } key_value | (temp i); } // 恢复DIO为输出模式 GPIO_InitStructure.GPIO_Mode GPIO_Mode_Out_PP; GPIO_Init(GPIOC, GPIO_InitStructure); STB 1; return key_value; }4.2 计数器功能实现结合显示和按键功能实现完整的计数器int main(void) { uint16_t counter 0; uint8_t last_key 0, current_key 0; // 初始化系统时钟和外设 RCC_Configuration(); init_TM1638(); while(1) { current_key Read_key(); // 检测按键上升沿 if((last_key 0) (current_key ! 0)) { switch(current_key) { case 0x01: counter; break; // K1: 加1 case 0x02: counter--; break; // K2: 减1 case 0x04: counter 0; break; // K3: 清零 default: break; } // 限制范围0-9999 if(counter 9999) counter 9999; if(counter 0) counter 0; displayNumber(counter); } last_key current_key; delay_ms(50); // 简单防抖 } }5. 项目优化与扩展5.1 显示效果优化亮度调节通过修改显示控制指令实现void setBrightness(uint8_t level) { if(level 7) level 7; Write_COM(0x88 | level); }滚动显示实现数字滚动动画效果void scrollDisplay(uint16_t num, uint8_t speed) { uint8_t digits[4]; // 数字分解代码... for(int i0; i4; i) { for(int j0; j8; j) { Write_DATA(i*2, digitPattern[digits[i]] j); delay_ms(speed); } } }5.2 功能扩展思路多模式计数器正向计数倒计时频率计菜单系统通过长按/短按实现模式切换增加参数设置功能数据存储使用STM32内部Flash保存计数值上电后恢复上次计数值// Flash读写示例 void saveCounter(uint16_t value) { FLASH_Unlock(); FLASH_ErasePage(0x0800F000); FLASH_ProgramHalfWord(0x0800F000, value); FLASH_Lock(); } uint16_t readCounter(void) { return *(__IO uint16_t*)0x0800F000; }在实际项目中我发现TM1638的按键扫描功能非常实用但需要注意防抖处理。经过多次测试50ms的延时既能有效防抖又不会影响操作体验。另外如果显示内容变化频繁建议将亮度调高以获得更好的视觉效果。

更多文章