用户名 立即注册 找回密码

微雪课堂

搜索
微雪课堂 STM32 STM32CubeMX系列教程 查看内容

STM32CubeMX系列教程11:串行外设接口SPI(二)

2016-5-3 15:35| 发布者: MyMX1213| 查看: 90587| 评论: 14|原作者: MyMX1213

摘要: 本章介绍如何用程序通过SPI控制w25Q125fv。
1.新建工程

        本章程序在串口printf工程的基础上修改,复制串口printf的工程,修改文件夹名。击STM32F746I.ioc打开STM32cubeMX的工程文件重新配置。SPI1选择全双工主模式,不开启NSS。配置PA7为SPI_MOSI,PA6为SPI_MISO,PA5为SPI_SCK,PA4配置为GPIO输出模式,作为片选信号。

     

SPI配置中设置数据长度为8bit,MSB先输出分频为64分频,则波特率为1.6875 MBits/s。其他为默认设置。
Motorla格式,CPOL设置为Low,CPHA设置为第一个边沿。不开启CRC检验,NSS为软件控制。


在GPIO管脚配置中设置PA4的用户标签为SPI1_CS。



生成报告以及代码,编译程序。在spi.c文件中可以看到ADC初始化函数。在stm32f7xx_hal_spi.h头文件中可以看到spi的操作函数。分别对应轮询,中断和DMA三种控制方式。


下面为W25QXX的驱动文件。下载加压并添加进工程中。


在工程目录下新建文件夹BSP,把刚才下载的文件复制进去。

在工程框中,选择工程名按鼠标右键添加组,修改组名称为Drivers/BSP,选择刚才BSP文件夹的路径,添加W25QXX.c文件。


点击图标打开工程选项设置,选择C/C++栏,在Include Paths中添加头文件路径..\BSP。


重新编译工程,看是否有错误。


2.W25Qxx驱动函数介绍

在W25QXX.c文件中有很多操作函数,这个只接收几个简单的函数。

01/**
02  * @brief  Read Manufacture/Device ID.
03    * @param  return value address
04  * @retval None
05  */
06void BSP_W25Qx_Read_ID(uint8_t *ID)
07{
08    uint8_t cmd[4] = {READ_ID_CMD,0x00,0x00,0x00};
09  
10    W25Qx_Enable();
11    /* Send the read ID command */
12    HAL_SPI_Transmit(&hspi1, cmd, 4, W25Qx_TIMEOUT_VALUE);   
13    /* Reception of the data */
14    HAL_SPI_Receive(&hspi1,ID, 2, W25Qx_TIMEOUT_VALUE);
15    W25Qx_Disable();
16}


以上为读W25Qxx读ID函数,函数开始先定义一个数组cmd保存读ID命令,其中READ_ID_CMD为读ID命令90H,在W25QXX.h头文件中通过宏定义。数组后三个值为地址000000H。



W25Qx_Enable(),W25Qx_Disable()分别为使能和失能SPI设备,即拉低和拉高/CS电平。在W25QXX.h头文件中可以找到如下宏定义。


在GPIO管脚配置中设置PA4的用户标签为SPI1_CS,所以mxconstants.h头文件中有如下宏定义。


HAL_SPI_Transmit(&hspi1, cmd, 4, W25Qx_TIMEOUT_VALUE) 为通过SPI将cmd中四个字节的命令发送出去。 然后通过HAL_SPI_Receive(&hspi1,ID, 2, W25Qx_TIMEOUT_VALUE);函数介绍两个字节的数据保存在对应的地址ID中。W25Qx_TIMEOUT_VALUE为超时。其值为1000.


如下为W25QXX读函数。函数开始先将要发送的数据(命令和地址)存储在cmd数组中,然后后通过HAL_SPI_Transmit()函数发送出去,接着通过HAL_SPI_Receive()接收读取的数据。

