赞
踩
SPI协议介绍
SPI协议是由摩托罗拉公司提出的通讯协议(Serial Peripheral Interface),即串行外围设备接口, 是一种高速全双工的通信总线。它被广泛地使用在ADC、LCD等设备与MCU间,要求通讯速率较高的场合。
学习本章时,可与I2C章节对比阅读,体会两种通讯总线的差异以及EEPROM存储器与FLASH存储器的区别。下面我们分别对SPI协议的物理层及协议层进行讲解。
SPI通讯设备之间的常用连接方式见图
SPI通讯使用3条总线及片选线,3条总线分别为SCK、MOSI、MISO,片选线为SS,它们的作用介绍如下:
(1) SS ( Slave Select):从设备选择信号线,常称为片选信号线,也称为NSS、CS,以下用NSS表示。当有多个SPI从设备与SPI主机相连时, 设备的其它信号线SCK、MOSI及MISO同时并联到相同的SPI总线上,即无论有多少个从设备,都共同只使用这3条总线; 而每个从设备都有独立的这一条NSS信号线,本信号线独占主机的一个引脚,即有多少个从设备,就有多少条片选信号线。 I2C协议中通过设备地址来寻址、选中总线上的某个设备并与其进行通讯;而SPI协议中没有设备地址,它使用NSS信号线来寻址, 当主机要选择从设备时,把该从设备的NSS信号线设置为低电平,该从设备即被选中,即片选有效, 接着主机开始与被选中的从设备进行SPI通讯。所以SPI通讯以NSS线置低电平为开始信号,以NSS线被拉高作为结束信号。
(2) SCK (Serial Clock):时钟信号线,用于通讯数据同步。它由通讯主机产生,决定了通讯的速率,不同的设备支持的最高时钟频率不一样, 如STM32的SPI时钟频率最大为fpclk/2,两个设备之间通讯时,通讯速率受限于低速设备。
(3) MOSI (Master Output, Slave Input):主设备输出/从设备输入引脚。主机的数据从这条信号线输出, 从机由这条信号线读入主机发送的数据,即这条线上数据的方向为主机到从机。
(4) MISO (Master Input,,Slave Output):主设备输入/从设备输出引脚。主机从这条信号线读入数据, 从机的数据由这条信号线输出到主机,即在这条线上数据的方向为从机到主机。
SPI通讯时序
由CPOL及CPHA的不同状态,SPI分成了四种模式,如图, 主机与从机需要工作在相同的模式下才可以正常通讯,实际中采用较多的是“模式0”与“模式3”。
初始化SPI的 GPIO
- void SPI_FLASH_Init(void)
- {
- SPI_InitTypeDef SPI_InitStructure;
- GPIO_InitTypeDef GPIO_InitStructure;
-
- /* 使能GPIO和SPI时钟 */
- FLASH_SPI_APBxClock_FUN ( FLASH_SPI_CLK, ENABLE );
- FLASH_SPI_SCK_APBxClock_FUN ( FLASH_SPI_SCK_CLK, ENABLE );
- FLASH_SPI_MISO_APBxClock_FUN ( FLASH_SPI_MISO_CLK, ENABLE );
- FLASH_SPI_MOSI_APBxClock_FUN ( FLASH_SPI_MOSI_CLK, ENABLE );
- FLASH_SPI_CS_APBxClock_FUN ( FLASH_SPI_CS_CLK, ENABLE );
-
- /* 配置SPI功能引脚:SCK 时钟引脚 */
- GPIO_InitStructure.GPIO_Pin = FLASH_SPI_SCK_PIN;
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
- GPIO_Init(FLASH_SPI_SCK_PORT, &GPIO_InitStructure);
-
- /* 配置SPI功能引脚:MISO 主机输入从机输出引脚 */
- GPIO_InitStructure.GPIO_Pin = FLASH_SPI_MISO_PIN;
- GPIO_Init(FLASH_SPI_MISO_PORT, &GPIO_InitStructure);
-
- /* 配置SPI功能引脚:MISO 主机输出从机输入引脚 */
- GPIO_InitStructure.GPIO_Pin = FLASH_SPI_MOSI_PIN;
- GPIO_Init(FLASH_SPI_MOSI_PORT, &GPIO_InitStructure);
-
- /* 配置SPI功能引脚:CS 串行Flash片选引脚 */
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
- GPIO_InitStructure.GPIO_Pin = FLASH_SPI_CS_PIN;
- GPIO_Init(FLASH_SPI_CS_PORT, &GPIO_InitStructure);
-
- /* 首先禁用串行Flash,等需要操作串行Flash时再使能即可 */
- FLASH_SPI_CS_DISABLE();
-
- /* SPI外设配置 */
- /*
- * FLASH芯片:
- * 在CLK上升沿时到DIO数据采样输入.
- * 在CLK下降沿时在DIO进行数据输出。
- * 据此设置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);
- }
- void SPI_FLASH_BulkErase(void)
- {
- /* 发送FLASH写使能命令 */
- SPI_FLASH_WriteEnable();
-
- /* 整片擦除 Erase */
- /* 选择串行FLASH: CS低电平 */
- FLASH_SPI_CS_ENABLE();
- /* 发送整片擦除指令*/
- SPI_FLASH_SendByte(W25X_ChipErase);
- /* 禁用串行FLASH: CS高电平 */
- FLASH_SPI_CS_DISABLE();
-
- /* 等待擦除完毕*/
- SPI_FLASH_WaitForWriteEnd();
- }
- void SPI_FLASH_BufferWrite(u8* pBuffer, u32 WriteAddr, u16 NumByteToWrite)
- {
- u8 NumOfPage = 0, NumOfSingle = 0, Addr = 0, count = 0, temp = 0;
-
- Addr = WriteAddr % SPI_FLASH_PageSize;
- count = SPI_FLASH_PageSize - Addr;
- NumOfPage = NumByteToWrite / SPI_FLASH_PageSize;
- NumOfSingle = NumByteToWrite % SPI_FLASH_PageSize;
-
- if (Addr == 0) /* 若地址与 SPI_FLASH_PageSize 对齐 */
- {
- if (NumOfPage == 0) /* NumByteToWrite < SPI_FLASH_PageSize */
- {
- 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);
- }
- }
- else /* 若地址与 SPI_FLASH_PageSize 不对齐 */
- {
- if (NumOfPage == 0) /* NumByteToWrite < SPI_FLASH_PageSize */
- {
- if (NumOfSingle > count) /* (NumByteToWrite + WriteAddr) > SPI_FLASH_PageSize */
- {
- temp = NumOfSingle - count;
-
- SPI_FLASH_PageWrite(pBuffer, WriteAddr, count);
- WriteAddr += count;
- pBuffer += count;
-
- SPI_FLASH_PageWrite(pBuffer, WriteAddr, temp);
- }
- else
- {
- SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumByteToWrite);
- }
- }
- else /* NumByteToWrite > SPI_FLASH_PageSize */
- {
- 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);
- }
- }
- }
- }
- void SPI_FLASH_BufferRead(u8* pBuffer, u32 ReadAddr, u16 NumByteToRead)
- {
- /* 选择串行FLASH: CS低电平 */
- FLASH_SPI_CS_ENABLE();
-
- /* 发送 读 指令 */
- 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_DISABLE();
- }
函数介绍
1.bsp_spi_flash.c
- /* 包含头文件 ----------------------------------------------------------------*/
- #include "bsp/spi_flash/bsp_spi_flash.h"
-
- /* 私有类型定义 --------------------------------------------------------------*/
- /* 私有宏定义 ----------------------------------------------------------------*/
- #define SPI_FLASH_PageSize 256
- #define SPI_FLASH_PerWritePageSize 256
- #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
- /* 私有变量 ------------------------------------------------------------------*/
- /* 扩展变量 ------------------------------------------------------------------*/
- /* 私有函数原形 --------------------------------------------------------------*/
- /* 函数体 --------------------------------------------------------------------*/
-
- /**
- * 函数功能: 串行FLASH初始化
- * 输入参数: 无
- * 返 回 值: uint32_t:返回串行Flash型号ID
- * 说 明:初始化串行Flash底层驱动GPIO和SPI外设
- */
- void SPI_FLASH_Init(void)
- {
- SPI_InitTypeDef SPI_InitStructure;
- GPIO_InitTypeDef GPIO_InitStructure;
-
- /* 使能GPIO和SPI时钟 */
- FLASH_SPI_APBxClock_FUN ( FLASH_SPI_CLK, ENABLE );
- FLASH_SPI_SCK_APBxClock_FUN ( FLASH_SPI_SCK_CLK, ENABLE );
- FLASH_SPI_MISO_APBxClock_FUN ( FLASH_SPI_MISO_CLK, ENABLE );
- FLASH_SPI_MOSI_APBxClock_FUN ( FLASH_SPI_MOSI_CLK, ENABLE );
- FLASH_SPI_CS_APBxClock_FUN ( FLASH_SPI_CS_CLK, ENABLE );
-
- /* 配置SPI功能引脚:SCK 时钟引脚 */
- GPIO_InitStructure.GPIO_Pin = FLASH_SPI_SCK_PIN;
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
- GPIO_Init(FLASH_SPI_SCK_PORT, &GPIO_InitStructure);
-
- /* 配置SPI功能引脚:MISO 主机输入从机输出引脚 */
- GPIO_InitStructure.GPIO_Pin = FLASH_SPI_MISO_PIN;
- GPIO_Init(FLASH_SPI_MISO_PORT, &GPIO_InitStructure);
-
- /* 配置SPI功能引脚:MISO 主机输出从机输入引脚 */
- GPIO_InitStructure.GPIO_Pin = FLASH_SPI_MOSI_PIN;
- GPIO_Init(FLASH_SPI_MOSI_PORT, &GPIO_InitStructure);
-
- /* 配置SPI功能引脚:CS 串行Flash片选引脚 */
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
- GPIO_InitStructure.GPIO_Pin = FLASH_SPI_CS_PIN;
- GPIO_Init(FLASH_SPI_CS_PORT, &GPIO_InitStructure);
-
- /* 首先禁用串行Flash,等需要操作串行Flash时再使能即可 */
- FLASH_SPI_CS_DISABLE();
-
- /* SPI外设配置 */
- /*
- * FLASH芯片:
- * 在CLK上升沿时到DIO数据采样输入.
- * 在CLK下降沿时在DIO进行数据输出。
- * 据此设置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);
- }
-
- /**
- * 函数功能: 擦除扇区
- * 输入参数: SectorAddr:待擦除扇区地址,要求为4096倍数
- * 返 回 值: 无
- * 说 明:串行Flash最小擦除块大小为4KB(4096字节),即一个扇区大小,要求输入参数
- * 为4096倍数。在往串行Flash芯片写入数据之前要求先擦除空间。
- */
- void SPI_FLASH_SectorErase(u32 SectorAddr)
- {
- /* 发送FLASH写使能命令 */
- SPI_FLASH_WriteEnable();
- SPI_FLASH_WaitForWriteEnd();
- /* 擦除扇区 */
- /* 选择串行FLASH: CS低电平 */
- FLASH_SPI_CS_ENABLE();
- /* 发送扇区擦除指令*/
- 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_DISABLE();
- /* 等待擦除完毕*/
- SPI_FLASH_WaitForWriteEnd();
- }
-
- /**
- * 函数功能: 擦除整片
- * 输入参数: 无
- * 返 回 值: 无
- * 说 明:擦除串行Flash整片空间
- */
- void SPI_FLASH_BulkErase(void)
- {
- /* 发送FLASH写使能命令 */
- SPI_FLASH_WriteEnable();
-
- /* 整片擦除 Erase */
- /* 选择串行FLASH: CS低电平 */
- FLASH_SPI_CS_ENABLE();
- /* 发送整片擦除指令*/
- SPI_FLASH_SendByte(W25X_ChipErase);
- /* 禁用串行FLASH: CS高电平 */
- FLASH_SPI_CS_DISABLE();
-
- /* 等待擦除完毕*/
- SPI_FLASH_WaitForWriteEnd();
- }
-
- /**
- * 函数功能: 往串行FLASH按页写入数据,调用本函数写入数据前需要先擦除扇区
- * 输入参数: pBuffer:待写入数据的指针
- * WriteAddr:写入地址
- * NumByteToWrite:写入数据长度,必须小于等于SPI_FLASH_PerWritePageSize
- * 返 回 值: 无
- * 说 明:串行Flash每页大小为256个字节
- */
- void SPI_FLASH_PageWrite(u8* pBuffer, u32 WriteAddr, u16 NumByteToWrite)
- {
- /* 发送FLASH写使能命令 */
- SPI_FLASH_WriteEnable();
-
- /* 寻找串行FLASH: CS低电平 */
- FLASH_SPI_CS_ENABLE();
- /* 写送写指令*/
- 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;
- //printf("Err: SPI_FLASH_PageWrite too large!\n");
- }
-
- /* 写入数据*/
- while (NumByteToWrite--)
- {
- /* 发送当前要写入的字节数据 */
- SPI_FLASH_SendByte(*pBuffer);
- /* 指向下一字节数据 */
- pBuffer++;
- }
-
- /* 禁用串行FLASH: CS 高电平 */
- FLASH_SPI_CS_DISABLE();
-
- /* 等待写入完毕*/
- SPI_FLASH_WaitForWriteEnd();
- }
-
- /**
- * 函数功能: 往串行FLASH写入数据,调用本函数写入数据前需要先擦除扇区
- * 输入参数: pBuffer:待写入数据的指针
- * WriteAddr:写入地址
- * NumByteToWrite:写入数据长度
- * 返 回 值: 无
- * 说 明:该函数可以设置任意写入数据长度
- */
- void SPI_FLASH_BufferWrite(u8* pBuffer, u32 WriteAddr, u16 NumByteToWrite)
- {
- u8 NumOfPage = 0, NumOfSingle = 0, Addr = 0, count = 0, temp = 0;
-
- Addr = WriteAddr % SPI_FLASH_PageSize;
- count = SPI_FLASH_PageSize - Addr;
- NumOfPage = NumByteToWrite / SPI_FLASH_PageSize;
- NumOfSingle = NumByteToWrite % SPI_FLASH_PageSize;
-
- if (Addr == 0) /* 若地址与 SPI_FLASH_PageSize 对齐 */
- {
- if (NumOfPage == 0) /* NumByteToWrite < SPI_FLASH_PageSize */
- {
- 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);
- }
- }
- else /* 若地址与 SPI_FLASH_PageSize 不对齐 */
- {
- if (NumOfPage == 0) /* NumByteToWrite < SPI_FLASH_PageSize */
- {
- if (NumOfSingle > count) /* (NumByteToWrite + WriteAddr) > SPI_FLASH_PageSize */
- {
- temp = NumOfSingle - count;
-
- SPI_FLASH_PageWrite(pBuffer, WriteAddr, count);
- WriteAddr += count;
- pBuffer += count;
-
- SPI_FLASH_PageWrite(pBuffer, WriteAddr, temp);
- }
- else
- {
- SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumByteToWrite);
- }
- }
- else /* NumByteToWrite > SPI_FLASH_PageSize */
- {
- 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);
- }
- }
- }
- }
-
- /**
- * 函数功能: 从串行Flash读取数据
- * 输入参数: pBuffer:存放读取到数据的指针
- * ReadAddr:读取数据目标地址
- * NumByteToRead:读取数据长度
- * 返 回 值: 无
- * 说 明:该函数可以设置任意读取数据长度
- */
- void SPI_FLASH_BufferRead(u8* pBuffer, u32 ReadAddr, u16 NumByteToRead)
- {
- /* 选择串行FLASH: CS低电平 */
- FLASH_SPI_CS_ENABLE();
-
- /* 发送 读 指令 */
- 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_DISABLE();
- }
-
- /**
- * 函数功能: 读取串行Flash型号的ID
- * 输入参数: 无
- * 返 回 值: u32:串行Flash的型号ID
- * 说 明: FLASH_ID IC型号 存储空间大小
- 0xEF3015 W25X16 2M byte
- 0xEF4015 W25Q16 4M byte
- 0XEF4017 W25Q64 8M byte
- 0XEF4018 W25Q128 16M byte (YS-F1Pro开发板默认配置)
- */
- u32 SPI_FLASH_ReadID(void)
- {
- u32 Temp = 0, Temp0 = 0, Temp1 = 0, Temp2 = 0;
-
- /* 选择串行FLASH: CS低电平 */
- FLASH_SPI_CS_ENABLE();
-
- /* 发送命令:读取芯片型号ID */
- SPI_FLASH_SendByte(W25X_JedecDeviceID);
-
- /* 从串行Flash读取一个字节数据 */
- Temp0 = SPI_FLASH_SendByte(Dummy_Byte);
-
- /* 从串行Flash读取一个字节数据 */
- Temp1 = SPI_FLASH_SendByte(Dummy_Byte);
-
- /* 从串行Flash读取一个字节数据 */
- Temp2 = SPI_FLASH_SendByte(Dummy_Byte);
-
- /* 禁用串行Flash:CS高电平 */
- FLASH_SPI_CS_DISABLE();
-
- Temp = (Temp0 << 16) | (Temp1 << 8) | Temp2;
- return Temp;
- }
-
- /**
- * 函数功能: 读取串行Flash设备ID
- * 输入参数: 无
- * 返 回 值: u32:串行Flash的设备ID
- * 说 明:
- */
- u32 SPI_FLASH_ReadDeviceID(void)
- {
- u32 Temp = 0;
-
- /* 选择串行FLASH: CS低电平 */
- FLASH_SPI_CS_ENABLE();
-
- /* 发送命令:读取芯片设备ID * */
- SPI_FLASH_SendByte(W25X_DeviceID);
- SPI_FLASH_SendByte(Dummy_Byte);
- SPI_FLASH_SendByte(Dummy_Byte);
- SPI_FLASH_SendByte(Dummy_Byte);
-
- /* 从串行Flash读取一个字节数据 */
- Temp = SPI_FLASH_SendByte(Dummy_Byte);
-
- /* 禁用串行Flash:CS高电平 */
- FLASH_SPI_CS_DISABLE();
-
- return Temp;
- }
-
- /**
- * 函数功能: 启动连续读取数据串
- * 输入参数: ReadAddr:读取地址
- * 返 回 值: 无
- * 说 明: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.
- */
- void SPI_FLASH_StartReadSequence(u32 ReadAddr)
- {
- /* Select the FLASH: Chip Select low */
- FLASH_SPI_CS_ENABLE();
-
- /* 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);
- }
-
- /**
- * 函数功能: 从串行Flash读取一个字节数据
- * 输入参数: 无
- * 返 回 值: u8:读取到的数据
- * 说 明:This function must be used only if the Start_Read_Sequence
- * function has been previously called.
- */
- u8 SPI_FLASH_ReadByte(void)
- {
- return (SPI_FLASH_SendByte(Dummy_Byte));
- }
-
- /**
- * 函数功能: 往串行Flash读取写入一个字节数据并接收一个字节数据
- * 输入参数: byte:待发送数据
- * 返 回 值: u8:接收到的数据
- * 说 明:无
- */
- u8 SPI_FLASH_SendByte(u8 byte)
- {
- /* 循环等待直到SPI 数据寄存器DR为空,即当DR寄存器不为空时持续等待 */
- while (SPI_I2S_GetFlagStatus(FLASH_SPIx , SPI_I2S_FLAG_TXE) == RESET);
-
- /* 通过SPI外设发送一个字节数据 */
- SPI_I2S_SendData(FLASH_SPIx , byte);
-
- /* 等待接收到数据 */
- while (SPI_I2S_GetFlagStatus(FLASH_SPIx , SPI_I2S_FLAG_RXNE) == RESET);
-
- /* 读取SPI总线接收到一个字节数据并返回 */
- return SPI_I2S_ReceiveData(FLASH_SPIx );
- }
-
- /**
- * 函数功能: 往串行Flash读取写入半字(16bit)数据并接收半字数据
- * 输入参数: byte:待发送数据
- * 返 回 值: u16:接收到的数据
- * 说 明:无
- */
- u16 SPI_FLASH_SendHalfWord(u16 HalfWord)
- {
- /* 循环等待直到SPI 数据寄存器DR为空,即当DR寄存器不为空时持续等待 */
- while (SPI_I2S_GetFlagStatus(FLASH_SPIx , SPI_I2S_FLAG_TXE) == RESET);
-
- /* 通过SPI外设发送半字数据 */
- SPI_I2S_SendData(FLASH_SPIx , HalfWord);
-
- /* 等待接收到数据 */
- while (SPI_I2S_GetFlagStatus(FLASH_SPIx , SPI_I2S_FLAG_RXNE) == RESET);
-
- /* 读取SPI总线接收到半字数据并返回 */
- return SPI_I2S_ReceiveData(FLASH_SPIx );
- }
-
- /**
- * 函数功能: 使能串行Flash写操作
- * 输入参数: 无
- * 返 回 值: 无
- * 说 明:无
- */
- void SPI_FLASH_WriteEnable(void)
- {
- /* 选择串行FLASH: CS低电平 */
- FLASH_SPI_CS_ENABLE();
-
- /* 发送命令:写使能 */
- SPI_FLASH_SendByte(W25X_WriteEnable);
-
- /* 禁用串行Flash:CS高电平 */
- FLASH_SPI_CS_DISABLE();
- }
-
- /**
- * 函数功能: 等待数据写入完成
- * 输入参数: 无
- * 返 回 值: 无
- * 说 明:Polls the status of the Write In Progress (WIP) flag in the
- * FLASH's status register and loop until write opertaion
- * has completed.
- */
- void SPI_FLASH_WaitForWriteEnd(void)
- {
- u8 FLASH_Status = 0;
-
- /* Select the FLASH: Chip Select low */
- FLASH_SPI_CS_ENABLE();
-
- /* Send "Read Status Register" instruction */
- SPI_FLASH_SendByte(W25X_ReadStatusReg);
-
- /* Loop as long as the memory is busy with a write cycle */
- do
- {
- /* Send a dummy byte to generate the clock needed by the FLASH
- and put the value of the status register in FLASH_Status variable */
- FLASH_Status = SPI_FLASH_SendByte(Dummy_Byte);
- }
- while ((FLASH_Status & WIP_Flag) == SET); /* Write in progress */
-
- /* Deselect the FLASH: Chip Select high */
- FLASH_SPI_CS_DISABLE();
- }
-
-
- /**
- * 函数功能: 进入掉电模式
- * 输入参数: 无
- * 返 回 值: 无
- * 说 明:无
- */
- void SPI_Flash_PowerDown(void)
- {
- /* Select the FLASH: Chip Select low */
- FLASH_SPI_CS_ENABLE();
-
- /* Send "Power Down" instruction */
- SPI_FLASH_SendByte(W25X_PowerDown);
-
- /* Deselect the FLASH: Chip Select high */
- FLASH_SPI_CS_DISABLE();
- }
-
- /**
- * 函数功能: 唤醒串行Flash
- * 输入参数: 无
- * 返 回 值: 无
- * 说 明:无
- */
- void SPI_Flash_WAKEUP(void)
- {
- /* Select the FLASH: Chip Select low */
- FLASH_SPI_CS_ENABLE();
-
- /* Send "Power Down" instruction */
- SPI_FLASH_SendByte(W25X_ReleasePowerDown);
-
- /* Deselect the FLASH: Chip Select high */
- FLASH_SPI_CS_DISABLE();
- }
-
2.bsp_spi_flash.h
- #ifndef __SPI_FLASH_H__
- #define __SPI_FLASH_H__
-
- /* 包含头文件 ----------------------------------------------------------------*/
- #include <stm32f10x.h>
-
- /* 类型定义 ------------------------------------------------------------------*/
- /* 宏定义 --------------------------------------------------------------------*/
- //#define SPI_FLASH_ID 0xEF3015 //W25X16
- //#define SPI_FLASH_ID 0xEF4015 //W25Q16
- //#define SPI_FLASH_ID 0XEF4017 //W25Q64
- #define SPI_FLASH_ID 0XEF4018 //W25Q128 YS-F1Pro开发默认使用
-
- /************************** SPI Flash 连接引脚定义********************************/
- #define FLASH_SPIx SPI1
- #define FLASH_SPI_APBxClock_FUN RCC_APB2PeriphClockCmd
- #define FLASH_SPI_CLK RCC_APB2Periph_SPI1
-
- #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
-
- #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
-
- #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_APBxClock_FUN RCC_APB2PeriphClockCmd
- #define FLASH_SPI_CS_CLK RCC_APB2Periph_GPIOA
- #define FLASH_SPI_CS_PORT GPIOA
- #define FLASH_SPI_CS_PIN GPIO_Pin_4
-
- #define FLASH_SPI_CS_ENABLE() GPIO_ResetBits(FLASH_SPI_CS_PORT, FLASH_SPI_CS_PIN)
- #define FLASH_SPI_CS_DISABLE() GPIO_SetBits(FLASH_SPI_CS_PORT, FLASH_SPI_CS_PIN)
-
-
- #define CALIBRATE_DATA_ADDR 2*4096
-
- /* 扩展变量 ------------------------------------------------------------------*/
- /* 函数声明 ------------------------------------------------------------------*/
- 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__ */
3.main.c
- /* 包含头文件 ----------------------------------------------------------------*/
- #include "stm32f10x.h"
- #include "bsp/led/bsp_led.h"
- #include "bsp/usart/bsp_debug_usart.h"
- #include "bsp/spi_flash/bsp_spi_flash.h"
-
- /* 私有类型定义 --------------------------------------------------------------*/
- typedef enum { FAILED = 0, PASSED = !FAILED} TestStatus;
- /* 私有宏定义 ----------------------------------------------------------------*/
- /* 获取缓冲区的长度 */
- #define countof(a) (sizeof(a) / sizeof(*(a)))
- #define TxBufferSize1 (countof(TxBuffer1) - 1)
- #define RxBufferSize1 (countof(TxBuffer1) - 1)
- #define BufferSize (countof(Tx_Buffer)-1)
-
- #define FLASH_WriteAddress 0x00000
- #define FLASH_ReadAddress FLASH_WriteAddress
- #define FLASH_SectorToErase FLASH_WriteAddress
-
- /* 私有变量 ------------------------------------------------------------------*/
- /* 发送缓冲区初始化 */
- uint8_t Tx_Buffer[] = " 感谢您选用硬石stm32开发板\n今天是个好日子";
- uint8_t Rx_Buffer[BufferSize];
-
- __IO uint32_t DeviceID = 0;
- __IO uint32_t FlashID = 0;
- __IO TestStatus TransferStatus1 = FAILED;
-
- /* 扩展变量 ------------------------------------------------------------------*/
- /* 私有函数原形 --------------------------------------------------------------*/
- static void Delay(uint32_t time);
- static TestStatus Buffercmp(uint8_t* pBuffer1, uint8_t* pBuffer2, uint16_t BufferLength);
-
- /* 函数体 --------------------------------------------------------------------*/
-
- /**
- * 函数功能: 主函数.
- * 输入参数: 无
- * 返 回 值: 无
- * 说 明: 无
- */
- int main(void)
- {
- /* 调试串口初始化配置,115200-N-8-1.使能串口发送和接受 */
- DEBUG_USART_Init();
- /*初始化LED*/
- LED_GPIO_Init();
-
- /* 调用格式化输出函数打印输出数据 */
- printf("这是一个16M byte串行flash(W25Q128)读写测试实验\n");
-
- /* 16M串行flash W25Q128初始化 */
- SPI_FLASH_Init();
-
- /* Get SPI Flash Device ID */
- DeviceID = SPI_FLASH_ReadDeviceID();
-
- Delay( 1 );
-
- /* Get SPI Flash ID */
- FlashID = SPI_FLASH_ReadID();
-
- printf("FlashID is 0x%X, Manufacturer Device ID is 0x%X\n", FlashID, DeviceID);
-
- /* Check the SPI Flash ID */
- if (FlashID == SPI_FLASH_ID) /* #define sFLASH_ID 0XEF4018 */
- {
- printf("检测到华邦串行flash W25Q128 !\n");
-
- /* 擦除SPI的扇区以写入 */
- SPI_FLASH_SectorErase(FLASH_SectorToErase);
-
- /* 将发送缓冲区的数据写到flash中 */
- SPI_FLASH_BufferWrite(Tx_Buffer, FLASH_WriteAddress, BufferSize);
- SPI_FLASH_BufferWrite(Tx_Buffer, 252, BufferSize);
- printf("写入的数据为:\n%s \n", Tx_Buffer);
-
- /* 将刚刚写入的数据读出来放到接收缓冲区中 */
- SPI_FLASH_BufferRead(Rx_Buffer, FLASH_ReadAddress, BufferSize);
- printf("读出的数据为:\n %s\n", Rx_Buffer);
-
- /* 检查写入的数据与读出的数据是否相等 */
- TransferStatus1 = Buffercmp(Tx_Buffer, Rx_Buffer, BufferSize);
-
- if( PASSED == TransferStatus1 )
- {
- printf("16M串行flash(W25Q128)测试成功!\r");
- LED1_ON;
- }
- else
- {
- printf("16M串行flash(W25Q128)测试失败!\r");
- LED2_ON;
- }
- }
- else
- {
- printf("获取不到 W25Q128 ID!\n");
- LED3_ON;
- }
-
- /* 无限循环 */
- while (1)
- {
- }
- }
-
- /*
- * 函数名:Buffercmp
- * 描述 :比较两个缓冲区中的数据是否相等
- * 输入 :-pBuffer1 src缓冲区指针
- * -pBuffer2 dst缓冲区指针
- * -BufferLength 缓冲区长度
- * 输出 :无
- * 返回 :-PASSED pBuffer1 等于 pBuffer2
- * -FAILED pBuffer1 不同于 pBuffer2
- */
- static TestStatus Buffercmp(uint8_t* pBuffer1, uint8_t* pBuffer2, uint16_t BufferLength)
- {
- while(BufferLength--)
- {
- if(*pBuffer1 != *pBuffer2)
- {
- return FAILED;
- }
-
- pBuffer1++;
- pBuffer2++;
- }
- return PASSED;
- }
-
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。