文章目录
一、ADC采集FIFO缓存UART发送系统顶层模块设计
1.系统整体结构
在前一篇文章中已经设计了adc_fifo.v和fifo_uart_tx.v两个模块,实现了将连续AD采集数据存储到fifo、从fifo读取数据通过串口发送两部分功能,需设计顶层模块,将这两个模块和fifo模块连接,系统整体结构如下图所示:
顶层模块为adc_fifo.v和fifo_uart_tx.v提供Start信号,表示ADC连续采集开始,各模块执行对应功能。
2.顶层模块端口说明
端口名称 | 方向 | 说明 |
---|---|---|
Clk | input | 系统时钟 |
Rst_n | input | 系统复位 |
Start | input | 开始采集数据并发送 标志位 |
AD_Done | output | AD采集完成标志位 |
Uart_done | output | UART发送完成标志位 |
uart_tx | output | 串口数据发送端 |
ADC_OUT | input | ADC串行数字信号 |
ADC_CS_N | output | ADC片选 |
ADC_DIN | output | 串行数据送给ADC芯片 |
ADC_SCLK | output | ADC时钟 |
ADC_Done | output | 单次AD采集完成标志位,可删除,仿真用 |
ADC_Done信号为单次AD采集完成标志位,在仿真时使用,板级验证时不需要该端口。
Start为ADC连续采集开始标志,由于ADC采集和UART速率不同,AD_Done表示ADC连续采集完成,Uart_done表示采集的所有数据发送完成。
3.顶层模块代码设计
module adc_data_send_top(
input Clk, //系统时钟
input Rst_n, //系统复位
input Start, //开始采集数据并发送 标志位
output wire AD_Done, //AD采集完成标志位
output wire Uart_done, //UART发送完成标志位
output wire uart_tx, //串口数据发送端
input ADC_OUT, //ADC串行数字信号
output wire ADC_CS_N, //ADC片选
output wire ADC_DIN, //串行数据送给ADC芯片
output wire ADC_SCLK, //ADC时钟
output wire ADC_Done //单次AD采集完成标志位,可删除,仿真用
);
wire [11:0] FIFO_DATA;
wire [11:0] FIFO_Q;
wire rdreq,wrreq,empty,full;
//ADC数据存入FIFO
adc_fifo adc_fifo(
.Clk(Clk),
.Rst_n(Rst_n),
.Start(Start), //开始采集数据标志位
.AD_Done(AD_Done), //采集完成标志位
.ADC_CS_N(ADC_CS_N),
.ADC_DIN(ADC_DIN),
.ADC_SCLK(ADC_SCLK),
.ADC_OUT(ADC_OUT),
.ADC_Done(ADC_Done), //单次AD采集完成标志位,可删除,仿真用
.full(full), //FIFO满标志位
.wrreq(wrreq), //FIFO写使能
.FIFO_DATA(FIFO_DATA) //FIFO数据输入
);
//FIFO缓存模块
fifo data_fifo(
.clock(Clk),
.data(FIFO_DATA),
.rdreq(rdreq),
.sclr(!Rst_n),
.wrreq(wrreq),
.empty(empty),
.full(full),
.q(FIFO_Q)
);
//将FIFO存储的ADC数据用串口发送
fifo_uart_tx data_tx(
.Clk(Clk),
.Rst_n(Rst_n),
.Start(Start), //开始发送数据标志位
.empty(empty), //FIFO空标志位
.FIFO_Q(FIFO_Q), //FIFO数据输入
.rdreq(rdreq), //FIFO读使能
.Uart_done(Uart_done),
.uart_tx(uart_tx)
);
endmodule
二、ADC采集FIFO缓存UART发送系统仿真
1.ADC采集数据模拟
模拟ADC芯片采集过程,在SCLK下降沿时改变ADC_OUT的值,将这个过程封装成task任务。
task GENE_ADC_OUT;//生成ADC数据
input [15:0] v_data;
integer i;
begin
wait(!ADC_CS_N);//等待CS_N被拉低
for(i=0;i<16;i=i+1) begin
@(negedge ADC_SCLK)
ADC_OUT = v_data[15-i];
end
end
endtask
2.PC串口接收模拟
将之前设计好的串口接收模块例化进来,在仿真时只需观测PC_uart_rx端口,查看接收到的数据和AD采集数据是否一致。
uart_data_rx PC_rx(
.Clk(Clk),
.Rst_n(Rst_n),
.rx(uart_tx),
.baud_set(3'd2), //波特率为9600
.data(PC_uart_rx),
.rx_done(PC_rx_done)
);
3.仿真文件
利用repeat重复128次,调用GENE_ADC_OUT任务模拟生成AD采集的数据,等待单次AD采集结束(ADC_Done置1)后生成下一次数据。
`timescale 1ns/1ns
module adc_data_send_top_tb();
reg Clk,Rst_n;
reg Start;//开始采集数据并发送 标志位
wire AD_Done;//采集完成标志位
reg ADC_OUT;
reg [15:0] ADC_DATA_GEN;
wire ADC_CS_N,ADC_DIN,ADC_SCLK;
wire ADC_Done;
wire uart_tx,Uart_done;
wire [7:0] PC_uart_rx;
adc_data_send_top adc(
.Clk(Clk),
.Rst_n(Rst_n),
.Start(Start),//开始采集数据并发送 标志位
.AD_Done(AD_Done),//采集完成标志位
.ADC_OUT(ADC_OUT),
.ADC_CS_N(ADC_CS_N),
.ADC_DIN(ADC_DIN),
.ADC_SCLK(ADC_SCLK),
.ADC_Done(ADC_Done),
.Uart_done(Uart_done),
.uart_tx(uart_tx)
);
uart_data_rx PC_rx(
.Clk(Clk),
.Rst_n(Rst_n),
.rx(uart_tx),
.baud_set(3'd2), //波特率为9600
.data(PC_uart_rx),
.rx_done(PC_rx_done)
);
initial Clk = 1'b1;
always #10 Clk = ~Clk;
initial begin
Rst_n = 1'b0;
Start = 1'b0;
ADC_OUT = 1'b0;
ADC_DATA_GEN = 16'h0100;
#201;
Rst_n = 1'b1;
#20;
Start = 1'b1;#20;
Start = 1'b0;#20;
repeat(128) begin
GENE_ADC_OUT(ADC_DATA_GEN);
wait(ADC_Done);
ADC_DATA_GEN = ADC_DATA_GEN + 8;
end
wait(AD_Done);
wait(Uart_done);
#200000;
$stop;
end
task GENE_ADC_OUT;//生成ADC数据
input [15:0] v_data;
integer i;
begin
wait(!ADC_CS_N);//等待CS_N被拉低
for(i=0;i<16;i=i+1) begin
@(negedge ADC_SCLK)
ADC_OUT = v_data[15-i];
end
end
endtask
endmodule
4.仿真结果
整体波形:
前面红框的部分为AD采集过程,从仿真结果可以明显看出AD采集速率和串口传输速率的差异,因此在利用FPGA做高速AD采集项目时,必须先将采集数据缓存再通过串口发送,不缓存会影响采样速率或者导致采样数据丢失。
AD采集波形:
Start信号为1时,开始采集数据,采集到的第一个数据为16’h0100。
采集到最后一个数据时,AD_Done置1,表示连续采集完成,最后一个数据为16’h04f8。
串口发送波形:
采集到的第一个数据被存入fifo时,串口开始发送数据,观察第一个PC_rx_done脉冲后的数据,接收到了8’h01和8’h00,与AD采集到的第一个数据16’h0100一致。
采集到的最后两个数据为8’h04和8’hf8,与AD采集到的最后一个数据16’h04f8一致,发送完最后一个数据后,Uart_done置1,所有数据发送完毕。
整个系统的文件及仿真文件下载链接:
FPGA数据采集传输系统