spi协议详解(51入门系列教程协议协议)

上一贴中咱简单聊了一下通信协议,特别介绍了一下IIC。这贴咱来聊聊SPI,SPI(Serial Peripheral Interface,串行外设接口),来源于Motorola。最早在其MC86HC系列的处理器上定义使用,和IIC一样,也是系出名门。不过这货从协议层面上比IIC简单多了。飞利浦对IIC的协议和硬件接口做出了严格的要求,制定出完善的标准。而摩托罗拉对SPI则没有做出这么多的约束,似乎一下子就把这种接口开源出去了。所以现在流行的SPI接口,有很多类变种,有的甚至无比奇葩,下面简单来聊聊:

先说说硬件结构,看这个图

spi协议详解(51入门系列教程协议协议)(1)

标准的SPI总线接口一般会有4根通信线路,一条总线上,也可以挂多个SPI从设备

(1)MOSI(master out slave in)

主器件输出从器件输入,简单点理解就是主设备向从设备发送数据,也有些器件称之为SDO,就是输出的意思。

(2)MISO(master in slave out)

主器件输入从器件输出,也就是主设备读取从设备的数据,另外一种称法是SDI,也就是输入的意思。

只用到了SCLK、IO和/RST三个端口,其实就是省略了标准接口中只能够单向传输的MISO和MOSI。直接做成了能够双向传输的I/O,其实细细看DS1302的时序。和SPI一样一样的,变种二、奇葩三线SPI、五线SPI。还有很多记不起来的芯片和一些LCD屏幕,譬如一些小尺寸LCD,只需要写数据,就可能会用到/SS、SCLK、MOSI;譬如一些多受控管脚的IC,除了/SS、SCLK、MOSI、MISO外。可能还会添加DCx管脚用于受控指令的传输,这里百家争鸣,朵朵奇葩,啥都有。变种三、Quad SPI,这个是非常有针对性的变种。就是为了数据传输的速度,其实Quad SPI就是标准SPI硬件接口,只不过把数据发送模式分成了多种模式。

这些模式中,可以将原本只能够单向传输的MOSI和MISO都调整成双向传输,就能达到数据传输速度加倍的效果。甚至把某些控制管脚也用于数据传输,那速度就是杠杠滴了。譬如一些Quad SPI Nor Flash之类的芯片,无论哪种SPI,其实它们的基本结构都是类似的。主从设备内部都是一个移位寄存器,连接起来构成一个环形的拓扑。

spi协议详解(51入门系列教程协议协议)(2)

再来看看通信的协议,需要发送/接收的数据,都在每个SCLK的边沿进行传输。传输的数据为8位,按位传输,高位在前,低位在后,在主器件产生的从器件使能信号作用下,两个双向移位寄存器进行数据交换。假设主机的buff=0xaa,从机的buff=0x55,上升沿发送、下降沿接收、高位先发送。每个时钟的上升沿移动数据

spi协议详解(51入门系列教程协议协议)(3)

是不是很简单呀?通信时序很简单。但是moto对时钟SCLK做了一些比较有意思的约束,通过CPOL,CPHA对SCLK的限定,实现SPI的四种不同模式。CPOL(Clock Polarity,时钟极性),用来设置时钟的空闲状态:

为1时,时钟空闲时为高电平

为0时,时钟空闲时为低电平

CPHA(Clock Phase,时钟相位),用来配置数据传输的时钟边沿

为0时,在时钟周期的第一边沿传输数据(或者理解为前一边沿)

为1时,在时钟周期的第二边沿传输数据(或者理解为后一边沿)

模式配置
Mode 0CPOL=0,CPHA=0
Mode 1CPOL=0,CPHA=1
Mode 2CPOL=1,CPHA=0
Mode 3CPOL=1,CPHA=1

这里值得引起注意的地方是,对第一边沿和第二边沿的理解。当CPOL=0,也就是时钟空闲为低电平时,第一边沿应该是时钟由低电平变成高电平的边沿,也就是第一个上升沿,那么第二边沿就是下降沿了。同理,当CPOL=1,也就是时钟空闲为高电平时,第一边沿应该是时钟由高电平变成低电平的边沿,也就是第一个下降沿。那么第二边沿就是上升沿了,

下面把四种模式的示意图bia出来

spi协议详解(51入门系列教程协议协议)(4)

大家可以对比文字和图片,瞧瞧这四种模式,好了,啰嗦了半天。来点好玩的吧!手头上SPI的器件不多,专门跑去ADI官网申请了一篇SPI接口的AD。AD7685,SPI接口的16位AD转换器,就是这货

spi协议详解(51入门系列教程协议协议)(5)

电路图

spi协议详解(51入门系列教程协议协议)(6)

随手画的,勿喷啊!简便起见,没有使用压随器,直接分压接入。不过要注意AD7685的输入阻抗要求,数据端口SDI直接连到高电平。其实是一个三线制的SPI接口,看看AD7685的时序吧!

spi协议详解(51入门系列教程协议协议)(7)

一次传输16位AD数据,在第一个上升沿进行第一位数据的传输。所以是Mode 0的SPI器件,也就是 CPOL=0,CPHA=0。51无需去理会CPOL和CPHA,只需要知道AD7685的要求即可,上代码:

#include <reg51.h>

#define Vref 5.0

sbit smgbit1 = P3^7;

sbit smgbit2 = P3^6;

sbit SCK=P2^2;

sbit SDO=P2^1;

sbit CNV=P2^0;

sbit dp = P1^7;

smgdisp_cache[] = {0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xf8,0x80,0x90};

unsigned int timer_count,Vol,count;

unsigned char temp=0;

float Voltage;

void delay_ms(unsigned int xms)

{

unsigned int i,j;

for(i=xms;i>0;i--)

{

for(j=124;j>0;j--);

}

}

unsigned int adconvert(void)

{

int result;

unsigned char i;

CNV=0;

delay_ms(2);

CNV=1;

delay_ms(2);

for(i=0;i<16;i ) //SPI的数据传输哟

{

SCK=1;

CNV=0;

result=result<<1;

if(SDO)

result=result|0x01;

SCK=0;

}

return(result);

}

void init()

{

smgbit1 = 0;

smgbit2 = 1;

TMOD=0x01;//设置定时器0为工作方式1

TH0=0xfc;

TL0=0X66;

EA=1;//开总中断

ET0=1;//开定时器0中断

TR0=1;//启动定时器0

}

void SMG_Dis()

{

if(smgbit2){

P1 = smgdisp_cache[temp];

}

if(smgbit1){

P1 = smgdisp_cache[temp/10];

dp = 0;

}

}

void time0() interrupt 1

{ P1 = 0xff; //消除鬼影

timer_count ;

if(timer_count == 100)

{ count ;

Vol = adconvert();

Voltage = Vref*Vol/65535;

temp = (unsigned char)(Voltage*10.0);

timer_count = 0;

}

smgbit1 = ~smgbit1;

smgbit2 = ~smgbit2;

SMG_Dis();

TF0 = 0;

TH0 = 0xfc;

TL0 = 0x66;

}

void main()

{ init();

while(1){

}

}

采集AD值,通过数码管显示,数码管用定时器扫描

spi协议详解(51入门系列教程协议协议)(8)

GIF显示从0-2.5v的采样,突然想起手上还有个点阵LED模块。

spi协议详解(51入门系列教程协议协议)(9)

这货好像也是个SPI接口呢

spi协议详解(51入门系列教程协议协议)(10)

MAX7219LED驱动IC,赶紧看看数据手册

spi协议详解(51入门系列教程协议协议)(11)

果然,看看时序

spi协议详解(51入门系列教程协议协议)(12)

依然还是模式0啊,写好代码

#include <reg51.h>

#include <intrins.h>

#define uchar unsigned char

#define uint unsigned int

//定义Max7219端口

sbit Max7219_pinCLK = P2^2;

sbit Max7219_pinCS = P2^1;

sbit Max7219_pinDIN = P2^0;

void Delay_xms(uint x)

{

uint i,j;

for(i=0;i<x;i )

for(j=0;j<112;j );

}

//--------------------------------------------

//功能:向MAX7219(U3)写入字节SPI

//入口参数:DATA

//出口参数:无

//说明:

void Write_Max7219_byte(uchar DATA)

{

uchar i;

Max7219_pinCS=0;

for(i=8;i>=1;i--)

{

Max7219_pinCLK=0;

Max7219_pinDIN=DATA&0x80;

DATA=DATA<<1;

Max7219_pinCLK=1;

}

}

//-------------------------------------------

//功能:向MAX7219写入数据

//入口参数:address、dat

//出口参数:无

//说明:

void Write_Max7219(uchar address,uchar dat)

{

Max7219_pinCS=0;

Write_Max7219_byte(address); //写入地址,即数码管编号

Write_Max7219_byte(dat); //写入数据,即数码管显示数字

Max7219_pinCS=1;

}

void Init_MAX7219(void)

{

Write_Max7219(0x09, 0x00); //译码方式:BCD码

Write_Max7219(0x0a, 0x00); //亮度

Write_Max7219(0x0b, 0x07); //扫描界限;8个数码管显示

Write_Max7219(0x0c, 0x01); //掉电模式:0,普通模式:1

Write_Max7219(0x0f, 0x00); //显示测试:1;测试结束,正常显示:0

}

void main(void)

{

uchar i,j;

uchar temp[]={0x00,0x66,0x99,0x81,0x42,0x24,0x18,0x00};//LED点阵取模,❤型

Init_MAX7219();

while(1)

{

for(i=1;i<9;i )

Write_Max7219(i,temp[i-1]);

for(i=1;i<16;i ){

Write_Max7219(0x0a, i);

Delay_xms(50);}

for(i=15;i>0;i--){

Write_Max7219(0x0a, i);

Delay_xms(50);}

}

}

上个图

spi协议详解(51入门系列教程协议协议)(13)

其实代码还是可以闪烁的,只需要向MAX7219相关寄存器通过SPI写入值即可

spi协议详解(51入门系列教程协议协议)(14)

SPI相比IIC而言的话简单不少啊,今天就先到这里。

了解更多51系列教程,请关注“云汉电子社区”官方微信公众号ickeybbs,或者登录云汉电子社区官方网站(bbs.ickey.cn)

,

免责声明:本文仅代表文章作者的个人观点,与本站无关。其原创性、真实性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容文字的真实性、完整性和原创性本站不作任何保证或承诺,请读者仅作参考,并自行核实相关内容。文章投诉邮箱:anhduc.ph@yahoo.com

    分享
    投诉
    首页