当前位置:首页>java>STM32"零代码”控制数码管显示数字

STM32"零代码”控制数码管显示数字

  • 2026-10-16 08:46:31
STM32"零代码”控制数码管显示数字

一、 数码管场景应用

消费电子领域:以 1-4 位 8 段共阴 / 共阳数码管为主,核心用于小家电、便携数码产品等,显示温度、时间、电量、数值等极简信息,适配低成本、小型化需求,多为贴片 / 小型插件式,采用动态扫描驱动,亮度高、强光下易读,可与 LED 指示灯搭配实现复合显示。

工业场景:以工业级 8/16 段数码管、4-8 位联排款为主,耐高低温、抗干扰振动,适配工控复杂环境,广泛用于仪器仪表、电气自动化设备、机床、流水线等,现场直显运行参数、故障代码、计数数值等,多为插件式加固封装,静态驱动保障显示稳定,常搭配专用驱动芯片 / 模块,易对接工控系统。

其他场景:此外,数码管还广泛应用于车载交通(车载仪表显车速 / 油量、充电桩显功率 / 电量、公交 / 地铁显站点 / 票价、停车场计费屏)、商用零售(收银机 / 点钞机显金额、奶茶机 / 咖啡机显档位 / 杯数、自助售货机显价格 / 库存)、民生安防(门禁 / 道闸显编号 / 时间、燃气 / 水表显用量、烟雾 / 气体检测仪显浓度 / 报警值)、文教健身(计时器 / 计分牌显时间 / 分数、健身器材显数据、电子琴显档位 / 节奏)等场景,均以极简数字直显为核心,适配各场景快速读数、高性价比需求。

二、 数码管的工作原理

三、 数码管驱动电路

数码管驱动电路是实现单片机 / PLC 等主控器件与数码管之间信号、功率匹配的核心电路,核心作用为提供足够驱动电流、控制各段亮灭逻辑,适配数码管共阴 / 共阳的引脚结构,按驱动方式主要分为静态驱动和动态扫描驱动两类,还可通过专用驱动芯片简化电路设计,适配不同位数、应用场景的需求,整体电路设计遵循低成本、少 I/O 占用、显示稳定原则。
数码管分共阴极和共阳极,单段点亮需约 5-20mA 电流,主控 I/O 口输出电流不足,需搭配三极管 / 场效应管 / 限流电阻做电流放大、限流,避免器件烧毁。
为减少主控资源占用、简化电路,主流方案会搭配专用数码管驱动芯片,典型芯片:TM1637(I2C,内置扫描,适配 1-4 位 8 段)、74HC595(串行移位,拓展 I/O,多片级联实现多位驱动)、MAX7219(SPI,适配 8 位 8 段,支持亮度调节)。

四、绘制STM32驱动单数码管原理图

本文使用STM32控制一个7段共阴数码管来显示1位数字。
元件清单:

序号

名称

型号

数量

Lib名称

1

单片机
stm32f103c8
1
STM32F103C8

2

数码管驱动芯片
74hc245
1
74hc245
3
共阴
数码管

7SEG-MPX1-CC

1

7SEG-MPX1-CC

*数码管型号中,共阴为CC(Common Cathode),共阳为CA(Common Anode)。
原理图如下(为了原理图画图在proteus中排版方便):
通过下边的方式可以简单判断,proteus中数码管模型引脚顺序,下图可以判断,数码管仿真模型引脚左一引脚是a段。
在原理图中,为了画图、排版方便,可以对元件模型进行旋转、镜像操作。

五、 74HC245简介

74HC245兼容TTL器件引脚的高速CMOS总线收发器(bustransceiver),典型的CMOS型三态缓冲门电路,八路信号收发器。由于单片机或CPU的数据/地址/控制总线端口都有一定的负载能力,如果负载超过其负载能力,一般应加驱动器。主要应用于大屏显示,以及其它的消费类电子产品中增加驱动。
管脚定义:74HC245的引脚包括输出使能(OE),方向控制(DIR),A组输入/输出(A0-A7),B组输入/输出(B0-B7),电源(VDD)和地(GND)。
管脚说明
逻辑图:74HC245的逻辑图可以形象地展示其逻辑功能,只有当输出使能OE为0时,芯片才能被使能,不然芯片所有输入输出端口处于高阻态(既不是高电平也不是低电平)。
74HC245真值表
真值表是在逻辑中使用的一类数学表,用来确定一个表达式是否为真或有效。H代表高电平,L代表低电平,X代表随机电平(可高可低)

六、 编程

首先复制上一个例程中的程序到一个新的文件夹,修改文件夹名字为:D04_STM32_7seg_x1。

接下来,有请豆包:
使用stm32f103一位共阴驱动数码管显示数字:1)使用hal库函数;2)PB0~PB6连接数码管的a~g段;3)循环显示数字0~9,时间间隔1秒;4)数字显示使用单独一个函数,参数使用要显示的数字;5)时钟使用HSI,hclk配置为64MHz。
本次生成的程序中,直接引用了头文件,stm32f1xx_hal.h 。
*这也是AI在使用中的“弊端”,必须给够足够的上下文条件,A*I生成程序才能使用
但是下边的代码里有多处错误
报错原因:在前文的例子中,Error_Handler()函数在main.h声明,因此在main.c文件中,可以放在文件的最下边实现Error_Handler()函数的具体功能。
但是在本例中,直接使用A*I生成程序,提示词中少了一些前置条件,所以生成的程序会有一些问题。
处理方式1:在生成的程序中,将Error_Handler()函数的声明放在main.c文件的顶部位置。
处理方式2:将引用的头文件更换成main.h。
两种方式都可以消除程序中的错误❌️。
这里选用第二种。
最后main.c完整程序如下:
#include "main.h"// 共阴数码管段码表(0~9),bit0=a(PB0), bit1=b(PB1)...bit6=g(PB6)const uint8_t seg_code[10] = { 0x3F, // 0: a,b,c,d,e,f亮 0x06, // 1: b,c亮 0x5B, // 2: a,b,g,e,d亮 0x4F, // 3: a,b,g,c,d亮 0x66, // 4: f,g,b,c亮 0x6D, // 5: a,f,g,c,d亮 0x7D, // 6: a,f,g,c,d,e亮 0x07, // 7: a,b,c亮 0x7F, // 8: 全亮 0x6F // 9: a,b,c,d,f,g亮};/** * @brief 系统时钟配置:HSI 8MHz -> PLL 64MHz,HCLK=64MHz * @param 无 * @retval 无 */void SystemClock_Config(void){ RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; // 配置HSI和PLL RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI; // 使用内部HSI时钟 RCC_OscInitStruct.HSIState = RCC_HSI_ON; // 开启HSI RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT; // HSI默认校准值(8MHz) RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; // 开启PLL RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI_DIV2; // HSI分频2(8MHz/2=4MHz)作为PLL输入 RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL16; // PLL倍频16(4MHz×16=64MHz) if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { Error_Handler(); } // 配置系统时钟源和总线分频 RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; // 系统时钟=PLL(64MHz) RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; // AHB分频1 → HCLK=64MHz RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2; // APB1分频2 → PCLK1=32MHz RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; // APB2分频1 → PCLK2=64MHz // 配置FLASH延迟(64MHz需要2个等待周期) if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK) { Error_Handler(); }}/** * @brief 初始化PB0~PB6为推挽输出模式 * @param 无 * @retval 无 */void GPIO_Init_Display(void){ GPIO_InitTypeDef GPIO_InitStruct = {0}; // 使能GPIOB时钟(APB2总线,时钟64MHz) __HAL_RCC_GPIOB_CLK_ENABLE(); // 配置PB0~PB6为推挽输出,速度50MHz GPIO_InitStruct.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 |  GPIO_PIN_3 | GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_6; 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); // 初始低电平,数码管熄灭 HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3| GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6, GPIO_PIN_RESET);}/** * @brief 显示指定数字(0~9),参数为要显示的数字 * @param num: 0~9(超出范围则熄灭数码管) * @retval 无 */void Display_Number(uint8_t num){ uint8_t code = 0; // 校验数字范围,仅处理0~9 if(num < 10) { code = seg_code[num]; // 匹配对应段码 } // 逐引脚设置电平(高电平点亮共阴数码管对应段) HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, (code & 0x01) ? GPIO_PIN_SET : GPIO_PIN_RESET); // a段 HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, (code & 0x02) ? GPIO_PIN_SET : GPIO_PIN_RESET); // b段 HAL_GPIO_WritePin(GPIOB, GPIO_PIN_2, (code & 0x04) ? GPIO_PIN_SET : GPIO_PIN_RESET); // c段 HAL_GPIO_WritePin(GPIOB, GPIO_PIN_3, (code & 0x08) ? GPIO_PIN_SET : GPIO_PIN_RESET); // d段 HAL_GPIO_WritePin(GPIOB, GPIO_PIN_4, (code & 0x10) ? GPIO_PIN_SET : GPIO_PIN_RESET); // e段 HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5, (code & 0x20) ? GPIO_PIN_SET : GPIO_PIN_RESET); // f段 HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, (code & 0x40) ? GPIO_PIN_SET : GPIO_PIN_RESET); // g段}/** * @brief 错误处理函数(死循环) * @param 无 * @retval 无 */void Error_Handler(void){ while(1) { // 时钟配置失败时可添加LED闪烁等提示 }}/** * @brief 主函数:循环显示0~9,间隔1秒 * @param 无 * @retval 无 */int main(void){ // 1. HAL库初始化(初始化SysTick,用于HAL_Delay) HAL_Init(); // 2. 配置系统时钟为HSI→64MHz SystemClock_Config(); // 3. 初始化数码管GPIO GPIO_Init_Display(); uint8_t disp_num = 0; // 显示数字变量 while(1) { Display_Number(disp_num); // 显示当前数字 HAL_Delay(1000); // 延时1秒(基于SysTick,时钟64MHz下准确) disp_num = (disp_num + 1) % 10; // 数字循环0~9 }}#ifdef USE_FULL_ASSERTvoid assert_failed(uint8_t *file, uint32_t line){ // 断言失败处理(可选)}#endif
删掉注释后,main.h文件如下:
/* Define to prevent recursive inclusion -------------------------------------*/#ifndef __MAIN_H#define __MAIN_H#ifdef __cplusplusextern "C" {#endif/* Includes ------------------------------------------------------------------*/#include"stm32f1xx_hal.h"/* Exported functions prototypes ---------------------------------------------*/voidError_Handler(void);#ifdef __cplusplus}#endif#endif/* __MAIN_H */

