一.SPI简介
SPI协议(Serial Peripheral Interface)是摩托罗拉公司提出的通讯协议,即串行外围设备接口,是一种高速全双工的通信总线。它被广泛地使用在ADC、LCD 、Flash等设备与MCU之间的通讯,适用于要求通讯速率较高的场合。
1.SPI的物理层
SPI总线通常由4条总线组成,分别为SCK(时钟信号线),MOSI(主机发送从机接收线),MISO(主机接收从机发送线)和CS(从设备使能信号,有的地方叫NSS)。SPI不同于IIC,在协议层中没有从机设备寻址的功能,所以需要通过CS信号去控制从设备的选择。数据传输时也是采用小端模式
MOSI:主机输出从机输入,用于主设备向从设备发送数据;
MISO:主机输入从机输出,用于主设备接收来自从设备发送的数据;
SCK:数据时钟线,用于产生时钟信号
CS:设备使能信号,用于使能数据传输(一般为低电平有效)
2.SPI通信模式
通常对于从设备而言,其出厂时就已经配置好相应的通信模式,无法被改变,所以我们必须通过修改主机的程序对其通信模式进行配置,使而知一致才能进行通信,通过CPOL(时钟极性)和CPHA(时钟相位)来控制我们主设备的通信模式。四种模式分别为:
CPOL=0,表示当SCK=0时处于空闲态,所以有效状态就是SCK处于高电平时;
CPOL=1,表示当SCK=1时处于空闲态,所以有效状态就是SCK处于低电平时;
CPHA=0,表示数据采样是在第1个边沿,数据发送在第2个边沿;
CPHA=1,表示数据采样是在第2个边沿,数据发送在第1个边沿。
CPOL表示,时钟线处于空闲态时的电平;CPHA表示,在时钟的哪一个状态进行数据采样和发送
以实际的写Flash的波形为例
主机写数据到从机
主机从从机读取数据
第一条为MOSI(输出态),第二条为MISO(输入态),第三条为SCK(输出态),第四条为CS(输出态)
从图中可以看出,和IIC协议不一样,SPI协议没有起始信号而停止信号,只有当CS处于低电平时,数据才能开始传输,配置为CPOL=0,CPHA=0,所以在空闲时SCK为低电平,在第一个边沿进行数据采集,时钟高电平时期数据保持稳定,第二个边沿发送数据
3.代码示例
/******************************************************//**
* @brief 写一个字节数据
* @param[in] wByte 写入的数据
* @return NULL
* @note
*********************************************************/
void WriteByte(u8 wByte)
{
u8 i;
FLASH_SI_OUT; //MOSI设置为输出态
FLASH_SCK_OUT; //SCK设置为输出态
for(i = 0; i < 8; i++)
{
FLASH_SCK_LOW; //SCK变低,进行数据采样
if(wByte & 0x80)
{
FLASH_SI_HIGH; //改变SI数据线数据,写入从机
}
else
{
FLASH_SI_LOW;
}
wByte <<= 1;
FLASH_SCK_HIGH; //SCK变高,进行数据发送
}
FLASH_SCK_LOW; //发送完一个字节,SCK处于空闲态
}
/******************************************************//**
* @brief 读一个字节
* @return NULL
* @note
*********************************************************/
u8 ReadByte(void)
{
u8 i, data = 0;
FLASH_SO_IN; //SO配置为输入用于接收数据
FLASH_SCK_OUT;
for(i = 0; i < 8; i++)
{
FLASH_SCK_LOW; //准备采集数据
data <<= 1;
if(FLASH_SO) //采集SO的数据
{
data |= 1;
}
FLASH_SCK_HIGH; //接收数据
}
FLASH_SCK_LOW; //SCK至于空闲态
return data;
}
程序中没有加延时的原因是因为在11MHz的MCU主频下 执行多条汇编语句进行的延时,所以不用在额外使用延时函数。