小小白对SPI的理解,基于米联客例程的FPGA实现

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

看仿真
在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

9527华安

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值