基于 STM32F767 的 4 位数码管显示与数字递减设计
一、实验设计电路
- 数码管连接
- **段选引脚连接**:采用共阴极数码管,其段选引脚 `a - g` 分别连接到 STM32F767 开发板的 GPIOA 的 0 - 7 引脚。这种连接方式使得通过控制 GPIOA 相应引脚的电平状态,能够输出不同的段码,从而在数码管上显示出对应的数字或字符。
- **位选引脚连接**:数码管的位选引脚连接到 GPIOB 的 4 - 7 引脚。通过控制这些位选引脚的电平,可以选择要显示的具体某一位数码管,实现 4 位数码管的动态扫描显示。
- **电源连接**:共阴极数码管的公共阴极接地,以确保数码管正常工作。开发板为数码管提供合适的电源支持,一般通过开发板内部的电源管理电路将外部输入电源转换为适合数码管驱动的电平。
- 系统供电:使用 USB 接口或电源适配器为 STM32F767 开发板供电,开发板内部电路对输入电源进行稳压、滤波等处理后,为芯片及外部设备提供稳定的工作电压,例如为 GPIO 引脚提供 3.3V 的电平输出能力,以满足数码管的驱动要求。
三、工作原理
-
数码管显示原理:
-
初始化:在
main
函数开始时,先调用SEG_Display_Init
函数对数码管相关的 GPIO 引脚进行初始化。在SEG_Display_Init
函数中,分别使能 GPIOA 和 GPIOB 的时钟,然后将 GPIOA 的 0 - 7 引脚配置为推挽输出模式(GPIO_MODE_OUTPUT_PP
),无上拉或下拉电阻(GPIO_NOPULL
),输出速度为低速(GPIO_SPEED_FREQ_LOW
),用于段码输出;将 GPIOB 的 4 - 7 引脚也配置为相同的推挽输出模式,用于位选控制。 -
显示更新:在
while(1)
无限循环中,SEG_Display_Update
函数负责数码管的显示更新操作。首先,通过
HAL_GPIO_WritePin
函数将 GPIOB 的所有位选引脚(DIGIT_GPIO_PIN_0 - DIGIT_GPIO_PIN_3
)设置为高电平,关闭所有位选,防止数码管显示出现残影等异常情况。接着,根据当前的digit_index
值从digit_value
数组中获取要显示位对应的数字值,再通过该数字值从seg_code
段码表中获取对应的段码值seg_data
。然后,使用位操作和
HAL_GPIO_WritePin
函数逐个将段码值输出到 GPIOA 的相应引脚,实现段码的正确输出。之后,根据digit_index
的值通过switch
语句选择对应的位选引脚并将其设置为低电平,从而选中要显示的数码管位。最后,digit_index
自增 1,当digit_index
大于 3 时,重新将其设置为 0,实现 4 位数码管的循环扫描显示。 -
数字递减逻辑:
Decrease_Digit_Value
函数实现了数字的逐个递减逻辑。当digit_value
数组中的所有数字都为 0 时,将其重置为{0, 0, 6, 5}
。否则,从最低位(digit_value[3]
)开始判断,如果当前位为 0,则将其设置为 9,并继续判断前一位是否为 0,依次类推,直到找到不为 0 的位并将其递减 1,从而实现数字的逐个递减功能。在main
函数的while(1)
循环中,通过delay_count
变量来控制数字递减的频率,当delay_count
大于 200 时,调用Decrease_Digit_Value
函数进行数字递减,并将delay_count
重置为 0。
-
四、引脚使用
- GPIOA 引脚(段选):
GPIOA Pin 0 - GPIOA Pin 7
:在数码管显示过程中,这些引脚用于输出段码信号。根据要显示的数字或字符,从seg_code
段码表中获取对应的段码值,然后通过位操作将该段码值的每一位分别输出到相应的 GPIOA 引脚。例如,当显示数字 “0” 时,段码值为0x3F
(二进制0011 1111
),则将GPIOA Pin 0
、GPIOA Pin 1
、GPIOA Pin 2
、GPIOA Pin 3
、GPIOA Pin 4
、GPIOA Pin 5
设置为高电平,GPIOA Pin 6
、GPIOA Pin 7
设置为低电平,从而使数码管的相应段点亮,显示出数字 “0”。
- GPIOB 引脚(位选):
GPIOB Pin 4 - GPIOB Pin 7
:这些引脚用于控制 4 位数码管的位选。在显示更新过程中,通过设置这些引脚的电平状态,可以选择要显示的具体某一位数码管。例如,当digit_index
为 0 时,将GPIOB Pin 4
设置为低电平,其他位选引脚设置为高电平,此时只有第一位数码管被选中并显示相应的数字或字符;当digit_index
为 1 时,将GPIOB Pin 5
设置为低电平,依此类推,实现 4 位数码管的动态扫描显示。
五、实验结果
经过代码的编译、下载和运行,在 4 位数码管上成功显示出从初始值 {0, 0, 6, 5}
开始逐个递减的数字。数码管显示清晰、稳定,数字递减的速度适中。
提供图片方便查找引脚【大炮打苍蝇既视感】
完整代码
#include "stm32f7xx.h"
#include "main.h"
#include "./led/bsp_led.h"
// 数码管段选引脚对应的GPIO引脚宏定义(假设连接到GPIOA的0-7引脚)
#define SEGMENT_GPIO_PORT GPIOA
#define SEGMENT_GPIO_PIN_0 GPIO_PIN_0
#define SEGMENT_GPIO_PIN_1 GPIO_PIN_1
#define SEGMENT_GPIO_PIN_2 GPIO_PIN_2
#define SEGMENT_GPIO_PIN_3 GPIO_PIN_3
#define SEGMENT_GPIO_PIN_4 GPIO_PIN_4
#define SEGMENT_GPIO_PIN_5 GPIO_PIN_5
#define SEGMENT_GPIO_PIN_6 GPIO_PIN_6
#define SEGMENT_GPIO_PIN_7 GPIO_PIN_7
// 数码管位选引脚对应的GPIO引脚宏定义(假设连接到GPIOB的4-7引脚)
#define DIGIT_GPIO_PORT GPIOB
#define DIGIT_GPIO_PIN_0 GPIO_PIN_4
#define DIGIT_GPIO_PIN_1 GPIO_PIN_5
#define DIGIT_GPIO_PIN_2 GPIO_PIN_6
#define DIGIT_GPIO_PIN_3 GPIO_PIN_7
// 用于存储4位数码管要显示的数字,每个元素对应一位数码管的数值
uint8_t digit_value[4] = {0, 0, 6, 5};
// 位选索引,用于控制当前操作的是哪一位数码管
uint8_t digit_index = 0;
// 用于延时计数,实现合适的显示刷新频率
uint32_t delay_count = 0;
void SEG_Display_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
// 使能段选引脚对应的GPIOA时钟
__HAL_RCC_GPIOA_CLK_ENABLE();
// 配置段选引脚为输出模式
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Pin = SEGMENT_GPIO_PIN_0 | SEGMENT_GPIO_PIN_1 | SEGMENT_GPIO_PIN_2 | SEGMENT_GPIO_PIN_3 |
SEGMENT_GPIO_PIN_4 | SEGMENT_GPIO_PIN_5 | SEGMENT_GPIO_PIN_6 | SEGMENT_GPIO_PIN_7;
HAL_GPIO_Init(SEGMENT_GPIO_PORT, &GPIO_InitStruct);
// 使能位选引脚对应的GPIOB时钟
__HAL_RCC_GPIOB_CLK_ENABLE();
// 配置位选引脚为输出模式
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Pin = DIGIT_GPIO_PIN_0 | DIGIT_GPIO_PIN_1 | DIGIT_GPIO_PIN_2 | DIGIT_GPIO_PIN_3;
HAL_GPIO_Init(DIGIT_GPIO_PORT, &GPIO_InitStruct);
}
// 共阴极数码管段码表,顺序为0-9
const uint8_t seg_code[10] = {
0x3F, // 0
0x06, // 1
0x5B, // 2
0x4F, // 3
0x66, // 4
0x6D, // 5
0x7D, // 6
0x07, // 7
0x7F, // 8
0x6F // 9
};
void SEG_Display_Update(void)
{
// 先关闭所有位选,防止残影等异常显示
HAL_GPIO_WritePin(DIGIT_GPIO_PORT, DIGIT_GPIO_PIN_0 | DIGIT_GPIO_PIN_1 | DIGIT_GPIO_PIN_2 | DIGIT_GPIO_PIN_3, GPIO_PIN_SET);
// 获取当前要显示位对应的数字值
uint8_t num_to_display = digit_value[digit_index];
// 根据数字值获取对应的段码
uint8_t seg_data = seg_code[num_to_display];
// 将段码输出到段选引脚
// 逐个设置段选引脚电平
HAL_GPIO_WritePin(SEGMENT_GPIO_PORT, SEGMENT_GPIO_PIN_0, (seg_data & 0x01)? GPIO_PIN_SET : GPIO_PIN_RESET);
HAL_GPIO_WritePin(SEGMENT_GPIO_PORT, SEGMENT_GPIO_PIN_1, (seg_data & 0x02)? GPIO_PIN_SET : GPIO_PIN_RESET);
HAL_GPIO_WritePin(SEGMENT_GPIO_PORT, SEGMENT_GPIO_PIN_2, (seg_data & 0x04)? GPIO_PIN_SET : GPIO_PIN_RESET);
HAL_GPIO_WritePin(SEGMENT_GPIO_PORT, SEGMENT_GPIO_PIN_3, (seg_data & 0x08)? GPIO_PIN_SET : GPIO_PIN_RESET);
HAL_GPIO_WritePin(SEGMENT_GPIO_PORT, SEGMENT_GPIO_PIN_4, (seg_data & 0x10)? GPIO_PIN_SET : GPIO_PIN_RESET);
HAL_GPIO_WritePin(SEGMENT_GPIO_PORT, SEGMENT_GPIO_PIN_5, (seg_data & 0x20)? GPIO_PIN_SET : GPIO_PIN_RESET);
HAL_GPIO_WritePin(SEGMENT_GPIO_PORT, SEGMENT_GPIO_PIN_6, (seg_data & 0x40)? GPIO_PIN_SET : GPIO_PIN_RESET);
HAL_GPIO_WritePin(SEGMENT_GPIO_PORT, SEGMENT_GPIO_PIN_7, (seg_data & 0x80)? GPIO_PIN_SET : GPIO_PIN_RESET);
// 选择当前要显示的位
switch (digit_index) {
case 0:
HAL_GPIO_WritePin(DIGIT_GPIO_PORT, DIGIT_GPIO_PIN_0, GPIO_PIN_RESET);
break;
case 1:
HAL_GPIO_WritePin(DIGIT_GPIO_PORT, DIGIT_GPIO_PIN_1, GPIO_PIN_RESET);
break;
case 2:
HAL_GPIO_WritePin(DIGIT_GPIO_PORT, DIGIT_GPIO_PIN_2, GPIO_PIN_RESET);
break;
case 3:
HAL_GPIO_WritePin(DIGIT_GPIO_PORT, DIGIT_GPIO_PIN_3, GPIO_PIN_RESET);
break;
default:
break;
}
// 更新位选索引,准备下一次显示下一位
digit_index++;
if (digit_index > 3) {
digit_index = 0;
}
}
// 实现数字逐个递减的逻辑
void Decrease_Digit_Value(void)
{
if (digit_value[0] == 0 && digit_value[1] == 0 && digit_value[2] == 0 && digit_value[3] == 0) {
digit_value[0] = 0;
digit_value[1] = 0;
digit_value[2] = 6;
digit_value[3] = 5;
} else {
if (digit_value[3] == 0) {
digit_value[3] = 9;
if (digit_value[2] == 0) {
digit_value[2] = 9;
if (digit_value[1] == 0) {
digit_value[1] = 9;
digit_value[0]--;
} else {
digit_value[1]--;
}
} else {
digit_value[2]--;
}
} else {
digit_value[3]--;
}
}
}
// 新增一个全局变量,用于指定数码管连接的GPIO端口,初始化为GPIOA,方便后续修改
int main(void)
{
/* 系统时钟初始化成216 MHz */
SystemClock_Config();
/* LED 端口初始化 */
LED_GPIO_Config();
// 初始化数码管相关GPIO引脚
SEG_Display_Init();
// 熄灭红绿蓝灯
LED1_OFF;
LED2_OFF;
LED3_OFF;
uint8_t index = 0; // 用于索引段码表的变量
while (1)
{
SEG_Display_Update();
if (delay_count++ > 200)
{
Decrease_Digit_Value();
delay_count = 0;
}
// HAL_GPIO_WritePin(DIGIT_GPIO_PORT, DIGIT_GPIO_PIN_0 , 0);//0是点亮 DIGIT_GPIO_PIN_0 是第一个数码管
// HAL_GPIO_WritePin(DIGIT_GPIO_PORT, DIGIT_GPIO_PIN_1 , 1);
// HAL_GPIO_WritePin(DIGIT_GPIO_PORT, DIGIT_GPIO_PIN_2 , 1);
// HAL_GPIO_WritePin(DIGIT_GPIO_PORT, DIGIT_GPIO_PIN_3 , 1);
//
HAL_Delay(1);
}
}
/**
* @brief System Clock 配置
* system Clock 配置如下 :
* System Clock source = PLL (HSE)
* SYSCLK(Hz) = 216000000
* HCLK(Hz) = 216000000
* AHB Prescaler = 1
* APB1 Prescaler = 4
* APB2 Prescaler = 2
* HSE Frequency(Hz) = 25000000
* PLL_M = 25
* PLL_N = 432
* PLL_P = 2
* PLL_Q = 9
* VDD(V) = 3.3
* Main regulator output voltage = Scale1 mode
* Flash Latency(WS) = 7
* @param 无
* @retval 无
*/
void SystemClock_Config(void)
{
RCC_ClkInitTypeDef RCC_ClkInitStruct;
RCC_OscInitTypeDef RCC_OscInitStruct;
HAL_StatusTypeDef ret = HAL_OK;
/* 使能HSE,配置HSE为PLL的时钟源,配置PLL的各种分频因子M N P Q
* PLLCLK = HSE/M*N/P = 25M / 25 *432 / 2 = 216M
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLM = 25;
RCC_OscInitStruct.PLL.PLLN = 432;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
RCC_OscInitStruct.PLL.PLLQ = 9;
ret = HAL_RCC_OscConfig(&RCC_OscInitStruct);
if(ret != HAL_OK)
{
while(1) { ; }
}
/* 激活 OverDrive 模式以达到216M频率 */
ret = HAL_PWREx_EnableOverDrive();
if(ret != HAL_OK)
{
while(1) { ; }
}
/* 选择PLLCLK作为SYSCLK,并配置 HCLK, PCLK1 and PCLK2 的时钟分频因子
* SYSCLK = PLLCLK = 216M
* HCLK = SYSCLK / 1 = 216M
* PCLK2 = SYSCLK / 2 = 108M
* PCLK1 = SYSCLK / 4 = 54M
*/
RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2);
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;
/* 在HAL_RCC_ClockConfig函数里面同时初始化好了系统定时器systick,配置为1ms中断一次 */
ret = HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_7);
if(ret != HAL_OK)
{
while(1) { ; }
}
}
/*********************************************END OF FILE**********************/
写的好的话麻烦留个赞,需要工程私信我