赞
踩
前面已经完成了ADC采集模块的编写,也了解了FIFO存储器IP核的调用,数据采集传输系统还需要将采集到的数据用串口发送出去。
串口发送波特率为115200 bps时,每次发送10位数据,串口每秒可以发送11520字节数据,每个字节数据需要发送1s /11520 = 86.8us。而单次ADC转换速率大概3.7us,采集得到12位数字信号,需要用串口传输两次,串口传输速率远远小于AD采集速率。连续采样时,为了避免采样数据丢失,需要先将AD采集数据保存到FIFO存储器中,再由串口慢慢从FIFO存储器读取数据并发送。
端口名称 | 方向 | 说明 |
---|---|---|
Clk | input | 系统时钟 |
Rst_n | input | 系统复位 |
Start | input | 开始采集标志位 |
AD_Done | output | 采集完成标志位 |
ADC_OUT | input | ADC串行数字信号 |
ADC_CS_N | output | ADC片选 |
ADC_DIN | output | 串行数据送给ADC芯片 |
ADC_SCLK | output | ADC时钟 |
ADC_Done | output | 单次AD采集完成标志位,仿真时使用 |
full | input | FIFO满标志位 |
wrreq | output | FIFO写使能 |
FIFO_DATA [11:0] | output | FIFO数据输入 |
在本模块中,将单次AD采集代码例化进来,多次调用该模块连续采集信号,并将AD转换结果存储至fifo存储器。ADC_Done信号为单次AD采集完成标志位,在仿真时使用,板级验证时不需要该端口。full、wrreq、FIFO_DATA三个信号为本模块和fifo模块之间传输的信号。本模块与fifo模块之间的关系如下:
将ADC采集FIFO缓存划分为以下三个状态:
IDLE:空闲状态
WAIT_ADC_DONE:等待单次AD采集完成
WRITE_FIFO:延时一拍,数据写入FIFO
横线上方为状态跳转条件,横线下方为该状态对信号的操作。状态转换图如下:
空闲状态时,若ADC连续采集信号ADC_State为1,则使能单次AD采集标志ADC_Start。单次采集完成,ADC_Done信号为1,写请求使能,跳入下一状态。在下一状态,数据被写入FIFO。状态机代码如下:
always@(posedge Clk or negedge Rst_n)
if(!Rst_n) begin
ADC_Cnt <= 11'd0;
ADC_Start <= 1'b0;
wrreq <= 1'b0;
FIFO_DATA <= 12'd0;
state <= IDLE;
end
else begin
case(state)
IDLE:
if(ADC_State) begin
ADC_Start <= 1'b1;//开启单次AD采集
state <= WAIT_ADC_DONE;
end
else
state <= IDLE;
WAIT_ADC_DONE:begin
ADC_Start <= 1'b0;
if(ADC_Done == 1'b1) begin//等待AD采集完成
FIFO_DATA <= ADC_DATA;
wrreq = 1'b1;
ADC_Cnt <= ADC_Cnt + 1'b1;
state <= WRITE_FIFO;
end
else
state <= WAIT_ADC_DONE;
end
WRITE_FIFO:begin
wrreq = 1'b0;
if(ADC_Cnt == ADC_Cnt_MAX)
ADC_Cnt <= 11'd0;
state <= IDLE;
end
default:state <= IDLE;
endcase
end
采集完成标志位和连续采集状态标志位可在fifo的满标志位full置1时赋值,此时连续采集的次数固定,利用计数器ADC_Cnt作为赋值条件可以自定义采集次数ADC_Cnt_MAX,需注意fifo的深度必须大于等于ADC_Cnt_MAX。
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
AD_Done <= 1'b0;
else if(ADC_Cnt == ADC_Cnt_MAX)
AD_Done <= 1'b1;
else
AD_Done <= 1'b0;
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
ADC_State <= 1'b0;
else if(Start)
ADC_State <= 1'b1;
else if(ADC_Cnt == ADC_Cnt_MAX)
ADC_State <= 1'b0;
adc_fifo.v源代码:
module adc_fifo(
input Clk, //系统时钟
input Rst_n, //系统复位
input Start, //开始采集标志位
output reg AD_Done, //采集完成标志位
input wire ADC_OUT, //ADC串行数字信号
output wire ADC_CS_N, //ADC片选
output wire ADC_DIN, //串行数据送给ADC芯片
output wire ADC_SCLK, //ADC时钟
output wire ADC_Done, //单次AD采集完成标志位,仿真时使用
input full, //FIFO满标志位
output reg wrreq, //FIFO写使能
output reg [11:0] FIFO_DATA //FIFO数据输入
);
parameter ADC_Cnt_MAX = 11'd128; //AD采集次数
/*****模块间信号连线*****/
reg ADC_Start; //单次AD采集开始标志位
wire [11:0] ADC_DATA; //单次AD采集数据
/*****本模块内部寄存器、参数定义*****/
reg ADC_State; //连续采集状态
reg [10:0] ADC_Cnt; //采集128次计数器
reg [2:0] state;
localparam
IDLE = 3'b001, //空闲状态
WAIT_ADC_DONE = 3'b010, //等待单次AD采集完成
WRITE_FIFO = 3'b100; //延时一拍,数据写入FIFO
always@(posedge Clk or negedge Rst_n)
if(!Rst_n) begin
ADC_Cnt <= 11'd0;
ADC_Start <= 1'b0;
wrreq <= 1'b0;
FIFO_DATA <= 12'd0;
state <= IDLE;
end
else begin
case(state)
IDLE:
if(ADC_State) begin
ADC_Start <= 1'b1;//开启单次AD采集
state <= WAIT_ADC_DONE;
end
else
state <= IDLE;
WAIT_ADC_DONE:begin
ADC_Start <= 1'b0;
if(ADC_Done == 1'b1) begin//等待AD采集完成
FIFO_DATA <= ADC_DATA;
wrreq = 1'b1;
ADC_Cnt <= ADC_Cnt + 1'b1;
state <= WRITE_FIFO;
end
else
state <= WAIT_ADC_DONE;
end
WRITE_FIFO:begin
wrreq = 1'b0;
if(ADC_Cnt == ADC_Cnt_MAX)
ADC_Cnt <= 11'd0;
state <= IDLE;
end
default:state <= IDLE;
endcase
end
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
AD_Done <= 1'b0;
else if(ADC_Cnt == ADC_Cnt_MAX)
AD_Done <= 1'b1;
else
AD_Done <= 1'b0;
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
ADC_State <= 1'b0;
else if(Start)
ADC_State <= 1'b1;
else if(ADC_Cnt == ADC_Cnt_MAX)
ADC_State <= 1'b0;
//ADC采集模块
adc128s052 adc1(
.Clk(Clk),
.Rst_n(Rst_n),
.DATA(ADC_DATA), //并行数字信号
.Channel(3'd6), //通道选择
.Start(ADC_Start), //开始标志位
.Conv_done(ADC_Done),//完成标志位
.ADC_CS_N(ADC_CS_N), //片选
.ADC_DIN(ADC_DIN), //串行数据送给ADC芯片
.ADC_SCLK(ADC_SCLK), //ADC时钟
.ADC_OUT(ADC_OUT) //串行数字信号
);
defparam adc1.DIV_PARAM = 8;//ADC时钟50/8 = 6.25Mhz
endmodule
端口名称 | 方向 | 说明 |
---|---|---|
Clk | input | 系统时钟 |
Rst_n | input | 系统复位 |
Start | input | 开始发送数据标志位 |
empty | input | FIFO空标志位 |
FIFO_Q [11:0] | input | 数据输入 |
rdreq | output | FIFO读使能 |
Uart_done | output | 所有数据发送完毕 |
uart_tx | output | 串口数据发送端 |
本模块中,将串口发送模块例化进来,多次调用该模块发送从FIFO读取的数据。empty、FIFO_Q、rdreq为本模块和fifo之间连接的信号。本模块和fifo模块之间的关系如下:
将UART读取FIFO缓存并发送划分为以下五个状态:
IDLE:空闲状态
DELY:空一拍延时,等待FIFO_Q数据更新
SEND_HIGH:发送ADC高四位数据
SEND_LOW:发送ADC低八位数据
WAIT_SEND_DONE:等待发送结束
横线上方为状态跳转条件,横线下方为该状态对信号的操作。状态转换图如下:
若fifo为空,则empty为1,状态机处于空闲状态。fifo非空时,读请求rdreq置1,两个时钟周期后才能够发送数据,因此中间加入DELY状态,延时一个时钟周期,fifo中数据被放到FIFO_Q端口,这个数据在SEND_HIGH状态时被发送。串口先发送AD采集高四位,后发送AD采集低八位。状态机代码如下:
always@(posedge Clk or negedge Rst_n)
if(!Rst_n) begin
rdreq <= 1'b0;
send_en <= 1'b0;
send_data <= 8'd0;
UART_Cnt <= 11'd0;
state <= IDLE;
end
else begin
case(state)
IDLE:
if(empty == 1'b1) begin
if(UART_Cnt == UART_Cnt_MAX)
UART_Cnt <= 11'd0;
state <= IDLE;
end
else begin
rdreq <= 1'b1;
state <= DELY;
end
DELY:begin//空一拍延时,此状态FIFO_Q数据更新
rdreq <= 1'b0;
state <= SEND_HIGH;
end
SEND_HIGH:begin
send_en <= 1'b1;
send_data <= {4'd0,FIFO_Q[11:8]};//发送ADC高四位
state <= SEND_LOW;
end
SEND_LOW:begin
if(tx_done)begin//等待发送完成
send_en <= 1'b1;
send_data <= FIFO_Q[7:0];//发送ADC低八位
state <= WAIT_SEND_DONE;
end
else begin
state <= SEND_LOW;
send_en <= 1'b0;
end
end
WAIT_SEND_DONE:begin
if(tx_done) begin//等待发送完成
UART_Cnt <= UART_Cnt + 1'b1;
state <= IDLE;
end
else begin
state <= WAIT_SEND_DONE;
send_en <= 1'b0;
end
end
default:state <= IDLE;
endcase
end
添加串口发送计数器UART_Cnt,计数达到采样次数时,所有数据发送完毕,Uart_done置1。
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
Uart_done <= 1'b0;
else if(UART_Cnt == UART_Cnt_MAX)
Uart_done <= 1'b1;
else
Uart_done <= 1'b0;
fifo_uart_tx.v源代码:
module fifo_uart_tx(
input Clk, //系统时钟
input Rst_n, //系统复位
input Start, //开始发送数据标志位
input empty, //FIFO空标志位
input [11:0] FIFO_Q, //FIFO数据输入
output reg rdreq, //FIFO读使能
output reg Uart_done, //所有数据发送完毕
output wire uart_tx //串口数据发送端
);
parameter UART_Cnt_MAX = 11'd128; //发送数据个数
/*****模块间信号连线*****/
reg send_en; //单次发送使能
reg [7:0] send_data; //单次发送数据
wire tx_done; //单次发送结束标志
/*****本模块内部寄存器、参数定义*****/
reg [10:0] UART_Cnt; //发送128次计数器
reg [4:0] state;
localparam
IDLE = 5'b00001, //空闲状态
DELY = 5'b00010, //空一拍延时,等待FIFO_Q数据更新
SEND_HIGH = 5'b00100, //发送ADC高四位数据
SEND_LOW = 5'b01000, //发送ADC低八位数据
WAIT_SEND_DONE = 5'b10000; //等待发送结束
always@(posedge Clk or negedge Rst_n)
if(!Rst_n) begin
rdreq <= 1'b0;
send_en <= 1'b0;
send_data <= 8'd0;
UART_Cnt <= 11'd0;
state <= IDLE;
end
else begin
case(state)
IDLE:
if(empty == 1'b1) begin
if(UART_Cnt == UART_Cnt_MAX)
UART_Cnt <= 11'd0;
state <= IDLE;
end
else begin
rdreq <= 1'b1;
state <= DELY;
end
DELY:begin//空一拍延时,此状态FIFO_Q数据更新
rdreq <= 1'b0;
state <= SEND_HIGH;
end
SEND_HIGH:begin
send_en <= 1'b1;
send_data <= {4'd0,FIFO_Q[11:8]};//发送ADC高四位
state <= SEND_LOW;
end
SEND_LOW:begin
if(tx_done)begin//等待发送完成
send_en <= 1'b1;
send_data <= FIFO_Q[7:0];//发送ADC低八位
state <= WAIT_SEND_DONE;
end
else begin
state <= SEND_LOW;
send_en <= 1'b0;
end
end
WAIT_SEND_DONE:begin
if(tx_done) begin//等待发送完成
UART_Cnt <= UART_Cnt + 1'b1;
state <= IDLE;
end
else begin
state <= WAIT_SEND_DONE;
send_en <= 1'b0;
end
end
default:state <= IDLE;
endcase
end
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
Uart_done <= 1'b0;
else if(UART_Cnt == UART_Cnt_MAX)
Uart_done <= 1'b1;
else
Uart_done <= 1'b0;
//串口发送模块
uart_data_tx data_tx(
.Clk(Clk),
.Rst_n(Rst_n),
.send_en(send_en),
.data(send_data),
.baud_set(3'd2), //波特率9600
.tx(uart_tx), //数据发送端
.tx_done(tx_done)
);
endmodule
将这两部分模块编写完成后,通过顶层模块将这两个模块和fifo模块连接,并通过仿真模拟AD采集过程和发送过程,顶层模块和系统仿真在下一个笔记中给出。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。