STM32驱动点亮8位数码管的简单思路

1. 想实现什么?

纯粹就是想用 STM32 控制一下 8 位数码管。如果用单片机直接扫,占用引脚太多了。想到了 MAX7219 这颗串行芯片,只需要 3 根线就能控制 8 位,随便 Proteus 里面跑个仿真验证一下思路。

2. 硬件怎么连?(就三根线)

在 Proteus 里的接线很简单,主打一个“省引脚”。

  • 控制线(模拟SPI)
    • PA0DIN(数据)
    • PA1LOAD(片选/锁存)
    • PA2CLK(时钟)
  • 显示连线
    • 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)
    {
        // 硬件自动完成动态扫描,主循环无需再做处理
    }
}
Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