目录

1. SPI协议概述

2. STM32 SPI特性及架构

2.1 SPI外设简介

2.2 STM32的SPI架构剖析

2.2.1 通讯引脚

2.2.2 时钟控制逻辑

2.2.3 数据控制逻辑

3. 通信过程

4. 硬件设计

5. 软件设计


1. SPI协议概述

关于SPI协议不做赘述,见详解。

https://blog.csdn.net/XieWinter/article/details/94738361

2. STM32 SPI特性及架构

STM32芯片也集成了专门用于SPI协议通讯的外设。

2.1 SPI外设简介

STM32的SPI外设可用作通讯的主机及从机,支持最高的SCK时钟频率为fpclk/2 (STM32F429型号的芯片默认fpclk1为90MHz,fpclk2为45MHz),完全支持SPI协议的4种模式,数据帧长度可设置为8位或16位,可设置数据MSB先行或LSB先行。

双线双工模式: 通常使用

双线单向模式:MOSI/MISO数据线向一个方向传输数据,可以加快一倍的速度

单线模式:半双工

● 基于三条线的全双工同步传输

● 基于双线的单工同步传输,其中一条可作为双向数据线

● 8 位或 16 位传输帧格式选择

● 主模式或从模式操作

● 多主模式功能

● 8 个主模式波特率预分频器(最大值为 fPCLK/2)

● 从模式频率(最大值为 fPCLK/2)

● 对于主模式和从模式都可实现更快的通信

● 对于主模式和从模式都可通过硬件或软件进行 NSS 管理:动态切换主/从操作

● 可编程的时钟极性和相位

● 可编程的数据顺序,最先移位 MSB 或 LSB

● 可触发中断的专用发送和接收标志

● SPI 总线忙状态标志

● SPI TI 模式

● 用于确保可靠通信的硬件 CRC 功能:

— 在发送模式下可将 CRC 值作为最后一个字节发送

— 根据收到的最后一个字节自动进行 CRC 错误校验

● 可触发中断的主模式故障、上溢和 CRC 错误标志

● 具有 DMA 功能的 1 字节发送和接收缓冲器:发送和接收请求

2.2 STM32的SPI架构剖析

2.2.1 通讯引脚

SPI的所有硬件架构都从图 中左侧MOSI、MISO、SCK及NSS线展开的。

处于不同外设总线上的SPI,最高通信速率有所差异。其中SPI1、SPI4、SPI5、SPI6是APB2上的设备,最高通信速率达45Mbtis/s,SPI2、SPI3是APB1上的设备,最高通信速率为22.5Mbits/s。

2.2.2 时钟控制逻辑

SCK线的时钟信号,由波特率发生器根据“控制寄存器CR1”中的BR[0:2]位控制,该位是对fpclk时钟的分频因子,对fpclk的分频结果就是SCK引脚的输出时钟频率。

2.2.3 数据控制逻辑

SPI的MOSI及MISO都连接到数据移位寄存器上,数据移位寄存器的数据来源及目标接收、发送缓冲区以及MISO、MOSI线。

通过写SPI的“数据寄存器DR”把数据填充到发送缓冲区中,通讯读“数据寄存器DR”,可以获取接收缓冲区中的内容。

其中数据帧长度,可以通过“控制寄存器CR1”的“DFF位”配置成8位及16位模式;配置“LSBFIRST位”可选择MSB先行还是LSB先行。

3. 通信过程

主模式收发流程及事件说明如下:

(1) 控制NSS信号线,产生起始信号(图中没有画出);

(2) 把要发送的数据写入到“数据寄存器DR”中,该数据会被存储到发送缓冲区;

(3) 通讯开始,SCK时钟开始运行。MOSI把发送缓冲区中的数据一位一位地传输出去;MISO则把数据一位一位地存储进接收缓冲区中;

(4) 当发送完一帧数据的时候,“状态寄存器SR”中的“TXE标志位”会被置1,表示传输完一帧,发送缓冲区已空;类似地,当接收完一帧数据的时候,“RXNE标志位”会被置1,表示传输完一帧,接收缓冲区非空;

(5) 等待到“TXE标志位”为1时,若还要继续发送数据,则再次往“数据寄存器DR”写入数据即可;等待到“RXNE标志位”为1时,通过读取“数据寄存器DR”可以获取接收缓冲区中的内容。

4. 硬件设计

