目录
八、按键控制模块
九、FIFO控制模块
十、DAC控制模块
十一、系统整合
前两篇将数据采集系统的基本模块详细阐述了一下,下边就开始介绍这几个基本模块互联通信的控制模块,包含按键控制模块和FIFO控制模块。
八、按键控制模块
按键控制模块主要功能为:当按键按下时,控制ADC模数转换tran_num次。
该模块信号端口列表如下表:
表8.1 按键控制模块端口信号列表
信号名称 | I/O | 位数 | 功能描述 |
clk | I | 1 | 系统时钟50MHz |
rst_n | I | 1 | 系统复位 |
key_flag | I | 1 | 按键有效信号 |
tran_num | I | 7 | 转换次数 |
adc_done | I | 1 | ADC一次转换完成标志 |
adc_en | O | 1 | ADC转换使能标志 |
该模块代码为:key_ctrl.v
//-------------------------------------------------------------------
// PHF的CSDN
//File name: key_ctrl.v
//Last modified Date: 2020/5/23
//Last Version:
//Descriptions: 按键控制模块:按键按下,控制ADC转换tran_num次
//-------------------------------------------------------------------
module key_ctrl(
input clk , //系统时钟50MHz
input rst_n , //系统复位
input key_flag , //按键有效信号
input [ 6: 0] tran_num , //转换次数
input adc_done , //ADC一次转换完成标志
output reg adc_en //ADC转换使能标志
);
reg [ 6: 0] cnt ; //计数器,计数ADC转换次数
reg add_flag ; //计数器加1标志
wire add_cnt ; //加一条件
wire end_cnt ; //结束条件
//按键按下,add_flag为1,开始计数
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
add_flag <= 0;
end
else if(key_flag) begin
add_flag <= 1;
end
else if(end_cnt)begin
add_flag <= 0;
end
end
//计数器,ADC使能一次,计数器加1
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt <= 0;
end
else if(add_cnt)begin
if(end_cnt)
cnt <= 0;
else
cnt <= cnt + 1'b1;
end
end
assign add_cnt = add_flag==1 && adc_en;
assign end_cnt = add_cnt && cnt== tran_num-1;
//adc_en输出控制
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
adc_en <= 0;
end
else if(key_flag || (add_flag && adc_done)) begin
adc_en <= 1;
end
else begin
adc_en <= 0;
end
end
endmodule
该部分由于比较简单,不给仿真波形图。
九、FIFO控制模块
FIFO控制模块功能为:当ADC转换完成后,将数据存入FIFO,同时在往串口发送数据时控制从FIFO读取数据并将12位数据分成两次8位发送到串口。
该模块信号端口列表如下表:
表9.1:FIFO控制模块信号端口列表
信号名称 | I/O | 位数 | 功能描述 |
clk | I | 1 | 系统时钟50MHz |
rst_n | I | 1 | 系统复位 |
empty | I | 1 | fifo数据空信号 |
tx_done | I | 1 | 串口数据发送完成标志 |
q | I | 12 | 从fifo读出的数据 |
adc_done | I | 1 | ADC转换完成一次标志 |
adc_data_out | I | 12 | ADC转换完成的12位数据 |
rdreq | O | 1 | fifo读使能 |
send_en | O | 1 | 串口发送使能 |
data_byte | O | 8 | 输出发送的数据 |
wrreq | O | 1 | FIFO写使能,在ADC转换结束后置1 |
data_to_fifo | O | 12 | ADC转换完成的数据存入FIFO |
FIFO控制模块代码为:fifo_ctrl.v
//-------------------------------------------------------------------
// PHF的CSDN
//File name: fifo_ctrl.v
//Last modified Date: 2020/5/23
//Last Version:
//Descriptions: FIFO控制模块:当ADC转换完成后,将数据存入FIFO,
// 同时在往串口发送数据时控制从FIFO读取数据并将12位
// 数据分成两次8位发送到串口
//-------------------------------------------------------------------
module fifo_ctrl(
input clk , //系统时钟50MHz
input rst_n , //系统复位
input empty , //fifo数据空信号
input tx_done , //串口数据发送完成标志
input [ 11: 0] q , //从fifo读出的数据
input adc_done , //ADC转换完成一次标志
input [ 11: 0] adc_data_out, //ADC转换完成的12位数据
output reg rdreq , //fifo读使能
output reg send_en , //串口发送使能
output reg [7:0] data_byte , //输出发送的数据
output reg wrreq , //FIFO写使能,在ADC转换结束后置1
output reg [11:0] data_to_fifo //ADC转换完成的数据存入FIFO
);
parameter IDLE = 3'b000 ; //空闲状态
parameter DATA_R = 3'b001 ; //当fifo数据不为空时,读取fifo数据
parameter DATA_S = 3'b010 ; //将fifo的12位数据发送出去
parameter SEND_1 = 3'b011 ; //发送前4位数据
parameter SEND_2 = 3'b100 ; //发送后8位数据
reg [ 2: 0] state_c ; //状态改变
reg [ 2: 0] state_n ; //现在状态
//状态机
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
state_c <= IDLE;
end
else begin
state_c <= state_n;
end
end
//状态改变条件
always @(*)begin
case(state_c)
IDLE:begin
if(empty==0)
state_n = DATA_R;
else
state_n = state_c;
end
DATA_R:state_n = DATA_S;
DATA_S:state_n = SEND_1;
SEND_1:begin
if(tx_done)
state_n = SEND_2;
else
state_n = state_c;
end
SEND_2:begin
if(tx_done)
state_n = IDLE;
else
state_n = state_c;
end
endcase
end
//各个状态下rdreq和send_en输出的结果
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
rdreq <= 0;
send_en <= 0;
end
else begin
case(state_c)
IDLE: begin
if(empty==0)
rdreq <= 1;
else
rdreq <= 0;
end
DATA_R:begin rdreq <= 0; end
DATA_S:begin send_en <= 1; data_byte <= {4'd0,q[11:8]}; end
SEND_1:begin
send_en <= 0;
if(tx_done)begin
send_en <= 1;
data_byte <= q[7:0];
end
end
SEND_2:begin send_en <= 0; end
endcase
end
end
//ADC转换完成后将数据存入FIFO
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
data_to_fifo <= 0;
end
else if(adc_done) begin
data_to_fifo <= adc_data_out;
end
end
//ADC转换完成写使能打开
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
wrreq <= 0;
end
else if(adc_done) begin
wrreq <= 1;
end
else begin
wrreq <= 0;
end
end
endmodule
仿真测试如下:
十、DAC控制模块
DAC控制模块功能为:当接收串口指定的指令时,开始将ROM的正弦数据进行DAC转换。
该模块信号端口列表如下表:
表10.1:DAC控制模块信号端口列表
信号名称 | I/O | 位数 | 功能描述 |
clk | I | 1 | 系统时钟50MHz |
rst_n | I | 1 | 系统复位 |
data_rx | I | 8 | 从串口接收到的指令 |
rx_done | I | 1 | 1字节串口数据接收完成标志 |
dac_done | I | 1 | DAC转换完成标志 |
addr | O | 12 | ROM的地址线 |
dac_en | O | 1 | DAC转换使能标志 |
DAC控制模块代码为:dac_ctrl.v
//-------------------------------------------------------------------
// PHF的CSDN
//File name: dac_ctrl.v
//Last modified Date: 2020/5/23
//Last Version:
//Descriptions: DAC控制模块:当接收串口指定的指令时,
// 开始将ROM的正弦数据进行DAC转换
//-------------------------------------------------------------------
module dac_ctrl(
input clk ,//系统时钟50MHz
input rst_n ,//系统复位
input [ 7: 0] data_rx ,//从串口接收到的指令
input rx_done ,//1字节串口数据接收完成标志
input dac_done ,//DAC转换完成标志
output reg [11:0] addr ,//ROM的地址线
output reg dac_en //DAC转换使能标志
);
//控制地址线自加1
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
addr <= 0;
end
else if(dac_en) begin
addr <= addr + 1'b1;
end
end
//DAC转换使能标志
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
dac_en <= 0;
end
else if(rx_done || dac_done) begin
dac_en <= 1;
end
else begin
dac_en <= 0;
end
end
endmodule
该模块较为简单,不做仿真调试。
十一、系统整合
在完成所有模块设计并仿真测试后,对整个系统模块进行整合,新建一个顶层吧文件,将各个模块例化连接起来。
其代码如下:data_collection_top.v
//-------------------------------------------------------------------
// PHF的CSDN
//File name:
//Last modified Date:
//Last Version:
//Descriptions:
//-------------------------------------------------------------------
module data_collection_top(
input clk ,//系统时钟50MHz
input rst_n ,//系统复位
input rs232_tx ,//串口向FPGA发送数据
input adc_data_din,//adc数据输出到FPGA
input key_in ,//按键输入
output wire dac_din ,//dac串行数据接口控制和数据输出
output wire dac_sclk ,//dac串行数据接口时钟信号
output wire dac_cs ,//dac串行数据接口使能信号
output wire adc_din ,//ADC控制信号,通道控制选取
output wire adc_sclk ,//ADC串行数据接口时钟信号
output wire adc_cs ,//ADC串行数据接口使能信号
output wire rs232_rx //串口接收FPGA数据
);
wire [ 7: 0] data_rx ;
wire rx_done ;
wire dac_en ;
wire [ 11: 0] addr ;
wire dac_done ;
wire [ 11: 0] data_rom ;
wire empty ;
wire adc_en ;
wire key_flag ;
wire adc_done ;
wire tx_done ;
wire rdreq ;
wire send_en ;
wire [ 7: 0] data_byte ;
wire wrreq ;
wire [ 11: 0] data_to_fifo ;
wire [ 11: 0] adc_data_out ;
wire [ 11: 0] fifo_data_out;
//串口接收模块
UART_Byte_Rx u_UART_Byte_Rx(
.clk (clk) ,
.rst_n (rst_n) ,
.rs232_tx (rs232_tx) ,
.baud_set (3'd1) ,
.data_byte (data_rx) ,
.rx_done (rx_done)
);
//DAC控制模块
dac_ctrl u_dac_ctrl(
.clk (clk) ,
.rst_n (rst_n) ,
.data_rx (data_rx) ,
.rx_done (rx_done) ,
.dac_done (dac_done) ,
.addr (addr) ,
.dac_en (dac_en)
);
//ROM模块
single_port_rom
#(.DATA_WIDTH(12), .ADDR_WIDTH(12))
u_single_port_rom(
.addr (addr) ,
.clk (clk) ,
.q (data_rom)
);
//DAC驱动模块
dac_driver1 u_dac_driver(
.clk (clk) ,
.rst_n (rst_n) ,
.dac_data_in({4'b1100,data_rom}),
.dac_en (dac_en) ,
.din (dac_din) ,
.dac_sclk (dac_sclk) ,
.cs (dac_cs) ,
.dac_down (dac_done) ,
.dac_state ()
);
//ADC驱动模块
adc_driver u_adc_driver(
.clk (clk) ,//系统时钟50MHz
.rst_n (rst_n) ,//复位
.channel (3'd0) ,//通道选择
.adc_en (adc_en) ,//使能单次转换,该信号为周期有效高脉冲使能一次转换
.dout (adc_data_din) ,//ADC转换结果,由ADC输给FPGA
.din (adc_din) ,//ADC控制信号,通道控制选择
.adc_sclk (adc_sclk) ,//ADC串行数据接口时钟信号
.cs (adc_cs) ,//ADC串行数据接口使能信号
.data_out (adc_data_out) ,//ADC转换结果
.adc_done (adc_done) ,//转换完成信号,完成后输出一个周期高脉冲
.adc_state () //ADC工作状态:0为空闲状态,1为转换状态
);
//按键控制模块
key_ctrl u_key_ctrl(
.clk (clk) ,
.rst_n (rst_n) ,
.key_flag (key_flag) , //按键信号
.tran_num (7'd100) , //转换次数
.adc_done (adc_done) ,
.adc_en (adc_en)
);
//按键消抖模块
key_filter u_key_filter(
.clk (clk) ,
.rst_n (rst_n) ,
.key_in (key_in) ,
.key_flag (key_flag) ,
.key_state ()
);
//FIFO控制模块
fifo_ctrl u_fifo_ctrl(
.clk (clk) , //系统时钟50MHz
.rst_n (rst_n) , //系统复位
.empty (empty) , //fifo数据空信号
.tx_done (tx_done) , //串口数据发送完成标志
.q (fifo_data_out) , //从fifo读出的数据
.adc_done (adc_done) ,
.adc_data_out(adc_data_out),
.rdreq (rdreq) , //fifo读使能
.send_en (send_en) , //串口发送使能
.data_byte (data_byte) , //输出发送的数据
.wrreq (wrreq) ,
.data_to_fifo(data_to_fifo)
);
//同步FIFO模块
sync_fifo
#(.WIDTH (12) , //缓存的数据宽度
.DEPTH (100) , //缓存的数据深度
.MAX_DEPTH_BIT(7)) //可设置的最大深度位数7,即最大深度为2^7-1
u_sync_fifo
(
.clk (clk) , //系统时钟
.rst_n (rst_n) , //系统复位
.wrreq (wrreq) , //写使能
.data (data_to_fifo) , //写数据
.rdreq (rdreq) , //读使能
.q (fifo_data_out) , //读数据
.empty (empty) , //空信号
.full () , //满信号
.half () , //半满信号
.usedw () //fifo中剩余数据个数
);
//串口发送模块
Uart_Byte_Tx u_Uart_Byte_Tx(
.clk (clk) ,
.rst_n (rst_n) ,
.send_en (send_en) ,
.data_byte (data_byte) ,
.baud_set (3'd1) ,
.rs232_tx (rs232_rx) ,
.tx_done (tx_done) ,
.uart_state()
);
endmodule
在quartus prime17.1软件上进行分析与综合后,按照电路连接进行管脚分配,如图:
全编译后,将sof文件下载到板子上,将DAC的A输出口与ADC的1通道口用杜邦线相连,完成电路连接。然后打开串口助手,采用16进制发送和接收显示,先向串口发送控制DA转换的指令(这里设置为ff),这样DAC按照ROM里正弦信号进行转换输出模拟的正弦信号。然后再按下按键,控制ADC转换100次,输出到串口显示在PC上。
其串口的放松与接收图如下图:
其中每16位数据表示一个ADC转换结果,例如第一个数据06 1C,前四位都是为0,为了串口发送数据方便这样设置的,实际ADC采样结果为0x61C。
总体RTL视图如下图: