[单片机芯片] 【沁恒CH32V307开发板测评】SPI测试

[复制链接]
 楼主| 星享社 发表于 2025-7-31 22:41 | 显示全部楼层 |阅读模式
这次来玩一下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个扇区。使用它可以做大容量数据存储。
14304688b7f24de7bd.png
2、SPI相关引脚初始化


3、SPI初始化
  1.     SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
  2.     SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
  3.     SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
  4.     SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;
  5.     SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
  6.     SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
  7.     SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4;
  8.     SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
  9.     SPI_InitStructure.SPI_CRCPolynomial = 7;
  10.     SPI_Init(SPI3, &SPI_InitStructure);

  11.     SPI_Cmd(SPI3, ENABLE);


4、SPI发送函数
  1. /*********************************************************************
  2. * @fn      SPI3_ReadWriteByte
  3. *
  4. * [url=home.php?mod=space&uid=247401]@brief[/url]   SPI3 read or write one byte.
  5. *
  6. * @param   TxData - write one byte data.
  7. *
  8. * [url=home.php?mod=space&uid=266161]@return[/url]  Read one byte data.
  9. */
  10. u8 SPI3_ReadWriteByte(u8 TxData)
  11. {
  12.     while(SPI_I2S_GetFlagStatus(SPI3, SPI_I2S_FLAG_TXE) == RESET);

  13.     SPI_I2S_SendData(SPI3, TxData);

  14.     while(SPI_I2S_GetFlagStatus(SPI3, SPI_I2S_FLAG_RXNE) == RESET);

  15.     return SPI_I2S_ReceiveData(SPI3);
  16. }


