STM32SPI实验:外部Flash掉电记忆实验---STM32 HAL库专栏

🎬 渡水无言个人主页渡水无言

专栏传送门 : 《linux专栏》《嵌入式linux驱动开发》《linux系统移植专栏》

专栏传送门 : 《freertos专栏》 《STM32 HAL库专栏》《linux裸机开发专栏

专栏传送门《产品测评专栏

⭐️流水不争先,争的是滔滔不绝

📚博主简介:第二十届中国研究生电子设计竞赛全国二等奖 |国家奖学金 | 省级三好学生

| 省级优秀毕业生获得者 | csdn新星杯TOP18 | 半导纵横专栏博主 | 211在读研究生

在这里主要分享自己学习的linux嵌入式领域知识;有分享错误或者不足的地方欢迎大佬指导,也欢迎各位大佬互相三连

目录

前言

一、外部flash实验

1.1、Flash模块介绍

1.2、硬件连线图

1.3、实验预期效果

[1.4、CubeMX 完整配置步骤](#1.4、CubeMX 完整配置步骤)

1.4.1、配置SPI接口

1.4.2项目管理与生成

二、代码编写

[2.1、SPI HAL 库核心接口函数](#2.1、SPI HAL 库核心接口函数)

[2.1.1、 仅发送数据:HAL_SPI_Transmit](#2.1.1、 仅发送数据:HAL_SPI_Transmit)

2.1.2、仅接收数据:HAL_SPI_Receive

2.1.3、全双工通信:HAL_SPI_TransmitReceive

2.2、flash写入过程

2.3、正式编程思路

1、声明一个函数用来保存LED的亮灭状态

[2、从 Flash 加载 LED 状态:SaveLoadLEDState](#2、从 Flash 加载 LED 状态:SaveLoadLEDState)

[3、调用SaveLEDState 保存LED当前状态](#3、调用SaveLEDState 保存LED当前状态)

4、加载LED状态

main.c总代码

总结


前言

上一期博客我们完成了对SPI 总线的介绍,这期博客我们完成一个外部flash实验,具体功能为:将LED的亮灭状态保存在flash中,使得开发板断电再重新上电后,灯的亮灭状态不改变。


一、外部flash实验

实验总体介绍:将LED的亮灭状态保存在flesh中,使得开发板断电再重新上电后,灯的亮灭状态不改变。

1.1、Flash模块介绍

Flesh模块相当于一块移动硬盘,存储在这里的数据,掉电后不会丢失。

我们这个实验使用的器件为W25Qxx。

W25Qxx系列是一种低成本、小型化、使用简单的非易失性存储器,常应用于数据存储、字库存储、固件程序存储等场景。

1.2、硬件连线图

1.3、实验预期效果

将LED的亮灭状态保存在flesh中,使得开发板断电再重新上电后,灯的亮灭状态不改变。

1.4、CubeMX 完整配置步骤

打开上一小节完成的cubemx

1.4.1、配置SPI接口

打开connectivity可以看到有两个SPI接口,随便选一个就行。

比如我们这里选SPI1

然后再选择SPI的工作模式:

常从机编程比主机更加复杂,绝大多数情况下,选择第一种

这个时候,会出现一个新选项:

此时右侧可以看到已经被分配好的三个引脚DI,CLK.DO:

此时我们可以看到PA4要我们手动设置一下,设置为通用推挽模式

我们还要继续配置一下SPI的参数,配置如下所示

Basic Parameters:

帧格式为 Motorola 标准,

数据位长度 8 Bits(与外部 Flash 单字节传输要求一致),

数据传输顺序为 MSB First。

Clock Parameters:波特率预分频为 8,实际通信速率为 1000.0 KBits/s(1 MHz,兼顾稳定性与 Flash 芯片的速度上限);

时钟极性 CPOL 为 High(空闲时 SCK 为高电平),时钟相位 CPHA 为 2 Edge(在第二个时钟边沿采样数据),共同构成 SPI 模式 3,需与 Flash 芯片手册的时序要求严格匹配。

Advanced Parameters:关闭 CRC 校验(Flash 读写场景无需额外校验)

NSS 信号类型为 Software(软件控制片选引脚 PA4,便于手动拉低 / 拉高以选中 / 取消选中 Flash 芯片)。

引脚分配:

PA4 作为 GPIO_Output 手动控制片选,

PA5、PA6、PA7 分别复用为 SPI1_SCK、SPI1_MISO、SPI1_MOSI,完整覆盖 SPI 通信所需的 4 根核心信号线,与外部 Flash 模块的接线一一对应。

1.4.2项目管理与生成

给项目取名、设置位置、选择开发的工具链,再点击GENERATE CODE生成

二、代码编写

2.1、SPI HAL 库核心接口函数

在 STM32 HAL 库中,SPI 通信主要依赖 3 个核心接口,我们将基于这三个接口实现 Flash 的读写操作:

函数接口 作用
HAL_SPI_Transmit(...) 仅发送数据(主机→从机)
HAL_SPI_Receive(...) 仅接收数据(从机→主机)
HAL_SPI_TransmitReceive(...) 全双工通信:发送数据的同时接收数据

2.1.1、 仅发送数据:HAL_SPI_Transmit

函数原型:

复制代码
HAL_StatusTypeDef HAL_SPI_Transmit(SPI_HandleTypeDef *hspi,
                                    uint8_t *pData,
                                    uint16_t Size,
                                    uint32_t Timeout)

hspi:SPI 句柄指针(CubeMX 自动生成,如&hspi1);

pData:要发送的数据缓冲区指针;

Size:要发送的数据长度(单位:字节);

Timeout:超时时间(单位:ms),HAL_MAX_DELAY表示无限等待。

示例:向从机 1 发送 2 字节数据

复制代码
uint8_t dataToSend[] = {0x5a, 0x33}; // 要发送的数据
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET); // 拉低NSS,选中从机1
HAL_SPI_Transmit(&hspi1, dataToSend, 2, HAL_MAX_DELAY); // 发送2字节
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);   // 拉高NSS,取消选中

2.1.2、仅接收数据:HAL_SPI_Receive

函数原型:

复制代码
HAL_StatusTypeDef HAL_SPI_Receive(SPI_HandleTypeDef *hspi,
                                   uint8_t *pData,
                                   uint16_t Size,
                                   uint32_t Timeout)
  • hspi:SPI 句柄指针;
  • pData:接收数据的缓冲区指针;
  • Size:要接收的数据长度(单位:字节);
  • Timeout:超时时间。

示例:从从机 1 接收 2 字节数据

复制代码
uint8_t dataRcvd[] = {0xFF, 0xFF}; // 接收缓冲区,初始化为0xFF(保持MOSI高电平)
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET); // 选中从机1
HAL_SPI_Receive(&hspi1, dataRcvd, 2, HAL_MAX_DELAY); // 接收2字节
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);   // 取消选中

2.1.3、全双工通信:HAL_SPI_TransmitReceive

发送数据的同时接收数据,函数原型:

复制代码
HAL_StatusTypeDef HAL_SPI_TransmitReceive(SPI_HandleTypeDef *hspi,
                                          uint8_t *pTxData,
                                          uint8_t *pRxData,
                                          uint16_t Size,
                                          uint32_t Timeout)
  • hspi:SPI 句柄指针;
  • pTxData:要发送的数据缓冲区指针;
  • pRxData:接收数据的缓冲区指针;
  • Size:发送 / 接收的数据长度(两者必须相等);
  • Timeout:超时时间。

示例:发送 2 字节的同时接收 2 字节

复制代码
uint8_t txData[] = {0x5a, 0x33}; // 要发送的数据
uint8_t rxData[2];               // 接收缓冲区
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET); // 选中从机1
HAL_SPI_TransmitReceive(&hspi1, txData, rxData, 2, HAL_MAX_DELAY); // 全双工通信
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);   // 取消选中

