SPI协议+verilog

本文详细介绍了SPI(SerialPeripheralInterface)协议的工作原理,包括主设备与从设备的交互、引脚功能、CPOL和CPHA参数对模式的影响,以及Verilog中的SPI主机模块设计实例。
摘要由CSDN通过智能技术生成

SPI协议

SPI是一种同步串行通信协议,有一个主设备和一个或者多个从设备组成,主设备启动与从设备的同步通信,同步是通过同一个时钟完成的。

有4个引脚:
1、SCK
2、SSEL:片选信号
3、MOSI:主机发送从机接收
4、MISO:从机发送主机接收

CPOL,SPI总线的极性,决定SPI总线空闲时的时钟信号是高电平还是低电平。
CPOL=1表示空闲时高电平;反之。
CPHA,SPI总线的相位,决定SPI总线在哪个跳变沿采样数据。
CPHA=0表示从第一个跳变沿开始采样;反之从第二个跳变沿开始采样
注意:这里不是通过固定的上升沿和下降沿采样

CPOL=0,表示当SCLK=0时处于空闲态,所以有效状态就是SCLK处于高电平时。
CPOL=1,表示当SCLK=1时处于空闲态,所以有效状态就是SCLK处于低电平时。
CPHA=0,表示数据采样是在第1个边沿,数据发送在第2个边沿。
CPHA=1,表示数据采样是在第2个边沿,数据发送在第1个边沿。

由于CPOL和CPHA的不同组合,形成了SPI总线的4种不同模式

如图为模式0的波形图:
在这里插入图片描述

verilog

SPI mater文件:

`timescale 1ns / 1ps

/*
SPI的主机模块,该模块工作在SPI的模式0
在这里,用100MHZ的时钟分频产生了一个1MHZ的SCK时钟,将它作为程序的主时钟
*/

module spi_master(
    input clk,
    output reg sck,  //1MHZ

    input rst,
    input spi_send,
    input [7:0] spi_data_out,  
    output reg spi_send_done,

 //   input busy,
    input miso,
    output reg mosi,
    output reg cs
    );

    reg [3:0]count; //数据计数 0~7
    reg [7:0] delay_count;  //时钟分频计数 0~49
    //状态机分为4个状态:等待、拉低CS、发送数据、结束数据
    localparam IDLE = 0,
                CS_L = 1,
                DATA = 2,
                FINISH = 3;
    reg [4:0] cur_st, nxt_st;
    reg [7:0] reg_data;
    reg sck_reg;

    //时钟分频
    always @(posedge clk) begin
        if(~rst)
            delay_count <= 0;
        else if(delay_count == 49)
            delay_count <= 0;
        else 
            delay_count <= delay_count+1;
    end
    always @(posedge clk) begin
        if(~rst)
            sck_reg <= 0;
        else if(delay_count == 49)
            sck_reg <= ~sck_reg;
    end

    //模式0,sck只有在cs拉低时才变化,其他时刻都为低电平
    always @(*) begin
        if(cs)
            sck <= 0;
        // else if(cur_st == FINISH) 
        //     sck <= 0;
        else if(~cs) 
            sck <= sck_reg;
        else
            sck <= 0;
    end

    always @(posedge sck_reg) begin
        if(~rst)
            cur_st <= IDLE;
        else
            cur_st <= nxt_st;
    end

    always @(*) begin
        case(cur_st)
        IDLE: begin
            if(spi_send) 
                nxt_st = CS_L;
            else
                nxt_st = IDLE;
        end
        CS_L:
            nxt_st = DATA;
        DATA: begin
            if(count == 4'd7)
                nxt_st = FINISH; 
            else
                nxt_st = DATA;
        end      
        FINISH:
            nxt_st = IDLE;
        default: nxt_st = IDLE;
        endcase
    end

    //产生发送结束标志
    always @(*) begin
        if(~rst)
            spi_send_done = 0;
        else if(cur_st == FINISH)
            spi_send_done = 1;
        else
            spi_send_done = 0;
    end

    //产生CS
    always @(posedge sck_reg) begin
        if(~rst)
            cs <= 1;
        else if(cur_st == CS_L)
            cs <= 0;
        else if(cur_st == DATA)
            cs <= 0;
        else cs <= 1;
    end

    //发送数据计数
    always @(posedge sck_reg) begin
        if(~rst)
            count <= 'b0;
        else if(cur_st == DATA)
            count <= count+1'b1;
        else if(cur_st==IDLE || cur_st==FINISH)
            count <= 'b0;
    end

    //MOSI数据,上升沿采集数据,下降沿切换数据
    always @(negedge sck_reg) begin
        if(~rst)
            mosi <= 0;
        else if(cur_st == DATA) begin
            reg_data[7:1] <= reg_data[6:0];
            mosi <= reg_data[7];
        end
        else if(spi_send)    //当状态机从IDLE 变为CS_L时,立马更新数据
            reg_data <= spi_data_out;
    end

endmodule

tb文件:

`timescale 1ns / 1ps

module tb();

    reg clk;
    wire sck;

    reg rst;
    reg spi_send;
    reg [7:0] spi_data_out;
    wire spi_send_done;

    reg miso;
    wire mosi;
    wire cs;

    always #5 clk=~clk;

    integer i;
    initial begin
        clk = 0;
        rst = 0;
        #10;
        rst = 1;

        spi_send = 1;
        miso = 0;
        for(i=0;i<10;i=i+1) begin
            @(negedge clk) begin
                spi_data_out = i+1;
                #10000;
            end
        end
        spi_send = 0;
    end

    spi_master u1(
        .clk(clk),
        .sck(sck),
        .rst(rst),
        .spi_send(spi_send),
        .spi_data_out(spi_data_out),
        .spi_send_done(spi_send_done),
        .miso(miso),
        .mosi(mosi),
        .cs(cs)
    );


endmodule

在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值