FPGA即插即用Verilog驱动系列——SPI发送模块

发布于:2025-08-16 ⋅ 阅读:(16) ⋅ 点赞:(0)

实现功能:

按字节以spi模式3发送数据,如果要stm32接收,请在cubemx中将对应的spi接口设置为模式3,详情见代码开头注释

// spi_byte_master.v
// 经过优化的SPI主设备模块,每次使能发送一个字节。
// 它实现了SPI模式3 (CPOL=1, CPHA=1),即时钟空闲为高电平,在第二个边沿(上升沿)采样数据。

module spi_byte_master(
    input              clk,        // SPI 工作时钟 (例如 10MHz)
    input              rst_n,      // 异步复位,低有效
    input              ena_mo,     // 模块使能,一个高脉冲触发一次字节传输
    input      [7:0]   spi_tdata,  // 要发送的8位数据
    input              spi_miso,   // SPI MISO 信号
    output reg         spi_mosi,   // SPI MOSI 信号
    output reg         spi_sck,    // SPI SCK 信号
    output reg         spi_nss,    // SPI 片选信号
    output reg [7:0]   spi_rdata,  // 接收到的8位数据
    output             tr_done     // 一字节传输完成信号
);

    // 状态机状态定义
    localparam S_IDLE  = 2'b00; // 等待使能
    localparam S_TX_L  = 2'b01; // SCK 低电平,改变数据
    localparam S_TX_H  = 2'b10; // SCK 高电平,采样数据
    localparam S_DONE  = 2'b11; // 传输完成

    // 状态机寄存器
    reg [1:0] state, next_state;
    // 位计数器
    reg [3:0] bit_cnt;
    // 用于锁存待发送数据的寄存器
    reg [7:0] tdata_reg;

    // FSM - 状态转移逻辑 (组合逻辑)
    always @(*) begin
        next_state = state; // 默认保持当前状态
        case (state)
            S_IDLE: begin
                if (ena_mo)
                    next_state = S_TX_L;
            end
            S_TX_L: begin
                next_state = S_TX_H;
            end
            S_TX_H: begin
                // 发送完8位后进入完成状态
                if (bit_cnt == 4'd7)
                    next_state = S_DONE;
                else
                    next_state = S_TX_L;
            end
            S_DONE: begin
                // 完成后立即返回IDLE,等待下一次触发
                next_state = S_IDLE;
            end
            default: next_state = S_IDLE;
        endcase
    end

    // FSM - 状态输出和数据处理逻辑 (时序逻辑)
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            state <= S_IDLE;
            bit_cnt <= 4'd0;
            spi_sck <= 1'b1;    // SPI模式3: 空闲时SCK为高
            spi_mosi <= 1'b0;
            spi_nss <= 1'b1;    // 片选默认无效
            spi_rdata <= 8'd0;
            tdata_reg <= 8'd0;
        end else begin
            state <= next_state;

            // 根据状态执行操作
            case (state)
                S_IDLE: begin
                    spi_nss <= 1'b1; // 在IDLE状态,取消片选
                    if (ena_mo) begin
                        spi_nss <= 1'b0; // 使能,立即拉低片选,选中从设备
                        bit_cnt <= 4'd0; // 准备发送第一位
                        tdata_reg <= spi_tdata; // 锁存待发送数据
                    end
                end
                
                S_TX_L: begin
                    // 在SCK下降沿改变数据 (CPHA=1)
                    spi_mosi <= tdata_reg[7 - bit_cnt];
                    spi_sck <= 1'b0;
                end

                S_TX_H: begin
                    spi_sck <= 1'b1;
                    // 在SCK上升沿采样数据 (CPHA=1)
                    spi_rdata[7 - bit_cnt] <= spi_miso;
                    bit_cnt <= bit_cnt + 1;
                end

                S_DONE: begin
                    // 传输完成,为下一次传输做准备
                    bit_cnt <= 4'd0;
                    // nss 信号将会在下一个周期的 IDLE 状态被拉高
                end
            endcase
        end
    end

    // 完成信号,在S_DONE状态时拉高一个时钟周期
    assign tr_done = (state == S_DONE);

endmodule


网站公告

今日签到

点亮在社区的每一天
去签到