首页 > 技术文章 > GD32F303 驱动 W25Q64

qinlongqiang 2020-07-08 18:22 原文

F303 驱动 W25Q64 遇到的问题(收的的数据被右移一位)

 

问题出现:

  昨天在移植W25Q64的驱动到F3飞控上,使用正点原子的F4的驱动,细节处做了修改,但是无论如何器件ID无法正确读出,一直读出 FFEF,但手册上是说明 EF16。

定位问题:

  首先看到读取芯片ID的函数

复制代码
//读取芯片ID
u16 SPI_Flash_ReadID(void)
{
    u16 Temp = 0;
    
    SPI_FLASH_CS_L();
    SPI2_ReadWriteByte(0X90);   //发送读取ID命令
    SPI2_ReadWriteByte(0x00);
    SPI2_ReadWriteByte(0x00);
    SPI2_ReadWriteByte(0x00);
    ( SPI2_ReadWriteByte(0x00); ) 
    Temp|=SPI2_ReadWriteByte(0xFF)<<8;
    Temp|=SPI2_ReadWriteByte(0xFF);
    SPI_FLASH_CS_H();
    return Temp;
}
复制代码

 

  其中 三句 SPI2_ReadWriteByte(0x00);是发送3个任意帧,然后第四帧就可以开始读取数据,于是,我就多加了一句(括号中),发现读出ef16,这说明,STM32的硬件SPI配置无误,SPI可以通讯,但是数据像是被右移了一位。但是程序此处不能修改,问题应该出现在SPI收发,因为此时W25Q64依旧无法读取数据,不可能在每一个W25Q64要读取前多加这一条语句,这无法根本解决问题。

  于是看到SPI收发函数

复制代码
//SPIx 读写一个字节
//TxData:要写入的字节
//返回值:读取到的字节
u8 SPI2_ReadWriteByte(u8 TxData)
{        
//    u8 retry=0;  
    
    while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET) //检查指定的SPI标志位设置与否:发送缓存空标志位
    {
//        retry++;
//        if(retry>200)return 0;
    }
    SPI_SendData8(SPI2, TxData); //通过外设SPIx发送一个数据
//    SPI_I2S_SendData16(SPI2, TxData);
//    retry=0;

    while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET)//检查指定的SPI标志位设置与否:接受缓存非空标志位
    {
//        retry++;
//        if(retry>200)return 0;
    }                                  
    return SPI_ReceiveData8(SPI2); //返回通过SPIx最近接收的数据 
//    return SPI_I2S_ReceiveData16(SPI2);
}
复制代码

  其中,未打注释就是原子的例程,后来我查到很多人用 SPI_I2S_ReceiveData();这个函数,区别就是这句函数形参是u16,而例程是u8,会不会是这里的问题呢?于是我用打注释的两句发送/接收函数代替了例程的发送/接收函数,结果仍是不对.....

  整的一天都在上网查资料,修改,失败....不断循环

  今天早上一来,灵光一现。难道F303和F4有什么细微不同我还没发现?(我这么想是因为之前移植软件IIC的驱动,F3无响应,而F1有响应,查了快一周才发现F3是不支持位带操作的),于是再检查SPI初始化函数(昨天看了不知道多少遍了...),配合网上一查,看到ST官方F3 Discover板子的SPI初始化,在使能SPI前,还有一句函数 SPI_RxFIFOThresholdConfig(SPI2, SPI_RxFIFOThreshold_QF); 于是加上去试试,没想到正常了,ID读取正确,可对Flsah执行读取/存入操作!问题完美解决!

下面是SPI初始化函数(不包含CS脚)

 

复制代码
void SPI2_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    SPI_InitTypeDef  SPI_InitStructure;
    
    RCC_AHBPeriphClockCmd( SPI2_GPIO_RCC, ENABLE );
    RCC_APB1PeriphClockCmd(SPI2_RCC, ENABLE);

    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF; 
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStructure.GPIO_Pin   = SPI2_CLK_Pin|SPI2_DI_Pin|SPI2_DO_Pin;
    GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_UP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_Level_3;
    GPIO_Init(SPI2_GPIO_Port, &GPIO_InitStructure);
    
    GPIO_PinAFConfig(SPI2_GPIO_Port, SPI2_CLK_PinSource, GPIO_AF_5);
    GPIO_PinAFConfig(SPI2_GPIO_Port, SPI2_DI_PinSource,  GPIO_AF_5);
    GPIO_PinAFConfig(SPI2_GPIO_Port, SPI2_DO_PinSource,  GPIO_AF_5);

    SPI_InitStructure.SPI_Direction         = SPI_Direction_2Lines_FullDuplex;  //设置SPI单向或者双向的数据模式:SPI设置为双线双向全双工
    SPI_InitStructure.SPI_Mode              = SPI_Mode_Master;      //设置SPI工作模式:设置为主SPI
    SPI_InitStructure.SPI_DataSize          = SPI_DataSize_8b;      //设置SPI的数据大小:SPI发送接收8位帧结构
    SPI_InitStructure.SPI_CPOL              = SPI_CPOL_High;        //串行同步时钟的空闲状态为高电平
    SPI_InitStructure.SPI_CPHA              = SPI_CPHA_2Edge;       //串行同步时钟的第二个跳变沿(上升或下降)数据被采样
    SPI_InitStructure.SPI_NSS               = SPI_NSS_Soft;         //NSS信号由硬件(NSS管脚)还是软件(使用SSI位)管理:内部NSS信号有SSI位控制
    SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;    //定义波特率预分频的值:波特率预分频值为256
    SPI_InitStructure.SPI_FirstBit          = SPI_FirstBit_MSB;     //指定数据传输从MSB位还是LSB位开始:数据传输从MSB位开始
    SPI_InitStructure.SPI_CRCPolynomial     = 7;                    //CRC值计算的多项式
    SPI_Init(SPI2, &SPI_InitStructure);  
 
    SPI_RxFIFOThresholdConfig(SPI2, SPI_RxFIFOThreshold_QF);
    
    SPI_Cmd(SPI2, ENABLE); //使能SPI外设
}
复制代码

 

 

 

原因分析:

  官方对这个函数的说明是 "Configures the FIFO reception threshold for the selected SPI." 也就是对选中的SPI的接收FIFO的阈值进行配置。SPI_RxFIFOThreshold_QF: RXNE event is generated if the FIFO level is greater or equal to 1/4.也就是 如果FIFO使用大于等于1/4RXNE事件才发生。

推荐阅读