秉火429笔记之十七 SPI--操作FLASH
目录
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相关推荐
- 秉火429笔记之七位带操作
目录 位带概况 位带概况 位操作,简单的理解,可以单独的对一个比特位读和写,51单片机非常常见.类如,51单片机通过关键字sbit来实现位定义,STM32并没有这个关键字,而是通过访问位带别名区(Bi ...
- 秉火429笔记之八 RCC时钟
目录 1. RCC 作用概述 2. RCC框图剖析-时钟树 3. 编程要点 4. 源码实例 1. RCC 作用概述 RCC :reset clock control 复位和时钟控制器. 设置系统时钟S ...
- 秉火429笔记之十八 ETH--以太网
目录 1. 以太网简介 2. SMI 接口 (站管理接口) 2.1 SMI帧格式 2.2 SMI 读写操作 3. MII 和 RMII接口 4. MAC 802.3 4.1 MAC 802.3 帧格 ...
- 秉火429笔记之十五 DMA--直接存储区访问
目录 1. 简介 2. 功能 2.1 外设通道 2.2 仲裁器 2.3 FIFO 2.4 存储器端口和外设端口 2.5 编程接口 3. DMA数据配置 1. 简介 直接存储器访问 (DMA) 用于在外 ...
- 秉火429笔记之十二 看门狗
目录 1. 概述 2. 独立看门狗(IWDG) 3. 窗口看门狗(WWDG) 1. 概述 STM32有两个看门狗,一个是独立看门狗,另外一个是窗口看门狗,独立看门狗号称宠物狗,窗口看门狗号称警犬. 一 ...
- 秉火429笔记之十三 通信基本概念
目录 1. 串口通讯与并口通讯 2. 全双工.半双工及单工通讯 3. 同步通讯与异步通讯 4. 通信速率 1. 串口通讯与并口通讯 按数据传送的方式,通讯可分为串行通讯与并行通讯,串行通讯是指设备之间 ...
- 秉火429笔记之十 EXTI-外部中断/事件
目录 1. EXTI 简介 2. EXTI 功能框图 3. 功能点 4. 编码要点 1. EXTI 简介 外部中断/事件控制器(EXTI)管理了控制器的23个中断/事件线.每个中断/事件线都对应有一个 ...
- 秉火429笔记之十六 I2C--操作EEPROM
目录 1. I2C 概述 2. 硬件设计 3. 代码实现 1. I2C 概述 参考该链接,不在赘述:https://blog.csdn.net/XieWinter/article/details/91 ...
- 秉火429笔记之二寄存器
目录 1. 寄存器概述 2. STM32芯片架构 3. STM32 存储器映射 1. 寄存器概述 寄存器是中央处理器内的组成部分.寄存器是有限存贮容量的高速存贮部件,它们可用来暂存指令.数据和地址.在 ...
最新文章
- EF 4.1 MSDN公约的翻译和整理
- 零下273.14度稳定运行!中科院自主研发稀释制冷机,高端科研仪器取得突破进展...
- C# 采用系统委托的方式处理线程内操作窗体控件(转载)
- awaitUntil() 方法的使用
- JS制作日历小事件和数码时钟--JavaScript实例集锦(初学)
- 校办研修之计算机培训简报,“2018校本研修培训”第二期学习简报
- linux 网络 路由,网络路由的顺序在Linux中是否重要?
- 移动国家号(MCC)
- 软件工程 speedsnail 第二次冲刺1次
- 10个最重要SQL Server事务日志神话
- 1 为发现 php,magento 1.8.1中发现sitemap模块的一个bug
- i.MX6 交叉编译zlib、yasm、xvidcore、libpng、x264、jpegsrc、ffmpeg
- 深度学习在语音识别中的算法、应用、数据集、行业分析
- 51单片机课设--篮球计分器
- 移动端旅游电商靠什么吸引了金主?
- PHP打造的轻量级单文件管理系统FileBox V1.10.0.2版
- java poi 追加_使用POI 向Excel中追加数据
- 再谈宋星博客的留言与seo培训联盟
- 公司-摩拜单车:摩拜单车
- 公司邮箱怎么注册?公司企业邮箱怎么登录?
热门文章
- java面试题 sql_SQL 面试题目及答案 | java面试题
- [篇五章三]-关于 Windows 10 安装好后系统自带的微软输入法没有输入框的 BUG 解决办法
- 北京外企 外服控股:人服国企借道资产重组上市,发 力数字化谋新篇
- OSSIM开源安全信息管理系统(十七)
- Linux删除只读文件系统
- 一个月薪5k+的广州安全服务实习生的真实生活
- Android9显示模糊,app进入后台显示模糊效果
- 魔兽世界怀旧服——按键精灵 Java版(自动技能,练级释放者)
- 在线答题小程序数据库设计
- ui设计教程分享:关于Logo设计要素