5、读写FLASH函数
  1. /*********************************************************************
  2. * @fn      SPI_Flash_ReadSR
  3. *
  4. * @brief   Read W25Qxx status register.
  5. *        !!BIT7  6   5   4   3   2   1   0
  6. *        !!SPR   RV  TB  BP2 BP1 BP0 WEL BUSY
  7. *
  8. * @return  byte - status register value.
  9. */
  10. u8 SPI_Flash_ReadSR(void)
  11. {
  12.     u8 byte = 0;

  13.     FLASH_CS_LOW;
  14.     SPI3_ReadWriteByte(W25X_ReadStatusReg);
  15.     byte = SPI3_ReadWriteByte(0Xff);
  16.     FLASH_CS_HIGH;

  17.     return byte;
  18. }

  19. /*********************************************************************
  20. * @fn      SPI_FLASH_Write_SR
  21. *
  22. * @brief   Write W25Qxx status register.
  23. *
  24. * @param   sr - status register value.
  25. *
  26. * @return  none
  27. */
  28. void SPI_FLASH_Write_SR(u8 sr)
  29. {
  30.     FLASH_CS_LOW;
  31.     SPI3_ReadWriteByte(W25X_WriteStatusReg);
  32.     SPI3_ReadWriteByte(sr);
  33.     FLASH_CS_HIGH;
  34. }

  35. /*********************************************************************
  36. * @fn      SPI_Flash_Wait_Busy
  37. *
  38. * @brief   Wait flash free.
  39. *
  40. * @return  none
  41. */
  42. void SPI_Flash_Wait_Busy(void)
  43. {
  44.     while((SPI_Flash_ReadSR() & 0x01) == 0x01)
  45.         ;
  46. }

  47. /*********************************************************************
  48. * @fn      SPI_FLASH_Write_Enable
  49. *
  50. * @brief   Enable flash write.
  51. *
  52. * @return  none
  53. */
  54. void SPI_FLASH_Write_Enable(void)
  55. {
  56.     FLASH_CS_LOW;
  57.     SPI3_ReadWriteByte(W25X_WriteEnable);
  58.     FLASH_CS_HIGH;
  59. }

  60. /*********************************************************************
  61. * @fn      SPI_FLASH_Write_Disable
  62. *
  63. * @brief   Disable flash write.
  64. *
  65. * @return  none
  66. */
  67. void SPI_FLASH_Write_Disable(void)
  68. {
  69.     FLASH_CS_LOW;
  70.     SPI3_ReadWriteByte(W25X_WriteDisable);
  71.     FLASH_CS_HIGH;
  72. }

  73. /*********************************************************************
  74. * @fn      SPI_Flash_ReadID
  75. *
  76. * @brief   Read flash ID.
  77. *
  78. * @return  Temp - FLASH ID.
  79. */
  80. u16 SPI_Flash_ReadID(void)
  81. {
  82.     u16 Temp = 0;

  83.     FLASH_CS_LOW;
  84.     SPI3_ReadWriteByte(W25X_ManufactDeviceID);
  85.     SPI3_ReadWriteByte(0x00);
  86.     SPI3_ReadWriteByte(0x00);
  87.     SPI3_ReadWriteByte(0x00);
  88.     Temp |= SPI3_ReadWriteByte(0xFF) << 8;
  89.     Temp |= SPI3_ReadWriteByte(0xFF);
  90.     FLASH_CS_HIGH;

  91.     return Temp;
  92. }

  93. /*********************************************************************
  94. * @fn      SPI_Flash_Erase_Sector
  95. *
  96. * @brief   Erase one sector(4Kbyte).
  97. *
  98. * @param   Dst_Addr - 0 !! 2047
  99. *
  100. * @return  none
  101. */
  102. void SPI_Flash_Erase_Sector(u32 Dst_Addr)
  103. {
  104.     Dst_Addr *= 4096;
  105.     SPI_FLASH_Write_Enable();
  106.     SPI_Flash_Wait_Busy();
  107.     FLASH_CS_LOW;
  108.     SPI3_ReadWriteByte(W25X_SectorErase);
  109.     SPI3_ReadWriteByte((u8)((Dst_Addr) >> 16));
  110.     SPI3_ReadWriteByte((u8)((Dst_Addr) >> 8));
  111.     SPI3_ReadWriteByte((u8)Dst_Addr);
  112.     FLASH_CS_HIGH;
  113.     SPI_Flash_Wait_Busy();
  114. }

  115. /*********************************************************************
  116. * @fn      SPI_Flash_Read
  117. *
  118. * @brief   Read data from flash.
  119. *
  120. * @param   pBuffer -
  121. *          ReadAddr -Initial address(24bit).
  122. *          size - Data length.
  123. *
  124. * @return  none
  125. */
  126. void SPI_Flash_Read(u8 *pBuffer, u32 ReadAddr, u16 size)
  127. {
  128.     u16 i;

  129.     FLASH_CS_LOW;
  130.     SPI3_ReadWriteByte(W25X_ReadData);
  131.     SPI3_ReadWriteByte((u8)((ReadAddr) >> 16));
  132.     SPI3_ReadWriteByte((u8)((ReadAddr) >> 8));
  133.     SPI3_ReadWriteByte((u8)ReadAddr);

  134.     for(i = 0; i < size; i++)
  135.     {
  136.         pBuffer[i] = SPI3_ReadWriteByte(0XFF);
  137.     }

  138.     FLASH_CS_HIGH;
  139. }

  140. /*********************************************************************
  141. * @fn      SPI_Flash_Write_Page
  142. *
  143. * @brief   Write data by one page.
  144. *
  145. * @param   pBuffer -
  146. *          WriteAddr - Initial address(24bit).
  147. *          size - Data length.
  148. *
  149. * @return  none
  150. */
  151. void SPI_Flash_Write_Page(u8 *pBuffer, u32 WriteAddr, u16 size)
  152. {
  153.     u16 i;

  154.     SPI_FLASH_Write_Enable();
  155.     FLASH_CS_LOW;
  156.     SPI3_ReadWriteByte(W25X_PageProgram);
  157.     SPI3_ReadWriteByte((u8)((WriteAddr) >> 16));
  158.     SPI3_ReadWriteByte((u8)((WriteAddr) >> 8));
  159.     SPI3_ReadWriteByte((u8)WriteAddr);

  160.     for(i = 0; i < size; i++)
  161.     {
  162.         SPI3_ReadWriteByte(pBuffer[i]);
  163.     }

  164.     FLASH_CS_HIGH;
  165.     SPI_Flash_Wait_Busy();
  166. }

  167. /*********************************************************************
  168. * @fn      SPI_Flash_Write_NoCheck
  169. *
  170. * @brief   Write data to flash.(need Erase)
  171. *          All data in address rang is 0xFF.
  172. *
  173. * @param   pBuffer -
  174. *          WriteAddr - Initial address(24bit).
  175. *          size - Data length.
  176. *
  177. * @return  none
  178. */
  179. void SPI_Flash_Write_NoCheck(u8 *pBuffer, u32 WriteAddr, u16 size)
  180. {
  181.     u16 pageremain;

  182.     pageremain = 256 - WriteAddr % 256;

  183.     if(size <= pageremain)
  184.         pageremain = size;

  185.     while(1)
  186.     {
  187.         SPI_Flash_Write_Page(pBuffer, WriteAddr, pageremain);

  188.         if(size == pageremain)
  189.         {
  190.             break;
  191.         }
  192.         else
  193.         {
  194.             pBuffer += pageremain;
  195.             WriteAddr += pageremain;
  196.             size -= pageremain;

  197.             if(size > 256)
  198.                 pageremain = 256;
  199.             else
  200.                 pageremain = size;
  201.         }
  202.     }
  203. }

  204. /*********************************************************************
  205. * @fn      SPI_Flash_Write
  206. *
  207. * @brief   Write data to flash.(no need Erase)
  208. *
  209. * @param   pBuffer -
  210. *          WriteAddr - Initial address(24bit).
  211. *          size - Data length.
  212. *
  213. * @return  none
  214. */
  215. void SPI_Flash_Write(u8 *pBuffer, u32 WriteAddr, u16 size)
  216. {
  217.     u32 secpos;
  218.     u16 secoff;
  219.     u16 secremain;
  220.     u16 i;

  221.     secpos = WriteAddr / 4096;
  222.     secoff = WriteAddr % 4096;
  223.     secremain = 4096 - secoff;

  224.     if(size <= secremain)
  225.         secremain = size;

  226.     while(1)
  227.     {
  228.         SPI_Flash_Read(SPI_FLASH_BUF, secpos * 4096, 4096);

  229.         for(i = 0; i < secremain; i++)
  230.         {
  231.             if(SPI_FLASH_BUF[secoff + i] != 0XFF)
  232.                 break;
  233.         }

  234.         if(i < secremain)
  235.         {
  236.             SPI_Flash_Erase_Sector(secpos);

  237.             for(i = 0; i < secremain; i++)
  238.             {
  239.                 SPI_FLASH_BUF[i + secoff] = pBuffer[i];
  240.             }

  241.             SPI_Flash_Write_NoCheck(SPI_FLASH_BUF, secpos * 4096, 4096);
  242.         }
  243.         else
  244.         {
  245.             SPI_Flash_Write_NoCheck(pBuffer, WriteAddr, secremain);
  246.         }

  247.         if(size == secremain)
  248.         {
  249.             break;
  250.         }
  251.         else
  252.         {
  253.             secpos++;
  254.             secoff = 0;

  255.             pBuffer += secremain;
  256.             WriteAddr += secremain;
  257.             size -= secremain;

  258.             if(size > 4096)
  259.             {
  260.                 secremain = 4096;
  261.             }
  262.             else
  263.             {
  264.                 secremain = size;
  265.             }
  266.         }
  267.     }
  268. }

  269. /*********************************************************************
  270. * @fn      SPI_Flash_Erase_Chip
  271. *
  272. * @brief   Erase all FLASH pages.
  273. *
  274. * @return  none
  275. */
  276. void SPI_Flash_Erase_Chip(void)
  277. {
  278.     SPI_FLASH_Write_Enable();
  279.     SPI_Flash_Wait_Busy();
  280.     FLASH_CS_LOW;
  281.     SPI3_ReadWriteByte(W25X_ChipErase);
  282.     FLASH_CS_HIGH;
  283.     SPI_Flash_Wait_Busy();
  284. }

  285. /*********************************************************************
  286. * @fn      SPI_Flash_PowerDown
  287. *
  288. * @brief   Enter power down mode.
  289. *
  290. * @return  none
  291. */
  292. void SPI_Flash_PowerDown(void)
  293. {
  294.     FLASH_CS_LOW;
  295.     SPI3_ReadWriteByte(W25X_PowerDown);
  296.     FLASH_CS_HIGH;
  297.     Delay_Us(3);
  298. }

  299. /*********************************************************************
  300. * @fn      SPI_Flash_WAKEUP
  301. *
  302. * @brief   Power down wake up.
  303. *
  304. * @return  none
  305. */
  306. void SPI_Flash_WAKEUP(void)
  307. {
  308.     FLASH_CS_LOW;
  309.     SPI3_ReadWriteByte(W25X_ReleasePowerDown);
  310.     FLASH_CS_HIGH;
  311.     Delay_Us(3);
  312. }
     6、头文件
  1. /* Winbond SPIFalsh ID */
  2. #define W25Q80                   0XEF13
  3. #define W25Q16                   0XEF14
  4. #define W25Q32                   0XEF15
  5. #define W25Q64                   0XEF16
  6. #define W25Q128                  0XEF17

  7. /* Winbond SPIFalsh Instruction List */
  8. #define W25X_WriteEnable         0x06
  9. #define W25X_WriteDisable        0x04
  10. #define W25X_ReadStatusReg       0x05
  11. #define W25X_WriteStatusReg      0x01
  12. #define W25X_ReadData            0x03
  13. #define W25X_FastReadData        0x0B
  14. #define W25X_FastReadDual        0x3B
  15. #define W25X_PageProgram         0x02
  16. #define W25X_BlockErase          0xD8
  17. #define W25X_SectorErase         0x20
  18. #define W25X_ChipErase           0xC7
  19. #define W25X_PowerDown           0xB9
  20. #define W25X_ReleasePowerDown    0xAB
  21. #define W25X_DeviceID            0xAB
  22. #define W25X_ManufactDeviceID    0x90
  23. #define W25X_JedecDeviceID       0x9F

  24. #define FLASH_CS_HIGH GPIO_WriteBit(GPIOE, GPIO_Pin_6, 1)
  25. #define FLASH_CS_LOW  GPIO_WriteBit(GPIOE, GPIO_Pin_6, 0)

  26. u8 SPI1_ReadWriteByte(u8 TxData);
  27. void SPI_Flash_Init(void);
  28. u8 SPI_Flash_ReadSR(void);
  29. void SPI_FLASH_Write_SR(u8 sr);
  30. void SPI_Flash_Wait_Busy(void);
  31. void SPI_FLASH_Write_Enable(void);
  32. void SPI_FLASH_Write_Disable(void);
  33. u16 SPI_Flash_ReadID(void);
  34. void SPI_Flash_Erase_Sector(u32 Dst_Addr);
  35. void SPI_Flash_Read(u8 *pBuffer, u32 ReadAddr, u16 size);
  36. void SPI_Flash_Write_Page(u8 *pBuffer, u32 WriteAddr, u16 size);
  37. void SPI_Flash_Write_NoCheck(u8 *pBuffer, u32 WriteAddr, u16 size);
  38. void SPI_Flash_Write(u8 *pBuffer, u32 WriteAddr, u16 size);
  39. void SPI_Flash_Erase_Chip(void);
  40. void SPI_Flash_PowerDown(void);
  41. void SPI_Flash_WAKEUP(void);
    7、主函数
  1. /*********************************************************************
  2. * @fn      main
  3. *
  4. * @brief   Main program.
  5. *
  6. * @return  none
  7. */
  8. int main(void)
  9. {
  10.         u8  readData[SIZE];
  11.     u16 Flash_Model;
  12.     u8 writeData[SIZE] = {0};
  13.         NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
  14.         SystemCoreClockUpdate();
  15.         Delay_Init();
  16.         USART_Printf_Init(115200);       
  17.         printf("SystemClk:%d\r\n",SystemCoreClock);
  18.         printf( "ChipID:%08x\r\n", DBGMCU_GetCHIPID() );
  19.         printf("This is printf example\r\n");
  20.            SPI_Flash_Init();

  21.     Flash_Model = SPI_Flash_ReadID();
  22.     for(uint16_t i = 0;i < SIZE;i++) {
  23.         writeData[i] = i;
  24.     }

  25.     switch(Flash_Model)
  26.     {
  27.         case W25Q80:
  28.             printf("W25Q80 OK!\r\n");

  29.             break;

  30.         case W25Q16:
  31.             printf("W25Q16 OK!\r\n");

  32.             break;

  33.         case W25Q32:
  34.             printf("W25Q32 OK!\r\n");

  35.             break;

  36.         case W25Q64:
  37.             printf("W25Q64 OK!\r\n");

  38.             break;

  39.         case W25Q128:
  40.             printf("W25Q128 OK!\r\n");

  41.             break;

  42.         default:
  43.             printf("Fail!\r\n");

  44.             break;
  45.     }
  46.     printf("Start Erase W25Qxx....\r\n");
  47.     SPI_Flash_Erase_Sector(0);
  48.     printf("W25Qxx Erase Finished!\r\n");

  49.     Delay_Ms(500);
  50.     printf("Start Read W25Qxx....\r\n");
  51.     SPI_Flash_Read(readData, 0x0, SIZE);
  52.     printf("read OK\r\n");

  53.     Delay_Ms(500);
  54.     printf("Start Write W25Qxx....\r\n");
  55.     printf("W25Qxx Write Finished!\r\n");
  56.     for(uint8_t i = 0;i < SIZE;i++) {
  57.         printf("%d ",writeData[i]);
  58.         if (i % 50 == 0) {
  59.             printf("\r\n");
  60.         }
  61.     }
  62.     SPI_Flash_Write((u8 *)writeData, 0, SIZE);

  63.    

  64.     Delay_Ms(500);
  65.     printf("Start Read W25Qxx....\r\n");
  66.     SPI_Flash_Read(readData, 0x0, SIZE);
  67.     printf("read OK\r\n");
  68.     for(uint8_t i = 0;i < SIZE;i++) {
  69.         printf("%d ",readData[i]);
  70.         if (i % 50 == 0) {
  71.             printf("\r\n");
  72.         }
  73.     }
  74.    

  75.     Delay_Ms(500);
  76.     printf("Start erase chip....\r\n");
  77.     SPI_Flash_Erase_Chip();
  78.     printf("chip erased finsh\r\n");

  79.     printf("Start Read W25Qxx....\r\n");
  80.     SPI_Flash_Read(readData, 0x0, SIZE);
  81.     printf("%s\r\n", readData);

  82.         while(1)
  83.     {

  84.         }
  85. }
     8、下载验证