此函数完美适配 SPI 全双工特性,在发送指令的同时接收 Flash 返回的数据,是 Flash 读写操作的核心接口。

2.2、flash写入过程

2.3、正式编程思路

1、声明一个函数用来保存LED的亮灭状态

复制代码
static  void SaveLEDState(uint8_t ledstate);

Flash 底层操作:LoadLEDState(读取 LED 状态)、SaveLEDState(保存 LED 状态);

按键消抖与 LED 控制:通过状态对比捕获按键松开瞬间,实现 LED 翻转;

上电初始化:从 Flash 加载 LED 状态,实现掉电记忆。

2、从 Flash 加载 LED 状态:SaveLoadLEDState

从 W25Qxx Flash 的 0x000000 地址读取 1 字节数据(LED 状态:0 = 熄灭,1 = 点亮)。

复制代码
static void SaveLEDState(uint8_t ledstate)
{
 
// 1 .写使能
    查找芯片手册第20页: 发送数据0x06(参考编程接口)
  // 2 .扇区擦除
  手册35页:发送数据0x20(参考编程接口)
uint8_t sectorEraseCmd[] = {0x20 , 0x00 ,  0x00 , 0x00}
0x20:指令码 , 后面3个0x00为24位扇区首地址
  // 3 .延迟100ms
  // 4 .写使能
  // 5 .页编程
手册33页,指令码为0x02
uint8_t pageProgCmd[5] ;
pageProgCmd[0] = 0x02 ; //指令码
//24位地址
pageProgCmd[1] = pageProgCmd[2] = pageProgCmd[3] = 0 ;
//写入LED亮灭状态
pageProgCmd[4] = ledstate;
再进行发送。
  // 6 .延迟10ms
}