01/**
02  * @brief  Reads an amount of data from the QSPI memory.
03  * @param  pData: Pointer to data to be read
04  * @param  ReadAddr: Read start address
05  * @param  Size: Size of data to read   
06  * @retval QSPI memory status
07  */
08uint8_t BSP_W25Qx_Read(uint8_t* pData, uint32_t ReadAddr, uint32_t Size)
09{
10    uint8_t cmd[4];
11  
12    /* Configure the command */
13    cmd[0] = READ_CMD;
14    cmd[1] = (uint8_t)(ReadAddr >> 16);
15    cmd[2] = (uint8_t)(ReadAddr >> 8);
16    cmd[3] = (uint8_t)(ReadAddr);
17      
18    W25Qx_Enable();
19    /* Send the read ID command */
20    HAL_SPI_Transmit(&hspi1, cmd, 4, W25Qx_TIMEOUT_VALUE); 
21    /* Reception of the data */
22    if (HAL_SPI_Receive(&hspi1, pData,Size,W25Qx_TIMEOUT_VALUE) != HAL_OK)
23  {
24    return W25Qx_ERROR;
25  }
26    W25Qx_Disable();
27    return W25Qx_OK;
28}


如下为W25Qxx写操作函数,采用页编程指令(02H),每次最多可以写入256字节。所以写函数中将要写入的数据分多次写入W25Qxx中,每次只写256个字节,不断循环直到数据完全写完。写数据前先使能写操作。

01/**
02  * @brief  Writes an amount of data to the QSPI memory.
03  * @param  pData: Pointer to data to be written
04  * @param  WriteAddr: Write start address
05  * @param  Size: Size of data to write,No more than 256byte.   
06  * @retval QSPI memory status
07  */
08uint8_t BSP_W25Qx_Write(uint8_t* pData, uint32_t WriteAddr, uint32_t Size)
09{
10    uint8_t cmd[4];
11    uint32_t end_addr, current_size, current_addr;
12    uint32_t tickstart = HAL_GetTick();
13      
14    /* Calculation of the size between the write address and the end of the page */
15  current_addr = 0;
16  
17  while (current_addr <= WriteAddr)
18  {
19    current_addr += W25Q128FV_PAGE_SIZE;
20  }
21  current_size = current_addr - WriteAddr;
22  
23  /* Check if the size of the data is less than the remaining place in the page */
24  if (current_size > Size)
25  {
26    current_size = Size;
27  }
28  
29  /* Initialize the adress variables */
30  current_addr = WriteAddr;
31  end_addr = WriteAddr + Size;
32      
33  /* Perform the write page by page */
34  do
35  {
36        /* Configure the command */
37        cmd[0] = PAGE_PROG_CMD;
38        cmd[1] = (uint8_t)(current_addr >> 16);
39        cmd[2] = (uint8_t)(current_addr >> 8);
40        cmd[3] = (uint8_t)(current_addr);
41  
42        /* Enable write operations */
43        BSP_W25Qx_WriteEnable();
44      
45        W25Qx_Enable();
46    /* Send the command */
47    if (HAL_SPI_Transmit(&hspi1,cmd, 4, W25Qx_TIMEOUT_VALUE) != HAL_OK)
48    {
49      return W25Qx_ERROR;
50    }
51      
52    /* Transmission of the data */
53    if (HAL_SPI_Transmit(&hspi1, pData,current_size, W25Qx_TIMEOUT_VALUE) != HAL_OK)
54    {
55      return W25Qx_ERROR;
56    }
57            W25Qx_Disable();
58        /* Wait the end of Flash writing */
59        while(BSP_W25Qx_GetStatus() == W25Qx_BUSY);
60        {
61            /* Check for the Timeout */
62            if((HAL_GetTick() - tickstart) > W25Qx_TIMEOUT_VALUE)
63            {       
64                return W25Qx_TIMEOUT;
65            }
66        }
67      
68    /* Update the address and size variables for next page programming */
69    current_addr += current_size;
70    pData += current_size;
71    current_size = ((current_addr + W25Q128FV_PAGE_SIZE) > end_addr) ? (end_addr - current_addr) : W25Q128FV_PAGE_SIZE;
72  } while (current_addr < end_addr);
73  
74    return W25Qx_OK;
75}


