第 25 章 SPI

您所在的位置:网站首页 野火f429信号频率 第 25 章 SPI

第 25 章 SPI

2023-12-20 15:35| 来源: 网络整理| 查看: 265

注意:资料查询请查看本人资源(免费)

25.1 SPI 协议简介 1.物理层

SPI 通讯使用 3 条总线及片选线,3 条总线分别为 SCK、MOSI、MISO,片选线

(1) ( Slave Select):从设备选择信号线,常称为片选信号线,也称为 NSS、CS,以下用 NSS 表示。

(2) SCK (Serial Clock):时钟信号线,用于通讯数据同步。它由通讯主机产生,决定了通讯的速率,

不同的设备支持的最高时钟频率不一样,如 STM32 的 SPI 时钟频率最大为 fpclk/2,两个设备之间

通讯时,通讯速率受限于低速设备。

(3) MOSI (Master Output,Slave Input):主设备输出/从设备输入引脚。主机的数据从这条信号线输

出,从机由这条信号线读入主机发送的数据,即这条线上数据的方向为主机到从机。

(4) MISO(Master Input,,Slave Output):主设备输入/从设备输出引脚。主机从这条信号线读入数

据,从机的数据由这条信号线输出到主机,即在这条线上数据的方向为从机到主机。

2.协议层

与 I2C 的类似,SPI 协议定义了通讯的起始和停止信号、数据有效性、时钟同步

(1)SPI 基本通讯过程

(2)通讯的起始和停止信号

(3)数据有效性

(4)CPOL/CPHA 及通讯模式

25.2 STM32 的 SPI 特性及架构

1.STM32 的 SPI 外设简介

2.STM32 的 SPI 架构剖析

(1)通讯引脚

(2)时钟控制逻辑

(3)数据控制逻辑

(4)整体控制逻辑

3.通讯过程

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

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

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

把数据一位一位地存储进接收缓冲区中;

(4) 当发送完一帧数据的时候,“状态寄存器 SR”中的“TXE 标志位”会被置 1,表示传输完一

帧,发送缓冲区已空;

(5) 等待到“TXE 标志位”为 1 时,若还要继续发送数据,则再次往“数据寄存器 DR”写入数据

即可;

假如我们使能了 TXE 或 RXNE 中断,TXE 或 RXNE 置 1 时会产生 SPI 中断信号,进入同一个中

断服务函数,到 SPI 中断服务程序后,可通过检查寄存器位来了解是哪一个事件,再分别进行处

理。也可以使用 DMA 方式来收发“数据寄存器 DR”中的数据。

25.3 SPI 初始化结构体详解

(1) SPI_Direction

(2) SPI_Mode

(3) SPI_DataSize

(4) SPI_CPOL 和 SPI_CPHA

(5) SPI_NSS

(6) SPI_BaudRatePrescaler

(7) SPI_FirstBit

(8) SPI_CRCPolynomial

25.4 SPI—读写串行 FLASH 实验 1. 硬件设计 2.软件设计 2.1 编程要点

(1) 初始化通讯使用的目标引脚及端口时钟;

(2) 使能 SPI 外设的时钟;

(3) 配置 SPI 外设的模式、地址、速率等参数并使能 SPI 外设;

(4) 编写基本 SPI 按字节收发的函数;

(5) 编写对 FLASH 擦除及读写操作的的函数;

(6) 编写测试程序,对读写数据进行校验。

2.2 代码分析

(1)SPI 硬件相关宏定义

(2)初始化 SPI 的 GPIO

1… 使用 GPIO_InitTypeDef 定义 GPIO 初始化结构体变量,以便下面用于存储 GPIO 配置;

2… 调用库函数 RCC_APB2PeriphClockCmd 来使能 SPI 引脚使用的 GPIO 端口时钟。

3… 向 GPIO 初始化结构体赋值,把 SCK/MOSI/MISO 引脚初始化成复用推挽模式。而 CS(NSS) 引脚由于使用软件控制,我们把它配置为普通的推挽输出模式。

4… 使用以上初始化结构体的配置,调用 GPIO_Init 函数向寄存器写入参数,完成 GPIO 的初始化。

(3)配置 SPI 的模式

(4)使用 SPI 发送和接收一个字节的数据

1… 本函数中不包含 SPI 起始和停止信号,只是收发的主要过程,所以在调用本函数前后要做好

起始和停止信号的操作;

2… 对 SPITimeout 变量赋值为宏 SPIT_FLAG_TIMEOUT。这个 SPITimeout 变量在下面的 while 循

