当前位置: 首页 > article >正文

STM32——HAL库开发笔记16(SPI外部flash实验2)(参考来源:b站铁头山羊)

本次实验承接上一节实验,对后续代码进行编写,重点介绍SPI的数据读写。

四、编程接口

1、 HAL StatusTypeDef HAL_SPI_Transmit(SPI_HandleTypeDef *hspi , 
                                       uint8_t *pData , 
                                       uint16_t Size ,
                                       uint32_t Timeout)

   作用:发送数据

   参数说明:hspi :填写SPI句柄的指针  将句柄取地址&hspi1
            pData : 填写要发送的数据
            Size :填写要发送的数据的数量,以字节为单位。
            Timeout :填写超时时间,单位是ms。
                     填写HAL_MAX_DELAY :表示无限长超时时间

例:主机向从机1发送 0x5a , 0x33
    step1: 申明一个数组存取要发送的数据
           uint8_t dataToSend[] = {0x5a , 0x33};
    step2:第二边沿采集 相对应的NSS引脚写入低电压
          HAL_GPIO_WritePin(       ,GPIO_PIN_RESET);
    step3:发送数据
           HAL_SPI_Transmit(&hspi1 , dataToSend , 2 , HAL_MAX_DELAY);
    step4:写入完成,将对应NSS电平高
           HAL_GPIO_WritePin(       ,GPIO_PIN_SET);

-----------------------------------------------------------------------------------------
 
2、HAL StatusTypeDef HAL SPI Receive(SPI_HandleTypeDef *hspi , 
                                       uint8_t *pData , 
                                       uint16_t Size ,
                                       uint32_t Timeout)

   作用:接收数据

   参数说明:hspi :填写SPI句柄的指针  将句柄取地址&hspi1
            pData : 填写接收缓冲区指针
            Size :填写要接收的数据的数量,以字节为单位。
            Timeout :填写超时时间,单位是ms。
                     填写HAL_MAX_DELAY :表示无限长超时时间

例:从从机1接收两个字节数据
    step1: 申明一个数组作为接收缓冲区,赋初始值0xff,发送高电压,不赋初值会赋一些随机值。
           uint8_t dataRcvd[] = {0xff , 0xff};
    step2:第二边沿采集 相对应的NSS引脚写入低电压
          HAL_GPIO_WritePin(       ,GPIO_PIN_RESET);
    step3:发送数据
           HAL_SPI_Receive(&hspi1 , dataRcvd , 2 , HAL_MAX_DELAY);
    step4:写入完成,将对应NSS电平高
           HAL_GPIO_WritePin(       ,GPIO_PIN_SET);

-----------------------------------------------------------------------------------------


3、HAL StatusTypeDef HAL_SPI_TransmitReceive(SPI_HandleTypeDef *hspi , 
                                       uint8_t *pTxData , 
                                       uint8_t *pRxData , 
                                       uint16_t Size ,
                                       uint32_t Timeout)

   作用:发送数据的同时接收数据
   参数说明:hspi :填写SPI句柄的指针  将句柄取地址&hspi1
            pTxData :填写要发送的数据
            pRxData :填写要接收的数据
            Size :填写要收发的数据的数量。收数量=发数量,以字节为单位。
            Timeout :填写超时时间,单位是ms。
                     填写HAL_MAX_DELAY :表示无限长超时时间

例:发送{0x5a , 0x33},接收2字节数据

    step1: 申明两个数组存储发送数据和作为接收缓冲区。
           uint8_t txData[] = {0x5a , 0x33};
           uint8_t rxData[] = {};
    step2:第二边沿采集 相对应的NSS引脚写入低电压
          HAL_GPIO_WritePin(       ,GPIO_PIN_RESET);
    step3:发送接收数据
           HAL_SPI_TransmitReceive(&hspi1 , txData , rxData , 2 , HAL_MAX_DELAY);
    step4:写入完成,将对应NSS电平高
           HAL_GPIO_WritePin(       ,GPIO_PIN_SET);




五、flash模块

我们的flash模块型号为W25Q64,其中64代表其容量为64Mbit,也就是8M字节。详情见下图

flash的写入过程

在写入数据之前我们需要对写入数据的位置进行擦除。flash擦除的最小单元是扇区,也就是4K字节。擦除需要延迟一定的时间,如100ms。然后就是写入,数据写入的最小单元是页,也就是256字节,即页编程,也需要延迟,如10ms。在写入和擦除之前,我们需要对此芯片进行解锁,即写使能。

六、编程思路

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

static void SaveLEDState(uint8_t ledstate);

声明需要写在下图所示位置

2、实现函数

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;// 返回读取到的数据
}

5、程序开始运行之前

ledstate = LoadLEDState();  //从flash里面加载LED状态

if (ledstate == 1)  
    {
         HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_RESET);   //点亮LED
    }
    else {
        HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_SET);   //熄灭LED
    }

七、代码(main.c)

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @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"
#include "spi.h"
#include "gpio.h"

/* 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 */