七 运行结果

已关注
关注
重播 分享 赞

八、程序说明

数码管字形编码定义:
const uint8_t seg_code[10]
七段数码管原理上就是将LED灯排列成
字形状后,根据数字形状,显示对应LED段的过程。
因此,显示0~9分别对应10个编码,可以提前使用数组定义这个编码。
这里涉及到一个语法知识:

C 数组

可以点击文末“阅读原文”,打开C数组链接进一步学习。
GPIO引脚初始化:GPIO_Init_Display()
对PB0~PB6初始化为推完输出,无上拉电阻和下拉电阻。
数码管显示函数:void Display_Number(uint8_t num)
图中,第90行从数组中取对应数字的字形编码。
L94~L100依次从字形编码中取出每一个二进制位,根据0/1,输出到对应引脚。
code & 0x04的执行过程
?运算符全称是条件运算符,也叫三目运算符,因为它需要 3 个操作数,语法格式如下:
条件表达式 ? 表达式1: 表达式2;

执行逻辑:先判断条件表达式的真假核心作用:替代简单的if-else语句,让代码更简洁。:

如果为真(非 0),执行表达式1,并将其结果作为整个表达式的返回值;

如果为假(0),执行表达式2,并将其结果作为整个表达式的返回值。

核心作用:替代简单的if-else语句,让代码更简洁。

因此,(code & 0x04) ? GPIO_PIN_SET : GPIO_PIN_RESET)代码的执行过程就是,先计算code & 0x04的值,根据计算结果,再使用“?”运算符,返回GPIO_PIN_SET或者GPIO_PIN_RESET。
而在库函数中,它们的定义如下:

本合集程序及原理图已上传代码托管平台,可以在本公众号聊天窗口回复“下载”,获取下载链接。

---------------END---------------

*本文中数码管工作原理视频来源于B站up主——爱上半导体,如有侵权,可联系本人删除。

最新文章

随机文章