[STM32F1] VSCODE STM32 裸机之SPI总线 W25Q64

[复制链接]
 楼主| zero949079783 发表于 2021-11-28 23:06 | 显示全部楼层 |阅读模式
本帖最后由 zero949079783 于 2021-11-28 23:10 编辑

开发环境:VSCODE(gcc编译链)+STM32CubeMX(也可以使用HUAWEI-LiteOS-Studio) 。
代码:链接:https://pan.baidu.com/s/1uXfIR0GFQOBZPl1NfQP08w  
提取码:6b0c
2.png 1.png

SPI协议简介
    SPI的通信原理很简单,一般主从方式工作,这种模式通常有一个主设备和一个或多个从设备,通常采用的是4根线,它们是MISO(数据输入,针对主机来说)、MOSI(数据输出,针对主机来说)、SCLK(时钟,主机产生)、CS/SS(片选,一般由主机发送或者直接使能,通常为低电平有效)

●SPI接口介绍

SCK:时钟信号,由主设备产生,所以主设备SCK信号为输出模式,从设备的SCK信号为输入模式。

CS:使能信号,由主设备控制从设备,,所以主设备CS信号为输出模式,从设备的CS信号为输入模式。

MOSI:主设备数据输出,从设备数据输入,所以主设备MOSI信号为输出模式,从设备的MOSI信号为输入模式。

MISO:主设备数据输入,从设备数据输出,所以主设备MISO信号为输入模式,从设备的MISO信号为输出模式。

SPI传输模式

SPI总线传输一共有4种模式,这4种模式分别由时钟极性(CPOL)和时钟相位(CPHA)来定义。
3.png

CPOL:规定了SCK时钟信号空闲状态的电平

CPHA:规定了数据是在SCK时钟的上升沿还是下降沿被采样

----------- ------------------------------------

模式0:CPOL=0,CPHA =0  SCK空闲为低电平,数据在SCK的上升沿被采样(提取数据)

模式1:CPOL=0,CPHA =1  SCK空闲为低电平,数据在SCK的下降沿被采样(提取数据)

模式2:CPOL=1,CPHA =0  SCK空闲为高电平,数据在SCK的下降沿被采样(提取数据)

模式3:CPOL=1,CPHA =1  SCK空闲为高电平,数据在SCK的上升沿被采样(提取数据)

