这次来玩一下CH32V307 SPI接口通信
一、SPI是什么
1、SPI介绍
SPI(Serial Peripheral Interface,串行外设接口)是一种由摩托罗拉公司开发的同步串行通信协议,专用于短距离设备间的数据交换。它通过主从模式工作:一个主设备(如CH32V307)控制时钟并发起通信,一个或多个从设备(如传感器、存储器)响应指令。其核心特点是全双工通信(可同时收发数据)与高速传输(可达50MHz)
2、工作原理
SPI仅需4根物理连线实现全双工通信:
信号线
| 作用
|
方向
| SCLK
| 主设备输出的同步时钟(速率可配置)
| 主→从
| MOSI
| 主设备发送数据,从设备接收(Master-Out-Slave-In)
| 主→从
| MISO
| 从设备发送数据,主设备接收(Master-In-Slave-Out)
| 从→主
| CS/SS
| 片选信号(低电平激活从设备)
| 主→从
|
数据传输原理:
主从设备内置移位寄存器,连接成环形结构。时钟驱动下:
- 主设备通过MOSI逐位移出数据(如0xAA: 1→0→1→0→1→0→1→0)
- 从设备通过MISO同步移出数据
- 8个时钟周期完成1字节交换,形成“数据循环”
SPI通过 CPOL(时钟极性) 和 CPHA(时钟相位) 定义四种模式,决定数据采样时机
3、优缺点
优点
| 缺点
| 速率远超I²C(可达50MHz)
| 无硬件应答:无法自动确认数据接收成功
| 全双工通信:同时收发数据
| 引脚占用多:每增加一个从机需多1根片选线
| 硬件简单:无需上拉电阻,功耗低
| 传输距离短:通常<1米(易受干扰)
| 无地址冲突:通过CS引脚直接选从机
| 无标准协议:不同厂商实现可能不兼容
| 灵活数据长度:支持8/16/32位传输
| 单主设备限制:无法多主机协同
|
二、SPI读写W25Q16测试
1、简单介绍一下W25Q16芯片
W25Q16是一个16M BIT的FLASH芯片,它有8192个可编程页,一个能够写入256个字节;页擦除一次性可以擦除4K/32K/64KB或者整个芯片;分别有512个可擦除扇区,32个可擦除扇区。
它的组成是总共有32个块,每个块有64KB,一个块又分为16个扇区。使用它可以做大容量数据存储。
2、SPI相关引脚初始化
3、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(SPI3, &SPI_InitStructure);
SPI_Cmd(SPI3, ENABLE);
4、SPI发送函数
/*********************************************************************
* @fn SPI3_ReadWriteByte
*
* [url=home.php?mod=space&uid=247401]@brief[/url] SPI3 read or write one byte.
*
* @param TxData - write one byte data.
*
* [url=home.php?mod=space&uid=266161]@return[/url] Read one byte data.
*/
u8 SPI3_ReadWriteByte(u8 TxData)
{
while(SPI_I2S_GetFlagStatus(SPI3, SPI_I2S_FLAG_TXE) == RESET);
SPI_I2S_SendData(SPI3, TxData);
while(SPI_I2S_GetFlagStatus(SPI3, SPI_I2S_FLAG_RXNE) == RESET);
return SPI_I2S_ReceiveData(SPI3);
}
5、读写FLASH函数
/*********************************************************************
* @fn SPI_Flash_ReadSR
*
* @brief Read W25Qxx status register.
* !!BIT7 6 5 4 3 2 1 0
* !!SPR RV TB BP2 BP1 BP0 WEL BUSY
*
* @return byte - status register value.
*/
u8 SPI_Flash_ReadSR(void)
{
u8 byte = 0;
FLASH_CS_LOW;
SPI3_ReadWriteByte(W25X_ReadStatusReg);
byte = SPI3_ReadWriteByte(0Xff);
FLASH_CS_HIGH;
return byte;
}
/*********************************************************************
* @fn SPI_FLASH_Write_SR
*
* @brief Write W25Qxx status register.
*
* @param sr - status register value.
*
* @return none
*/
void SPI_FLASH_Write_SR(u8 sr)
{
FLASH_CS_LOW;
SPI3_ReadWriteByte(W25X_WriteStatusReg);
SPI3_ReadWriteByte(sr);
FLASH_CS_HIGH;
}
/*********************************************************************
* @fn SPI_Flash_Wait_Busy
*
* @brief Wait flash free.
*
* @return none
*/
void SPI_Flash_Wait_Busy(void)
{
while((SPI_Flash_ReadSR() & 0x01) == 0x01)
;
}
/*********************************************************************
* @fn SPI_FLASH_Write_Enable
*
* @brief Enable flash write.
*
* @return none
*/
void SPI_FLASH_Write_Enable(void)
{
FLASH_CS_LOW;
SPI3_ReadWriteByte(W25X_WriteEnable);
FLASH_CS_HIGH;
}
/*********************************************************************
* @fn SPI_FLASH_Write_Disable
*
* @brief Disable flash write.
*
* @return none
*/
void SPI_FLASH_Write_Disable(void)
{
FLASH_CS_LOW;
SPI3_ReadWriteByte(W25X_WriteDisable);
FLASH_CS_HIGH;
}
/*********************************************************************
* @fn SPI_Flash_ReadID
*
* @brief Read flash ID.
*
* @return Temp - FLASH ID.
*/
u16 SPI_Flash_ReadID(void)
{
u16 Temp = 0;
FLASH_CS_LOW;
SPI3_ReadWriteByte(W25X_ManufactDeviceID);
SPI3_ReadWriteByte(0x00);
SPI3_ReadWriteByte(0x00);
SPI3_ReadWriteByte(0x00);
Temp |= SPI3_ReadWriteByte(0xFF) << 8;
Temp |= SPI3_ReadWriteByte(0xFF);
FLASH_CS_HIGH;
return Temp;
}
/*********************************************************************
* @fn SPI_Flash_Erase_Sector
*
* @brief Erase one sector(4Kbyte).
*
* @param Dst_Addr - 0 !! 2047
*
* @return none
*/
void SPI_Flash_Erase_Sector(u32 Dst_Addr)
{
Dst_Addr *= 4096;
SPI_FLASH_Write_Enable();
SPI_Flash_Wait_Busy();
FLASH_CS_LOW;
SPI3_ReadWriteByte(W25X_SectorErase);
SPI3_ReadWriteByte((u8)((Dst_Addr) >> 16));
SPI3_ReadWriteByte((u8)((Dst_Addr) >> 8));
SPI3_ReadWriteByte((u8)Dst_Addr);
FLASH_CS_HIGH;
SPI_Flash_Wait_Busy();
}
/*********************************************************************
* @fn SPI_Flash_Read
*
* @brief Read data from flash.
*
* @param pBuffer -
* ReadAddr -Initial address(24bit).
* size - Data length.
*
* @return none
*/
void SPI_Flash_Read(u8 *pBuffer, u32 ReadAddr, u16 size)
{
u16 i;
FLASH_CS_LOW;
SPI3_ReadWriteByte(W25X_ReadData);
SPI3_ReadWriteByte((u8)((ReadAddr) >> 16));
SPI3_ReadWriteByte((u8)((ReadAddr) >> 8));
SPI3_ReadWriteByte((u8)ReadAddr);
for(i = 0; i < size; i++)
{
pBuffer[i] = SPI3_ReadWriteByte(0XFF);
}
FLASH_CS_HIGH;
}
/*********************************************************************
* @fn SPI_Flash_Write_Page
*
* @brief Write data by one page.
*
* @param pBuffer -
* WriteAddr - Initial address(24bit).
* size - Data length.
*
* @return none
*/
void SPI_Flash_Write_Page(u8 *pBuffer, u32 WriteAddr, u16 size)
{
u16 i;
SPI_FLASH_Write_Enable();
FLASH_CS_LOW;
SPI3_ReadWriteByte(W25X_PageProgram);
SPI3_ReadWriteByte((u8)((WriteAddr) >> 16));
SPI3_ReadWriteByte((u8)((WriteAddr) >> 8));
SPI3_ReadWriteByte((u8)WriteAddr);
for(i = 0; i < size; i++)
{
SPI3_ReadWriteByte(pBuffer[i]);
}
FLASH_CS_HIGH;
SPI_Flash_Wait_Busy();
}
/*********************************************************************
* @fn SPI_Flash_Write_NoCheck
*
* @brief Write data to flash.(need Erase)
* All data in address rang is 0xFF.
*
* @param pBuffer -
* WriteAddr - Initial address(24bit).
* size - Data length.
*
* @return none
*/
void SPI_Flash_Write_NoCheck(u8 *pBuffer, u32 WriteAddr, u16 size)
{
u16 pageremain;
pageremain = 256 - WriteAddr % 256;
if(size <= pageremain)
pageremain = size;
while(1)
{
SPI_Flash_Write_Page(pBuffer, WriteAddr, pageremain);
if(size == pageremain)
{
break;
}
else
{
pBuffer += pageremain;
WriteAddr += pageremain;
size -= pageremain;
if(size > 256)
pageremain = 256;
else
pageremain = size;
}
}
}
/*********************************************************************
* @fn SPI_Flash_Write
*
* @brief Write data to flash.(no need Erase)
*
* @param pBuffer -
* WriteAddr - Initial address(24bit).
* size - Data length.
*
* @return none
*/
void SPI_Flash_Write(u8 *pBuffer, u32 WriteAddr, u16 size)
{
u32 secpos;
u16 secoff;
u16 secremain;
u16 i;
secpos = WriteAddr / 4096;
secoff = WriteAddr % 4096;
secremain = 4096 - secoff;
if(size <= secremain)
secremain = size;
while(1)
{
SPI_Flash_Read(SPI_FLASH_BUF, secpos * 4096, 4096);
for(i = 0; i < secremain; i++)
{
if(SPI_FLASH_BUF[secoff + i] != 0XFF)
break;
}
if(i < secremain)
{
SPI_Flash_Erase_Sector(secpos);
for(i = 0; i < secremain; i++)
{
SPI_FLASH_BUF[i + secoff] = pBuffer[i];
}
SPI_Flash_Write_NoCheck(SPI_FLASH_BUF, secpos * 4096, 4096);
}
else
{
SPI_Flash_Write_NoCheck(pBuffer, WriteAddr, secremain);
}
if(size == secremain)
{
break;
}
else
{
secpos++;
secoff = 0;
pBuffer += secremain;
WriteAddr += secremain;
size -= secremain;
if(size > 4096)
{
secremain = 4096;
}
else
{
secremain = size;
}
}
}
}
/*********************************************************************
* @fn SPI_Flash_Erase_Chip
*
* @brief Erase all FLASH pages.
*
* @return none
*/
void SPI_Flash_Erase_Chip(void)
{
SPI_FLASH_Write_Enable();
SPI_Flash_Wait_Busy();
FLASH_CS_LOW;
SPI3_ReadWriteByte(W25X_ChipErase);
FLASH_CS_HIGH;
SPI_Flash_Wait_Busy();
}
/*********************************************************************
* @fn SPI_Flash_PowerDown
*
* @brief Enter power down mode.
*
* @return none
*/
void SPI_Flash_PowerDown(void)
{
FLASH_CS_LOW;
SPI3_ReadWriteByte(W25X_PowerDown);
FLASH_CS_HIGH;
Delay_Us(3);
}
/*********************************************************************
* @fn SPI_Flash_WAKEUP
*
* @brief Power down wake up.
*
* @return none
*/
void SPI_Flash_WAKEUP(void)
{
FLASH_CS_LOW;
SPI3_ReadWriteByte(W25X_ReleasePowerDown);
FLASH_CS_HIGH;
Delay_Us(3);
}
6、头文件
/* Winbond SPIFalsh ID */
#define W25Q80 0XEF13
#define W25Q16 0XEF14
#define W25Q32 0XEF15
#define W25Q64 0XEF16
#define W25Q128 0XEF17
/* Winbond SPIFalsh Instruction List */
#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 FLASH_CS_HIGH GPIO_WriteBit(GPIOE, GPIO_Pin_6, 1)
#define FLASH_CS_LOW GPIO_WriteBit(GPIOE, GPIO_Pin_6, 0)
u8 SPI1_ReadWriteByte(u8 TxData);
void SPI_Flash_Init(void);
u8 SPI_Flash_ReadSR(void);
void SPI_FLASH_Write_SR(u8 sr);
void SPI_Flash_Wait_Busy(void);
void SPI_FLASH_Write_Enable(void);
void SPI_FLASH_Write_Disable(void);
u16 SPI_Flash_ReadID(void);
void SPI_Flash_Erase_Sector(u32 Dst_Addr);
void SPI_Flash_Read(u8 *pBuffer, u32 ReadAddr, u16 size);
void SPI_Flash_Write_Page(u8 *pBuffer, u32 WriteAddr, u16 size);
void SPI_Flash_Write_NoCheck(u8 *pBuffer, u32 WriteAddr, u16 size);
void SPI_Flash_Write(u8 *pBuffer, u32 WriteAddr, u16 size);
void SPI_Flash_Erase_Chip(void);
void SPI_Flash_PowerDown(void);
void SPI_Flash_WAKEUP(void);
7、主函数
/*********************************************************************
* @fn main
*
* @brief Main program.
*
* @return none
*/
int main(void)
{
u8 readData[SIZE];
u16 Flash_Model;
u8 writeData[SIZE] = {0};
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
SystemCoreClockUpdate();
Delay_Init();
USART_Printf_Init(115200);
printf("SystemClk:%d\r\n",SystemCoreClock);
printf( "ChipID:%08x\r\n", DBGMCU_GetCHIPID() );
printf("This is printf example\r\n");
SPI_Flash_Init();
Flash_Model = SPI_Flash_ReadID();
for(uint16_t i = 0;i < SIZE;i++) {
writeData[i] = i;
}
switch(Flash_Model)
{
case W25Q80:
printf("W25Q80 OK!\r\n");
break;
case W25Q16:
printf("W25Q16 OK!\r\n");
break;
case W25Q32:
printf("W25Q32 OK!\r\n");
break;
case W25Q64:
printf("W25Q64 OK!\r\n");
break;
case W25Q128:
printf("W25Q128 OK!\r\n");
break;
default:
printf("Fail!\r\n");
break;
}
printf("Start Erase W25Qxx....\r\n");
SPI_Flash_Erase_Sector(0);
printf("W25Qxx Erase Finished!\r\n");
Delay_Ms(500);
printf("Start Read W25Qxx....\r\n");
SPI_Flash_Read(readData, 0x0, SIZE);
printf("read OK\r\n");
Delay_Ms(500);
printf("Start Write W25Qxx....\r\n");
printf("W25Qxx Write Finished!\r\n");
for(uint8_t i = 0;i < SIZE;i++) {
printf("%d ",writeData[i]);
if (i % 50 == 0) {
printf("\r\n");
}
}
SPI_Flash_Write((u8 *)writeData, 0, SIZE);
Delay_Ms(500);
printf("Start Read W25Qxx....\r\n");
SPI_Flash_Read(readData, 0x0, SIZE);
printf("read OK\r\n");
for(uint8_t i = 0;i < SIZE;i++) {
printf("%d ",readData[i]);
if (i % 50 == 0) {
printf("\r\n");
}
}
Delay_Ms(500);
printf("Start erase chip....\r\n");
SPI_Flash_Erase_Chip();
printf("chip erased finsh\r\n");
printf("Start Read W25Qxx....\r\n");
SPI_Flash_Read(readData, 0x0, SIZE);
printf("%s\r\n", readData);
while(1)
{
}
}
8、下载验证
主要做了以下测试,擦除扇区、读写数据、擦除整个芯片,擦除整个芯片耗时6ms,这速度擦除还是可以的
|
|