3、调用SaveLEDState 保存LED当前状态

4、加载LED状态

复制代码
static uint8_t LoadLEDState(void);  //申明加载状态函数,写在代码54行即上次声明的函数下面。
//实现函数:手册23页
读取数据的指令码为0x03
 
static uint8_t LoadLEDState(void)
{
   uint8_t readDataCmd[]={0x03,0x00,0x00,0x00};
   uint8_t ledstate= 0xff;//用来接收读出的数据,即LED状态
   //拉低NSS
   HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_RESET);
   HAL_SPI_Transmit(&hspi1 , readDataCmd , 4 , HAL_MAX_DELAY);  //发送数据
   HAL_SPI_Transmit(&hspi1 ,  &ledstate , 1 , HAL_MAX_DELAY);
   //拉高NSS
   HAL_GPIO_WritePin(GPIOA , GPIO_PIN_4 , GPIO_PIN_SET);
   return ledstate;// 返回读取到的数据
}

main.c总代码

cpp 复制代码
/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  *                  功能:按键控制LED翻转,LED状态保存到W25Qxx SPI Flash,掉电不丢失
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2025 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */

/* Includes ------------------------------------------------------------------*/
#include "main.h"   // HAL库核心头文件
#include "spi.h"    // SPI外设驱动头文件
#include "gpio.h"   // GPIO外设驱动头文件

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
// 无额外头文件需要引入
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
// 无自定义类型
/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
// 硬件相关宏定义(可选,增强代码可读性)
#define NSS_PIN     GPIO_PIN_4    // Flash片选引脚:PA4
#define NSS_PORT    GPIOA         // 片选引脚端口
#define KEY_PIN     GPIO_PIN_0    // 按键引脚:PA0
#define KEY_PORT    GPIOA         // 按键引脚端口
#define LED_PIN     GPIO_PIN_13   // LED引脚:PC13
#define LED_PORT    GPIOC         // LED引脚端口
/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
// 无自定义宏
/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
// 无全局私有变量
/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);  // 系统时钟配置函数声明(HAL库自动生成)
/* USER CODE BEGIN PFP */
static void SaveLEDState(uint8_t ledstate);  // 声明:保存LED状态到Flash
static uint8_t LoadLEDState(void);           // 声明:从Flash加载LED状态
/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

// 重复声明(兼容代码格式,实际可删除)
static uint8_t LoadLEDState(void);  

/**
 * @brief  从W25Qxx Flash读取LED状态
 * @note   读取地址:0x000000,仅读取1字节数据
 * @param  无
 * @retval uint8_t: 读取到的LED状态(0=熄灭,1=点亮,0xFF=读取异常)
 */
static uint8_t LoadLEDState(void)
{
   // 读数据指令:0x03 + 24位地址(0x000000),参考W25Qxx手册23页
   uint8_t readDataCmd[]={0x03,0x00,0x00,0x00};
   uint8_t ledstate= 0xff;  // 接收缓冲区,初始值0xFF(SPI接收时MOSI保持高电平)
   
   // 1. 拉低NSS引脚,选中Flash芯片
   HAL_GPIO_WritePin(NSS_PORT, NSS_PIN, GPIO_PIN_RESET);
   
   // 2. 发送读指令+地址(4字节)
   HAL_SPI_Transmit(&hspi1 , readDataCmd , 4 , HAL_MAX_DELAY);  
   
   // 3. 接收1字节数据(LED状态):HAL库无纯接收,通过发送0xFF实现
   HAL_SPI_Transmit(&hspi1 , &ledstate , 1 , HAL_MAX_DELAY);
   
   // 4. 拉高NSS引脚,取消选中Flash
   HAL_GPIO_WritePin(NSS_PORT, NSS_PIN, GPIO_PIN_SET);
   
   // 5. 返回读取到的LED状态
   return ledstate;
}

