
一、文档概述1.1 项目简介本工程基于 STM32F1 系列单片机实现8 路共阴极数码管动态扫描显示硬件采用 PB8~PB15 作为数码管段选输出GPIOA4/5/6 驱动 3-8 译码器做位选搭配 SysTick 系统定时器提供高精度毫秒 / 微秒延时完成多数码管稳定刷新。1.2 硬件资源分配表格功能引脚说明数码管段选PB8~PB15推挽输出输出段码左移 8 位适配高 8 位引脚3-8 译码器位选输入PA4(LSA)、PA5(LSB)、PA6(LSC)低电平有效选通对应数码管系统时钟72MHzSysTick 分频 8 分频提供精准延时数码管类型共阴极内置 0~F 标准段码表gsmg_code1.3 文件结构plaintext├── main.c // 主程序入口显示测试逻辑 ├── public.h // 通用类型定义、延时函数声明、公共头文件 ├── SysTick.c // SysTick定时器初始化、us/ms延时底层实现 ├── SysTick.h // SysTick模块头文件 ├── smg.c // 数码管初始化、动态扫描显示函数实现 ├── smg.h // 数码管硬件宏定义、函数声明二、各模块详细说明2.1 public.h 通用公共模块2.1.1 核心功能引入 STM32 标准库头文件stm32f10x.h提供寄存器、GPIO 标准函数兼容 51 单片机变量别名uint8/uint16降低跨芯片开发迁移成本统一延时函数声明全工程延时接口标准化。2.1.2 接口声明c运行void delay_ms(uint16 nms); // 毫秒级延时 void delay_10us(uint16 nus); // 10微秒单位延时2.2 SysTick 系统定时器延时模块SysTick.c/ SysTick.h2.2.1 设计原理采用内核 SysTick 定时器时钟源配置为 HCLK/872MHz 系统时钟下计数时钟 9MHz1 次计数 1/9μs通过预分频系数预计算fac_us、fac_ms实现无阻塞精准软件延时不占用定时器外设。2.2.2 全局静态变量fac_us1 微秒所需 SysTick 计数次数fac_ms1 毫秒所需 SysTick 计数次数。2.2.3 函数接口说明void SysTick_Init(u8 SYSCLK)功能SysTick 时钟源配置初始化延时计数系数参数SYSCLK系统主时钟本项目固定传入 72配置SysTick_CLKSource_HCLK_Div8八分频模式。void delay_us(u32 nus)功能微秒级精准延时逻辑装载计数值→开启定时器→轮询计数完成标志→关闭定时器清空计数器。void delay_ms(u16 nms)功能毫秒级延时供数码管动态扫描消闪使用底层逻辑同delay_us使用毫秒预计算系数。void delay_10us(u16 nus)封装函数输入为 10μs 倍数内部调用delay_us。2.3 数码管驱动模块smg.c/smg.h2.3.1 硬件宏定义smg.h段选端口GPIOB PB8~PB15推挽输出 50MHz位选译码端口GPIOA PA4/PA5/PA63-8 译码器控制电平宏封装LSA/LSA_H快速控制引脚高低电平简化译码逻辑全局段码数组gsmg_code外部声明存放共阴极标准 0~F 段码。2.3.2 全局段码表c运行u8 gsmg_code[17]{0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07, 0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};索引 0~15数字 0~9、字母 A~F共阴极特性段脚高电平点亮数码管段。2.3.3 初始化函数SMG_Init(void)执行流程开启 GPIOB 时钟配置 PB8~PB15 推挽输出上电段码清零所有数码管段熄灭开启 GPIOA 时钟配置 PA4/5/6 推挽输出译码引脚全部拉高关闭所有数码管3-8 译码器低电平选通。2.3.4 核心显示函数void smg_display(u8 dat[],u8 pos)输入参数dat[]待显示数字数组取值 0~15pos起始显示位置1~8对应 8 个数码管。动态扫描实现逻辑位置偏移转换pos_temp pos - 1将 1 起始坐标转为数组 0 起始索引循环从起始位到第 8 位数码管逐位刷新switch分支控制 LSA/LSB/LSC 电平3-8 译码器选通当前数码管段码处理段码字节左移 8 位写入 PB8~PB15 高 8 位 GPIO延时 1ms 保持视觉可见执行段消隐段码清零消除动态扫描拖影单次数码管扫描完成后全部译码引脚拉高关闭所有数码管防止残影。消隐机制说明每一位数码管显示完成后先清空段选输出再切换下一位避免前一位段码残留导致重影是动态数码管稳定显示核心优化点。2.4 main.c 主应用程序2.4.1 功能说明数码管驱动测试例程8 个数码管依次显示数字 0、1、2、3、4、5、6、7。执行流程定义显示缓存数组buf[8] {0,1,2,3,4,5,6,7}初始化 SysTick 延时模块系统时钟 72MHz初始化数码管 GPIO 端口死循环持续调用显示函数从第 1 位开始完整刷新 8 位数码管。三、核心工作原理3.1 动态扫描显示原理8 个数码管共用一套段选引脚依靠 3-8 译码器分时选通单个数码管利用人眼视觉暂留24Hz 刷新无闪烁快速循环点亮所有数码管硬件成本远低于静态驱动。3.2 段码移位逻辑段选引脚为 PB8~PB15GPIO 高 8 位标准段码为 8 位低字节数据因此执行gsmg_code[num] 8将段码移动至高 8 位输出匹配硬件接线。3.3 防闪烁优化方案单管显示延时 1ms保证亮度逐位消隐切换数码管前段码清零扫描结束关闭全部位选无残留电平。四、函数调用关系plaintextmain() ├─ SysTick_Init(72) ├─ SMG_Init() └─ while(1) → smg_display(buf, 1) └─ delay_ms(1) // 依赖SysTick底层延时 └─ SysTick定时器计数逻辑五、关键宏与常量说明系统时钟72MHz适配 STM32F1 标准最小系统译码电平规则LSC/LSB/LSA 任意拉低对应选通 1~8 号数码管全拉高无数码管点亮段码表长度 17预留索引 16 用于自定义熄灭 / 小数点拓展GPIO 模式统一 50MHz 推挽输出驱动能力满足数码管发光需求。六、详细代码1. seg.c seg.h 数码管显示#include smg.h //共阴极0~F段码 u8 gsmg_code[17]{0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07, 0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71}; void SMG_Init(void) { GPIO_InitTypeDef GPIO_InitStruct; //段选初始化 PB8~PB15 RCC_APB2PeriphClockCmd(SMG_PORT_RCC,ENABLE); GPIO_InitStruct.GPIO_Pin SMG_PIN; GPIO_InitStruct.GPIO_Mode GPIO_Mode_Out_PP; GPIO_InitStruct.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(SMG_PORT,GPIO_InitStruct); GPIO_Write(SMG_A_DP_PORT,0x00); //上电段选全灭 //位选初始化 RCC_APB2PeriphClockCmd(SEL_RCC,ENABLE); GPIO_InitStruct.GPIO_Pin LSA_PIN|LSB_PIN|LSC_PIN; GPIO_InitStruct.GPIO_Speed GPIO_Speed_50MHz; GPIO_InitStruct.GPIO_Mode GPIO_Mode_Out_PP; GPIO_Init(SEL_PORT,GPIO_InitStruct); //初始化直接关闭所有数码管 LSA_H; LSB_H; LSC_H; } void smg_display(u8 dat[],u8 pos) { u8 i0; u8 pos_temppos-1; for(ipos_temp;i8;i) { //切换当前位选 switch(i) { case 0: LSC_H;LSB_H;LSA_H;break; case 1: LSC_H;LSB_H;LSA;break; case 2: LSC_H;LSB;LSA_H;break; case 3: LSC_H;LSB;LSA;break; case 4: LSC;LSB_H;LSA_H;break; case 5: LSC;LSB_H;LSA;break; case 6: LSC;LSB;LSA_H;break; case 7: LSC;LSB;LSA;break; } //输出段码 PB8~PB15对应段码高8位需要左移8位 GPIO_Write(SMG_A_DP_PORT, (u16)gsmg_code[dat[i-pos_temp]] 8); delay_ms(1); // 延长显示延时视觉稳定 //消隐段码清零 GPIO_Write(SMG_A_DP_PORT,0x00); } // 循环结束后强制拉高所有译码输入关闭全部数码管 LSC_H; LSB_H; LSA_H; }2. SysTick.c SysTick.h 定时器代码#include SysTick.h #include public.h static u8 fac_us0; static u16 fac_ms0; void SysTick_Init(u8 SYSCLK) { SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8); fac_us SYSCLK / 8; fac_ms (u16)fac_us * 1000; } void delay_us(u32 nus) { u32 temp; SysTick-LOAD nus * fac_us; SysTick-VAL 0x00; SysTick-CTRL | SysTick_CTRL_ENABLE_Msk; do { temp SysTick-CTRL; } while((temp 0x01) !(temp (1 16))); SysTick-CTRL ~SysTick_CTRL_ENABLE_Msk; SysTick-VAL 0X00; } void delay_ms(u16 nms) { u32 temp; SysTick-LOAD (u32)nms * fac_ms; SysTick-VAL 0x00; SysTick-CTRL | SysTick_CTRL_ENABLE_Msk; do { temp SysTick-CTRL; } while((temp 0x01) !(temp (1 16))); SysTick-CTRL ~SysTick_CTRL_ENABLE_Msk; SysTick-VAL 0X00; } void delay_10us(u16 nus) { delay_us(nus * 10); } #ifndef __SYSTICK_H #define __SYSTICK_H #include public.h void SysTick_Init(u8 SYSCLK); void delay_us(u32 nus); void delay_ms(u16 nms); void delay_10us(u16 nus); #endif3.public.c publc.h公共代码#ifndef __PUBLIC_H #define __PUBLIC_H #include stm32f10x.h // 兼容STC8/51的简写类型 typedef uint8_t uint8; typedef uint16_t uint16; typedef uint32_t uint32; void delay_10us(uint16 nus); void delay_ms(uint16 nms); #endif #ifndef __PUBLIC_H #define __PUBLIC_H #include stm32f10x.h // 兼容51/STC类型别名 typedef uint8_t uint8; typedef uint16_t uint16; void delay_ms(uint16 nms); void delay_10us(uint16 nus); #endif