SPISPISPISPI 通信
� 原理
Spi接口是一种外围串行接口,主要由四根线组成:SDI(数据输入),SDO(数据输出),
SCK(时钟),CS(片选)。
(1)SDO主机输出/ 从机输入。
(2)SDI主机输入/ 从机输出 。
(3)SCK – 时钟信号,由主设备产生。
(4)CS – 从设备使能信号,由主设备控制。
在一个基于SPI的设备中,至少有一个主控设备。与普通的串行通讯不同,普通的串行
通讯一次连续传送至少8位数据,而SPI允许数据一位一位的传送,甚至允许暂停,因为SPI
的数据输入和输出线独立,所以允许同时完成数据的输入和输出。在点对点的通信中, SPI
接口不需要进行寻址操作,且为全双工通信,工作简单高效。然而SPI接口也有缺点:没有
指定的流控制,没有应答机制确认是否接收到数据。
SPI通讯是通过数据交换完成的。在主机提供的时钟脉冲SCK下,SDI,SDO完成数据传
输。数据输出通过 SDO线,在SCK时钟上升沿或下降沿时改变,在紧接着的下降沿或上升
沿被从机读取,完成一位数据传输。输入情况同理。因此,在至少8次时钟信号的改变(上
沿和下沿为一次),可以完成8位数据的传输。
SPI总线有四种工作方式可以选择,根据外设工作要求,其输出串行同步时钟极性和相
位可以进行配置,时钟极性(CPOL)对传输协议没有重大的影响。如果 CPOL=0,串行同
步时钟的空闲状态为低电平;如果CPOL=1,串行同步时钟的空闲状态为高电平。时钟相位
(CPHA)能够配置用于选择两种不同的传输协议之一进行数据传输。如果CPHA=0,在串
行同步时钟的第一个跳变沿(上升或下降)数据被采样;如果CPHA=1,在串行同步时钟的
第二个跳变沿(上升或下降)数据被采样。SPI主\从设备时钟相位和极性应该一致。本次实
践采用第一种工作方式,具体情况将在之后的仿真波形中分析介绍。
� SPI—MOSI代码
(见附录)
� 仿真及分析
1
数据发送:
图 1、数据发送
如图 1 所示,传输时钟 SCK 由 busy 信号控制,在 busy 信号有效期间产生八个周期的
主从设备交换数据时钟信号,在每一个周期内完成一位数据的发送和接收。测试中,待发送
数据为 10101001。经过数据线 SDO 发送后,在 SCK 的第一个跳边沿进行数据采样,在每
个 sck 下降沿完成一位数据的交换。由上图可知,在一个 busy 信号有效期内,sdo 发送的数
据为 10101001,与待发送数据一致。
而 busy 信号的产生,与片选信号 cs、写信号 wr 和地址信号 addr 有关。在 busy 信号为
低电平时,只有片选有效,且当前为写状态,才能由 addr 决定是否使能 busy 信号,开始数
据发送。而在 busy 信号使能时,只有片选有效,才能进行数据发送,当片选无效则暂停下
一位数据发送。当然,每发送完 8bit 数据,busy 信号会自动关闭使能状态,等待命令……
2
数据接收:
图 2、数据接收
数据接收与数据发送使用同一个时钟信号 SCK 和片选信号 CS。其工作情况大致如下:在
每个 SCK 时钟的上升沿对接收数据进行采样。由图 2 可知,在一个 busy 信号使能阶段,采
到的数据为 10110111,即十进制 183。在 busy 信号关闭使能后,只有片选使能且处于允许
读状态,接收到的数据才会被存储,否则丢弃。
Chipscope 数据抓取
DataPort[5]-DataPort[0] : busy、sdo、addr、cs、wr、rd
DataPort[21]-DataPort[14] :待发送 8bit 数据
DataPort[13]-DataPort[6] : 接收的 8bit 数据
3
� 总结
完成时间:12.4--12.12
前期:该阶段主要是熟悉 SPI 工作原理,进一步掌握和认识 SPI 通信协议。Chipscore 的使
用之前未曾接触,在这一阶段,我先通过简单编程结合开发板抓取数据进行分析达到
对其的基本掌握。
中期:在熟悉 SPI 的工作原理之后,开始尝试编写代码。写了两三次代码,效果均不理想,
很多问题在编写代码的时候没有考虑清楚,导致到了仿真阶段结果与预期有所差距,
且代码冗长复杂。参考了一些资料,效果也不是很好,特别是数据传输暂停部分,很
多都省略了。不过借鉴别人写的代码也让我收获了不少编写的经验,有些情况下,运
用不同的逻辑思维可以让代码更简洁、更具有健壮性。当然期间也遇到了一些自己无
法解决的问题,非常感谢福星学长耐心的指导,让我学到了不少知识和经验。
后期:该阶段主要是对代码进行再修改、波形仿真以及抓数据调试。
问题及分析:
小问题遇到不少,不过大多都通过 error 的提示,或者上网搜索,找到了问题的原因,
并予以解决。也有软件上的原因,比如:第一次装 ISE 的时候可能没有安装好,上板调试的
时候,cable 不能识别。经过测试分析发现 ISE 里的 drive 没有装上。考虑到这样一个问题的
出现可能还会附带有一些软件上的漏洞,重装了一遍 ISE,问题解决。
使用 chipscope 的时候,芯片配置不对连接失败,查阅该电路板的资料,重新配置,问
题解决。在 chipscope 里面有些触发信号找不到,经分析是被优化了,通过简单修改代码避
免它被优化后,问题解决。运行 chipscope 后发现 waveform 始终没反应,经过一番分析,认
为时钟线的引脚配置有问题,重换一个时钟信号线 I/O 引脚,问题解决。解决后发现抓取的
波形没有明显的高低跳变,经分析可能是参考时钟选取不对,重选后问题解决。
附录:
流程图
4
代码:
//////////////////////////////////////////////////////////////////////////////////
module spi_mosi(addr,rd,wr,cs,clk,sdi,sdo,sck,spi_bps,data);
input wire addr;
input wire rd;
input wire wr;
input wire cs;
input wire clk;
output[size:1] data;
input sdi;
inout sdo;
inout sck;
inout spi_bps;
reg[size:1] out_data=0;
reg sck_buffer = 0;
reg sdo_buffer = 0;
reg busy = 0;
reg [size:1] in_buffer = 0;
reg [size:1] out_buffer = 0;
reg [6:0]
reg [7:0]
reg
reg[12:0]
reg[size:1] in_data=8'b10101001;
spi_bps_reg=0;
clk_cnt;
count = 0;
clkcount = 0;
5
parameter size=8;
parameter
parameter
para=6400;
para_half=3200;
assign data=out_data;
assign sck = sck_buffer;
assign sdo = sdo_buffer;
assign spi_bps=spi_bps_reg;
//////////////////////////////////////////////////////////////////////////////////
//////////////
/////////////
////////////
////////////////////////////////////////////////////////////////////////////////
always@(posedge clk)
begin
分频模块
//////////////////////////////////////
//////////////////////////////////////
//////////////////////////////////////
if((clk_cnt>=0)&&(clk_cnt
=para_half)&&(clk_cntbegin
case(addr)
1'b0: begin in_buffer = in_data; busy = 1'b1; end
//待发数据存入缓存区,
1'b1: begin busy = 1'b0;end
endcase
end
转入工作状态
end
else
begin
if(cs)
begin
clkcount = clkcount + 1'b1;
if(clkcount >= 8'b10)
begin
clkcount = 0;
if((count % 2) == 0)
//控制 SCK 周期
// 发送数据
begin
sdo_buffer = in_buffer[8];
in_buffer = in_buffer << 1;
end
if(count > 0 && count < 17)
//在未达到 8 个 sck 周期时,每次触发
条件满足,信号反转一次
begin
sck_buffer = ~sck_buffer;
end
count = count + 1'b1;
if(count > 17)
begin
count = 0;
busy = 1'b0;
end
end
end
end
end
always@(posedge sck_buffer)
begin
out_buffer = out_buffer << 1;
out_buffer[1] = sdi;
end
endmodule
//接收从机发送过来的数据
7
触发信号控制部分:
module spi_mem(spi_bps,rd,wr,cs,sdi,addr);
input
output
output
output
output
output
spi_bps;
rd;
wr;
cs;
sdi;
addr;
reg
reg
reg
reg
reg
rd_reg;
wr_reg;
cs_reg;
sdi_reg;
addr_reg;
assign rd=rd_reg;
assign wr=wr_reg;
assign cs=cs_reg;
assign sdi=sdi_reg;
assign addr=addr_reg;
parameter size=8;
cnt_set;
reg[size:1]
reg[size:1]
cnt_sdi;
always@(posedge spi_bps)
begin
if(cnt_set<8'd250) begin cnt_set<=cnt_set+1'b1;end
else begin cnt_set<=8'd0;end
end
always@(posedge spi_bps)
begin
if(cnt_sdi<8'd9) begin cnt_sdi<=cnt_sdi+1'b1;end
else begin cnt_sdi<=8'd0;end
end
always@(posedge spi_bps)
begin
if(cnt_sdi<8'd5) begin sdi_reg<=1'b1;end
8