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