FLASH芯片中还有WP和HOLD引脚。WP引脚可控制写保护功能,当该引脚为低电平时,禁止写入数据。我们直接接电源,不使用写保护功能。HOLD引脚可用于暂停通讯,该引脚为低电平时,通讯暂停,数据输出引脚输出高阻抗状态,时钟和数据输入引脚无效 。详情见芯片数据手册

“dummy”指该处可为任意数据,

5. 软件设计

注意:根据SPI协议可知,时钟是由主机提供的,因此,读字节的时候,发送数据使主机产生时钟,从而是主从的移位寄存器按位移动数据,从而使得从机的数据,转移到主机的数据寄存器,从而获取从机的相应数据。

#ifndef __SPI_FLASH_H__
#define __SPI_FLASH_H__#include "stm32f4xx.h"
#include <stdio.h>/* Private typedef -----------------------------------------------------------*/
//#define  sFLASH_ID                       0xEF3015     //W25X16
//#define  sFLASH_ID                       0xEF4015     //W25Q16
//#define  sFLASH_ID                        0XEF4017     //W25Q64
#define  sFLASH_ID                       0XEF4018     //W25Q128//#define SPI_FLASH_PageSize            4096
#define SPI_FLASH_PageSize              256
#define SPI_FLASH_PerWritePageSize      256/* Private define ------------------------------------------------------------*/
/*命令定义-开头*******************************/
#define W25X_WriteEnable              0x06
#define W25X_WriteDisable             0x04
#define W25X_ReadStatusReg          0x05
#define W25X_WriteStatusReg       0x01
#define W25X_ReadData                   0x03
#define W25X_FastReadData             0x0B
#define W25X_FastReadDual             0x3B
#define W25X_PageProgram              0x02
#define W25X_BlockErase               0xD8
#define W25X_SectorErase              0x20
#define W25X_ChipErase                0xC7
#define W25X_PowerDown                0xB9
#define W25X_ReleasePowerDown     0xAB
#define W25X_DeviceID                   0xAB
#define W25X_ManufactDeviceID       0x90
#define W25X_JedecDeviceID          0x9F #define WIP_Flag                  0x01  /* Write In Progress (WIP) flag */
#define Dummy_Byte                0xFF
/*命令定义-结尾*******************************//*SPI接口定义-开头****************************/
//SPI号
#define FLASH_SPI                           SPI3
#define FLASH_SPI_CLK                       RCC_APB1Periph_SPI3
#define FLASH_SPI_CLK_INIT                  RCC_APB1PeriphClockCmd
//SCK引脚
#define FLASH_SPI_SCK_PIN                   GPIO_Pin_3
#define FLASH_SPI_SCK_GPIO_PORT             GPIOB
#define FLASH_SPI_SCK_GPIO_CLK              RCC_AHB1Periph_GPIOB
#define FLASH_SPI_SCK_PINSOURCE             GPIO_PinSource3
#define FLASH_SPI_SCK_AF                    GPIO_AF_SPI3
//MISO引脚
#define FLASH_SPI_MISO_PIN                  GPIO_Pin_4
#define FLASH_SPI_MISO_GPIO_PORT            GPIOB
#define FLASH_SPI_MISO_GPIO_CLK             RCC_AHB1Periph_GPIOB
#define FLASH_SPI_MISO_PINSOURCE            GPIO_PinSource4
#define FLASH_SPI_MISO_AF                   GPIO_AF_SPI3
//MOSI引脚
#define FLASH_SPI_MOSI_PIN                   GPIO_Pin_5
#define FLASH_SPI_MOSI_GPIO_PORT            GPIOB
#define FLASH_SPI_MOSI_GPIO_CLK             RCC_AHB1Periph_GPIOB
#define FLASH_SPI_MOSI_PINSOURCE            GPIO_PinSource5
#define FLASH_SPI_MOSI_AF                    GPIO_AF_SPI3
//CS(NSS)引脚
#define FLASH_CS_PIN                         GPIO_Pin_8
#define FLASH_CS_GPIO_PORT                  GPIOI
#define FLASH_CS_GPIO_CLK                   RCC_AHB1Periph_GPIOI//控制CS(NSS)引脚输出低电平
#define SPI_FLASH_CS_LOW()      {FLASH_CS_GPIO_PORT->BSRRH=FLASH_CS_PIN;}
//控制CS(NSS)引脚输出高电平
#define SPI_FLASH_CS_HIGH()     {FLASH_CS_GPIO_PORT->BSRRL=FLASH_CS_PIN;}/*SPI接口定义-结尾****************************//*等待超时时间*/
#define SPIT_FLAG_TIMEOUT         ((uint32_t)0x1000)
#define SPIT_LONG_TIMEOUT         ((uint32_t)(10 * SPIT_FLAG_TIMEOUT))/*信息输出*/
#define FLASH_DEBUG_ON         0#define FLASH_INFO(fmt,arg...)           printf("<<-FLASH-INFO->> "fmt"\n",##arg)
#define FLASH_ERROR(fmt,arg...)          printf("<<-FLASH-ERROR->> "fmt"\n",##arg)
#define FLASH_DEBUG(fmt,arg...)          do{\if(FLASH_DEBUG_ON)\printf("<<-FLASH-DEBUG->> [%d]"fmt"\n",__LINE__, ##arg);\}while(0)void SPI_FLASH_Init(void);
void SPI_FLASH_SectorErase(uint32_t SectorAddr);
void SPI_FLASH_BulkErase(void);
void SPI_FLASH_PageWrite(uint8_t* pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite);
void SPI_FLASH_BufferWrite(uint8_t* pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite);
void SPI_FLASH_BufferRead(uint8_t* pBuffer, uint32_t ReadAddr, uint16_t NumByteToRead);
uint32_t SPI_FLASH_ReadID(void);
uint32_t SPI_FLASH_ReadDeviceID(void);
void SPI_FLASH_StartReadSequence(uint32_t ReadAddr);
void SPI_Flash_PowerDown(void);
void SPI_Flash_WAKEUP(void);uint8_t SPI_FLASH_ReadByte(void);
uint8_t SPI_FLASH_SendByte(uint8_t byte);
uint16_t SPI_FLASH_SendHalfWord(uint16_t HalfWord);
void SPI_FLASH_WriteEnable(void);
void SPI_FLASH_WaitForWriteEnd(void);#endif /* __SPI_FLASH_H__ */
 /********************************************************************************* @file    bsp_spi_flash.c* @version V1.0* @date    2015-xx-xx* @brief   spi flash 底层应用函数bsp *******************************************************************************/#include "bsp_spi_flash.h"static __IO uint32_t  SPITimeout = SPIT_LONG_TIMEOUT;   static uint16_t SPI_TIMEOUT_UserCallback(uint8_t errorCode);/*** @brief  SPI_FLASH初始化* @param  无* @retval 无*/