环中每次循环减 1,该循环通过调用库函数 SPI_I2S_GetFlagStatus 检测事件,若检测到事件,则

进入通讯的下一阶段,若未检测到事件则停留在此处一直检测,当检测 SPIT_FLAG_TIMEOUT

次都还没等待到事件则认为通讯失败,调用的 SPI_TIMEOUT_UserCallback 输出调试信息,并退

出通讯;

3… 通过检测 TXE 标志,获取发送缓冲区的状态,若发送缓冲区为空,则表示可能存在的上一个

数据已经发送完毕;

4… 等待至发送缓冲区为空后,调用库函数 SPI_I2S_SendData 把要发送的数据“byte”写入到 SPI

的数据寄存器 DR,写入 SPI 数据寄存器的数据会存储到发送缓冲区,由 SPI 外设发送出去;

5… 写入完毕后等待 RXNE 事件,即接收缓冲区非空事件。由于 SPI 双线全双工模式下 MOSI 与

MISO 数据传输是同步的 (请对比“SPI 通讯过程”阅读),当接收缓冲区非空时,表示上面的数据

发送完毕,且接收缓冲区也收到新的数据;

6… 等待至接收缓冲区非空时,通过调用库函数 SPI_I2S_ReceiveData 读取 SPI 的数据寄存器

DR,就可以获取接收缓冲区中的新数据了。代码中使用关键字“return”把接收到的这个数据

作为 SPI_FLASH_SendByte 函数的返回值,所以我们可以看到在下面定义的 SPI 接收数据函数

SPI_FLASH_ReadByte,它只是简单地调用了 SPI_FLASH_SendByte 函数发送数据“Dummy_Byte”,

然后获取其返回值 (因为不关注发送的数据,所以此时的输入参数“Dummy_Byte”可以为任意

值)。可以这样做的原因是 SPI 的接收过程和发送过程实质是一样的,收发同步进行,关键在于

我们的上层应用中,关注的是发送还是接收的数据。

(5)控制 FLASH 的指令

(6)定义 FLASH 指令编码表

(7)读取 FLASH 芯片 ID

(8)FLASH 写使能以及读取当前状态

(9)FLASH 扇区擦除

(10)FLASH 的页写入

(11)不定量数据写入

(12)从 FLASH 读取数据

bsp_DMA_SPI.h

#include "stm32f10x.h" /*SPI 接口定义-开头 ****************************/ #define FLASH_SPIx SPI1 #define FLASH_SPI_APBxClock_FUN RCC_APB2PeriphClockCmd #define FLASH_SPI_CLK RCC_APB2Periph_SPI1 //CS(NSS) 引脚 片选选普通 GPIO 即可 #define FLASH_SPI_CS_APBxClock_FUN RCC_APB2PeriphClockCmd #define FLASH_SPI_CS_CLK RCC_APB2Periph_GPIOC #define FLASH_SPI_CS_PORT GPIOC #define FLASH_SPI_CS_PIN GPIO_Pin_0 //SCK 引脚 #define FLASH_SPI_SCK_APBxClock_FUN RCC_APB2PeriphClockCmd #define FLASH_SPI_SCK_CLK RCC_APB2Periph_GPIOA #define FLASH_SPI_SCK_PORT GPIOA #define FLASH_SPI_SCK_PIN GPIO_Pin_5 //MISO 引脚 #define FLASH_SPI_MISO_APBxClock_FUN RCC_APB2PeriphClockCmd #define FLASH_SPI_MISO_CLK RCC_APB2Periph_GPIOA #define FLASH_SPI_MISO_PORT GPIOA #define FLASH_SPI_MISO_PIN GPIO_Pin_6 //MOSI 引脚 #define FLASH_SPI_MOSI_APBxClock_FUN RCC_APB2PeriphClockCmd #define FLASH_SPI_MOSI_CLK RCC_APB2Periph_GPIOA #define FLASH_SPI_MOSI_PORT GPIOA #define FLASH_SPI_MOSI_PIN GPIO_Pin_7 #define FLASH_SPI_CS_LOW() GPIO_ResetBits( FLASH_SPI_CS_PORT, FLASH_SPI_CS_PIN ) #define FLASH_SPI_CS_HIGH() GPIO_SetBits( FLASH_SPI_CS_PORT, FLASH_SPI_CS_PIN ) /* WIP(busy) 标志,FLASH 内部正在写入 */ #define WIP_Flag 0x01 /*自添*/ #define SPIT_FLAG_TIMEOUT ((uint32_t)0x1000) #define SPIT_LONG_TIMEOUT ((uint32_t)(10 * SPIT_FLAG_TIMEOUT)) #define FLASH_WriteAddress 0x00 #define FLASH_ReadAddress 0x00 #define BufferSize 20 #define BUFFER_SIZE 32 #define SPI_FLASH_PerWritePageSize 256 #define SPI_FLASH_PageSize 256 #define FLASH_SectorToErase 0x00 #define PASSED 0x01 /*SPI 接口定义-结尾 ****************************/ /*FLASH 常用命令 */ #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 sFLASH_ID 0XEF4017 #define Dummy_Byte 0xFF /*函数声明*/ void SPI_FLASH_Init(void); static uint32_t SPI_TIMEOUT_UserCallback(uint8_t errorCode); void FLASH_ERROR(char * errorCode); u8 SPI_FLASH_SendByte(u8 byte); u8 SPI_FLASH_ReadByte(void); u32 SPI_FLASH_ReadID(void); void SPI_FLASH_WriteEnable(void); void SPI_FLASH_WaitForWriteEnd(void); void SPI_FLASH_SectorErase(u32 SectorAddr); void SPI_FLASH_PageWrite(u8* pBuffer, u32 WriteAddr, u16 NumByteToWrite); void SPI_FLASH_BufferWrite(u8* pBuffer, u32 WriteAddr, u16 NumByteToWrite); void SPI_FLASH_BufferRead(u8* pBuffer, u32 ReadAddr, u16 NumByteToRead); uint8_t Buffercmp(uint8_t* pBuffer,uint8_t* pBuffer1, uint16_t BufferLength); extern uint32_t SPITimeout; extern const uint32_t aSRC_Const_Buffer[BUFFER_SIZE];

