zero949079783 发表于 2021-11-28 23:06

VSCODE STM32 裸机之SPI总线 W25Q64

本帖最后由 zero949079783 于 2021-11-28 23:10 编辑

开发环境:VSCODE(gcc编译链)+STM32CubeMX(也可以使用HUAWEI-LiteOS-Studio) 。代码:链接:https://pan.baidu.com/s/1uXfIR0GFQOBZPl1NfQP08w
提取码:6b0c
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)来定义。
CPOL:规定了SCK时钟信号空闲状态的电平
CPHA:规定了数据是在SCK时钟的上升沿还是下降沿被采样
----------- ------------------------------------
模式0:CPOL=0,CPHA =0SCK空闲为低电平,数据在SCK的上升沿被采样(提取数据)
模式1:CPOL=0,CPHA =1SCK空闲为低电平,数据在SCK的下降沿被采样(提取数据)
模式2:CPOL=1,CPHA =0SCK空闲为高电平,数据在SCK的下降沿被采样(提取数据)
模式3:CPOL=1,CPHA =1SCK空闲为高电平,数据在SCK的上升沿被采样(提取数据)
以模式0为例:SCK空闲为低电平,数据在SCK的上升沿被采样(提取数据),在SCK的下降沿切换数据线的数据。

#include "W25Q64.h"

void W25Q64_Config(void)
{
    W25Q64_CS_OUT_H();
}


/**
* @brief   W25164C在发送数据的同时接收指定长度的数据
* @param    dat—— 接收数据缓冲区首地址
* @param   recv_buf—— 接收数据缓冲区首地址
* @param   —— 要发送/接收数据的字节数
* @retval成功返回HAL_OK
*/
uint8_t SPI_FLASH_SendByte(uint8_t byte)
{
    uint8_t recv_buf = 0;

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

    return recv_buf;
}


/**
* @brief读取FLASH Device ID
* @param         无
* @retval FLASH Device ID
*/
uint32_t SPI_FLASH_ReadDeviceID(void)
{
      uint32_t flash_id;
      //使能W25Q64_CS,拉低CS

      W25Q64_CS_OUT_L();
      SPI_FLASH_SendByte(W25X_JedecDeviceID);
    //SPI_Receive(recv_buf,3);
   
      flash_id=SPI_FLASH_SendByte(Dummy_Byte);
      flash_id <<=8;
      flash_id|=SPI_FLASH_SendByte(Dummy_Byte);
      flash_id <<=8;
      flash_id|=SPI_FLASH_SendByte(Dummy_Byte);
      
      //W25Q64_CS,拉高CS
      W25Q64_CS_OUT_H();

      return flash_id;
}

/**
* @brief擦除FLASH扇区
* @paramSectorAddr:要擦除的扇区地址
* @retval 无
*/
void SPI_FLASH_SectorErase(uint32_t SectorAddr)
{
                /* 发送FLASH写使能命令 */
                SPI_FLASH_WriteEnable();
                SPI_FLASH_WaitForWriteEnd();
                //使能W25Q64_CS,拉低CS
                W25Q64_CS_OUT_L();
                SPI_FLASH_SendByte(W25X_SectorErase);
                /** 发送地址**/
                SPI_FLASH_SendByte((SectorAddr>>16)&0xff);      
                SPI_FLASH_SendByte((SectorAddr>>8)&0xff);
                SPI_FLASH_SendByte((SectorAddr)&0xff);
                //W25Q64_CS,拉高CS
                W25Q64_CS_OUT_H();
      
                SPI_FLASH_WaitForWriteEnd();
}

/**
* @brief等待WIP(BUSY)标志被置0,即等待到FLASH内部数据写入完毕
* @paramnone
* @retval none
*/
void SPI_FLASH_WaitForWriteEnd(void)
{      
                uint8_t status_reg=0;
                //使能W25Q64_CS,拉低CS
                W25Q64_CS_OUT_L();
                SPI_FLASH_SendByte(W25X_ReadStatusReg);
               
                do
                {
                        status_reg= SPI_FLASH_SendByte(Dummy_Byte);
                }while((status_reg & 0x01)== 1);      //
      
                //W25Q64_CS,拉高CS
                W25Q64_CS_OUT_H();
               
}

/**
* @brief读取FLASH数据
* @param         pBuffer,存储读出数据的指针
* @param   ReadAddr,读取地址
* @param   NumByteToRead,读取数据长度
* @retval 无
*/
void SPI_FLASH_BufferRead(uint32_t ReadAddr,uint8_t* pBuffer, uint16_t NumByteToRead)
{
                //使能W25Q64_CS,拉低CS
                W25Q64_CS_OUT_L();
                SPI_FLASH_SendByte(W25X_ReadData);
                /** 发送地址**/
                SPI_FLASH_SendByte((ReadAddr>>16)&0xff);      
                SPI_FLASH_SendByte((ReadAddr>>8)&0xff);
                SPI_FLASH_SendByte((ReadAddr)&0xff);
               
                while(NumByteToRead--)
                {
                        *pBuffer = SPI_FLASH_SendByte(Dummy_Byte);
                        pBuffer++;
                }
      
                //W25Q64_CS,拉高CS
                W25Q64_CS_OUT_H();
}

/**
* @brief向FLASH发送 写使能 命令
* @paramnone
* @retval none
*/
void SPI_FLASH_WriteEnable(void)
{
      //使能W25Q64_CS,拉低CS
                W25Q64_CS_OUT_L();
                SPI_FLASH_SendByte(W25X_WriteEnable);
      //W25Q64_CS,拉高CS
                W25Q64_CS_OUT_H();
}

