news 2026/4/21 8:44:16

用STM32 HAL库点亮你的第一个TM1638显示板:从接线到显示‘Hello World‘的保姆级教程

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
用STM32 HAL库点亮你的第一个TM1638显示板:从接线到显示‘Hello World‘的保姆级教程

STM32与TM1638的第一次亲密接触:从零开始构建动态显示系统

1. 硬件准备与连接指南

当你第一次拿到TM1638模块时,可能会被它紧凑的尺寸和密集的引脚所迷惑。这个集成了8位数码管、8个LED和8个按键的小巧模块,实际上只需要4根线与STM32相连就能发挥全部功能。让我们从最基础的硬件连接开始。

所需材料清单

  • STM32F103C8T6最小系统板(Blue Pill)
  • TM1638显示模块(8位数码管+8LED+8按键)
  • 杜邦线若干(建议使用不同颜色)
  • 5V电源(可从STM32板获取)

引脚连接对照表

TM1638引脚STM32引脚功能说明
VCC5V电源正极
GNDGND电源地
STBPB7片选信号
CLKPB8时钟信号
DIOPB9数据输入输出

注意:实际连接时,务必确保电源极性正确。我曾见过不止一个初学者因为接反电源而烧毁模块。

连接完成后,建议先用万用表检查以下几点:

  1. 电源电压是否稳定在5V左右
  2. 各信号线是否连通
  3. 是否存在短路现象

2. CubeMX环境配置

STM32CubeMX是ST官方提供的图形化配置工具,能极大简化初始化工作。以下是针对TM1638的配置步骤:

2.1 时钟配置

在RCC选项卡中:

  • 将HSE设置为Crystal/Ceramic Resonator
  • 系统时钟树配置为72MHz(STM32F103的最大主频)

2.2 GPIO设置

  1. 找到PB7、PB8、PB9三个引脚
  2. 将PB7和PB8配置为GPIO_Output
  3. 将PB9配置为GPIO_Output(初始状态,后续会动态切换)
// 生成的初始化代码片段 GPIO_InitStruct.Pin = GPIO_PIN_7|GPIO_PIN_8|GPIO_PIN_9; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

2.3 项目生成

  1. 选择Toolchain/IDE为MDK-ARM或STM32CubeIDE
  2. 勾选"Generate peripheral initialization as a pair of .c/.h files"
  3. 点击GENERATE CODE生成工程

3. TM1638驱动实现

TM1638采用类似SPI的通信协议,但有自己的独特之处。我们需要实现几个基础函数来操控它。

3.1 底层通信函数

// 设置DIO引脚方向 void TM1638_SetDIOInput(void) { GPIO_InitTypeDef GPIO_InitStruct = {0}; GPIO_InitStruct.Pin = GPIO_PIN_9; GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pull = GPIO_NOPULL; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); } void TM1638_SetDIOOutput(void) { GPIO_InitTypeDef GPIO_InitStruct = {0}; GPIO_InitStruct.Pin = GPIO_PIN_9; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); } // 写入一个字节 void TM1638_WriteByte(uint8_t data) { TM1638_SetDIOOutput(); for(uint8_t i=0; i<8; i++) { HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_RESET); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, (data & 0x01)?GPIO_PIN_SET:GPIO_PIN_RESET); data >>= 1; HAL_Delay(1); // 适当延时确保信号稳定 HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_SET); } }

3.2 显示控制函数

数码管显示需要先了解段码表。TM1638使用共阴数码管,每个数字对应特定的段码:

