简要说明
看见许多博主都有编写SPI协议,但是大都是对一个指定的时序进行FPGA的实现。于是就想编写一个比较常见的SPI通信协议,而且兼顾4种模式和不同数据长度。主要是用来对常见SPI协议的应用,和辅助对萌新对SPI时序,模式的理解。
SPI介绍
SPI协议是常见的低速通信协议,具体的协议介绍就不太谈了。其它博主有十份优秀的帖子,此处只是说明一下特别注意事项。
首先是对SPI的使用应该具体到一个芯片的datasheet的时序图,不同的芯片有不同的要求。
必须理解 cpol 定义数据线 时钟线空闲时的状态 0->低电平 1->高电平。
cpha 定义数据在时钟的第几个边沿有效 0->第一个边沿 1->第二个边沿。
SPI的4种模式就是[cpol,cpha] 组合而成,分别表示sclk空闲的状态和数据有效时刻。
Verilog的实现
在此处的Verilog的实现是基于常见的SPI协议,可以兼顾4种模式和不同数据长度。该程序的核心思想就是利用好cpol,cpha信号,所以对4种模式不需要特别的生成多个对应电路。
主机:
`timescale 1ns / 1ps
//
// Company: /-----\
// Engineer: [|^ ^|]
// | v |
// [-----]
// Create Date:
// Design Name:
// Module Name: spi_contrl
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module spi_master#(
parameter cpol = 1, // 定义数据线 时钟线空闲时的状怿 0->低电广 1->高电广
cpha = 1, // 定义数据在时钟的第几个上升沿有效 0->第一个上升沿 1->第二个上升沿
sclk_cycle=10, //定义sclk占几个clk_i
data_width=8 //定义收发位宽
)( input clk_i,
input rst_n_i,
input spi_m_start_i,
input [ data_width-1 : 0 ] tx_data_i ,
output reg [ data_width-1 : 0 ] rx_data_o ,
output sclk_o,
output cs_n_o,
output reg mosi_o,
input miso_i
);
reg [5:0] sclk_cnt =0; //sclk分频计数
reg sclk =0;
reg sclk_dly =0;
reg [5:0] prog_cnt=0; //spi程序运行计数 方便后面的赋值
reg cs_n ;
reg spi_start_dly=0;
reg [ data_width-1 : 0 ] tx_data=0;
reg [ data_width-1 : 0 ] rx_data=0;
对cs_n信号的处理//
always @(posedge clk_i or negedge rst_n_i)
if(!rst_n_i) begin
cs_n <= 1;
spi_start_dly <=0;
end
else begin
spi_start_dly <=spi_m_start_i;
if( !spi_start_dly && spi_m_start_i) /主机操作的上升沿就拉低cs_n
cs_n <=0;
else if (sclk_cnt==sclk_cycle-1 && prog_cnt == data_width) //通过计数器实现对spi是否完成的判断,完成后cs_n拉高
cs_n <=1;
else cs_n &