以模式0为例:SCK空闲为低电平,数据在SCK的上升沿被采样(提取数据),在SCK的下降沿切换数据线的数据。


  1. #include "W25Q64.h"

  2. void W25Q64_Config(void)
  3. {
  4.     W25Q64_CS_OUT_H();
  5. }


  6. /**
  7. * [url=home.php?mod=space&uid=247401]@brief[/url]   W25164C在发送数据的同时接收指定长度的数据
  8. * @param    dat  —— 接收数据缓冲区首地址
  9. * @param   recv_buf  —— 接收数据缓冲区首地址
  10. * @param   —— 要发送/接收数据的字节数
  11. * @retval  成功返回HAL_OK
  12. */
  13. uint8_t SPI_FLASH_SendByte(uint8_t byte)
  14. {
  15.     uint8_t recv_buf = 0;

  16.     HAL_SPI_TransmitReceive(&hspi1, &byte, &recv_buf, 1, 10000);

  17.     return recv_buf;
  18. }


  19. /**
  20.   * [url=home.php?mod=space&uid=247401]@brief[/url]  读取FLASH Device ID
  21.   * @param         无
  22.   * @retval FLASH Device ID
  23.   */
  24. uint32_t SPI_FLASH_ReadDeviceID(void)
  25. {
  26.         uint32_t flash_id;
  27.         //使能W25Q64_CS,拉低CS

  28.         W25Q64_CS_OUT_L();
  29.         SPI_FLASH_SendByte(W25X_JedecDeviceID);
  30.     //SPI_Receive(recv_buf,3);
  31.    
  32.         flash_id=SPI_FLASH_SendByte(Dummy_Byte);
  33.         flash_id <<=8;
  34.         flash_id|=SPI_FLASH_SendByte(Dummy_Byte);
  35.         flash_id <<=8;
  36.         flash_id|=SPI_FLASH_SendByte(Dummy_Byte);
  37.         
  38.         //W25Q64_CS,拉高CS
  39.         W25Q64_CS_OUT_H();

  40.         return flash_id;
  41. }

  42. /**
  43.   * [url=home.php?mod=space&uid=247401]@brief[/url]  擦除FLASH扇区
  44.   * @param  SectorAddr:要擦除的扇区地址
  45.   * @retval 无
  46.   */
  47. void SPI_FLASH_SectorErase(uint32_t SectorAddr)
  48. {
  49.                 /* 发送FLASH写使能命令 */
  50.                 SPI_FLASH_WriteEnable();
  51.                 SPI_FLASH_WaitForWriteEnd();
  52.                 //使能W25Q64_CS,拉低CS
  53.                 W25Q64_CS_OUT_L();
  54.                 SPI_FLASH_SendByte(W25X_SectorErase);
  55.                 /** 发送地址  **/
  56.                 SPI_FLASH_SendByte((SectorAddr>>16)&0xff);        
  57.                 SPI_FLASH_SendByte((SectorAddr>>8)&0xff);
  58.                 SPI_FLASH_SendByte((SectorAddr)&0xff);
  59.                 //W25Q64_CS,拉高CS
  60.                 W25Q64_CS_OUT_H();
  61.         
  62.                 SPI_FLASH_WaitForWriteEnd();
  63. }

  64. /**
  65.   * @brief  等待WIP(BUSY)标志被置0,即等待到FLASH内部数据写入完毕
  66.   * @param  none
  67.   * @retval none
  68.   */
  69. void SPI_FLASH_WaitForWriteEnd(void)
  70. {        
  71.                 uint8_t status_reg=0;
  72.                 //使能W25Q64_CS,拉低CS
  73.                 W25Q64_CS_OUT_L();
  74.                 SPI_FLASH_SendByte(W25X_ReadStatusReg);
  75.                
  76.                 do
  77.                 {
  78.                         status_reg  = SPI_FLASH_SendByte(Dummy_Byte);
  79.                 }while((status_reg & 0x01)== 1);        //
  80.         
  81.                 //W25Q64_CS,拉高CS
  82.                 W25Q64_CS_OUT_H();
  83.                
  84. }

  85. /**
  86.   * @brief  读取FLASH数据
  87.   * @param         pBuffer,存储读出数据的指针
  88.   * @param   ReadAddr,读取地址
  89.   * @param   NumByteToRead,读取数据长度
  90.   * @retval 无
  91.   */
  92. void SPI_FLASH_BufferRead(uint32_t ReadAddr,uint8_t* pBuffer, uint16_t NumByteToRead)
  93. {
  94.                 //使能W25Q64_CS,拉低CS
  95.                 W25Q64_CS_OUT_L();
  96.                 SPI_FLASH_SendByte(W25X_ReadData);
  97.                 /** 发送地址  **/
  98.                 SPI_FLASH_SendByte((ReadAddr>>16)&0xff);        
  99.                 SPI_FLASH_SendByte((ReadAddr>>8)&0xff);
  100.                 SPI_FLASH_SendByte((ReadAddr)&0xff);
  101.                
  102.                 while(NumByteToRead--)
  103.                 {
  104.                         *pBuffer = SPI_FLASH_SendByte(Dummy_Byte);
  105.                         pBuffer++;
  106.                 }
  107.         
  108.                 //W25Q64_CS,拉高CS
  109.                 W25Q64_CS_OUT_H();
  110. }

  111. /**
  112.   * @brief  向FLASH发送 写使能 命令
  113.   * @param  none
  114.   * @retval none
  115.   */
  116. void SPI_FLASH_WriteEnable(void)
  117. {
  118.         //使能W25Q64_CS,拉低CS
  119.                 W25Q64_CS_OUT_L();
  120.                 SPI_FLASH_SendByte(W25X_WriteEnable);
  121.         //W25Q64_CS,拉高CS
  122.                 W25Q64_CS_OUT_H();
  123. }

  124. /**
  125.   * @brief  对FLASH按页写入数据,调用本函数写入数据前需要先擦除扇区
  126.   * @param        pBuffer,要写入数据的指针
  127.   * @param WriteAddr,写入地址
  128.   * @param  NumByteToWrite,写入数据长度,必须小于等于SPI_FLASH_PerWritePageSize
  129.   * @retval 无
  130.   */
  131. void SPI_FLASH_PageWrite( uint32_t WriteAddr,uint8_t* pBuffer, uint16_t NumByteToWrite)
  132. {
  133.                 /* 发送FLASH写使能命令 */
  134.                 SPI_FLASH_WriteEnable();
  135.         //使能W25Q64_CS,拉低CS
  136.                 W25Q64_CS_OUT_L();
  137.                 SPI_FLASH_SendByte(W25X_PageProgram);
  138.                 /** 发送地址  **/
  139.                 SPI_FLASH_SendByte((WriteAddr>>16)&0xff);        
  140.                 SPI_FLASH_SendByte((WriteAddr>>8)&0xff);
  141.                 SPI_FLASH_SendByte((WriteAddr)&0xff);
  142.                
  143.                 while(NumByteToWrite--)
  144.                 {
  145.                         SPI_FLASH_SendByte(*pBuffer);
  146.                         pBuffer++;
  147.                 }
  148.         
  149.                 //W25Q64_CS,拉高CS
  150.                 W25Q64_CS_OUT_H();
  151.                 /* 等待写入完毕*/
  152.                 SPI_FLASH_WaitForWriteEnd();
  153. }


  154. /**
  155.   * @brief  对FLASH写入数据,调用本函数写入数据前需要先擦除扇区
  156.   * @param        pBuffer,要写入数据的指针
  157.   * @param  WriteAddr,写入地址
  158.   * @param  NumByteToWrite,写入数据长度
  159.   * @retval 无
  160.   */
  161. void SPI_FLASH_BufferWrite( uint32_t WriteAddr,uint8_t* pBuffer, uint16_t NumByteToWrite)
  162. {
  163.         uint8_t addr=0,NumOfPage = 0, NumOfSingle = 0,count = 0,temp = 0;
  164.         
  165.         /*mod运算求余,若writeAddr是SPI_FLASH_PageSize(256)整数倍,运算结果Addr值为0*/
  166.         addr = WriteAddr%SPI_FLASH_PageSize;
  167.         
  168.         /*差count个数据值,刚好可以对齐到页地址*/
  169.   count = SPI_FLASH_PageSize - addr;
  170.         
  171.         /*计算出要写多少整数页*/
  172.         NumOfPage =  NumByteToWrite /SPI_FLASH_PageSize;
  173.         /*mod运算求余,计算出剩余不满一页的字节数*/
  174.         NumOfSingle =  NumByteToWrite % SPI_FLASH_PageSize;
  175.         
  176.         /* addr=0,则WriteAddr 刚好按页对齐 aligned  */
  177.         if(addr==0)
  178.         {
  179.                         /* NumByteToWrite < SPI_FLASH_PageSize  数据长度小于256,直接写入*/
  180.                         if(NumOfPage == 0)
  181.                         {
  182.                                 SPI_FLASH_PageWrite(WriteAddr,pBuffer,NumByteToWrite);
  183.                         }
  184.                         
  185.                         else/* NumByteToWrite > SPI_FLASH_PageSize数据长度大于256, */
  186.                         {
  187.                                 /*先把整数页都写了*/
  188.                                 while(NumOfPage--)
  189.                                 {
  190.                                         SPI_FLASH_PageWrite(WriteAddr,pBuffer,SPI_FLASH_PageSize);
  191.                                         WriteAddr+= SPI_FLASH_PageSize;
  192.                                         pBuffer += SPI_FLASH_PageSize;
  193.                                 }
  194.                                 if(NumOfSingle != 0)
  195.                                 {
  196.                                         /*若有多余的不满一页的数据,把它写完*/
  197.                                         SPI_FLASH_PageWrite(WriteAddr,pBuffer,NumOfSingle);
  198.                                 }
  199.                                 
  200.                         }
  201.         }
  202.         /* 若地址与 SPI_FLASH_PageSize 不对齐  */
  203.         else
  204.         {
  205.                 /* NumByteToWrite < SPI_FLASH_PageSize数据长度小于256,直接写入 */
  206.                 if(NumOfPage == 0)
  207.                 {
  208.                         /*当前页剩余的count个位置比NumOfSingle小,一页写不完*/
  209.                         if (NumOfSingle > count)
  210.                         {
  211.                                          temp = NumOfSingle - count;
  212.                                         /*先写满当前页*/
  213.                                         SPI_FLASH_PageWrite( WriteAddr,pBuffer, count);
  214.                                        
  215.                                         WriteAddr += count;
  216.                                         pBuffer  += count;
  217.                                 
  218.                                         /*再写剩余的数据*/
  219.                                         SPI_FLASH_PageWrite(WriteAddr,pBuffer,temp);
  220.                         }
  221.                         else /*当前页剩余的count个位置能写完NumOfSingle个数据*/
  222.                         {
  223.                                         SPI_FLASH_PageWrite(WriteAddr,pBuffer, NumByteToWrite);
  224.                         }
  225.                 }
  226.                 /* NumByteToWrite > SPI_FLASH_PageSize 数据长度大于256*/
  227.                 else
  228.                 {
  229.                         /*地址不对齐多出的count分开处理,不加入这个运算*/
  230.                         NumByteToWrite -= count;
  231.       NumOfPage =  NumByteToWrite / SPI_FLASH_PageSize;
  232.       NumOfSingle = NumByteToWrite % SPI_FLASH_PageSize;
  233.                         
  234.                         /* 先写完count个数据,为的是让下一次要写的地址对齐 */
  235.                         SPI_FLASH_PageWrite( WriteAddr,pBuffer, count);
  236.                         
  237.                         /* 接下来就重复地址对齐的情况 */
  238.       WriteAddr +=  count;
  239.       pBuffer += count;
  240.                         /*把整数页都写了*/
  241.                         while (NumOfPage--)
  242.                         {
  243.                                 SPI_FLASH_PageWrite(WriteAddr,pBuffer, SPI_FLASH_PageSize);
  244.         WriteAddr +=  SPI_FLASH_PageSize;
  245.         pBuffer += SPI_FLASH_PageSize;
  246.                         }
  247.                         
  248.                                 /*若有多余的不满一页的数据,把它写完*/
  249.                         if (NumOfSingle != 0)
  250.       {
  251.         SPI_FLASH_PageWrite( WriteAddr,pBuffer, NumOfSingle);
  252.       }
  253.                 }
  254.         }
  255. }
  256. /***********************end***************************************/



  
您需要登录后才可以回帖 登录 | 注册

本版积分规则

33

主题

91

帖子

1

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