【沁恒CH32V307开发板测评】SPI测试
这次来玩一下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
*
* @brief SPI3 read or write one byte.
*
* @param TxData - write one byte data.
*
* @returnRead 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.
* !!BIT76 5 4 3 2 1 0
* !!SPR RVTBBP2 BP1 BP0 WEL BUSY
*
* @returnbyte - 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.
*
* @returnnone
*/
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.
*
* @returnnone
*/
void SPI_Flash_Wait_Busy(void)
{
while((SPI_Flash_ReadSR() & 0x01) == 0x01)
;
}
/*********************************************************************
* @fn SPI_FLASH_Write_Enable
*
* @brief Enable flash write.
*
* @returnnone
*/
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.
*
* @returnnone
*/
void SPI_FLASH_Write_Disable(void)
{
FLASH_CS_LOW;
SPI3_ReadWriteByte(W25X_WriteDisable);
FLASH_CS_HIGH;
}
/*********************************************************************
* @fn SPI_Flash_ReadID
*
* @brief Read flash ID.
*
* @returnTemp - 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
*
* @returnnone
*/
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.
*
* @returnnone
*/
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 = 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.
*
* @returnnone
*/
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);
}
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.
*
* @returnnone
*/
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.
*
* @returnnone
*/
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 != 0XFF)
break;
}
if(i < secremain)
{
SPI_Flash_Erase_Sector(secpos);
for(i = 0; i < secremain; i++)
{
SPI_FLASH_BUF = pBuffer;
}
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.
*
* @returnnone
*/
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.
*
* @returnnone
*/
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.
*
* @returnnone
*/
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_LOWGPIO_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.
*
* @returnnone
*/
int main(void)
{
u8readData;
u16 Flash_Model;
u8 writeData = {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;
}
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);
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);
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,这速度擦除还是可以的
板子照片? 沁恒CH32V307开发板 有两种 这篇测评很详细,特别是SPI接口的工作原理和优缺点分析,让我对SPI有了更深的理解。
丙丁先生 发表于 2025-8-1 22:11
沁恒CH32V307开发板 有两种
板子照片就没有拍了,用的是沁恒CH32V307VCT6-EVT-R2开发板 蚊子的噩梦 发表于 2025-8-2 13:13
这篇测评很详细,特别是SPI接口的工作原理和优缺点分析,让我对SPI有了更深的理解。
...
多谢大佬鼓励
页:
[1]