/**
 * @brief  将LED状态写入W25Qxx Flash
 * @note   写入地址:0x000000,流程:写使能→扇区擦除→写使能→页编程
 * @param  ledstate: 要保存的LED状态(0=熄灭,1=点亮)
 * @retval 无
 */
static void SaveLEDState(uint8_t ledstate)
{
 // 步骤1:写使能(Flash写入/擦除前必须执行,指令0x06)
 uint8_t writeEnableCmd[] = {0x06};
 HAL_GPIO_WritePin(NSS_PORT, NSS_PIN, GPIO_PIN_RESET);  // 选中Flash
 HAL_SPI_Transmit(&hspi1 , writeEnableCmd , 1 , HAL_MAX_DELAY); // 发送写使能指令
 HAL_GPIO_WritePin(NSS_PORT, NSS_PIN, GPIO_PIN_SET);     // 取消选中

 // 步骤2:扇区擦除(指令0x20 + 24位地址,擦除4KB扇区)
 uint8_t sectorEraseCmd[] = {0x20,0x00,0x00,0x00};
 HAL_GPIO_WritePin(NSS_PORT, NSS_PIN, GPIO_PIN_RESET);   // 选中Flash
 HAL_SPI_Transmit(&hspi1 , sectorEraseCmd , 4 , HAL_MAX_DELAY); // 发送擦除指令
 HAL_GPIO_WritePin(NSS_PORT, NSS_PIN, GPIO_PIN_SET);      // 取消选中
 HAL_Delay(100); // 擦除耗时约100ms,必须等待完成

 // 步骤3:再次写使能(擦除后写使能位自动清零)
 HAL_GPIO_WritePin(NSS_PORT, NSS_PIN, GPIO_PIN_RESET);   // 选中Flash
 HAL_SPI_Transmit(&hspi1 , writeEnableCmd , 1 , HAL_MAX_DELAY); // 发送写使能指令
 HAL_GPIO_WritePin(NSS_PORT, NSS_PIN, GPIO_PIN_SET);      // 取消选中

 // 步骤4:页编程(指令0x02 + 24位地址 + 数据,写入1字节)
 uint8_t pageProgCmd[5] ;
 pageProgCmd[0] = 0x02 ;        // 页编程指令码
 pageProgCmd[1] = 0x00 ;        // 地址高位
 pageProgCmd[2] = 0x00 ;        // 地址中位
 pageProgCmd[3] = 0x00 ;        // 地址低位
 pageProgCmd[4] = ledstate;     // 要保存的LED状态
 HAL_GPIO_WritePin(NSS_PORT, NSS_PIN, GPIO_PIN_RESET);   // 选中Flash
 HAL_SPI_Transmit(&hspi1 , pageProgCmd, 5 , HAL_MAX_DELAY); // 发送编程指令+数据
 HAL_GPIO_WritePin(NSS_PORT, NSS_PIN, GPIO_PIN_SET);      // 取消选中
 HAL_Delay(10);  // 写入耗时约10ms,等待完成
}

/* USER CODE END 0 */

