打印
[单片机芯片]

【沁恒CH32V307开发板测评】SPI测试

[复制链接]
232|5
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
这次来玩一下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,这速度擦除还是可以的




使用特权

评论回复
沙发
丙丁先生| | 2025-8-1 22:10 | 只看该作者
板子照片?

使用特权

评论回复
板凳
丙丁先生| | 2025-8-1 22:11 | 只看该作者
沁恒CH32V307开发板 有两种

使用特权

评论回复
地板
蚊子的噩梦| | 2025-8-2 13:13 | 只看该作者
这篇测评很详细,特别是SPI接口的工作原理和优缺点分析,让我对SPI有了更深的理解。

使用特权

评论回复
5
星享社|  楼主 | 2025-8-2 16:51 | 只看该作者
丙丁先生 发表于 2025-8-1 22:11
沁恒CH32V307开发板 有两种

板子照片就没有拍了,用的是沁恒CH32V307VCT6-EVT-R2开发板

使用特权

评论回复
6
星享社|  楼主 | 2025-8-2 16:51 | 只看该作者
蚊子的噩梦 发表于 2025-8-2 13:13
这篇测评很详细,特别是SPI接口的工作原理和优缺点分析,让我对SPI有了更深的理解。
...

多谢大佬鼓励

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

5

主题

17

帖子

0

粉丝