STM32驱动MAX7219点亮8位数码管的简单思路
·
STM32驱动点亮8位数码管的简单思路
1. 想实现什么?
纯粹就是想用 STM32 控制一下 8 位数码管。如果用单片机直接扫,占用引脚太多了。想到了 MAX7219 这颗串行芯片,只需要 3 根线就能控制 8 位,随便 Proteus 里面跑个仿真验证一下思路。
2. 硬件怎么连?(就三根线)
在 Proteus 里的接线很简单,主打一个“省引脚”。
- 控制线(模拟SPI):
- PA0 接
DIN(数据) - PA1 接
LOAD(片选/锁存) - PA2 接
CLK(时钟)
- PA0 接
- 显示连线:
- 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)
{
// 硬件自动完成动态扫描,主循环无需再做处理
}
}
更多推荐



所有评论(0)