扇区擦除函数,和写函数一样,擦除扇区前必先使能写操作。发送扇区擦除指令后不断读取W25Qxx的状态寄存器,判断flash是否为忙状态,如果不为忙则擦除操作完成。

01/**
02  * @brief  Erases the specified block of the QSPI memory.
03  * @param  BlockAddress: Block address to erase 
04  * @retval QSPI memory status
05  */
06uint8_t BSP_W25Qx_Erase_Block(uint32_t Address)
07{
08    uint8_t cmd[4];
09    uint32_t tickstart = HAL_GetTick();
10    cmd[0] = SECTOR_ERASE_CMD;
11    cmd[1] = (uint8_t)(Address >> 16);
12    cmd[2] = (uint8_t)(Address >> 8);
13    cmd[3] = (uint8_t)(Address);
14  
15    /* Enable write operations */
16    BSP_W25Qx_WriteEnable();
17  
18    /*Select the FLASH: Chip Select low */
19    W25Qx_Enable();
20    /* Send the read ID command */
21    HAL_SPI_Transmit(&hspi1, cmd, 4, W25Qx_TIMEOUT_VALUE);   
22    /*Deselect the FLASH: Chip Select high */
23    W25Qx_Disable();
24  
25    /* Wait the end of Flash writing */
26    while(BSP_W25Qx_GetStatus() == W25Qx_BUSY);
27    {
28        /* Check for the Timeout */
29    if((HAL_GetTick() - tickstart) > W25Q128FV_SECTOR_ERASE_MAX_TIME)
30    {       
31            return W25Qx_TIMEOUT;
32    }
33    }
34    return W25Qx_OK;
35}


3.添加应用程序

在main.c文件中声明变量,rData,wData分别存储读写的数据,ID存储读取的ID值。

1/* USER CODE BEGIN PV */
2/* Private variables ---------------------------------------------------------*/
3uint8_t wData[0x100];
4uint8_t rData[0x100];
5uint32_t i;
6uint8_t ID[2];
7/* USER CODE END PV */

在main函数中添加如下测试程序。

01/* USER CODE BEGIN 2 */
02  printf("\r\n SPI-W25Qxxx Example \r\n\r\n");
03 
04  /*##-1- Read the device ID  ########################*/
05  BSP_W25Qx_Init();
06  BSP_W25Qx_Read_ID(ID);
07  printf(" W25Qxxx ID is : 0x%02X 0x%02X \r\n\r\n",ID[0],ID[1]);
08    
09  /*##-2- Erase Block ##################################*/
10  if(BSP_W25Qx_Erase_Block(0) == W25Qx_OK)
11      printf(" SPI Erase Block ok\r\n");
12  else
13      Error_Handler();
14    
15  /*##-3- Written to the flash ########################*/
16  /* fill buffer */
17  for(i =0;i<0x100;i ++)
18  {
19          wData[i] = i;
20        rData[i] = 0;
21  }
22    
23  if(BSP_W25Qx_Write(wData,0x00,0x100)== W25Qx_OK)
24      printf(" SPI Write ok\r\n");
25  else
26      Error_Handler();
27 
28  /*##-4- Read the flash     ########################*/
29  if(BSP_W25Qx_Read(rData,0x00,0x100)== W25Qx_OK)
30      printf(" SPI Read ok\r\n\r\n");
31  else
32      Error_Handler();
33    
34  printf("SPI Read Data : \r\n");
35  for(i =0;i<0x100;i++)
36      printf("0x%02X  ",rData[i]);
37  printf("\r\n\r\n");
38    
39  /*##-5- check date          ########################*/   
40  if(memcmp(wData,rData,0x100) == 0 )
41      printf(" W25Q128FV SPI Test OK\r\n");
42  else
43      printf(" W25Q128FV SPI Test False\r\n");
44/* USER CODE END 2 */

        程序中先是初始化W25QXX,即发送使能重启(66H)和使能重启设备(99H)命令初始化flash,然后读取设备ID并输出。第2步中擦除flash,写入数据前必先要擦除内存。然后填充写入数据缓存并写入flahs中。最后读取刚才写入的数据,并检测数据是否正确。
        把程序中用的出错处理函数添加在main.c文件后面。

