问题:STM32硬件I2C驱动W25Q128时,指令的帧数据格式并不是通过ReceiveData和SendData直接读写数据,而是通过SwapByte函数实现的
SPI通信的流程
主机发送数据的流程是->主机要发送的数据填入发送缓冲区->发送缓冲区非空,数据填入移位寄存器->主机移位寄存器的数据通过MOSI线移入从机移位寄存器,同时从机移位寄存器的数据通过MISO移入主机的移位寄存器(移位寄存器的数据交换)。
主机接收数据的流程是->从机要发送的数据填入发送缓冲区->发送缓冲区非空,数据填入移位寄存器->等待主机发送任意数据(通常我们发送0xFF,因为数据手册中有些命令的帧数据格式必须发0xFF)->从机移位寄存器的数据通过MISO移入主机的移位寄存器,同时主机移位寄存器的数据通过MOSI线移入从机移位寄存器。
SPI通信的特点
因为SPI没有读和写的概念,实际上的每一次通信都是交换数据。换句话说,你要是发一个数据必然会接收到一个数据,你要是想接收一个数据必须要发送一个数据。
所以我们看不到ReceiveByte和SendByte直接读写数据,而是通过 先发送数据然后接受数据 封装的SwapByte(发送字节是在前面的)实现SPI通信
/**
* 函 数:SPI交换传输一个字节,使用SPI模式0
* 参 数:ByteSend 要发送的一个字节
* 返 回 值:接收的一个字节
*/
uint8_t SPI_SwapByte(uint8_t ByteSend)
{
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) != SET); //等待发送数据寄存器空
SPI_I2S_SendData(SPI1, ByteSend); //写入数据到发送数据寄存器,开始产生时序
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) != SET); //等待接收数据寄存器非空
return SPI_I2S_ReceiveData(SPI1); //读取接收到的数据并返回
}
SPI命令帧的格式解析
学习SPI通信的时候我感觉SPI通信某种意义上就是一个半双工通信,而不是全双工通信。
发送数据时接收到的那个数据和接受数据要发送的那个数据是没有任何实际的意义的
下图是Read JEDEC ID (9Fh)命令的时序图
对应的SPI读取流程如下
命令帧的数据格式我们只需要关注有效字节的发送和接收
/**
* 函 数:W25Q128读取ID号
* 参 数:MID 工厂ID,使用输出参数的形式返回
* 参 数:DID 设备ID,使用输出参数的形式返回
* 返 回 值:无
*/
void W25Q128_ReadID(uint8_t *MID, uint16_t *DID)
{
int MemoryID,CapacityID;
SPI_Start(); //SPI起始,内部就是拉低CS线
SPI_SwapByte(0x9F); //交换发送读取ID的指令
*MID = SPI_SwapByte(0xFF); //交换接收MID,通过输出参数返回
MemoryID = SPI_SwapByte(0xFF); //交换接收DID高8位
CapacityID = SPI_SwapByte(0XFF); //或上交换接收DID的低8位,通过输出参数返回
*DID = MemoryID<<8+CapacityID
SPI_Stop(); //SPI终止,拉高CS线
}
注:1,为什么发送的无效字节是0xFF
数据手册中原话 11. The first dummy is M7-M0 should be set to FFh
因为数据手册上有几个命令的第一个dummy字节要求是0xFF,为了完全兼容所有命令,我们选择0xFF
2,发送命令0X9F时我们并没有要SPI_SwapByte的返回值,也就是发送数据时接收到的那个数据,我们是把它当作无效字节丢掉的
这样我们就完成了一个命令帧格式的解析