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事件才发生。
