引言
SPI协议被广泛用于嵌入式通信,由Motorola(现在的NXP)在1980年代提出,它是一种“同步、全双工”的通信协议。
一、SPI的基本组成
SPI由4根线完成通信,它们分别是SCL(时钟线 )、MOSI(主发从收)、MISO(主收从发)、CS(片选信号) 组成。因为有两根线完成数据的收发,因此通信速度是比IIC要快很多的。另外SPI的实现起来要比IIC简单的多,除了占用引脚和错误校验有不足之外,SPI还是很优秀的。
二、SPI的四种通信模式
SPI有四种通信模式,根据时钟的极性(CPOL )即空闲时时钟的电平,和数相位(CPHA )即采样时刻,可分为以下四种:
- 模式0(CPOL = 0,CPHA = 0):时钟信号空闲时为低电平,数据在时钟信号的上升沿采样。
- 模式1(CPOL = 0,CPHA = 1):时钟信号空闲时为低电平,数据在时钟信号的下降沿采样。
- 模式2(CPOL = 1,CPHA = 0):时钟信号空闲时为高电平,数据在时钟信号的上升沿采样。
- 模式3(CPOL = 1,CPHA = 1):时钟信号空闲时为高电平,数据在时钟信号的下降沿采样。
三、SPI的接线方式
SPI 接线并不像UART那样同名反接,而是同名同接,即从机的MOSI接主机的MOSI,从机的MISO接主机的MISO,在一些设备上,从机的接口可能是SO和SI,这连个也分别对应MISO和MOSI。时钟线和片选线都一样。
四、SPI的模拟实现
理论再扎实也比不过动手做一次,这里来实现一次SPI协议,在上次我写了一份W25Q64的驱动代码,用的硬件SPI,那这里我来用软件实现一次。
先给四根通信线封装一层函数:
void MySPI_W_CSS(uint8_t bitval)
{
HAL_GPIO_WritePin(SPI_FLASH_CCS_PORT,FLASH_CCS_PIN,(GPIO_PinState)bitval);
}
void MySPI_W_SCK(uint8_t bitval)
{
HAL_GPIO_WritePin(SPI_FLASH_SCLK_PORT,FLASH_SCLK_PIN,(GPIO_PinState)bitval);
}
void MySPI_W_MOSI(uint8_t bitval)
{
HAL_GPIO_WritePin(SPI_FLASH_MOSI_PORT,FLASH_MOSI_PIN,(GPIO_PinState)bitval);
}
GPIO_PinState MySPI_R_MISO()
{
return HAL_GPIO_ReadPin(SPI_FLASH_MISO_PORT,FLASH_MISO_PIN);
}
再封装数据交换的函数(模式0):
void MySPI_CS_High()
{
MySPI_W_CSS(0);
}
void MySPI_CS_Low()
{
MySPI_W_CSS(1);
}
//发送或者读取数据
uint8_t MySPI_SwapByte(uint8_t Byte)
{
uint8_t i=0;
for(i = 0;i < 8; i++)
{
MySPI_W_MOSI(Byte & 0x80);
MySPI_W_SCK(1);
Byte <<= 1;
if(MySPI_R_MISO() == GPIO_PIN_SET)
{
Byte |= 0x01;
}
MySPI_W_SCK(0);
}
return Byte;
}
因为这是全双工的通信协议,因此不能像IIC一样发送数据和交换数据封装为两个函数,另外STM32的HAL库有将SPI的发送和接收封装为两个函数的原因是接收数据和发送数据共用一个DR寄存器,数据可以储存在寄存器中来读写。软件SPI没有这种条件,因此需要在实现的过程中根据时序严格来读取和发送数据。
因为我们先发送的高位,因此Byte在左移的过程中,高位被发送,同时接收到的数据的高位被写入到Byte的低位,循环往复8次,Byte的第0位被移动到第7位,最终可以接受到完整的一个字节以及发送完一个字节。
将软件SPI替换到硬件SPI的代码,再进行测试:
void W25QxxTest()
{
uint16_t head,tail;
for(uint16_t i = 0;i < 8192;i++)
{
data[i] = '1';
}
printf("现在进行软件SPI实验!\n\n");
uint32_t id = W25Qxx_GetID();
printf("芯片ID为:%X\n\n",id);
if(id == W25Q64_ID)
{
printf("正在擦除...\n");
W25Qxx_BlockErase(0x000000);
printf("擦除成功!\n\n");
printf("正在写入...写入数据量为8kb\n");
head = HAL_GetTick();
W25Qxx_WriteData(0x00000A,data,8192);
tail = HAL_GetTick();
printf("写入成功!,写入花费的时间为:%d ms\n\n",tail - head);
printf("正在读取数据,读取数据量为8kb\n");
head = HAL_GetTick();
W25Qxx_ReadData(0x00000A,test,8192);
tail = HAL_GetTick();
if(memcmp(test,data,8192) == 0)
{
printf("读取成功,读取花费的时间为:%d ms\n\n",tail - head);
}
else
{
printf("读取失败\n");
}
printf("读取到的数据是: %s\n",test);
}
}
五、软件SPI和硬件SPI的优缺点
软件SPI和硬件SPI各有优劣。
软件SPI
优点:
1.易于移植
2.灵活性高
3.节省硬件资源
缺点:
1.速度慢
2.CPU负担重,因为是CPU控制时序
硬件SPI
优点:
1.速度快,时序精准
2.节省CPU资源
3.稳定性好
缺点:
1.灵活性差,即固定的引脚通信
2.消耗硬件资源
往期博客写文章-优快云创作中心