/**
* @brief对FLASH按页写入数据,调用本函数写入数据前需要先擦除扇区
* @param      pBuffer,要写入数据的指针
* @param WriteAddr,写入地址
* @paramNumByteToWrite,写入数据长度,必须小于等于SPI_FLASH_PerWritePageSize
* @retval 无
*/
void SPI_FLASH_PageWrite( uint32_t WriteAddr,uint8_t* pBuffer, uint16_t NumByteToWrite)
{
                /* 发送FLASH写使能命令 */
                SPI_FLASH_WriteEnable();
      //使能W25Q64_CS,拉低CS
                W25Q64_CS_OUT_L();
                SPI_FLASH_SendByte(W25X_PageProgram);
                /** 发送地址**/
                SPI_FLASH_SendByte((WriteAddr>>16)&0xff);      
                SPI_FLASH_SendByte((WriteAddr>>8)&0xff);
                SPI_FLASH_SendByte((WriteAddr)&0xff);
               
                while(NumByteToWrite--)
                {
                        SPI_FLASH_SendByte(*pBuffer);
                        pBuffer++;
                }
      
                //W25Q64_CS,拉高CS
                W25Q64_CS_OUT_H();
                /* 等待写入完毕*/
                SPI_FLASH_WaitForWriteEnd();
}


/**
* @brief对FLASH写入数据,调用本函数写入数据前需要先擦除扇区
* @param      pBuffer,要写入数据的指针
* @paramWriteAddr,写入地址
* @paramNumByteToWrite,写入数据长度
* @retval 无
*/
void SPI_FLASH_BufferWrite( uint32_t WriteAddr,uint8_t* pBuffer, uint16_t NumByteToWrite)
{
      uint8_t addr=0,NumOfPage = 0, NumOfSingle = 0,count = 0,temp = 0;
      
      /*mod运算求余,若writeAddr是SPI_FLASH_PageSize(256)整数倍,运算结果Addr值为0*/
      addr = WriteAddr%SPI_FLASH_PageSize;
      
      /*差count个数据值,刚好可以对齐到页地址*/
count = SPI_FLASH_PageSize - addr;
      
      /*计算出要写多少整数页*/
      NumOfPage =NumByteToWrite /SPI_FLASH_PageSize;
      /*mod运算求余,计算出剩余不满一页的字节数*/
      NumOfSingle =NumByteToWrite % SPI_FLASH_PageSize;
      
      /* addr=0,则WriteAddr 刚好按页对齐 aligned*/
      if(addr==0)
      {
                        /* NumByteToWrite < SPI_FLASH_PageSize数据长度小于256,直接写入*/
                        if(NumOfPage == 0)
                        {
                              SPI_FLASH_PageWrite(WriteAddr,pBuffer,NumByteToWrite);
                        }
                        
                        else/* NumByteToWrite > SPI_FLASH_PageSize数据长度大于256, */
                        {
                              /*先把整数页都写了*/
                              while(NumOfPage--)
                              {
                                        SPI_FLASH_PageWrite(WriteAddr,pBuffer,SPI_FLASH_PageSize);
                                        WriteAddr+= SPI_FLASH_PageSize;
                                        pBuffer += SPI_FLASH_PageSize;
                              }
                              if(NumOfSingle != 0)
                              {
                                        /*若有多余的不满一页的数据,把它写完*/
                                        SPI_FLASH_PageWrite(WriteAddr,pBuffer,NumOfSingle);
                              }
                              
                        }
      }
      /* 若地址与 SPI_FLASH_PageSize 不对齐*/
      else
      {
                /* NumByteToWrite < SPI_FLASH_PageSize数据长度小于256,直接写入 */
                if(NumOfPage == 0)
                {
                        /*当前页剩余的count个位置比NumOfSingle小,一页写不完*/
                        if (NumOfSingle > count)
                        {
                                       temp = NumOfSingle - count;
                                        /*先写满当前页*/
                                        SPI_FLASH_PageWrite( WriteAddr,pBuffer, count);
                                       
                                        WriteAddr += count;
                                        pBuffer+= count;
                              
                                        /*再写剩余的数据*/
                                        SPI_FLASH_PageWrite(WriteAddr,pBuffer,temp);
                        }
                        else /*当前页剩余的count个位置能写完NumOfSingle个数据*/
                        {
                                        SPI_FLASH_PageWrite(WriteAddr,pBuffer, NumByteToWrite);
                        }
                }
                /* NumByteToWrite > SPI_FLASH_PageSize 数据长度大于256*/
                else
                {
                        /*地址不对齐多出的count分开处理,不加入这个运算*/
                        NumByteToWrite -= count;
      NumOfPage =NumByteToWrite / SPI_FLASH_PageSize;
      NumOfSingle = NumByteToWrite % SPI_FLASH_PageSize;
                        
                        /* 先写完count个数据,为的是让下一次要写的地址对齐 */
                        SPI_FLASH_PageWrite( WriteAddr,pBuffer, count);
                        
                        /* 接下来就重复地址对齐的情况 */
      WriteAddr +=count;
      pBuffer += count;
                        /*把整数页都写了*/
                        while (NumOfPage--)
                        {
                              SPI_FLASH_PageWrite(WriteAddr,pBuffer, SPI_FLASH_PageSize);
      WriteAddr +=SPI_FLASH_PageSize;
      pBuffer += SPI_FLASH_PageSize;
                        }
                        
                              /*若有多余的不满一页的数据,把它写完*/
                        if (NumOfSingle != 0)
      {
      SPI_FLASH_PageWrite( WriteAddr,pBuffer, NumOfSingle);
      }
                }
      }
}
/***********************end***************************************/


页: [1]
查看完整版本: VSCODE STM32 裸机之SPI总线 W25Q64