基于 STM32F767 的 4 位数码管显示与数字递减设计

基于 STM32F767 的 4 位数码管显示与数字递减设计

一、实验设计电路

  1. 数码管连接

在这里插入图片描述

- **段选引脚连接**:采用共阴极数码管,其段选引脚 `a - g` 分别连接到 STM32F767 开发板的 GPIOA 的 0 - 7 引脚。这种连接方式使得通过控制 GPIOA 相应引脚的电平状态,能够输出不同的段码,从而在数码管上显示出对应的数字或字符。
- **位选引脚连接**:数码管的位选引脚连接到 GPIOB 的 4 - 7 引脚。通过控制这些位选引脚的电平,可以选择要显示的具体某一位数码管,实现 4 位数码管的动态扫描显示。
- **电源连接**:共阴极数码管的公共阴极接地,以确保数码管正常工作。开发板为数码管提供合适的电源支持,一般通过开发板内部的电源管理电路将外部输入电源转换为适合数码管驱动的电平。
  1. 系统供电:使用 USB 接口或电源适配器为 STM32F767 开发板供电,开发板内部电路对输入电源进行稳压、滤波等处理后,为芯片及外部设备提供稳定的工作电压,例如为 GPIO 引脚提供 3.3V 的电平输出能力,以满足数码管的驱动要求。

三、工作原理

  1. 数码管显示原理:

    • 初始化:在 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。

四、引脚使用

  1. GPIOA 引脚(段选):
    • GPIOA Pin 0 - GPIOA Pin 7:在数码管显示过程中,这些引脚用于输出段码信号。根据要显示的数字或字符,从 seg_code 段码表中获取对应的段码值,然后通过位操作将该段码值的每一位分别输出到相应的 GPIOA 引脚。例如,当显示数字 “0” 时,段码值为 0x3F(二进制 0011 1111),则将 GPIOA Pin 0GPIOA Pin 1GPIOA Pin 2GPIOA Pin 3GPIOA Pin 4GPIOA Pin 5 设置为高电平,GPIOA Pin 6GPIOA Pin 7 设置为低电平,从而使数码管的相应段点亮,显示出数字 “0”。
  2. 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**********************/

写的好的话麻烦留个赞,需要工程私信我

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值