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 @(*) beginnext_state = state; // 默认保持当前状态case (state)S_IDLE: beginif (ena_mo)next_state = S_TX_L;endS_TX_L: beginnext_state = S_TX_H;endS_TX_H: begin// 发送完8位后进入完成状态if (bit_cnt == 4'd7)next_state = S_DONE;elsenext_state = S_TX_L;endS_DONE: begin// 完成后立即返回IDLE,等待下一次触发next_state = S_IDLE;enddefault: next_state = S_IDLE;endcaseend// FSM - 状态输出和数据处理逻辑 (时序逻辑)always @(posedge clk or negedge rst_n) beginif (!rst_n) beginstate <= 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 beginstate <= next_state;// 根据状态执行操作case (state)S_IDLE: beginspi_nss <= 1'b1; // 在IDLE状态,取消片选if (ena_mo) beginspi_nss <= 1'b0; // 使能,立即拉低片选,选中从设备bit_cnt <= 4'd0; // 准备发送第一位tdata_reg <= spi_tdata; // 锁存待发送数据endendS_TX_L: begin// 在SCK下降沿改变数据 (CPHA=1)spi_mosi <= tdata_reg[7 - bit_cnt];spi_sck <= 1'b0;endS_TX_H: beginspi_sck <= 1'b1;// 在SCK上升沿采样数据 (CPHA=1)spi_rdata[7 - bit_cnt] <= spi_miso;bit_cnt <= bit_cnt + 1;endS_DONE: begin// 传输完成,为下一次传输做准备bit_cnt <= 4'd0;// nss 信号将会在下一个周期的 IDLE 状态被拉高endendcaseendend// 完成信号,在S_DONE状态时拉高一个时钟周期assign tr_done = (state == S_DONE);endmodule