一、FPGA 开发SPI基础
为了避免每次SPI驱动重写,直接参数化,尽量一劳永逸。SPI master有啥用呢,你发现各种外围芯片的配置一般都是通过SPI配置的,只不过有三线和四线。SPI slave有什么用呢,当外部主机(cpu)要读取FPGA内部寄存器值,那就很有用了,FPGA寄存器就相当于RAM,cpu通过SPI寻址读写数据。代码仅供参考,勿做商业用途。
二、SPI三线,四线区别
三线制指的是CS,CLK,MOMI,是半双工方式;四线制指的是 CS,CLK,MOSI和MISO,是全双工方式。
三、SPI代码构思
1. SPI salve
1.支持三线SPI或者四线SPI。通过define切换。
2.支持指令长度、帧长自定义。
3.工作时钟可自定义,大于SPI clk的2倍。
用户只需修改:(1)几线SPI。(2)单帧长度。(3)指令长度。(4)寄存器开辟。
注意:指令最高bit表示读写,低写高读,其余bit表示地址。指令接着为数据端,两者位宽之和即为SPI单帧长。
//`define SPI_LINE //是否是三线SPI
`define SPI_FRAME_WIDTH 16 //SPI一帧长度为16
`define SPI_INS_WIDTH 8 //SPI指令长
`timescale 1ns/1ps
////
module spi_slave
(
input i_clk , //work clk
input i_rst_n ,
input i_spi_clk , //SPI clk
input i_spi_cs , //SPI cs
`ifdef SPI_LINE //条件编译
inout io_spi_sdio
`else
input i_spi_mosi , //SPI mosi
output o_spi_miso //SPI miso
`endif
);
//位宽计算函数
function integer clogb2 (input integer depth);
begin
for (clogb2=0; depth>0; clogb2=clogb2+1)
depth = depth >>1;
end
endfunction
reg r_cs = 1'b1; //打一拍
always @(posedge i_clk)
begin
r_cs <= i_spi_cs;
end
reg [1:0] r_spi_clk_edge = 2'b00; //SPI clk边沿检测
always @(posedge i_clk)
begin
r_spi_clk_edge <= {r_spi_clk_edge[0],i_spi_clk};
end //always
reg [clogb2(`SPI_FRAME_WIDTH-1)-1:0] r_spi_cnt = 'd0;
always @(posedge i_clk)
begin
if (r_cs) //cs为高则归零
r_spi_cnt <= 'd0;
else if (r_spi_clk_edge == 2'b10) //下降沿才计数
r_spi_cnt <= r_spi_cnt + 'd1;
end
////指令锁存
reg [`SPI_INS_WIDTH-1:0] r_ins = 'd0;
always @(posedge i_clk)
begin
if ((~r_cs) && (r_spi_clk_edge == 2'b01)) //上升沿锁存数据
begin
if ((r_spi_cnt >= 0) && (r_spi_cnt <= `SPI_INS_WIDTH-1))
`ifdef SPI_LINE //条件编译
r_ins <= {r_

最低0.47元/天 解锁文章
8万+

被折叠的 条评论
为什么被折叠?