bsp_DMA_SPI.c

#include "bsp_DMA_SPI.h" uint32_t SPITimeout; const uint32_t aSRC_Const_Buffer[BUFFER_SIZE]= { 0x01020304,0x05060708,0x090A0B0C,0x0D0E0F10, 0x11121314,0x15161718,0x191A1B1C,0x1D1E1F20, 0x21222324,0x25262728,0x292A2B2C,0x2D2E2F30, 0x31323334,0x35363738,0x393A3B3C,0x3D3E3F40, 0x41424344,0x45464748,0x494A4B4C,0x4D4E4F50, 0x51525354,0x55565758,0x595A5B5C,0x5D5E5F60, 0x61626364,0x65666768,0x696A6B6C,0x6D6E6F70, 0x71727374,0x75767778,0x797A7B7C,0x7D7E7F80 }; /** * @brief SPI_FLASH 初始化 * @param 无 * @retval 无 */ void SPI_FLASH_Init(void) { SPI_InitTypeDef SPI_InitStructure; GPIO_InitTypeDef GPIO_InitStructure; /* 使能 SPI 时钟 */ FLASH_SPI_APBxClock_FUN ( FLASH_SPI_CLK, ENABLE ); /* 使能 SPI 引脚相关的时钟 */ FLASH_SPI_CS_APBxClock_FUN ( FLASH_SPI_CS_CLK|FLASH_SPI_SCK_CLK| FLASH_SPI_MISO_PIN|FLASH_SPI_MOSI_PIN, ENABLE ); /* 配置 SPI 的 CS 引脚,普通 IO 即可 */ GPIO_InitStructure.GPIO_Pin = FLASH_SPI_CS_PIN; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_Init(FLASH_SPI_CS_PORT, &GPIO_InitStructure); /* 配置 SPI 的 SCK 引脚 */ GPIO_InitStructure.GPIO_Pin = FLASH_SPI_SCK_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_Init(FLASH_SPI_SCK_PORT, &GPIO_InitStructure); /* 配置 SPI 的 MF103-霸道引脚 */ GPIO_InitStructure.GPIO_Pin = FLASH_SPI_MISO_PIN; GPIO_Init(FLASH_SPI_MISO_PORT, &GPIO_InitStructure); /* 配置 SPI 的 MOSI 引脚 */ GPIO_InitStructure.GPIO_Pin = FLASH_SPI_MOSI_PIN; GPIO_Init(FLASH_SPI_MOSI_PORT, &GPIO_InitStructure); /* 停止信号 FLASH: CS 引脚高电平 */ FLASH_SPI_CS_HIGH(); /* SPI 模式配置 */ // FLASH 芯片 支持 SPI 模式 0 及模式 3,据此设置 CPOL CPHA SPI_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_4; SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; SPI_InitStructure.SPI_CRCPolynomial = 7; SPI_Init(FLASH_SPIx, &SPI_InitStructure); /* 使能 SPI */ SPI_Cmd(FLASH_SPIx, ENABLE); } /** * @brief I2C 等待事件超时的情况下会调用这个函数来处理 * @param errorCode:错误代码,可以用来定位是哪个环节出错. * @retval 返回 0,表示 IIC 读取失败. */ static uint32_t SPI_TIMEOUT_UserCallback(uint8_t errorCode) { /* 使用串口 printf 输出错误信息,方便调试 */ printf("I2C 等待超时!errorCode = %d",errorCode); return 0; } void FLASH_ERROR(char * errorCode){ printf("I2C error !errorCode=%s",errorCode); } /** * @brief 使用 SPI 发送一个字节的数据 * @param byte:要发送的数据 * @retval 返回接收到的数据 */ u8 SPI_FLASH_SendByte(u8 byte) { SPITimeout = SPIT_FLAG_TIMEOUT; /* 等待发送缓冲区为空,TXE 事件 */ while (SPI_I2S_GetFlagStatus(FLASH_SPIx, SPI_I2S_FLAG_TXE) == RESET) { if ((SPITimeout--) == 0) return SPI_TIMEOUT_UserCallback(0); } /* 写入数据寄存器,把要写入的数据写入发送缓冲区 */ SPI_I2S_SendData(FLASH_SPIx, byte); SPITimeout = SPIT_FLAG_TIMEOUT; /* 等待接收缓冲区非空,RXNE 事件 */ while (SPI_I2S_GetFlagStatus(FLASH_SPIx, SPI_I2S_FLAG_RXNE) == RESET) { if ((SPITimeout--) == 0) return SPI_TIMEOUT_UserCallback(1); } /* 读取数据寄存器,获取接收缓冲区数据 */ return SPI_I2S_ReceiveData(FLASH_SPIx); } /** * @brief 使用 SPI 读取一个字节的数据 * @param 无 * @retval 返回接收到的数据 */ u8 SPI_FLASH_ReadByte(void) { return (SPI_FLASH_SendByte(Dummy_Byte)); } /** * @brief 读取 FLASH ID * @param 无 * @retval FLASH ID */ u32 SPI_FLASH_ReadID(void) { u32 Temp = 0, Temp0 = 0, Temp1 = 0, Temp2 = 0; /* 开始通讯:CS 低电平 */ FLASH_SPI_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 高电平 */ FLASH_SPI_CS_HIGH(); /* 把数据组合起来,作为函数的返回值 */ Temp = (Temp0 u8 FLASH_Status = 0; /* 选择 FLASH: CS 低 */ FLASH_SPI_CS_LOW(); /* 发送 读状态寄存器 命令 */ SPI_FLASH_SendByte(W25X_ReadStatusReg); /* 若 FLASH 忙碌,则等待 */ do { /* 读取 FLASH 芯片的状态寄存器 */ FLASH_Status = SPI_FLASH_SendByte(Dummy_Byte); } while ((FLASH_Status & WIP_Flag) == SET); /* 正在写入标志 */ /* 停止信号 FLASH: CS 高 */ FLASH_SPI_CS_HIGH(); } /** * @brief 擦除 FLASH 扇区 * @param SectorAddr:要擦除的扇区地址 * @retval 无 */ void SPI_FLASH_SectorErase(u32 SectorAddr) { /* 发送 FLASH 写使能命令 */ SPI_FLASH_WriteEnable(); SPI_FLASH_WaitForWriteEnd(); /* 擦除扇区 */ /* 选择 FLASH: CS 低电平 */ FLASH_SPI_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 高电平 */ FLASH_SPI_CS_HIGH(); /* 等待擦除完毕 */ SPI_FLASH_WaitForWriteEnd(); } /** * @brief 对 FLASH 按页写入数据,调用本函数写入数据前需要先擦除扇区 * @param pBuffer,要写入数据的指针 * @param WriteAddr,写入地址 * @param NumByteToWrite,写入数据长度,必须小于等于页大小 * @retval 无 */ void SPI_FLASH_PageWrite(u8* pBuffer, u32 WriteAddr, u16 NumByteToWrite) { /* 发送 FLASH 写使能命令 */ SPI_FLASH_WriteEnable(); /* 选择 FLASH: CS 低电平 */ FLASH_SPI_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 small!"); } /* 写入数据 */ while (NumByteToWrite--) { /* 发送当前要写入的字节数据 */ SPI_FLASH_SendByte(*pBuffer); /* 指向下一字节数据 */ pBuffer++; } /* 停止信号 FLASH: CS 高电平 */ FLASH_SPI_CS_HIGH(); /* 等待写入完毕 */ SPI_FLASH_WaitForWriteEnd(); } /** * @brief 对 FLASH 写入数据,调用本函数写入数据前需要先擦除扇区 * @param pBuffer,要写入数据的指针 * @param WriteAddr,写入地址 * @param NumByteToWrite,写入数据长度 * @retval 无 7 */ void SPI_FLASH_BufferWrite(u8* pBuffer, u32 WriteAddr, u16 NumByteToWrite) { u8 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; /* 先写完 count 个数据,为的是让下一次要写的地址对齐 */ 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(u8* pBuffer, u32 ReadAddr, u16 NumByteToRead) { /* 选择 FLASH: CS 低电平 */ FLASH_SPI_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 高电平 */ FLASH_SPI_CS_HIGH(); } uint8_t Buffercmp(uint8_t* pBuffer,uint8_t* pBuffer1, uint16_t BufferLength) { /* 数据长度递减 */ while (BufferLength--) { /* 判断两个数据源是否对应相等 */ if (*pBuffer != *pBuffer1) { /* 对应数据源不相等马上退出函数,并返回 0 */ return 0; } /* 递增两个数据源的地址指针 */ pBuffer++; pBuffer1++; } /* 完成判断并且对应数据相对 */ return 1; }

(13)main 函数

#include "bsp_led.h" #include "./SPI/bsp_DMA_SPI.h" #include "bsp_usart.h" #include "bsp_led.h" void Delay(__IO u32 nCount); uint8_t SPI_Buf_Write[255]={"王祖豪是个好人么"}; uint8_t SPI_Buf_Read[255]; u32 DeviceID; u32 FlashID; uint8_t TransferStatus1; /** * @brief 主函数 * @param 无 * @retval 无 */ int main(void) { LED_GPIO_Config(); LED_BLUE; Delay(1000); /* 配置串口 1 为:115200 8-N-1 */ USART_Config(); printf("\r\n 这是一个 8Mbyte 串行 flash(W25Q64) 实验 \r\n"); /* 8M 串行 flash W25Q64 初始化 */ SPI_FLASH_Init(); /* 获取 Flash Device ID */ DeviceID = SPI_FLASH_ReadID(); Delay( 200 ); /* 获取 SPI Flash ID */ FlashID = SPI_FLASH_ReadID(); printf("\r\n FlashID is 0x%X,\ Manufacturer Device ID is 0x%X\r\n", FlashID, DeviceID); /* 检验 SPI Flash ID */ if (FlashID == sFLASH_ID) { printf("\r\n 检测到串行 flash W25Q64 !\r\n"); /* 擦除将要写入的 SPI FLASH 扇区,FLASH 写入前要先擦除 */ // 这里擦除 4K,即一个扇区,擦除的最小单位是扇区 SPI_FLASH_SectorErase(FLASH_SectorToErase); /* 将发送缓冲区的数据写到 flash 中 */ // 这里写一页,一页的大小为 256 个字节 SPI_FLASH_BufferWrite(SPI_Buf_Write, FLASH_WriteAddress, BufferSize); printf("\r\n 写入的数据为:%s \r\t", SPI_Buf_Write); /* 将刚刚写入的数据读出来放到接收缓冲区中 */ SPI_FLASH_BufferRead(SPI_Buf_Read, FLASH_ReadAddress, BufferSize); printf("\r\n 读出的数据为:%s \r\n", SPI_Buf_Read); /* 检查写入的数据与读出的数据是否相等 */ TransferStatus1 = Buffercmp(SPI_Buf_Write, SPI_Buf_Read, BufferSize); if ( PASSED == TransferStatus1 ) { LED_GREEN; printf("\r\n 8M 串行 flash(W25Q64) 测试成功!\n\r"); } else { LED_RED; printf("\r\n 8M 串行 flash(W25Q64) 测试失败!\n\r"); } }// if (FlashID == sFLASH_ID) else// if (FlashID == sFLASH_ID) { LED_RED; printf("\r\n 获取不到 W25Q64 ID!\n\r"); } while (1); } void Delay(__IO uint32_t nCount) //简单的延时函数 { for(; nCount != 0; nCount--); } /*********************************************END OF FILE**********************/


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3