void SPI_FLASH_Init(void)
{SPI_InitTypeDef  SPI_InitStructure;GPIO_InitTypeDef GPIO_InitStructure;/* 使能 FLASH_SPI 及GPIO 时钟 *//*!< SPI_FLASH_SPI_CS_GPIO, SPI_FLASH_SPI_MOSI_GPIO, SPI_FLASH_SPI_MISO_GPIO,SPI_FLASH_SPI_SCK_GPIO 时钟使能 */RCC_AHB1PeriphClockCmd (FLASH_SPI_SCK_GPIO_CLK | FLASH_SPI_MISO_GPIO_CLK|FLASH_SPI_MOSI_GPIO_CLK|FLASH_CS_GPIO_CLK, ENABLE);/*!< SPI_FLASH_SPI 时钟使能 */FLASH_SPI_CLK_INIT(FLASH_SPI_CLK, ENABLE);//设置引脚复用GPIO_PinAFConfig(FLASH_SPI_SCK_GPIO_PORT,FLASH_SPI_SCK_PINSOURCE,FLASH_SPI_SCK_AF); GPIO_PinAFConfig(FLASH_SPI_MISO_GPIO_PORT,FLASH_SPI_MISO_PINSOURCE,FLASH_SPI_MISO_AF); GPIO_PinAFConfig(FLASH_SPI_MOSI_GPIO_PORT,FLASH_SPI_MOSI_PINSOURCE,FLASH_SPI_MOSI_AF); /*!< 配置 SPI_FLASH_SPI 引脚: SCK */GPIO_InitStructure.GPIO_Pin = FLASH_SPI_SCK_PIN;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;  GPIO_Init(FLASH_SPI_SCK_GPIO_PORT, &GPIO_InitStructure);/*!< 配置 SPI_FLASH_SPI 引脚: MISO */GPIO_InitStructure.GPIO_Pin = FLASH_SPI_MISO_PIN;GPIO_Init(FLASH_SPI_MISO_GPIO_PORT, &GPIO_InitStructure);/*!< 配置 SPI_FLASH_SPI 引脚: MOSI */GPIO_InitStructure.GPIO_Pin = FLASH_SPI_MOSI_PIN;GPIO_Init(FLASH_SPI_MOSI_GPIO_PORT, &GPIO_InitStructure);  /*!< 配置 SPI_FLASH_SPI 引脚: CS */GPIO_InitStructure.GPIO_Pin = FLASH_CS_PIN;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;GPIO_Init(FLASH_CS_GPIO_PORT, &GPIO_InitStructure);/* 停止信号 FLASH: CS引脚高电平*/SPI_FLASH_CS_HIGH();/* FLASH_SPI 模式配置 */// FLASH芯片 支持SPI模式0及模式3,据此设置CPOL CPHASPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;SPI_InitStructure.SPI_Mode = SPI_Mode_Master;SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2;SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;SPI_InitStructure.SPI_CRCPolynomial = 7;SPI_Init(FLASH_SPI, &SPI_InitStructure);/* 使能 FLASH_SPI  */SPI_Cmd(FLASH_SPI, ENABLE);}/*** @brief  擦除FLASH扇区* @param  SectorAddr:要擦除的扇区地址* @retval 无*/
void SPI_FLASH_SectorErase(uint32_t SectorAddr)
{/* 发送FLASH写使能命令 */SPI_FLASH_WriteEnable();SPI_FLASH_WaitForWriteEnd();/* 擦除扇区 *//* 选择FLASH: CS低电平 */SPI_FLASH_CS_LOW();/* 发送扇区擦除指令*/SPI_FLASH_SendByte(W25X_SectorErase);/*发送擦除扇区地址的高位*/SPI_FLASH_SendByte((SectorAddr & 0xFF0000) >> 16);/* 发送擦除扇区地址的中位 */SPI_FLASH_SendByte((SectorAddr & 0xFF00) >> 8);/* 发送擦除扇区地址的低位 */SPI_FLASH_SendByte(SectorAddr & 0xFF);/* 停止信号 FLASH: CS 高电平 */SPI_FLASH_CS_HIGH();/* 等待擦除完毕*/SPI_FLASH_WaitForWriteEnd();
}/*** @brief  擦除FLASH扇区,整片擦除* @param  无* @retval 无*/
void SPI_FLASH_BulkErase(void)
{/* 发送FLASH写使能命令 */SPI_FLASH_WriteEnable();/* 整块 Erase *//* 选择FLASH: CS低电平 */SPI_FLASH_CS_LOW();/* 发送整块擦除指令*/SPI_FLASH_SendByte(W25X_ChipErase);/* 停止信号 FLASH: CS 高电平 */SPI_FLASH_CS_HIGH();/* 等待擦除完毕*/SPI_FLASH_WaitForWriteEnd();
}/*** @brief  对FLASH按页写入数据,调用本函数写入数据前需要先擦除扇区* @param   pBuffer,要写入数据的指针* @param WriteAddr,写入地址* @param  NumByteToWrite,写入数据长度,必须小于等于SPI_FLASH_PerWritePageSize* @retval 无*/
void SPI_FLASH_PageWrite(uint8_t* pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite)
{/* 发送FLASH写使能命令 */SPI_FLASH_WriteEnable();/* 选择FLASH: CS低电平 */SPI_FLASH_CS_LOW();/* 写页写指令*/SPI_FLASH_SendByte(W25X_PageProgram);/*发送写地址的高位*/SPI_FLASH_SendByte((WriteAddr & 0xFF0000) >> 16);/*发送写地址的中位*/SPI_FLASH_SendByte((WriteAddr & 0xFF00) >> 8);/*发送写地址的低位*/SPI_FLASH_SendByte(WriteAddr & 0xFF);if(NumByteToWrite > SPI_FLASH_PerWritePageSize){NumByteToWrite = SPI_FLASH_PerWritePageSize;FLASH_ERROR("SPI_FLASH_PageWrite too large!");}/* 写入数据*/while (NumByteToWrite--){/* 发送当前要写入的字节数据 */SPI_FLASH_SendByte(*pBuffer);/* 指向下一字节数据 */pBuffer++;}/* 停止信号 FLASH: CS 高电平 */SPI_FLASH_CS_HIGH();/* 等待写入完毕*/SPI_FLASH_WaitForWriteEnd();
}/*** @brief  对FLASH写入数据,调用本函数写入数据前需要先擦除扇区* @param pBuffer,要写入数据的指针* @param  WriteAddr,写入地址* @param  NumByteToWrite,写入数据长度* @retval 无*/
void SPI_FLASH_BufferWrite(uint8_t* pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite)
{uint8_t NumOfPage = 0, NumOfSingle = 0, Addr = 0, count = 0, temp = 0;/*mod运算求余,若writeAddr是SPI_FLASH_PageSize整数倍,运算结果Addr值为0*/Addr = WriteAddr % SPI_FLASH_PageSize;/*差count个数据值,刚好可以对齐到页地址*/count = SPI_FLASH_PageSize - Addr;    /*计算出要写多少整数页*/NumOfPage =  NumByteToWrite / SPI_FLASH_PageSize;/*mod运算求余,计算出剩余不满一页的字节数*/NumOfSingle = NumByteToWrite % SPI_FLASH_PageSize;/* Addr=0,则WriteAddr 刚好按页对齐 aligned  */if (Addr == 0) {/* NumByteToWrite < SPI_FLASH_PageSize */if (NumOfPage == 0) {SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumByteToWrite);}else /* NumByteToWrite > SPI_FLASH_PageSize */{/*先把整数页都写了*/while (NumOfPage--){SPI_FLASH_PageWrite(pBuffer, WriteAddr, SPI_FLASH_PageSize);WriteAddr +=  SPI_FLASH_PageSize;pBuffer += SPI_FLASH_PageSize;}/*若有多余的不满一页的数据,把它写完*/SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumOfSingle);}}/* 若地址与 SPI_FLASH_PageSize 不对齐  */else {/* NumByteToWrite < SPI_FLASH_PageSize */if (NumOfPage == 0) {/*当前页剩余的count个位置比NumOfSingle小,写不完*/if (NumOfSingle > count) {temp = NumOfSingle - count;/*先写满当前页*/SPI_FLASH_PageWrite(pBuffer, WriteAddr, count);WriteAddr +=  count;pBuffer += count;/*再写剩余的数据*/SPI_FLASH_PageWrite(pBuffer, WriteAddr, temp);}else /*当前页剩余的count个位置能写完NumOfSingle个数据*/{               SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumByteToWrite);}}else /* NumByteToWrite > SPI_FLASH_PageSize */{/*地址不对齐多出的count分开处理,不加入这个运算*/NumByteToWrite -= count;NumOfPage =  NumByteToWrite / SPI_FLASH_PageSize;NumOfSingle = NumByteToWrite % SPI_FLASH_PageSize;SPI_FLASH_PageWrite(pBuffer, WriteAddr, count);WriteAddr +=  count;pBuffer += count;/*把整数页都写了*/while (NumOfPage--){SPI_FLASH_PageWrite(pBuffer, WriteAddr, SPI_FLASH_PageSize);WriteAddr +=  SPI_FLASH_PageSize;pBuffer += SPI_FLASH_PageSize;}/*若有多余的不满一页的数据,把它写完*/if (NumOfSingle != 0){SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumOfSingle);}}}
}/*** @brief  读取FLASH数据* @param   pBuffer,存储读出数据的指针* @param   ReadAddr,读取地址* @param   NumByteToRead,读取数据长度* @retval 无*/
void SPI_FLASH_BufferRead(uint8_t* pBuffer, uint32_t ReadAddr, uint16_t NumByteToRead)
{/* 选择FLASH: CS低电平 */SPI_FLASH_CS_LOW();/* 发送 读 指令 */SPI_FLASH_SendByte(W25X_ReadData);/* 发送 读 地址高位 */SPI_FLASH_SendByte((ReadAddr & 0xFF0000) >> 16);/* 发送 读 地址中位 */SPI_FLASH_SendByte((ReadAddr& 0xFF00) >> 8);/* 发送 读 地址低位 */SPI_FLASH_SendByte(ReadAddr & 0xFF);/* 读取数据 */while (NumByteToRead--){/* 读取一个字节*/*pBuffer = SPI_FLASH_SendByte(Dummy_Byte);/* 指向下一个字节缓冲区 */pBuffer++;}/* 停止信号 FLASH: CS 高电平 */SPI_FLASH_CS_HIGH();
}/*** @brief  读取FLASH ID* @param  无* @retval FLASH ID*/
uint32_t SPI_FLASH_ReadID(void)
{uint32_t Temp = 0, Temp0 = 0, Temp1 = 0, Temp2 = 0;/* 开始通讯:CS低电平 */SPI_FLASH_CS_LOW();/* 发送JEDEC指令,读取ID */SPI_FLASH_SendByte(W25X_JedecDeviceID);/* 读取一个字节数据 */Temp0 = SPI_FLASH_SendByte(Dummy_Byte);/* 读取一个字节数据 */Temp1 = SPI_FLASH_SendByte(Dummy_Byte);/* 读取一个字节数据 */Temp2 = SPI_FLASH_SendByte(Dummy_Byte);/* 停止通讯:CS高电平 */SPI_FLASH_CS_HIGH();/*把数据组合起来,作为函数的返回值*/Temp = (Temp0 << 16) | (Temp1 << 8) | Temp2;return Temp;
}/*** @brief  读取FLASH Device ID* @param   无* @retval FLASH Device ID*/
uint32_t SPI_FLASH_ReadDeviceID(void)
{uint32_t Temp = 0;/* Select the FLASH: Chip Select low */SPI_FLASH_CS_LOW();/* Send "RDID " instruction */SPI_FLASH_SendByte(W25X_DeviceID);SPI_FLASH_SendByte(Dummy_Byte);SPI_FLASH_SendByte(Dummy_Byte);SPI_FLASH_SendByte(Dummy_Byte);/* Read a byte from the FLASH */Temp = SPI_FLASH_SendByte(Dummy_Byte);/* Deselect the FLASH: Chip Select high */SPI_FLASH_CS_HIGH();return Temp;
}
/*******************************************************************************
* Function Name  : SPI_FLASH_StartReadSequence
* Description    : Initiates a read data byte (READ) sequence from the Flash.
*                  This is done by driving the /CS line low to select the device,
*                  then the READ instruction is transmitted followed by 3 bytes
*                  address. This function exit and keep the /CS line low, so the
*                  Flash still being selected. With this technique the whole
*                  content of the Flash is read with a single READ instruction.
* Input          : - ReadAddr : FLASH's internal address to read from.
* Output         : None
* Return         : None
*******************************************************************************/
void SPI_FLASH_StartReadSequence(uint32_t ReadAddr)
{/* Select the FLASH: Chip Select low */SPI_FLASH_CS_LOW();/* Send "Read from Memory " instruction */SPI_FLASH_SendByte(W25X_ReadData);/* Send the 24-bit address of the address to read from -----------------------*//* Send ReadAddr high nibble address byte */SPI_FLASH_SendByte((ReadAddr & 0xFF0000) >> 16);/* Send ReadAddr medium nibble address byte */SPI_FLASH_SendByte((ReadAddr& 0xFF00) >> 8);/* Send ReadAddr low nibble address byte */SPI_FLASH_SendByte(ReadAddr & 0xFF);
}/*** @brief  使用SPI读取一个字节的数据* @param  无* @retval 返回接收到的数据*/
uint8_t SPI_FLASH_ReadByte(void)
{return (SPI_FLASH_SendByte(Dummy_Byte));
}/*** @brief  使用SPI发送一个字节的数据* @param  byte:要发送的数据* @retval 返回接收到的数据*/
uint8_t SPI_FLASH_SendByte(uint8_t byte)
{SPITimeout = SPIT_FLAG_TIMEOUT;/* 等待发送缓冲区为空,TXE事件 */while (SPI_I2S_GetFlagStatus(FLASH_SPI, SPI_I2S_FLAG_TXE) == RESET){if((SPITimeout--) == 0) return SPI_TIMEOUT_UserCallback(0);}/* 写入数据寄存器,把要写入的数据写入发送缓冲区 */SPI_I2S_SendData(FLASH_SPI, byte);SPITimeout = SPIT_FLAG_TIMEOUT;/* 等待接收缓冲区非空,RXNE事件 */while (SPI_I2S_GetFlagStatus(FLASH_SPI, SPI_I2S_FLAG_RXNE) == RESET){if((SPITimeout--) == 0) return SPI_TIMEOUT_UserCallback(1);}/* 读取数据寄存器,获取接收缓冲区数据 */return SPI_I2S_ReceiveData(FLASH_SPI);
}/*******************************************************************************
* Function Name  : SPI_FLASH_SendHalfWord
* Description    : Sends a Half Word through the SPI interface and return the
*                  Half Word received from the SPI bus.
* Input          : Half Word : Half Word to send.
* Output         : None
* Return         : The value of the received Half Word.
*******************************************************************************/
uint16_t SPI_FLASH_SendHalfWord(uint16_t HalfWord)
{SPITimeout = SPIT_FLAG_TIMEOUT;/* Loop while DR register in not emplty */while (SPI_I2S_GetFlagStatus(FLASH_SPI, SPI_I2S_FLAG_TXE) == RESET){if((SPITimeout--) == 0) return SPI_TIMEOUT_UserCallback(2);}/* Send Half Word through the FLASH_SPI peripheral */SPI_I2S_SendData(FLASH_SPI, HalfWord);SPITimeout = SPIT_FLAG_TIMEOUT;/* Wait to receive a Half Word */while (SPI_I2S_GetFlagStatus(FLASH_SPI, SPI_I2S_FLAG_RXNE) == RESET){if((SPITimeout--) == 0) return SPI_TIMEOUT_UserCallback(3);}/* Return the Half Word read from the SPI bus */return SPI_I2S_ReceiveData(FLASH_SPI);
}/*** @brief  向FLASH发送 写使能 命令* @param  none* @retval none*/
void SPI_FLASH_WriteEnable(void)
{/* 通讯开始:CS低 */SPI_FLASH_CS_LOW();/* 发送写使能命令*/SPI_FLASH_SendByte(W25X_WriteEnable);/*通讯结束:CS高 */SPI_FLASH_CS_HIGH();
}/*** @brief  等待WIP(BUSY)标志被置0,即等待到FLASH内部数据写入完毕* @param  none* @retval none*/
void SPI_FLASH_WaitForWriteEnd(void)
{uint8_t FLASH_Status = 0;/* 选择 FLASH: CS 低 */SPI_FLASH_CS_LOW();/* 发送 读状态寄存器 命令 */SPI_FLASH_SendByte(W25X_ReadStatusReg);SPITimeout = SPIT_FLAG_TIMEOUT;/* 若FLASH忙碌,则等待 */do{/* 读取FLASH芯片的状态寄存器 */FLASH_Status = SPI_FLASH_SendByte(Dummy_Byte);  {if((SPITimeout--) == 0) {SPI_TIMEOUT_UserCallback(4);return;}} }while ((FLASH_Status & WIP_Flag) == SET); /* 正在写入标志 *//* 停止信号  FLASH: CS 高 */SPI_FLASH_CS_HIGH();
}//进入掉电模式
void SPI_Flash_PowerDown(void)
{ /* 选择 FLASH: CS 低 */SPI_FLASH_CS_LOW();/* 发送 掉电 命令 */SPI_FLASH_SendByte(W25X_PowerDown);/* 停止信号  FLASH: CS 高 */SPI_FLASH_CS_HIGH();
}   //唤醒
void SPI_Flash_WAKEUP(void)
{/*选择 FLASH: CS 低 */SPI_FLASH_CS_LOW();/* 发上 上电 命令 */SPI_FLASH_SendByte(W25X_ReleasePowerDown);/* 停止信号 FLASH: CS 高 */SPI_FLASH_CS_HIGH();                   //等待TRES1
}   /*** @brief  等待超时回调函数* @param  None.* @retval None.*/
static  uint16_t SPI_TIMEOUT_UserCallback(uint8_t errorCode)
{/* 等待超时后的处理,输出错误信息 */FLASH_ERROR("SPI 等待超时!errorCode = %d",errorCode);return 0;
}/*********************************************END OF FILE**********************/

