声明:本篇文章面向在已对SPI的四种时序有所了解的人
我们采用SPI3模式以及将FPGA作从机,STM32作主机的方式讲解,在STM32控制部分采用的是半双工模式,但其实半双工与全双工区别不大,
稍加修改即可
本片文章是接续上篇文章的,如果未浏览上一篇文章的,可以在此跳转SPI驱动代码详解SPI代码详解FPGA-verilog部分(FPGA+STM32)(二)
本文是FPGA通过SPI接收的数据做出应答的模块,比如改变ADC采样率,将ADC采集的数据放在RAM中和从RAM中取出再发送给单片机
module samplingCtrl (
input clk,
input rstn,
input [31:0] FCW, //频率控制字,是由单片机发送过来的
input en, //采样使能控制信号,是由单片机发送过来的
input [11:0] AD9226_Din_0, //AD9226模块0采样后转换出来的数字量
input [11:0] AD9226_Din_1, //AD9226模块1采样后转换出来的数字量
input [11:0] AD9226_Din_2, //AD9226模块2采样后转换出来的数字量
input [6:0] rd_addr, //读RAM地址,是由单片机发送过来的
output reg clk_sampling_1, //AD9226模块0时钟,由频率控制字控制
output reg clk_sampling_2, //AD9226模块1时钟,由频率控制字控制
output reg clk_sampling_0, //AD9226模块2时钟,由频率控制字控制
output reg [11:0] AD9226_Dout_0, //从RAM0中读出的AD9226模块0采集的数据,将来发送给STM32
output reg [11:0] AD9226_Dout_1, //从RAM1中读出的AD9226模块1采集的数据,将来发送给STM32
output reg [11:0] AD9226_Dout_2, //从RAM2中读出的AD9226模块2采集的数据,将来发送给STM32
output reg busy //判断一组采样(设置的是1024个点或者512个点)是否完成
);
localparam OFFSET_VALID = 7'd0; //RAM偏移地址可以过滤掉采集的前几个不太正常的数据,但是一般数据都是正常的,这个功能暂时不开启
wire clk_sampling; //ADC模块的采样时钟
reg [1:0] r_en;
wire pos_en;
reg [1:0] r_clk_sampling;
wire pos_clk_sampling;
wire neg_clk_sampling;
reg wr_en;
reg [6:0] wr_addr;
wire [15:0] ram_dout0;
wire [15:0] ram_dout1;
wire [15:0] ram_dout2;
//
always @(posedge clk , negedge rstn)
begin
if(!rstn)
r_en <= 2'd0;
else
r_en <= {r_en[0], en};
end
assign pos_en = (!r_en[1]) &(r_en[0]);
always @(posedge clk , negedge rstn)
begin
if(!rstn)
r_clk_sampling <= 2'd0;
else
r_clk_sampling <= {r_clk_sampling[0], clk_sampling};
end
assign pos_clk_sampling = (!r_clk_sampling[1]) &(r_clk_sampling[0]);
assign neg_clk_sampling = (r_clk_sampling[1]) &(!r_clk_sampling[0]);
//
reg [3:0] state_c;
reg [3:0] state_n;
localparam s_idle = 4'b0001;
localparam s_waitPos = 4'b0010;
localparam s_store = 4'b0100;
localparam s_addAddr = 4'b1000;
wire idle_2_waitPos;
wire waitPos_2_store;
wire store_2_addAddr;
wire addAddr_2_waitPos;
wire addAddr_2_idle;
always @(posedge clk , negedge rstn)
begin
if(!rstn)
state_c <= s_idle;
else
state_c <= state_n;
end
//! fsm_extract 以下是三段式状态机
/***
第一段用来进行状态的变换
第二段是用来判断什么时候进行状态的变换
第三段是逻辑输出,处于什么状态输出什么样的逻辑值
***/
always @(*)
begin
case (state_c)
s_idle:
if(idle_2_waitPos)
state_n <= s_waitPos;
else
state_n <= state_c;
s_waitPos:
if(waitPos_2_store)
state_n <= s_store;
else
state_n <= state_c;
s_store:
if(store_2_addAddr)
state_n <= s_addAddr;
else
state_n <= state_c;
s_addAddr:
if(addAddr_2_waitPos)
state_n <= s_waitPos;
else if(addAddr_2_idle)
state_n <= s_idle;
else
state_n <= state_c;
default:
state_n = s_idle;
endcase
end
assign idle_2_waitPos = (state_c == s_idle) &&(pos_en);//使能采样后,进入等待状态,等待第一个采样上升沿到来
assign waitPos_2_store = (state_c == s_waitPos) &&(pos_clk_sampling);//当第一个上升沿到来的时候,进入存储状态
assign store_2_addAddr = (state_c == s_store) &&(neg_clk_sampling); //采样时钟下降进入地址移位状态
assign addAddr_2_waitPos = (state_c == s_addAddr) &&(wr_addr < (64 + OFFSET_VALID) );//如果此时还没超过设定存储大小,由地址移位状态进入等待上升沿状态
assign addAddr_2_idle = (state_c == s_addAddr) &&(wr_addr >= (64 + OFFSET_VALID) );//如果此时已经超过设定存储大小,由地址移位状态进入空闲状态
always @(posedge clk , negedge rstn)
begin
if(!rstn)
begin
busy <= 1'b0;
wr_en <= 1'b0;
wr_addr <= 7'd0;
end
else
case (state_n)
s_idle: //在空闲状态时,busy = 0表示告知单片机现在不处于采样存储阶段,w_en = 0表示现在还不允许对RAM写入adc采样数据
begin
busy <= 1'b0;
wr_en <= 1'b0;
wr_addr <= 7'd0;
end
s_waitPos: //在等待上升沿状态时,busy = 1表示告知单片机现在处于采样存储阶段,w_en = 0表示现在还不允许对RAM写入adc采样数据
begin
busy <= 1'b1;
wr_en <= 1'b0;
wr_addr <= wr_addr;
end
s_store: // 在存储状态时,busy = 1表示告知单片机现在处于采样存储阶段,w_en = 1表示现在允许对RAM写入adc采样数据
begin
busy <= 1'b1;
wr_en <= 1'b1;
wr_addr <= wr_addr;
end
s_addAddr: //在地址增加状态时,busy = 1表示告知单片机现在处于采样存储阶段,w_en = 0表示现在不允许对RAM写入adc采样数据,wr_addr 增加
begin
busy <= 1'b1;
wr_en <= 1'b0;
wr_addr <= wr_addr + 7'd1;
end
default:
begin
busy <= 1'b0;
wr_en <= 1'b0;
wr_addr <= 7'd0;
end
endcase
end
always @(posedge clk , negedge rstn)
begin
if(!rstn)
begin
clk_sampling_0 <= 1'b0;
clk_sampling_1 <= 1'b0;
clk_sampling_2 <= 1'b0;
end
else
begin
clk_sampling_0 <= clk_sampling;
clk_sampling_1 <= clk_sampling;
clk_sampling_2 <= clk_sampling;
end
end
always @(posedge clk , negedge rstn)
begin
if(!rstn)
begin
AD9226_Dout_0 <= 12'd0;
AD9226_Dout_1 <= 12'd0;
AD9226_Dout_2 <= 12'd0;
end
else
begin
AD9226_Dout_0 <= ram_dout0[11:0];
AD9226_Dout_1 <= ram_dout1[11:0];
AD9226_Dout_2 <= ram_dout2[11:0];
end
end
clkSamplingGen clkSamplingGen_inst(
.clk_50M(clk),
.rstn(rstn),
.pos_en(pos_en),
.FCW(FCW),
.clk_sampling(clk_sampling)
);
ram_2p ram_2p_inst0 (
.clock ( clk ),
.data ( {4'd0, AD9226_Din_0} ),
.rdaddress ( rd_addr ),
.wraddress ( wr_addr[6:0] ),
.wren ( wr_en ),
.q ( ram_dout0 )
);
ram_2p ram_2p_inst1 (
.clock ( clk ),
.data ( {4'd0, AD9226_Din_1} ),
.rdaddress ( rd_addr ),
.wraddress ( wr_addr[6:0] ),
.wren ( wr_en ),
.q ( ram_dout1 )
);
ram_2p ram_2p_inst2 (
.clock ( clk ),
.data ( {4'd0, AD9226_Din_2} ),
.rdaddress ( rd_addr ),
.wraddress ( wr_addr[6:0] ),
.wren ( wr_en ),
.q ( ram_dout2 )
);
endmodule