fpga spi从机发送数据左移了1bit
module spi_slave(
input rst_n, // 复位信号
input clk_20M, // 输入20Mhz时钟
input SS, // 从机选择信号,低电平有效
input SCLK, // SCLK输入
input MOSI, // MOSI输入
input [7:0] tx_data, // 要发送的数据
output reg MISO, // MISO输出
output reg [7:0] rx_data, // 接收到的数据
output reg rx_done, // 接收完成标识
output reg tx_done // 发送完成标识
);
reg [3:0] rx_status; // 接收过程状态机
reg [3:0] tx_status; // 发送过程状态机
reg [3:0] rx_cnt; // 接收bit计数
reg [3:0] tx_cnt; // 发送bit计数
reg [7:0] rx_shift_reg; // 接收移位寄存器,接收完成后数据转存至rx_data
reg [7:0] tx_shift_reg; // 发送移位寄存器,tx_data数据转存至tx_shift_reg
reg [1:0] SS_sync; // SS状态捕捉
reg [1:0] SCLK_sync; // SCLK状态捕捉
wire SS_fal; // SS下降沿
wire SCLK_ris; // SCLK上升沿
wire SCLK_fal; // SCLK下降沿
// ---- main code ----
assign SS_fal = SS_sync[1:0] == 2'b10; // SS由1变成0,判断SS为下降沿
assign SCLK_ris = SCLK_sync[1:0] == 2'b01; // SCLK由0变成1,判断SCLK为上升沿
assign SCLK_fal = SCLK_sync[1:0] == 2'b10; // SCLK由1变成0,判断SCLK为下降沿
// 边沿检测过程块,所有边沿检测均在此执行
always @(posedge clk_20M or negedge rst_n) begin
if(!rst_n) begin
SS_sync <= 2'b11; //SS时钟空闲状态位高电平
SCLK_sync <= 2'b00; //SCLK时钟空闲状态位低电平,CPOL=0,CPHA=0
end
else begin
SS_sync <= {SS_sync[0], SS};
SCLK_sync <= {SCLK_sync[0], SCLK};
end
end
// 功能描述:SPI接收字节过程块,FPGA运行过程中,不停判断SS低电平,
// 若有数据传输,则接收,当接收满一个字节后置位接收完成标识.
always @(posedge clk_20M or negedge rst_n) begin
if(!rst_n) begin
rx_shift_reg <= 8'd0;
rx_cnt <= 4'd0;
rx_done <= 1'b0;
rx_data <= 1'b0;
rx_status <= 4'd0;
end
else begin
if (SS_fal) begin // SS下降沿
rx_shift_reg <= 8'd0;
rx_cnt <= 4'd0;
rx_data <= 1'b0;
rx_done <= 1'b0;
rx_status <= 4'd0;
end
else begin
if (!SS) begin
case (rx_status)
4'd0: begin // 接收数据准备
rx_shift_reg <= 8'd0;
rx_cnt <= 4'd0;
rx_done <= 1'b0;
rx_status <= 4'd1;
end
4'd1: begin
if (SCLK_ris) begin // SCLK上升沿读取MOSI,移位写入8bit数据
rx_shift_reg <= {rx_shift_reg[6:0], MOSI}; // 上升沿读数据,左移
if(rx_cnt == 4'd7) begin
rx_cnt <= 4'd0;
rx_status <= 4'd2;
end
else begin
rx_cnt <= rx_cnt + 4'd1;
end
end
end
4'd2: begin
rx_data <= rx_shift_reg; // 接收8 bits完成,数据转存至rx_data
rx_done <= 1'b1; // 接收完成标识置1
rx_status <= 4'd3;
end
4'd3: begin
rx_done <= 1'b0;
rx_status <= 4'd0;
end
endcase
end // if (!SS) begin
end
end
end
// 功能描述:SPI字节发送过程块,当有数据需要发送时,发送send_data数据
always @(posedge clk_20M or negedge rst_n) begin
if(!rst_n) begin
tx_cnt <= 4'd0;
tx_done <= 1'b0;
tx_shift_reg <= 8'd0;
MISO <= 1'b0;
tx_status <= 4'd0;
end
else begin
if (SS_fal) begin // SS下降沿
tx_cnt <= 4'd0;
tx_done <= 1'b0;
tx_shift_reg <= 8'd0;
MISO <= 1'b0;
tx_status <= 4'd0;
end
else begin
if (!SS) begin
case (tx_status)
4'd0: begin // 开始准备发送,等待发送标识控制高电平
tx_done <= 1'b0;
tx_cnt <= 4'd0;
tx_shift_reg <= tx_data; // 转送数据,避免其他模块改写导致数据变化
tx_status <= 4'd1;
end
4'd1: begin
tx_status <= 4'd2;
end
4'd2: begin // 每一个SCLK下降沿向MISO写入一个bit
if (SCLK_fal) begin // 等待时钟下降沿
MISO <= tx_shift_reg[4'd7 - tx_cnt];
if(tx_cnt == 4'd7) begin
tx_cnt <= 4'd0;
tx_done <= 1'b1;
tx_status <= 4'd3;
end
else begin
tx_cnt <= tx_cnt + 4'd1;
end
end
end
4'd3: begin
tx_status <= 4'd4;
tx_done <= 1'b0;
end
4'd4: tx_status <= 4'd0;
endcase
end //if (!SS) begin
end
end
end
endmodule
这是spi发送数据左移1bit的仿真图,正确的应该是在第2bit结束时发送。
我的解答思路和尝试过的方法 :
我发现可能是我发送的状态机问题,我想更改一下我发送的状态机,但是一直达不到我想要的结果。
我想要达到的结果:
你这个 SPI 应该接受完后再启动发送
把接受的时序做好,所有的数据接收完成后再启动发送