秉火429笔记之十七 SPI--操作FLASH相关推荐

  1. 秉火429笔记之七位带操作

    目录 位带概况 位带概况 位操作,简单的理解,可以单独的对一个比特位读和写,51单片机非常常见.类如,51单片机通过关键字sbit来实现位定义,STM32并没有这个关键字,而是通过访问位带别名区(Bi ...

  2. 秉火429笔记之八 RCC时钟

    目录 1. RCC 作用概述 2. RCC框图剖析-时钟树 3. 编程要点 4. 源码实例 1. RCC 作用概述 RCC :reset clock control 复位和时钟控制器. 设置系统时钟S ...

  3. 秉火429笔记之十八 ETH--以太网

    目录 1. 以太网简介 2.  SMI 接口 (站管理接口) 2.1 SMI帧格式 2.2 SMI 读写操作 3. MII 和 RMII接口 4. MAC 802.3 4.1 MAC 802.3 帧格 ...

  4. 秉火429笔记之十五 DMA--直接存储区访问

    目录 1. 简介 2. 功能 2.1 外设通道 2.2 仲裁器 2.3 FIFO 2.4 存储器端口和外设端口 2.5 编程接口 3. DMA数据配置 1. 简介 直接存储器访问 (DMA) 用于在外 ...

  5. 秉火429笔记之十二 看门狗

    目录 1. 概述 2. 独立看门狗(IWDG) 3. 窗口看门狗(WWDG) 1. 概述 STM32有两个看门狗,一个是独立看门狗,另外一个是窗口看门狗,独立看门狗号称宠物狗,窗口看门狗号称警犬. 一 ...

  6. 秉火429笔记之十三 通信基本概念

    目录 1. 串口通讯与并口通讯 2. 全双工.半双工及单工通讯 3. 同步通讯与异步通讯 4. 通信速率 1. 串口通讯与并口通讯 按数据传送的方式,通讯可分为串行通讯与并行通讯,串行通讯是指设备之间 ...

  7. 秉火429笔记之十 EXTI-外部中断/事件

    目录 1. EXTI 简介 2. EXTI 功能框图 3. 功能点 4. 编码要点 1. EXTI 简介 外部中断/事件控制器(EXTI)管理了控制器的23个中断/事件线.每个中断/事件线都对应有一个 ...

  8. 秉火429笔记之十六 I2C--操作EEPROM

    目录 1. I2C 概述 2. 硬件设计 3. 代码实现 1. I2C 概述 参考该链接,不在赘述:https://blog.csdn.net/XieWinter/article/details/91 ...

  9. 秉火429笔记之二寄存器

    目录 1. 寄存器概述 2. STM32芯片架构 3. STM32 存储器映射 1. 寄存器概述 寄存器是中央处理器内的组成部分.寄存器是有限存贮容量的高速存贮部件,它们可用来暂存指令.数据和地址.在 ...

最新文章

  1. EF 4.1 MSDN公约的翻译和整理
  2. 零下273.14度稳定运行!中科院自主研发稀释制冷机,高端科研仪器取得突破进展...
  3. C# 采用系统委托的方式处理线程内操作窗体控件(转载)
  4. awaitUntil() 方法的使用
  5. JS制作日历小事件和数码时钟--JavaScript实例集锦(初学)
  6. 校办研修之计算机培训简报,“2018校本研修培训”第二期学习简报
  7. linux 网络 路由,网络路由的顺序在Linux中是否重要?
  8. 移动国家号(MCC)
  9. 软件工程 speedsnail 第二次冲刺1次
  10. 10个最重要SQL Server事务日志神话
  11. 1 为发现 php,magento 1.8.1中发现sitemap模块的一个bug
  12. i.MX6 交叉编译zlib、yasm、xvidcore、libpng、x264、jpegsrc、ffmpeg
  13. 深度学习在语音识别中的算法、应用、数据集、行业分析
  14. 51单片机课设--篮球计分器
  15. 移动端旅游电商靠什么吸引了金主?
  16. PHP打造的轻量级单文件管理系统FileBox V1.10.0.2版
  17. java poi 追加_使用POI 向Excel中追加数据
  18. 再谈宋星博客的留言与seo培训联盟
  19. 公司-摩拜单车:摩拜单车
  20. 公司邮箱怎么注册?公司企业邮箱怎么登录?

热门文章

  1. java面试题 sql_SQL 面试题目及答案 | java面试题
  2. [篇五章三]-关于 Windows 10 安装好后系统自带的微软输入法没有输入框的 BUG 解决办法
  3. 北京外企 外服控股:人服国企借道资产重组上市,发 力数字化谋新篇
  4. OSSIM开源安全信息管理系统(十七)
  5. Linux删除只读文件系统
  6. 一个月薪5k+的广州安全服务实习生的真实生活
  7. Android9显示模糊,app进入后台显示模糊效果
  8. 魔兽世界怀旧服——按键精灵 Java版(自动技能,练级释放者)
  9. 在线答题小程序数据库设计
  10. ui设计教程分享:关于Logo设计要素