spi master接口的fpga实现

hegangben
2026-01-06 / 0 评论 / 1 阅读 / 正在检测是否收录...

https://www.cnblogs.com/kingstacker/p/7490002.html
前言

当你器件的引脚贼少的时候,需要主机和从机通信,spi就派上了用场,它可以一对多,但只是片选到的从机能和主机通信,其他的挂机。

spi:serial peripheral interface 串行外围接口

大致了解:
mk1yt7p9.png

spi是个同步协议,数据在master和slaver间交换通过时钟sck,由于它是同步协议,时钟速率就可以各种变换。

sck:主机提供,从机不能操控,从器件由主机产生的时钟控制。数据只有在sck来了的上升沿或者下降沿才传输。

高级一点的spi芯片有配置寄存器,高级一点的工作有四种模式,采样相位和sck空闲电平可配置。

当然在这里我们主要实现简单的spi协议:sck是系统时钟的四分频,wr请求信号有效时,主机开始工作,数据位8bit,sck空闲时低电平,工作时第一个沿数据传输。只有一个从机,cs低电平片选。
mk1ytgpp.png

看下结构:
mk1ytlqo.png

接口定义:
mk1ytsgi.png

编码实现:(版权所有,请勿用于商业用途,仅供学习使用)

//************************************************
//  Filename      : spi_ms_test1.v
//  Author        : Kingstacker
//  Company       : School
//  Email         : kingstacker_work@163.com
//  Device        : Altera cyclone4 ep4ce6f17c8
//  Description   : spi master module;data 8bit;sck is 4 div of the clk;
//************************************************
module  spi_ms #(parameter WIDTH = 8)(
    //input;
    input    wire    clk,
    input    wire    rst_n,
    input    wire    wr, //send request;
    input    wire    [WIDTH-1:0]    master_din, //the data you want send;
    input    wire    miso, //the data form slave;
    //output;
    output   reg     cs, //slave select;
    output   reg     sck, //data exchange clock;
    output   reg     mosi,    //master out;
    output   reg     [WIDTH-1:0]    master_dout //the data you received;
);
localparam CLK_HZ = 50_000_000;  //clk frequency;
localparam SCK_HZ = 12_500_000;  //sck frequency;
localparam DIV_NUMBER = CLK_HZ / SCK_HZ;
localparam CNT_MAX = (DIV_NUMBER >>1) - 1'b1;
localparam DATA_CNT_MAX = 5'd31;
localparam MOSI_CNT_MAX = 3'd7;
localparam IDEL = 2'b00;
localparam SEND = 2'b01;
localparam FINISH = 2'b10;
reg cnt; //sck cnt;
reg sck_en; //enable sck;
reg data_cnt_en;
reg sck_reg1;
reg sck_reg2;
wire sck_p; //posedge sck;
wire sck_n; //negedge sck;
wire send_over;
reg [1:0] cstate;
reg [4:0] data_cnt; //cnt the send data;
reg [2:0] mosi_cnt;
reg [WIDTH-1:0] master_din_reg;
reg [WIDTH-1:0] master_dout_reg;
//produce sck;
always @(posedge clk or negedge rst_n) begin
    if (~rst_n) begin
        cnt <= 0;
        sck <= 1'b0;
    end //if
    else begin
        if (sck_en == 1'b1) begin
            if (cnt == CNT_MAX) begin
                cnt <= 0;
                sck <= ~sck;
            end
            else begin
                cnt <= cnt + 1'b1;
                sck <= sck;
            end
        end
        else begin
            cnt <= 0;
            sck <= 1'b0;
        end
    end //else
end //always
//produce sck_p and sck_n;
always @(posedge clk or negedge rst_n) begin
    if (~rst_n) begin
        sck_reg1 <= 1'b0;
        sck_reg2 <= 1'b0;
    end //if
    else begin
        sck_reg1 <= sck;
        sck_reg2 <= sck_reg1;
    end //else
end //always
assign sck_p = (sck_reg1 & (~sck_reg2)); //sck posedge;
assign sck_n = ((~sck_reg1) & sck_reg2); //sck negedge;
//fsm;hot code;
always @(posedge clk or negedge rst_n) begin
    if (~rst_n) begin
        cstate <= IDEL;
    end
    else begin
        case (cstate)
            IDEL:    cstate <= (wr)? SEND : IDEL;
            SEND:    cstate <= (send_over) ? FINISH : SEND;
            FINISH:  cstate <= IDEL;
            default: cstate <= IDEL;
        endcase //case
    end
end
always @(posedge clk or negedge rst_n) begin
    if (~rst_n) begin
        cs <= 1'b1;
        data_cnt_en <= 1'b0;
        sck_en <= 1'b0;
        master_din_reg <= 0;
        master_dout <= 0;
    end
    else begin
        case (cstate)
            IDEL: begin
            data_cnt_en <= 1'b0;
            master_din_reg <= (wr) ? master_din : master_din_reg; //load the data you want send to slaver;
            end
            SEND: begin
                data_cnt_en <= 1'b1;
                cs <= 1'b0;
                sck_en <= 1'b1;
                master_dout <= (send_over) ? master_dout_reg : master_dout; //master receiverd data;
            end
            FINISH: begin                  //send and load ok;
                sck_en <= 1'b0;
                cs <= 1'b1;
                data_cnt_en <= 1'b0;
            end
            default: begin
                cs <= 1'b1;
                sck_en <= 1'b0;
                data_cnt_en <= 1'b0;
            end
        endcase //case
    end
end
always @(posedge clk or negedge rst_n) begin
    if (~rst_n) begin
        data_cnt <= 0;
    end
    else begin
        data_cnt <= (data_cnt_en) ? (data_cnt + 1'b1) : 5'd0; //4 div * 8bit = 32 cnt;
    end
end
assign send_over = (data_cnt == DATA_CNT_MAX) ? 1'b1 : 1'b0;
//rising edge miso;
always @(posedge clk or negedge rst_n) begin
    if (~rst_n) begin
        master_dout_reg <= 0;
    end
    else begin
        master_dout_reg <= (sck_p) ? {master_dout_reg[6:0],miso} : master_dout_reg;
    end
end
//mosi;
always @(posedge clk or negedge rst_n) begin
    if (~rst_n) begin
        mosi_cnt <= 0;
    end
    else begin
        if (sck_n) begin
            if (mosi_cnt == MOSI_CNT_MAX) begin
                mosi_cnt <= 0;
            end
            else begin
                mosi_cnt <= mosi_cnt + 1'b1;
            end
        end
        else begin
            mosi_cnt <= mosi_cnt;
        end
    end
end
always @(posedge clk or negedge rst_n) begin
    if (~rst_n) begin
        mosi <= 1'b0;
    end
    else begin
        mosi <= (sck_n) ? master_din_reg[MOSI_CNT_MAX-mosi_cnt] : mosi;
    end
end
endmodule

仿真:
mk1yu4nt.png

综合资源使用:
mk1yud3j.png

Fmax:
mk1yuizy.png

0

评论 (0)

取消