|
SPI协议:是由摩托罗拉公司提出的通讯协议(Serial Peripheral Interface),即串行外围设备接口,是一种高速全双工的通信总线。它被广泛地使用在ADC、LCD 等设备与MCU 间,要求通讯速率较高的场合。
物理层: S S ( Slave Select):从设备选择信号线,常称为片选信号线,也称为NSS、CS,以下用NSS 表示。当有多个SPI 从设备与SPI 主机相连时,设备的其它信号线SCK、MOSI及MISO 同时并联到相同的SPI 总线上,即无论有多少个从设备,都共同只使用这3 条总线;而每个从设备都有独立的这一条NSS 信号线,本信号线独占主机的一个引脚,即有多少个从设备,就有多少条片选信号线。I2C 协议中通过设备地址来寻址、选中总线上的某个设备并与其进行通讯;而SPI 协议中没有设备地址,它使用NSS 信号线来寻址,当主机要选择从设备时,把该从设备的NSS 信号线设置为低电平,该从设备即被选中,即片选有效,接着主机开始与被选中的从设备进行SPI通讯。所以SPI通讯以NSS 线置低电平为开始信号,以NSS 线被拉高作为结束信号。
SCK (Serial Clock):时钟信号线,用于通讯数据同步。它由通讯主机产生,决定了通讯的速率,不同的设备支持的最高时钟频率不一样,如STM32 的SPI 时钟频率最大为fpclk/2,两个设备之间通讯时,通讯速率受限于低速设备。
MOSI (Master Output,Slave Input):主设备输出/从设备输入引脚。主机的数据从这条信号线输出,从机由这条信号线读入主机发送的数据,即这条线上数据的方向为主机到从机。
MISO(Master Input,,Slave Output):主设备输入/从设备输出引脚。主机从这条信号线读入数据,从机的数据由这条信号线输出到主机,即在这条线上数据的方向为从机到主机,只有这条线的信号由从机产生。 SPI设备之间的常用连线方式如下:
协议层: SPI 一共有四种通讯模式,它们的主要区别是总线空闲时SCK 的时钟状态以及数据采样时刻。为方便说明,在此引入“时钟极性CPOL”和“时钟相位CPHA”的概念。时钟极性CPOL 是指SPI 通讯设备处于空闲状态时,SCK 信号线的电平信号(即SPI 通讯开始前、NSS 线为高电平时SCK 的状态)。CPOL=0 时,SCK 在空闲状态时为低电平,CPOL=1 时,则相反。时钟相位CPHA 是指数据的采样的时刻,当CPHA=0 时,MOSI 或MISO 数据线上的信号将会在SCK 时钟线的“奇数边沿”被采样。当CPHA=1 时,数据线在SCK 的“偶数边沿”采样。
bsp_spi_flash.h文件: - #ifndef __SPI_FLASH_H
- #define __SPI_FLASH_H
- #include "stm32f10x.h"
- #include <stdio.h>
- #define sFLASH_ID 0XEF4017
- #define SPI_FLASH_PageSize 256
- #define SPI_FLASH_PerWritePageSize 256
- //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
- //WIP(busy) 标志,Flash内部正在写入
- #define WIP_flag 0x01
- #define Dummy_Byte 0xFF
- //SPI接口定义
- #define FLASH_SPIx SPI1
- #define FLASH_SPI_CLK RCC_APB2Periph_SPI1
- //片选引脚
- #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_CLK RCC_APB2Periph_GPIOA
- #define FLASH_SPI_SCK_PORT GPIOA
- #define FLASH_SPI_SCK_PIN GPIO_Pin_5
- //MISO引脚
- #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_CLK RCC_APB2Periph_GPIOA
- #define FLASH_SPI_MOSI_PORT GPIOA
- #define FLASH_SPI_MOSI_PIN GPIO_Pin_7
-
- #define SPI_FLASH_CS_LOW() GPIO_ResetBits(FLASH_SPI_CS_PORT, FLASH_SPI_CS_PIN)
- #define SPI_FLASH_CS_HIGH() GPIO_SetBits( FLASH_SPI_CS_PORT, FLASH_SPI_CS_PIN )
-
- #define SPIT_FLAG_TIMEOUT ((uint32_t)0x1000)
- #define SPIT_LONG_TIMEOUT ((uint32_t)(10 * SPIT_FLAG_TIMEOUT))
- #define FLASH_DEBUG_ON 1
- #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(u32 SectorAddr);
- void SPI_FLASH_BulkErase(void);
- 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);
- u32 SPI_FLASH_ReadID(void);
- u32 SPI_FLASH_ReadDeviceID(void);
- void SPI_FLASH_StartReadSequence(up32 ReadAddr);
- void SPI_Flash_PowerDown(void);
- void SPI_Flash_WAKEUP(void);
- u8 SPI_FLASH_ReadByte(void);
- u8 SPI_FLASH_SendByte(u8 byte);
- u16 SPI_FLASH_SendHalfWord(u16 HalfWord);
- void SPI_FLASH_WriteEnable(void);
- void SPI_FLASH_WaitForWriteEnd(void);
- #endif
复制代码
bsp_spi_flash.c文件: - #include "bsp_spi_flash.h"
- static __IO uint32_t SPITimeout = SPIT_LONG_TIMEOUT;
- static uint16_t SPI_TIMEOUT_UserCallback(uint8_t errorCode);
- void SPI_FLASH_Init(void)
- {
- SPI_InitTypeDef SPI_InitStructure;
- GPIO_InitTypeDef GPIO_InitStructure;
- RCC_APB2PeriphClockCmd(FLASH_SPI_CLK, ENABLE );
- RCC_APB2PeriphClockCmd(FLASH_SPI_CS_CLK|FLASH_SPI_SCK_CLK|
- FLASH_SPI_MISO_PIN|FLASH_SPI_MOSI_PIN, ENABLE );
- 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);
- GPIO_InitStructure.GPIO_Pin = FLASH_SPI_SCK_PIN;
- //配置SCK
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
- GPIO_Init(FLASH_SPI_SCK_PORT, &GPIO_InitStructure);
- //配置MISO
- GPIO_InitStructure.GPIO_Pin = FLASH_SPI_MISO_PIN;
- GPIO_Init(FLASH_SPI_MISO_PORT, &GPIO_InitStructure);
- //配置MOSI
- GPIO_InitStructure.GPIO_Pin = FLASH_SPI_MOSI_PIN;
- GPIO_Init(FLASH_SPI_MOSI_PORT, &GPIO_InitStructure);
- //cs高电平,通讯停止
- SPI_FLASH_CS_HIGH();
- //SPI模式配置
- 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_Cmd(FLASH_SPIx , ENABLE);
-
- }
- //擦除FLASH
- void SPI_FLASH_SectorErase(u32 SectorAddr)
- {
- SPI_FLASH_WriteEnable();
- SPI_FLASH_WaitForWriteEnd();
- 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);
- SPI_FLASH_CS_HIGH();
- SPI_FLASH_WaitForWriteEnd();
- }
- //擦除FLASH扇区
- void SPI_FLASH_BulkErase(void)
- {
- SPI_FLASH_WriteEnable();
- SPI_FLASH_CS_LOW();
- SPI_FLASH_SendByte(W25X_ChipErase);
- SPI_FLASH_CS_HIGH();
- SPI_FLASH_WaitForWriteEnd();
- }
- //按页写入数据
- void SPI_FLASH_PageWrite(u8* pBuffer, u32 WriteAddr, u16 NumByteToWrite)
- {
- SPI_FLASH_WriteEnable();
- 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++;
- }
- SPI_FLASH_CS_HIGH();
- 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)
- {
- if (NumOfPage == 0)
- {
- SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumByteToWrite);
- }
- else
- {
- 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
- {
- if (NumOfPage == 0)
- {
- if (NumOfSingle > count)
- {
- 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 -= 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)
- {
- 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++;
- }
- SPI_FLASH_CS_HIGH();
- }
- //读取ID
- u32 SPI_FLASH_ReadID(void)
- {
- u32 Temp = 0, Temp0 = 0, Temp1 = 0, Temp2 = 0;
- SPI_FLASH_CS_LOW();
- SPI_FLASH_SendByte(W25X_JedecDeviceID);
- Temp0 = SPI_FLASH_SendByte(Dummy_Byte);
- Temp1 = SPI_FLASH_SendByte(Dummy_Byte);
- Temp2 = SPI_FLASH_SendByte(Dummy_Byte);
- SPI_FLASH_CS_HIGH();
- Temp = (Temp0 << 16) | (Temp1 << 8) | Temp2;
- return Temp;
- }
- //读取DeviceID
- u32 SPI_FLASH_ReadDeviceID(void)
- {
- u32 Temp = 0;
- SPI_FLASH_CS_LOW();
- SPI_FLASH_SendByte(W25X_DeviceID);
- SPI_FLASH_SendByte(Dummy_Byte);
- SPI_FLASH_SendByte(Dummy_Byte);
- SPI_FLASH_SendByte(Dummy_Byte);
- Temp = SPI_FLASH_SendByte(Dummy_Byte);
- SPI_FLASH_CS_HIGH();
- return Temp;
- }
- void SPI_FLASH_StartReadSequence(u32 ReadAddr)
- {
- 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);
- }
- u8 SPI_FLASH_ReadByte(void)
- {
- return (SPI_FLASH_SendByte(Dummy_Byte));
- }
- u8 SPI_FLASH_SendByte(u8 byte)
- {
- SPITimeout = SPIT_FLAG_TIMEOUT;
- 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;
- 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 );
- }
- u16 SPI_FLASH_SendHalfWord(u16 HalfWord)
- {
- SPITimeout = SPIT_FLAG_TIMEOUT;
- while (SPI_I2S_GetFlagStatus(FLASH_SPIx , SPI_I2S_FLAG_TXE) == RESET)
- {
- if((SPITimeout--) == 0) return SPI_TIMEOUT_UserCallback(2);
- }
- SPI_I2S_SendData(FLASH_SPIx , HalfWord);
- SPITimeout = SPIT_FLAG_TIMEOUT;
- while (SPI_I2S_GetFlagStatus(FLASH_SPIx , SPI_I2S_FLAG_RXNE) == RESET)
- {
- if((SPITimeout--) == 0) return SPI_TIMEOUT_UserCallback(3);
- }
- return SPI_I2S_ReceiveData(FLASH_SPIx );
- }
- void SPI_FLASH_WriteEnable(void)
- {
- SPI_FLASH_CS_LOW();
- SPI_FLASH_SendByte(W25X_WriteEnable);
- SPI_FLASH_CS_HIGH();
- }
- void SPI_FLASH_WaitForWriteEnd(void)
- {
- u8 FLASH_Status = 0;
- SPI_FLASH_CS_LOW();
- SPI_FLASH_SendByte(W25X_ReadStatusReg);
- do
- {
- FLASH_Status = SPI_FLASH_SendByte(Dummy_Byte);
- }
- while ((FLASH_Status & WIP_flag ) == SET);
- SPI_FLASH_CS_HIGH();
- }
- void SPI_Flash_PowerDown(void)
- {
- SPI_FLASH_CS_LOW();
- SPI_FLASH_SendByte(W25X_PowerDown);
- SPI_FLASH_CS_HIGH();
- }
- void SPI_Flash_WAKEUP(void)
- {
- SPI_FLASH_CS_LOW();
- SPI_FLASH_SendByte(W25X_ReleasePowerDown);
- SPI_FLASH_CS_HIGH();
- }
-
- static uint16_t SPI_TIMEOUT_UserCallback(uint8_t errorCode)
- {
- FLASH_ERROR("SPI等待超时!errorCode = %d",errorCode);
- return 0;
- }
复制代码
main.c文件: - #include "stm32f10x.h"
- #include "bsp_usart.h"
- #include "bsp_led.h"
- #include "bsp_spi_flash.h"
- typedef enum { FAILED = 0, PASSED = !FAILED} TestStatus;
- #define TxBufferSize1 (countof(TxBuffer1) - 1)
- #define RxBufferSize1 (countof(TxBuffer1) - 1)
- #define countof(a) (sizeof(a) / sizeof(*(a)))
- #define BufferSize (countof(Tx_Buffer)-1)
- #define FLASH_WriteAddress 0x00000
- #define FLASH_ReadAddress FLASH_WriteAddress
- #define FLASH_SectorToErase FLASH_WriteAddress
- uint8_t Tx_Buffer[] = "hello stm32!!!\r\n";
- uint8_t Rx_Buffer[BufferSize];
- __IO uint32_t DeviceID = 0;
- __IO uint32_t FlashID = 0;
- __IO TestStatus TransferStatus1 = FAILED;
- void Delay(__IO uint32_t nCount);
- TestStatus Buffercmp(uint8_t* pBuffer1,uint8_t* pBuffer2, uint16_t BufferLength);
- int main(void)
- {
- LED_GPIO_Config();
- USART_Config();
- printf("\r\n 这是一个8M的串行flash W25Q64实验!\r\n");
- SPI_FLASH_Init();
- DeviceID = SPI_FLASH_ReadDeviceID();
- Delay( 200 );
- FlashID = SPI_FLASH_ReadID();
- printf("\r\n FlashID is 0x%X,\
- Manufacturer Device ID is 0x%X\r\n", FlashID, DeviceID);
- if (FlashID == sFLASH_ID)
- {
- printf("\r\n检测到串行flash W25Q64\r\n");
- SPI_FLASH_SectorErase(FLASH_SectorToErase);
- SPI_FLASH_BufferWrite(Tx_Buffer, FLASH_WriteAddress, BufferSize);
- printf("\r\n 写入的数据为:%s \r\t", Tx_Buffer);
- SPI_FLASH_BufferRead(Rx_Buffer, FLASH_ReadAddress, BufferSize);
- printf("\r\n 读出的数据为\r\n", Rx_Buffer);
- TransferStatus1 = Buffercmp(Tx_Buffer, Rx_Buffer, BufferSize);
- if( PASSED == TransferStatus1 )
- {
- green(ON);
- printf("\r\n 测试成功!\n\r");
- }
- else
- {
- red(ON);
- printf("\r\n 测试失败\n\r");
- }
- }// if (FlashID == sFLASH_ID)
- else// if (FlashID == sFLASH_ID)
- {
- red(ON);
- printf("\r\n 获取不到W25Q64 ID!\n\r");
- }
-
- while(1);
- }
- //比较两个缓冲区的内容是否一样
- TestStatus Buffercmp(uint8_t* pBuffer1, uint8_t* pBuffer2, uint16_t BufferLength)
- {
- while(BufferLength--)
- {
- if(*pBuffer1 != *pBuffer2)
- {
- return FAILED;
- }
- pBuffer1++;
- pBuffer2++;
- }
- return PASSED;
- }
- void Delay(__IO uint32_t nCount)
- {
- for(; nCount != 0; nCount--);
- }
复制代码
|