/* 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);
/* USER CODE BEGIN PFP */
static void SaveLEDState(uint8_t ledstate);
static uint8_t LoadLEDState(void);  //申明加载状态函数
/* USER CODE END PFP */

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

static uint8_t LoadLEDState(void);  //申明加载状态函数,写在代码54行即上次声明的函数下面。
//实现函数:手册23页
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;// 返回读取到的数据
}



static void SaveLEDState(uint8_t ledstate)
{
 // 1 .写使能
	uint8_t writeEnableCmd[] = {0x06};
	//发送0x06
  HAL_GPIO_WritePin(GPIOA , GPIO_PIN_4 , GPIO_PIN_RESET);
	HAL_SPI_Transmit(&hspi1 , writeEnableCmd , 1 , HAL_MAX_DELAY);
  HAL_GPIO_WritePin(GPIOA , GPIO_PIN_4 , GPIO_PIN_SET);
// 2 .扇区擦除
 uint8_t sectorEraseCmd[] = {0x20,0x00,0x00,0x00};
  HAL_GPIO_WritePin(GPIOA , GPIO_PIN_4 , GPIO_PIN_RESET);
	HAL_SPI_Transmit(&hspi1 , sectorEraseCmd , 4 , HAL_MAX_DELAY);
  HAL_GPIO_WritePin(GPIOA , GPIO_PIN_4 , GPIO_PIN_SET);
	
// 3 .延迟100ms
    HAL_Delay(100); 
 
// 4 .写使能
	HAL_GPIO_WritePin(GPIOA , GPIO_PIN_4 , GPIO_PIN_RESET);
	HAL_SPI_Transmit(&hspi1 , writeEnableCmd , 1 , HAL_MAX_DELAY);
  HAL_GPIO_WritePin(GPIOA , GPIO_PIN_4 , GPIO_PIN_SET);
// 5 .页编程
uint8_t pageProgCmd[5] ;
pageProgCmd[0] = 0x02 ; //指令码
//24位地址
 pageProgCmd[1] =0x00 ;
 pageProgCmd[2] =0x00 ; 
 pageProgCmd[3] = 0x00 ;
//写入LED亮灭状态
pageProgCmd[4] = ledstate;
 HAL_GPIO_WritePin(GPIOA , GPIO_PIN_4 , GPIO_PIN_RESET);
 HAL_SPI_Transmit(&hspi1 , pageProgCmd, 5 , HAL_MAX_DELAY);
 HAL_GPIO_WritePin(GPIOA , GPIO_PIN_4 , GPIO_PIN_SET);
 // 6 .延迟10ms
HAL_Delay(10);  
	
}
/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_SPI1_Init();
  /* USER CODE BEGIN 2 */

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
	uint8_t pre = 1,cur = 1;
  uint8_t ledstate = 0;   
  ledstate = LoadLEDState();
	if (ledstate == 1)
	{
		 HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_RESET);
	}
	else {
		HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_SET);   
	}
  
  while (1)
  {
     pre = cur ; 
     if( HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == GPIO_PIN_SET)
      { 
         cur = 1; 
      }
     else {        
         cur = 0;
          } 
     if (pre != cur)  
        {
					HAL_Delay(10);
          if (cur == 0)   
             {
        
             }
           else  
             {
               if(ledstate == 1)       
                 {
									 HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_SET);    
                   ledstate = 0;  
                   
                 }					
               else   
                 {
                  HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_RESET);   
                  ledstate = 1;  
                 }
							 SaveLEDState(ledstate);   //保存亮灭状态
             }
        }
		
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  /* USER CODE END 3 */
}
	
}
/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  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();
  }

  /** Initializes the CPU, AHB and APB buses clocks
  */
  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  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
  __disable_irq();
  while (1)
  {
  }
  /* USER CODE END Error_Handler_Debug */
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

最终通过断电上电和按键控制LED状态即可验证实验结果。


http://www.kler.cn/a/547630.html

相关文章:

  • 论文解读之GPT1:Improving Language Understanding by Generative Pre-Training
  • Unity 命令行设置运行在指定的显卡上
  • 基于大数据的电动汽车销售数据分析系统的设计与实现
  • FFmpeg+SDL实现简易视频播放器
  • DeepSeek帮助解决Oracle死锁问题
  • Spark 和 Flink
  • win11系统 Docker Desktop提示Docker Engine stopped解决全过程记录
  • 移动端测试的挑战与解决方案:兼容性、网络问题及实战策略
  • LLM之循环神经网络(RNN)
  • 几款C#开发的入门书籍与视频教程
  • SpringbootActuator未授权访问漏洞
  • HTML、Vue和PHP文件的区别与联系
  • 利用盲注技术获取表、列、具体数据
  • PyQt6/PySide6 的信号与槽原理
  • LeetCode 232: 用栈实现队列
  • Day64_20250212_图论part8_拓扑排序|dijkstrs(朴素版)
  • GRN前沿:GNNLink:使用图神经网络从单细胞RNA-seq数据预测基因调控链
  • Jenkinsfile怎么写
  • 浅识Linux高阶用法
  • 前端面试题+算法题(二)