当前位置:文档之家› SPI协议的Verilog 实现

SPI协议的Verilog 实现


而 busy 信号的产生,与片选信号 cs 、写信号 wr 和地址信号 addr 有关。在 busy 信号为 低电平时,只有片选有效,且当前为写状态,才能由 addr 决定是否使能 busy 信号,开始数 据发送。而在 busy 信号使能时,只有片选有效,才能进行数据发送,当片选无效则暂停下 一位数据发送。当然,每发送完 8bit 数据, busy 信号会自动关闭使能状态,等待命令……
//接收从机发送过来的数据
7
触发信号控制部分: module spi_mem(spi_bps,rd,wr,cs,sdi,addr); input output output output output output reg reg reg reg reg spi_bps; rd; wr; cs; sdi; addr; rd_reg; wr_reg; cs_reg; sdi_reg; addr_reg;
// 发送数据
begin sdo_buffer = in_buffer[8]; in_buffer = in_buffer << 1; end if(count > 0 && count < 17) 条件满足,信号反转一次 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 // 在未达到 8 个 sck 周期时,每次触发
� SPI—MOSI代码 (见附录)
� 仿真及分析
1
数据发送:
图 1、数据发送 如图 1 所示,传输时钟 SCK 由 busy 信号控制,在 busy 信号有效期间产生八个周期的 主从设备交换数据时钟信号,在每一个周期内完成一位数据的发送和接收。测试中,待发送 数据为 10101001 。经过数据线 SDO 发送后,在 SCK 的第一个跳边沿进行数据采样,在每 个 sck 下降沿完成一位数据的交换。由上图可知,在一个 busy 信号有效期内,sdo 发送的数 据为 10101001,与待发送数据一致。
附录:
流程图
ቤተ መጻሕፍቲ ባይዱ
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 count = 0; clkcount = 0; spi_bps_reg=0;
SPI 通信
� 原理
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主\从设备时钟相位和极性应该一致。本次实 践采用第一种工作方式,具体情况将在之后的仿真波形中分析介绍。
2
数据接收:
图 2、数据接收 数据接收与数据发送使用同一个时钟信号 SCK 和片选信号 CS。其工作情况大致如下: 在 每个 SCK 时钟的上升沿对接收数据进行采样。由图 2 可知,在一个 busy 信号使能阶段, 采 到的数据为 10110111,即十进制 183。在 busy 信号关闭使能后,只有片选使能且处于允许 读状态,接收到的数据才会被存储,否则丢弃。 Chipscope 数据抓取
// //接收数据存入 out_data
6
begin 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) // 控制 SCK 周期 begin clkcount = 0; if((count % 2) == 0) //待发数据存入缓存区,
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 引脚,问题解决。解决后发现抓取的 波形没有明显的高低跳变,经分析可能是参考时钟选取不对,重选后问题解决。
reg[12:0] clk_cnt; reg[size:1] in_data=8'b10101001;
5
parameter size=8; parameter para=6400; parameter 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)) begin clk_cnt<=clk_cnt+1'b1; spi_bps_reg<=1'b0; end else if((clk_cnt>=para_half)&&(clk_cnt<para)) begin clk_cnt<=clk_cnt+1'b1; spi_bps_reg<=1'b1; end else clk_cnt<=13'd0; end ////////////////////////////////////////////////////////////////////////////////// ////////////// ////////////////////////////////////// ///////////// 发送接收模块 ////////////////////////////////////// //////////// ////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// always@(cs or rd or busy) begin if(!busy && cs && rd) begin out_data = out_buffer; end else begin out_data = 8'b0;end end always@(posedge spi_bps) begin if(!busy) begin if(cs && wr) // 空闲状态 //片选使能且处于写状态
相关主题