const uint8_t digitToSegment[] = { // 0 1 2 3 4 5 6 7 8 9 0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F, // A B C D E F 0x77, 0x7C, 0x39, 0x5E, 0x79, 0x71 }; void TM1638_DisplayDigits(uint8_t digits[]) { const uint8_t digitAddr[] = {0xC0, 0xC2, 0xC4, 0xC6, 0xC8, 0xCA, 0xCC, 0xCE}; HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_RESET); TM1638_WriteByte(0x40); // 写入数据命令 HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_SET); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_RESET); TM1638_WriteByte(0xC0); // 设置起始地址 for(int i=0; i<8; i++) { TM1638_WriteByte(digitToSegment[digits[i]]); TM1638_WriteByte(0x00); // LED控制位 } HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_SET); }

4. 实现"Hello World"效果

现在,让我们用这个系统显示经典的"HELLO"和数字序列。

4.1 显示"HELLO"

void Display_Hello(void) { uint8_t helloDisplay[] = { 12, // H 14, // E 15, // L 15, // L 16, // O 0, // 空格 0, // 空格 0 // 空格 }; TM1638_DisplayDigits(helloDisplay); }

4.2 显示滚动数字

void Display_ScrollingNumbers(void) { uint8_t numbers[8]; for(uint8_t start=0; start<20; start++) { for(uint8_t i=0; i<8; i++) { numbers[i] = (start + i) % 10; } TM1638_DisplayDigits(numbers); HAL_Delay(300); } }

4.3 主程序集成

int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); // 初始化TM1638 HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_SET); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_SET); while(1) { Display_Hello(); HAL_Delay(2000); Display_ScrollingNumbers(); HAL_Delay(2000); } }

5. 进阶功能与调试技巧

掌握了基础显示后,我们可以进一步探索TM1638的其他功能。

5.1 LED控制

每个数码管旁边都有一个独立的LED,可以通过以下函数控制:

void TM1638_SetLEDs(uint8_t leds) { const uint8_t ledAddr[] = {0xC1, 0xC3, 0xC5, 0xC7, 0xC9, 0xCB, 0xCD, 0xCF}; for(int i=0; i<8; i++) { HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_RESET); TM1638_WriteByte(ledAddr[i]); TM1638_WriteByte((leds & (1<<i)) ? 1 : 0); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_SET); } }

5.2 按键扫描

TM1638的按键状态可以通过以下函数读取:

uint8_t TM1638_ReadKeys(void) { uint8_t keys = 0; HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_RESET); TM1638_WriteByte(0x42); // 读键扫数据命令 TM1638_SetDIOInput(); for(int i=0; i<4; i++) { uint8_t data = 0; for(int j=0; j<8; j++) { HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_RESET); if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_9)) { data |= (1<<j); } HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_SET); } // 解析按键数据 if(data & 0x01) keys |= (1 << (i*2)); if(data & 0x10) keys |= (1 << (i*2+1)); } HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_SET); TM1638_SetDIOOutput(); return keys; }

5.3 常见问题排查

问题1:显示乱码

  • 检查段码表是否正确
  • 确认数码管是共阴类型
  • 检查CLK信号是否有干扰

问题2:按键无响应

  • 确认DIO引脚方向切换正确
  • 检查按键扫描时序是否符合规格书要求
  • 测试电源电压是否稳定

问题3:显示亮度不足

  • 使用TM1638_SetBrightness()函数调整亮度
  • 检查电源供电能力
  • 确认限流电阻值是否合适

6. 项目扩展思路

掌握了基础功能后,可以考虑以下扩展方向:

  1. 多级菜单系统:结合按键功能实现菜单导航
  2. 实时时钟显示:添加RTC模块显示时间
  3. 传感器数据显示:连接温湿度传感器显示环境数据
  4. 动画效果:实现数字滚动、渐变等特效
// 简单的跑马灯效果示例 void LED_RunningLight(void) { for(int i=0; i<8; i++) { TM1638_SetLEDs(1<<i); HAL_Delay(200); } for(int i=6; i>0; i--) { TM1638_SetLEDs(1<<i); HAL_Delay(200); } }

在实际项目中,我发现TM1638的显示刷新率对视觉效果影响很大。通过优化通信时序,可以显著提高显示流畅度。另外,合理使用亮度调节功能可以在不同光照环境下获得最佳显示效果。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/21 8:40:51

别再用循环了!Matlab里flip函数这5个隐藏用法,数据处理效率翻倍

别再用循环了&#xff01;Matlab里flip函数这5个隐藏用法&#xff0c;数据处理效率翻倍 在Matlab的世界里&#xff0c;数据处理效率往往决定了项目的成败。许多用户习惯性地使用for循环或手动索引来反转数组&#xff0c;殊不知这种"原始"操作不仅代码冗长&#xff0c…

作者头像 李华
网站建设 2026/4/21 8:37:14

asyncio 的 Event Loop:定义、运行机制与工程实践

1. 为什么需要 Event Loop 在 asyncio 中&#xff0c;event loop 是整个异步运行时的调度核心。它本身并不“完成业务逻辑”&#xff0c;而是负责在适当的时机推进协程、触发回调、处理 I/O 事件、安排定时器&#xff0c;并把不同来源的异步工作组织成一套可预测的执行序列。 如…

作者头像 李华