> 技术文档 > FPGA即插即用Verilog驱动系列——SPI发送模块

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


实现功能:

按字节以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