打印
[STM32F1]

VSCODE STM32 裸机之SPI总线 W25Q64

[复制链接]
605|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本帖最后由 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 =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的下降沿切换数据线的数据。


#include "W25Q64.h"

void W25Q64_Config(void)
{
    W25Q64_CS_OUT_H();
}


/**
* [url=home.php?mod=space&uid=247401]@brief[/url]   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;
}


/**
  * [url=home.php?mod=space&uid=247401]@brief[/url]  读取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;
}

/**
  * [url=home.php?mod=space&uid=247401]@brief[/url]  擦除FLASH扇区
  * @param  SectorAddr:要擦除的扇区地址
  * @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内部数据写入完毕
  * @param  none
  * @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发送 写使能 命令
  * @param  none
  * @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,写入地址
  * @param  NumByteToWrite,写入数据长度,必须小于等于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,要写入数据的指针
  * @param  WriteAddr,写入地址
  * @param  NumByteToWrite,写入数据长度
  * @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***************************************/



  

使用特权

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

本版积分规则

32

主题

85

帖子

1

粉丝