/**
  * @brief  应用程序入口函数
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */
  // 无初始化前置操作
  /* USER CODE END 1 */

  /* MCU初始化:复位外设、初始化Flash、SysTick */
  HAL_Init();

  /* USER CODE BEGIN Init */
  // 无自定义初始化
  /* USER CODE END Init */

  /* 配置系统时钟(HSI时钟,主频8MHz) */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */
  // 无系统初始化扩展
  /* USER CODE END SysInit */

  /* 初始化所有配置的外设:GPIO、SPI1 */
  MX_GPIO_Init();
  MX_SPI1_Init();

  /* USER CODE BEGIN 2 */
  uint8_t pre = 1, cur = 1;  // 按键状态变量:pre=前状态,cur=当前状态(1=松开,0=按下)
  uint8_t ledstate = 0;      // LED状态变量(0=熄灭,1=点亮)
  
  // 上电后从Flash加载LED状态,实现掉电记忆
  ledstate = LoadLEDState();
  if (ledstate == 1)
  {
     // 加载状态为1:点亮LED(PC13低电平点亮)
     HAL_GPIO_WritePin(LED_PORT, LED_PIN, GPIO_PIN_RESET);
  }
  else
  {
     // 加载状态为0/异常:熄灭LED(PC13高电平熄灭)
     HAL_GPIO_WritePin(LED_PORT, LED_PIN, GPIO_PIN_SET);
  }
  /* USER CODE END 2 */

  /* 主循环 */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
     // 步骤1:更新按键前状态
     pre = cur ; 
     
     // 步骤2:读取按键引脚电平,更新当前状态
     if( HAL_GPIO_ReadPin(KEY_PORT, KEY_PIN) == GPIO_PIN_SET)
     { 
        cur = 1; // 按键松开(PA0上拉输入,高电平)
     }
     else
     {        
        cur = 0; // 按键按下(PA0低电平)
     } 
     
     // 步骤3:检测到按键状态变化(按下/松开)
     if (pre != cur)  
     {
        HAL_Delay(10); // 按键消抖:过滤机械抖动(10ms)
        
        if (cur == 0)   // 按键按下瞬间:无操作
        { }
        else            // 按键松开瞬间:翻转LED状态
        {
          if(ledstate == 1)       // 当前LED点亮→熄灭
          {
             HAL_GPIO_WritePin(LED_PORT, LED_PIN, GPIO_PIN_SET);    
             ledstate = 0;  // 更新LED状态变量
          }
          else                   // 当前LED熄灭→点亮
          {
             HAL_GPIO_WritePin(LED_PORT, LED_PIN, GPIO_PIN_RESET);   
             ledstate = 1;  // 更新LED状态变量
          }
          SaveLEDState(ledstate);   // 将新状态保存到Flash
        }
     }
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

/**
  * @brief  系统时钟配置函数
  * @note   配置HSI为系统时钟,主频8MHz
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  // 初始化RCC振荡器:HSI开启,无PLL
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }

  // 初始化CPU、AHB、APB总线时钟
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK)
  {
    Error_Handler();
  }
}

/* USER CODE BEGIN 4 */
// 无扩展函数
/* USER CODE END 4 */

/**
  * @brief  错误处理函数
  * @note   发生HAL库错误时进入死循环
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  __disable_irq();
  while (1)
  {
  }
  /* USER CODE END Error_Handler_Debug */
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  断言失败处理函数
  * @param  file: 源文件名指针
  * @param  line: 断言失败的行号
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
  /* USER CODE BEGIN 6 */
  /* 可添加打印:printf("Assert failed: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

总结

上一期博客我们完成了对SPI 总线的介绍,这期博客我们完成一个外部flash实验,具体功能为:将LED的亮灭状态保存在flash中,使得开发板断电再重新上电后,灯的亮灭状态不改变。

相关推荐
LCG元15 小时前
STM32项目开发:基于CAN总线的多节点通信与数据采集系统
stm32·单片机·嵌入式硬件
12.=0.16 小时前
【stm32_2.1】【快速入门】自举模式、Flash闪存、LED点灯——对二极管PN结解析
stm32·单片机·嵌入式硬件
辰哥单片机设计16 小时前
STM32智能风扇(机智云)
stm32·单片机·嵌入式硬件
【 STM32开发 】16 小时前
【STM32 + CubeMX】低功耗 -- SLEEP 睡眠模式
stm32·单片机·低功耗·sleep·睡眠模式
小白橘颂17 小时前
【C语言】基础概念梳理(一)
c语言·开发语言·stm32·单片机·mcu·物联网·51单片机
aini_lovee18 小时前
SIM7600模块STM32控制程序
stm32·单片机·嵌入式硬件
jianqiang.xue21 小时前
ESP32-S3 运行 Linux 全指南:从 RISC-V 模拟器移植到 8 秒快速启动
linux·stm32·单片机·mongodb·risc-v·esp32s3
busideyang21 小时前
STC8H单片机delay_ms函数闪烁不准?原因是参数溢出!
c语言·单片机·嵌入式硬件·嵌入式
Hello_Embed21 小时前
LVGL 入门(十五):接口优化
前端·笔记·stm32·单片机·嵌入式