SPI协议,大佬们讲得很多,但总感觉没有讲到小白的痛处,比如:极性和相位
极性指的是空闲状态下时钟的电平状态,这个很好理解,verilog代码也很好实现,
比如,极性为0,表示空闲状态下时钟的电平为低,verilog代码只需要将时钟寄存器的初始值设为0即可,反之亦然;
相位为0,表示在数据在时钟的第一个跳变沿被采集,啥意思?小白在这里就懵了,更别说写代码了,
以极性为0举例,假设极性为0,相位为0,那么,数据将在spi时钟的第一个跳变沿被采集,换言之,数据将在spi时钟的上升沿被采集,代码如何实现?
假设我要写一个主机,要实现数据将在spi时钟的上升沿被采集,那么,主机就必须让需要发出的数据在spi时钟的下降沿改变,为何?
你想啊,从机需要在spi时钟的上升沿采集数据,那么,数据线上的数据在spi时钟的上升沿到来之前肯定得稳定才行吧?如何做到?很简单,知己下降发送数据,从机上升沿采集数据嘛,这样一来,不久完美解决?
参考米联客的SPI例程,写得还是挺好的,我稍加了修改。
由于SPI接收比较简单,代码就不放出来了,这里介绍一下SPI接收模块的代码
module master_spi_tx#
(
parameter SYS_CLK = 50_000_000, //我添加的,为了使用不同板子
parameter SPI_CLK = 100_000, //我添加的,为了使用不同板子
parameter CPOL = 1'b0,
parameter CPHA = 1'b0
)
(
input clk_i,
output spi_tx_o,//mosi
output spi_clk_o,//SPI 时钟输出SCK
input spi_tx_en_i,//发送发送使能信号
input [7:0] spi_tx_data_i,//待发送数据
//output spi_en_o,//SPI 传输使能
output spi_cs_o, //这是我添加的,为了适应操作FLASH
output spi_busy_o//SPI正在传输,高代表忙
);
//SPI时钟分频单元
//parameter [9:0] SPI_DIV = 10'd499;//分频 假设(50Mhz/100K/ - 1'b1=499
parameter SPI_DIV = SYS_CLK/SPI_CLK - 1;
parameter SPI_DIV1 = SPI_DIV/2;
parameter PSET = CPHA ? 1'b1 : 1'b0;
reg [9:0] clk_div = 10'd0;
reg spi_en = 1'b0; //标志spi正在传输一个字节数据
always@(posedge clk_i)begin
if((clk_div < SPI_DIV) && spi_en)
clk_div <= clk_div + 1'b1;
else
clk_div <= 10'd0;
end
//产生SPI时钟
wire clk_en1 = (clk_div == SPI_DIV1)&&(!clk_end);//n
wire clk_en2 = (clk_div == SPI_DIV);//p
reg spi_clk = 1'b0;
always@(posedge clk_i)begin
if(spi_tx_en_i)
spi_clk <= 1'b0;
else if(clk_en2)
spi_clk <= 1'b0;
else if(clk_en1)
spi_clk <= 1'b1;
end
wire spi_strobe = CPHA ? clk_en1 : clk_en2 ; //主机发送数据的时机
//SPI发送数据
reg [3:0] tx_cnt = 4'd0;
reg [7:0] spi_tx_data_r=8'd0;
assign spi_tx_o = spi_tx_data_r[7];
wire clk_end = (clk_div == SPI_DIV1)&&(tx_cnt==4'd8);
reg pcnt = 1'b0;
always@(posedge clk_i)begin
//首先当发送使能有效,寄存数据
if(spi_tx_en_i) begin
spi_en <= 1'b1;
spi_tx_data_r <= spi_tx_data_i;
end
else if(!spi_en)begin//当bps_start_en为0让状态机处于停止状态
spi_tx_data_r <= 8'b1111_1111;
tx_cnt <= 4'd0;
pcnt <= 1'd0;
end
// 通过移位发送数据
if(clk_end)begin
tx_cnt <= 4'd0;
spi_en <= 1'b0;
end
else begin
if(clk_en1&&spi_en)
tx_cnt <= tx_cnt + 1'b1;
if(spi_strobe)begin
if(pcnt < PSET )
pcnt <= 1'b1;
else
spi_tx_data_r[7:0] <= {spi_tx_data_r[6:0],spi_tx_data_r[7]};
end
end
end
assign spi_busy_o = spi_en;
//assign spi_en_o = spi_en;
assign spi_cs_o = ~spi_en;
assign spi_clk_o = (CPOL == 1'b1) ? ~spi_clk : spi_clk;
endmodule
看仿真