01/* USER CODE BEGIN 4 */
02/**
03  * @brief  This function is executed in case of error occurrence.
04  * @param  None
05  * @retval None
06  */
07static void Error_Handler(void)
08{
09  printf("something wrong ....\r\n");
10  /* User may add here some code to deal with this error */
11  while(1)
12  {
13  }
14}
15/* USER CODE END 4 */

在main.c文件开头添加应用的头文件。

1/* USER CODE BEGIN Includes */
2#include <string.h>
3#include "W25QXX.h"
4/* USER CODE END Includes */</string.h>

将W25QXX DataFlash Board模块插入到Open746I开发板SPI1中,编译程序并下载到开发板。打开串口调试助手。设置波特率为115200。串口助手上会显示如下信息。



746

顶一下

刚表态过的朋友 (746 人)

相关阅读

发表评论

最新评论

引用 游客 2020-4-21 15:19
: W25QXX.c里面两个擦除的while循环要改成这样:   while (BSP_W25Qx_GetStatus() == W25Qx_BUSY)   {     /* Check for the Timeout */     if ((HAL_GetTick() -  ...
补充一下,不止两个,还有其它的while函数也不正确,另外擦除block的实际上是擦除sector的,可以改名再增加一个擦除block的函数。两个static的声明也要放到c文件中,不然有警告。
引用 游客 2020-4-21 11:11
: 用的W25Q64FV的片子可以读取ID,但是擦除函数跑飞了,不知道什么原因
W25QXX.c里面两个擦除的while循环要改成这样:
  while (BSP_W25Qx_GetStatus() == W25Qx_BUSY)
  {
    /* Check for the Timeout */
    if ((HAL_GetTick() - tickstart) > W25Q128FV_SECTOR_ERASE_MAX_TIME)
    {
      return W25Qx_TIMEOUT;
    }
  }
引用 游客 2020-3-6 01:29
#include "stm32f7xx.h"没定义怎么办
引用 游客 2020-3-5 18:19
这个例子是没问题的,在初始化的时候不要使能CRC校验。
引用 游客 2019-6-11 10:14
: 用的W25Q64FV的片子可以读取ID,但是擦除函数跑飞了,不知道什么原因
同样,擦除函数跑飞
引用 游客 2018-12-18 08:29
用的W25Q64FV的片子可以读取ID,但是擦除函数跑飞了,不知道什么原因
引用 游客 2018-5-10 15:06
我就奇怪了NSS明明有硬件支持为什么都用软件模式
引用 游客 2018-3-28 21:33
想问下DMA如何使用
引用 sjh13140 2018-3-8 11:05
请问spi需要配置中断吗
引用 游客 2017-12-8 18:50
我用的w25wq32  但是ID读出来是00 00
引用 游客 2017-8-1 20:44
W25QXX.c里面好多都没定义,小编改改
引用 游客 2017-5-26 08:39
: W25QXX.h能不能分享下,感谢了
示例程序可以到我们的wiki上下载,Open746的示例程序。
引用 游客 2017-5-25 18:21
W25QXX.h能不能分享下,感谢了
引用 Ezio_神仙醋 2017-1-19 15:05
good

查看全部评论(14)

微雪官网|产品资料|手机版|小黑屋|微雪课堂. ( 粤ICP备05067009号 )

GMT+8, 2025-4-2 15:32 , Processed in 0.031453 second(s), 21 queries .

返回顶部