SPI总线有四种工作方式(SPI0, SPI1, SPI2, SPI3),其中使用的最为广泛的是SPI0和SPI3方式。
时钟极性CPOL是用来配置SCLK的电平出于哪种状态时是空闲态或者有效态,时钟相位CPHA 是用来配置数据采样是在第几个边沿:
CPOL=0,表示当SCLK=0时处于空闲态,所以有效状态就是SCLK处于高电平时
CPOL=1,表示当SCLK=1时处于空闲态,所以有效状态就是SCLK处于低电平时
CPHA=0,表示数据采样是在第1个边沿,数据发送在第2个边沿
CPHA=1,表示数据采样是在第2个边沿,数据发送在第1个边沿
如上图,乃SPI四种模式的时序图:
CPOL=0,CPHA=0:此时空闲态时,SCLK处于低电平,数据采样是在第1个边沿,也就是SCLK由低电平到高电平的跳变,所以数据采样是在上升沿,数据发送是在下降沿。
CPOL=0,CPHA=1:此时空闲态时,SCLK处于低电平,数据发送是在第1个边沿,也就是SCLK由低电平到高电平的跳变,所以数据采样是在下降沿,数据发送是在上升沿。
CPOL=1,CPHA=0:此时空闲态时,SCLK处于高电平,数据采集是在第1个边沿,也就是SCLK由高电平到低电平的跳变,所以数据采集是在下降沿,数据发送是在上升沿。
CPOL=1,CPHA=1:此时空闲态时,SCLK处于高电平,数据发送是在第1个边沿,也就是SCLK由高电平到低电平的跳变,所以数据采集是在上升沿,数据发送是在下降沿。
长舒一口气,终于将SPI介绍完了。
首先确定了单片机A(Master MCU1)为主模式,单片机B(Slave MCU1)为从模式。各自也配置好了SLCK,MOSI,MISO和SCK的io引脚。选择了默认的SPI0模式。原理图如下:
按图连接好后,进行了“试音”阶段。单片机A发送1—10的数字给了单片机B;单片机B收到后,用流水灯作为回应。
①:数据发送程序(主机仅发送)
#define uchar unsigned char
#define uint unsigned int
#define ulong unsigned long
//---------------------------
#include
#include
//---------------------------
sbit SPICLK = P1^0; //时钟信号
sbit MOSI = P1^1; //主器件数据输出,从器件数据输入
sbit MISO = P1^2; //主器件数据输入,从器件数据输出
sbit SS = P1^3; //从器件使能信号
void Dat_Transmit(uchar dat) //发送数据程序
{
uchar i,datbuf; //主机数据暂存寄存器
datbuf=dat;
SS=1;
while(SS){;}
for(i=0;i<8;i++) //
{
while(SPICLK){;}
if(datbuf&0x80)
MISO=1;
else
MISO=0;
datbuf=(datbuf<<1);
while(~SPICLK){;}
}
}
void main(void)
{
uchar i;
while(1)
{
for(i=0;i<10;i++)
{
Dat_Transmit(i);
}
}
}
②:数据接收程序(从机仅接收)
#define uchar unsigned char
#define uint unsigned int
#define ulong unsigned long
//---------------------------
#include
#include
//---------------------------
sbit SPICLK = P1^0; //时钟信号
sbit MOSI = P1^1; //主器件数据输出,从器件数据输入
sbit MISO = P1^2; //主器件数据输入,从器件数据输出
sbit SS = P1^3; //从器件使能信号
//---------------------------
void Nop(void)
{
;
}
void Delay(uchar t)
{
while(t--){;}
}
uchar Data_Receive(void) //数据接收程序
{
uchar i,dat=0,temp;
bit bt;
SPICLK=1;
MISO=1;
SS=0; //选中器件
Nop();
Nop();
for(i=0;i<8;i++)
{
SPICLK=1;
Nop();
Nop();
Nop();
SPICLK=0;
Nop();
Nop();
bt=MISO;
if(bt)
temp=0x01;
else temp=0x00;
dat=(dat<<1);
dat=(dat|temp);
}
SS=1;
SPICLK=1;
return dat;
}
void main(void)
{
uchar exdat;
uchar i=0;
uchar code table[10]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,
0x7F,0x6F};
P2=0;
while(1)
{
exdat=Data_Receive();
P0=table[exdat];
for(i=0;i<200;i++)
Delay(200);
}
}
SPI总线注意点:
1、 Master配置SPI接口时钟的时候一定要考虑从设备的操作时序要求,因为Master这边的时钟极性和相位都是以Slave为基准的。因此在时钟极性的配置上一定要确定Slave是在SCK的下降沿还是上升沿输出数据,是在SCK的上升沿还是下降沿接收数据。
2、当Slave时钟频率小于Master时钟频率时,如果Master的SCK的速率太快,会出现Slave接收到的数据不正确,而SPI接口又没有应答机制确认Slave是否接收到数据从而导致通信传输数据错误。
3、SPI总线系统是一种同步串行外设接口,它可以使MCU与各种外围设备以串行方式进行通信以交换信息。除了MCU,还有FLASHRAM、网络控制器、LCD显示驱动器和A/D转换器等外围设置。
4、上面的代码所用指令是STC 89C51单片机所用如需用其它芯片请另行更改。
以上所有信息仅作为学习交流使用,不作为任何学习和商业标准。若您对文中任何信息有异议,欢迎随时提出,谢谢!
关于云创硬见
云创硬见是国内最具特色的电子工程师社区,融合了行业资讯、社群互动、培训学习、活动交流、设计与制造分包等服务,以开放式硬件创新技术交流和培训服务为核心,连接了超过30万工程师和产业链上下游企业,聚焦电子行业的科技创新,聚合最值得关注的产业链资源, 致力于为百万工程师和创新创业型企业打造一站式公共设计与制造服务平台。