主要做了以下测试,擦除扇区、读写数据、擦除整个芯片,擦除整个芯片耗时6ms,这速度擦除还是可以的
erase-chip0.png
PixPin_2025-07-31_22-39-56.png


蚊子的噩梦 发表于 2025-8-2 13:13 | 显示全部楼层
这篇测评很详细,特别是SPI接口的工作原理和优缺点分析,让我对SPI有了更深的理解。
 楼主| 星享社 发表于 2025-8-2 16:51 | 显示全部楼层
丙丁先生 发表于 2025-8-1 22:11
沁恒CH32V307开发板 有两种

板子照片就没有拍了,用的是沁恒CH32V307VCT6-EVT-R2开发板
 楼主| 星享社 发表于 2025-8-2 16:51 | 显示全部楼层
蚊子的噩梦 发表于 2025-8-2 13:13
这篇测评很详细,特别是SPI接口的工作原理和优缺点分析,让我对SPI有了更深的理解。
...

多谢大佬鼓励
彩虹彼岸 发表于 2025-8-7 16:15 | 显示全部楼层
老哥是自己的板子,还是用的沁恒的开发板??
Labyrinth 发表于 2025-8-9 21:15 | 显示全部楼层
老哥你分享的东西很给力,下次能否把代码打包一分,上传,小弟感谢了
 楼主| 星享社 发表于 2025-8-15 22:13 | 显示全部楼层
彩虹彼岸 发表于 2025-8-7 16:15
老哥是自己的板子,还是用的沁恒的开发板??

用的沁恒的开发板
NebulaHaven 发表于 2025-8-21 15:42 | 显示全部楼层
测试是基于哪块开发板??
您需要登录后才可以回帖 登录 | 注册

本版积分规则

9

主题

26

帖子

0

粉丝
快速回复 在线客服 返回列表 返回顶部