STM32驱动MAX7219点亮8位数码管的简单思路
STM32驱动点亮8位数码管的简单思路1. 想实现什么纯粹就是想用 STM32 控制一下 8 位数码管。如果用单片机直接扫占用引脚太多了。想到了 MAX7219 这颗串行芯片只需要 3 根线就能控制 8 位随便 Proteus 里面跑个仿真验证一下思路。2. 硬件怎么连就三根线在 Proteus 里的接线很简单主打一个“省引脚”。控制线模拟SPIPA0接DIN数据PA1接LOAD片选/锁存PA2接CLK时钟显示连线MAX7219 的段输出A~G, DP并联接到两块数码管上让它们字型同步。位选输出DIG0~DIG7分别对应接左边 4 位和右边 4 位的引脚一共 8 位控制。ISET脚接个10kΩ电阻到地用来调亮度。实际跑仿真的时候一定记住要补上 VCC 和 GND 的连线Proteus 里有时候不显眼容易漏掉。3. 程序逻辑是怎么想的代码核心其实就是自己写了一个**“模拟 SPI”**函数用 STM32 的标准库stm32f10x.h把 16 位的数据一点一点顺着 PA0、PA2 发出去。关键点 1段码自定义。我没有开 MAX7219 的 BCD 译码模式而是把它设为直接接受段码0x09寄存器写 0x00。这样我可以自己拼凑出F、E、2、8、C、A、8、7这种特定的显示数字。关键点 2无需定时器扫描。MAX7219 很省事我们只需要一次性把这 8 个字节的数据写到芯片里面它自己内部的硬件就会自动快速扫描点亮这 8 个数码管主循环while(1)直接躺平就行了。4. 这个小思路踩过的坑重要代码里我特意加了很多delay_cnt的微秒级延时。这纯粹是为了照顾 Proteus 仿真器。仿真环境因为 Proteus 的虚拟逻辑分析器如果遇到非常快的脉冲容易漏抓时序加了延时仿真就能完美运行。实物环境如果直接把这个代码烧录到真实的 STM32 开发板和 MAX7219 模块上这些过长的延时会导致显示非常暗淡或者闪烁。改成实物的话要把这些模拟延时代码精简掉或者直接开启 STM32 的硬件 SPI外设速度更快更稳定。5. 简评这个想法简单好用但也有局限优点接线太少太舒服了以前用 8 位数码管得用锁存器或一堆引脚现在 3 根线全搞定代码也短。局限性既然用的是最基础的 7 段数码管那就只能显示数字0-9和少部分字母A, B, C, E, F 等。做不了带汉字、带复杂图形的屏那是 LCD 的任务。只要你的目标就是个数字钟、温度计或者计数器这个思路完全够用。仿真效果参考代码#include stm32f10x.h // ------------------------------------------------------------ // 硬件引脚定义 (对应 Proteus 原理图 PA0/PA1/PA2) // ------------------------------------------------------------ #define MAX7219_DIN GPIO_Pin_0 // PA0 - DIN #define MAX7219_LOAD GPIO_Pin_1 // PA1 - LOAD (片选) #define MAX7219_CLK GPIO_Pin_2 // PA2 - CLK (时钟) // ------------------------------------------------------------ // 毫秒级延时函数 (72MHz 主频) // ------------------------------------------------------------ void delay_ms(uint32_t ms) { uint32_t i, j; for(i 0; i ms; i) for(j 0; j 8000; j); } // ------------------------------------------------------------ // 向 MAX7219 写入 16 位数据包 (带 Proteus 仿真兼容延时) // ------------------------------------------------------------ void MAX7219_Write(uint8_t address, uint8_t data) { uint16_t cmd ((uint16_t)address 8) | data; // 拼装 16 位数据 volatile uint8_t delay_cnt; // 用于微小延时的变量防止仿真器漏抓时序 // 1. 片选拉低开始通信 GPIO_ResetBits(GPIOA, MAX7219_LOAD); for(delay_cnt 0; delay_cnt 20; delay_cnt); // 等待片选电平稳定 // 2. 循环 16 次从最高位 (第15位) 逐位发送 for(int i 15; i 0; i--) { // 先把 CLK 拉低准备放数据 GPIO_ResetBits(GPIOA, MAX7219_CLK); // 放数据到 DIN 引脚 if(cmd (1 i)) GPIO_SetBits(GPIOA, MAX7219_DIN); else GPIO_ResetBits(GPIOA, MAX7219_DIN); for(delay_cnt 0; delay_cnt 20; delay_cnt); // 给 Proteus 缓冲时间识别数据线电平 // 产生 CLK 上升沿MAX7219 在上升沿读取 DIN 的数据 GPIO_SetBits(GPIOA, MAX7219_CLK); for(delay_cnt 0; delay_cnt 20; delay_cnt); // 维持高电平让仿真器识别上升沿 } // 3. 通信结束片选拉高锁存数据 GPIO_ResetBits(GPIOA, MAX7219_CLK); for(delay_cnt 0; delay_cnt 20; delay_cnt); GPIO_SetBits(GPIOA, MAX7219_LOAD); // LOAD 拉高数据被送入寄存器显示 for(delay_cnt 0; delay_cnt 20; delay_cnt); } // ------------------------------------------------------------ // MAX7219 初始化配置 // ------------------------------------------------------------ void MAX7219_Init(void) { MAX7219_Write(0x0C, 0x01); // 寄存器 0x0C关闭休眠模式正常显示 (0x01) MAX7219_Write(0x0F, 0x00); // 寄存器 0x0F关闭显示测试模式 (0x00) MAX7219_Write(0x09, 0x00); // 寄存器 0x09不开启 BCD 译码直接使用自定义段码 (0x00) MAX7219_Write(0x0B, 0x07); // 寄存器 0x0B扫描范围8 位全开 (0~7) MAX7219_Write(0x0A, 0x0F); // 寄存器 0x0A亮度调节0x0F 为最高亮度 // 清屏操作防止仿真初始化时闪烁之前的乱码 for(uint8_t i 1; i 8; i) { MAX7219_Write(i, 0x00); } } // ------------------------------------------------------------ // 主函数 // ------------------------------------------------------------ int main(void) { // 配置系统时钟为 72MHz SystemInit(); // 开启 GPIOA 时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); // 配置 PA0、PA1、PA2 为推挽输出 GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Pin MAX7219_DIN | MAX7219_CLK | MAX7219_LOAD; GPIO_InitStructure.GPIO_Mode GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOA, GPIO_InitStructure); // 初始电平CLK和DIN拉低LOAD拉高SPI总线空闲状态 GPIO_ResetBits(GPIOA, MAX7219_DIN | MAX7219_CLK); GPIO_SetBits(GPIOA, MAX7219_LOAD); // 等待外围器件 MAX7219 上电稳定 delay_ms(100); // 初始化 MAX7219 MAX7219_Init(); // 重新计算的专供 MAX7219 使用的段码 (内部顺序: DP A B C D E F G) // 对应需要显示的字符: F E 2 8 C A 8 7 uint8_t seg_data[8] { 0x47, // F 0x4F, // E 0x6D, // 2 0x7F, // 8 0x4E, // C 0x77, // A 0x7F, // 8 0x70 // 7 }; // 把 8 个字符的数据按顺序写入 MAX7219 的数据寄存器 (地址 0x01 ~ 0x08) for(uint8_t i 0; i 8; i) { MAX7219_Write(i 1, seg_data[i]); } while(1) { // 硬件自动完成动态扫描主循环无需再做处理 } }