硬件介绍:FPGA采用的是黑金的AX530,AD采集模块采用的是AN706(八通道十六位)。
实现功能:AD采集到8通道十六位的电压数据,然后经过串口发送到串口助手显示各个通道的实时电压数据。
程序源码介绍:顶层模块包含AD采集模块(AD_RX_module),AD数据转换模块(AD_Volt),AD数据到串口数据转换模块(AD_to_Uart),串口发送模块(Uart_tx_Module)。
以下将分模块介绍:
1、顶层程序AD_8C_16B:就是把底层模块中出现的输入输出脚连接起来。
//顶层程序 8chnnel-16bits 2020.7.9开始
//2020.7.14日,编程完毕
//实现功能:采集8路16位的AD数据,并发送到串口助手。采集电压范围在-5~5V之间,误差不超过千分之5
//优化方向:BCD.v中的for循环
module AD_8C_16B //输入输出端口声明,和模块定义,只有下面这里是逗号
(
//输入端口
clk_50M,
rst_n,
AD_data, //AD转成的16位数据
AD_BUSY, //AD的BUSY线
AD_FRETDATA, //为1时表示第一个数据来了,不用他,直接在RD的上升沿读取
//输出端口
AD_CS, //AD片选信号,此时用来读取数据
AD_RD, //此时通过变换的时钟读取数据,自己定义在上升沿读取吧
AD_RESET, //AD的复位信号,至少高电平50ns
AD_OS, //多重滤波操作,先不用
AD_CONVERT, //启动AD转化
TXD
);
//--------------------------------------------------------------------
//--定义外部端口端口
//-------------------------------------------------------------------
//定义输入端口
input clk_50M; //input output 默认类型都是wire型
input rst_n;
input [15:0] AD_data; //AD输入的转成16位数字信号
input AD_BUSY; //AD忙
input AD_FRETDATA; //为1时表示第一个数据来了,不用他,直接在RD的上升沿读取
//定义输出端口
output AD_CS; //片选,决定能否开始接收数据
output AD_RD; //输出的时钟,用于在其上升沿读出AD数据
output AD_RESET; //AD706在每次上电的时候复位一次
output AD_CONVERT; //启动AD转化
output [2:0] AD_OS; //默认设置为000,不设置采样倍率
output TXD;
//--------------------------------------------------------------------
//--定义内部端口
//-------------------------------------------------------------------
//防止被综合掉,否则signal tapII无法采集,用于wire型 -> /*synthesis keep*/
//*********AD_RX_module和AD_Volt间的数据传递*********
wire [127:0] AD_CH; //8个通道的16进制值
//*********AD_Volt和AD_to_Uart间的数据传递*********
wire [19:0] CH1_BCD; //8个通道的显示的电压值
wire [19:0] CH2_BCD; //8个通道的显示的电压值
wire [19:0] CH3_BCD; //8个通道的显示的电压值
wire [19:0] CH4_BCD; //8个通道的显示的电压值
wire [19:0] CH5_BCD; //8个通道的显示的电压值
wire [19:0] CH6_BCD; //8个通道的显示的电压值
wire [19:0] CH7_BCD; //8个通道的显示的电压值
wire [19:0] CH8_BCD; //8个通道的显示的电压值
wire [7:0] CH1_Sig; //通道1的正负号的asc
wire [7:0] CH2_Sig; //通道2的正负号的asc
wire [7:0] CH3_Sig; //通道3的正负号的asc
wire [7:0] CH4_Sig; //通道4的正负号的asc
wire [7:0] CH5_Sig; //通道5的正负号的asc
wire [7:0] CH6_Sig; //通道6的正负号的asc
wire [7:0] CH7_Sig; //通道7的正负号的asc
wire [7:0] CH8_Sig; //通道8的正负号的asc
//*********AD_to_Uart和Uart_tx_Module间的数据传递*********
wire [ 7:0] data_out; //AD_to_Uart和串口输出间的数据传递
wire ctl; //AD_to_Uart和串口输出间的控制位传递
//--------------------------------------------------------------------
//--逻辑实现
//-------------------------------------------------------------------
//例化AD_RX采集模块
AD_RX_module AD_RX_Init//输入输出端口声明,和模块定义,只有下面这里是逗号
(
//输入端口
.clk_50M (clk_50M),
.rst_n (rst_n),
.AD_data (AD_data), //AD转成的16位数据的输入端口
.AD_BUSY (AD_BUSY), //AD的BUSY线
.AD_FRETDATA (AD_FRETDATA), //为1时表示第一个数据来了,不用他,直接在RD的上升沿读取
//输出端口
.AD_CS (AD_CS), //AD片选信号,此时用来读取数据
.AD_RD (AD_RD), //此时通过变换的时钟读取数据,自己定义在上升沿读取吧
.AD_RESET (AD_RESET), //AD的复位信号,至少高电平50ns
.AD_OS (AD_OS), //多重滤波操作,先不用
.AD_CONVERT (AD_CONVERT), //启动AD转化
//FPGA内部输出
.AD_CH (AD_CH) //依次八个16位的AD数据输出给AD值的转换,CH1在最低16位.只是一堆数据没有转成电压大小的数据
);
AD_Volt AD_Volt_Init//输入输出端口声明,和模块定义,只有下面这里是逗号
(
//输入端口
.clk_50M (clk_50M),
.rst_n (rst_n), //硬件复位
.AD_RESET (AD_RESET), //软件复位
//FPGA内部输入
.AD_CH (AD_CH), //稳定的8个通道未转换的电压工16*8bits
//输出端口
.CH1_BCD (CH1_BCD), //8个通道的显示的电压值 20位
.CH2_BCD (CH2_BCD), //8个通道的显示的电压值
.CH3_BCD (CH3_BCD), //8个通道的显示的电压值
.CH4_BCD (CH4_BCD), //8个通道的显示的电压值
.CH5_BCD (CH5_BCD), //8个通道的显示的电压值
.CH6_BCD (CH6_BCD), //8个通道的显示的电压值
.CH7_BCD (CH7_BCD), //8个通道的显示的电压值
.CH8_BCD (CH8_BCD), //8个通道的显示的电压值
.CH1_Sig (CH1_Sig), //八个通道数据的正负号
.CH2_Sig (CH2_Sig),
.CH3_Sig (CH3_Sig),
.CH4_Sig (CH4_Sig),
.CH5_Sig (CH5_Sig),
.CH6_Sig (CH6_Sig),
.CH7_Sig (CH7_Sig),
.CH8_Sig (CH8_Sig)
);
//例化AD到串口模块
AD_to_Uart AD_to_Uart_Init//输入输出端口声明,和模块定义,只有下面这里是逗号
(
//输入端口
.clk_50M (clk_50M),
.rst_n (rst_n), //硬件复位
.AD_RESET (AD_RESET), //软件复位
.CH1_BCD (CH1_BCD), //8个通道的显示的电压值 20位
.CH2_BCD (CH2_BCD), //8个通道的显示的电压值
.CH3_BCD (CH3_BCD), //8个通道的显示的电压值
.CH4_BCD (CH4_BCD), //8个通道的显示的电压值
.CH5_BCD (CH5_BCD), //8个通道的显示的电压值
.CH6_BCD (CH6_BCD), //8个通道的显示的电压值
.CH7_BCD (CH7_BCD), //8个通道的显示的电压值
.CH8_BCD (CH8_BCD), //8个通道的显示的电压值
.CH1_Sig (CH1_Sig), //八个通道数据的正负号
.CH2_Sig (CH2_Sig),
.CH3_Sig (CH3_Sig),
.CH4_Sig (CH4_Sig),
.CH5_Sig (CH5_Sig),
.CH6_Sig (CH6_Sig),
.CH7_Sig (CH7_Sig),
.CH8_Sig (CH8_Sig),
//输出端口
.ctl (ctl), //数据控
.data_out (data_out) //数据出
);
//例化数据发送模块
Uart_tx_Module Uart_tx_init
(
.clk_50M (clk_50M),
.rst_n (rst_n),
.ctl (ctl),
.data_in (data_out), //输入
.TXD (TXD)
);
endmodule //成对
2、AD采集模块AD_RX_module :用于实现对8个通道的16位电压值的采集,采集后发送给AD数据转换模块(AD_Volt),可以实现把采集到的AD数据(-32767+32767)转化为(-50000mV+50000mV)实现保留电压的四位有效数字。然后通过最后的BCD模块,进行译码,实现把(-50000mV~+50000mV)中的每一位数据取出来,用于最后的串口发送。由于FPGA乘除法很占用逻辑资源,所以乘法是通过移位操作实现的,当然也可以用内部的乘法器。
//AD采集模块8chnnel-16bits 2020.7.9
//此模块进行数据AD数据采集不负责转换
module AD_RX_module //输入输出端口声明,和模块定义,只有下面这里是逗号
(
//输入端口
clk_50M,
rst_n,
AD_data, //AD转成的16位数据
AD_BUSY, //AD的BUSY线
AD_FRETDATA, //为1时表示第一个数据来了,不用他,直接在RD的上升沿读取
//输出端口
AD_CS, //AD片选信号,此时用来读取数据
AD_RD, //此时通过变换的时钟读取数据,自己定义在上升沿读取吧
AD_RESET, //AD的复位信号,至少高电平50ns
AD_OS, //多重滤波操作,先不用
AD_CONVERT, //启动AD转化
AD_CH //依次八个16位的AD数据输出给AD值的转换,CH1在最低16位.只是一堆数据没有转成电压大小的数据
);
//--------------------------------------------------------------------
//--定义外部端口端口
//-------------------------------------------------------------------
//定义输入端口
input clk_50M; //input output 默认类型都是wire型
input rst_n;
input [15:0] AD_data; //AD输入的转成16位数字信号
input AD_BUSY; //AD忙
input AD_FRETDATA; //为1时表示第一个数据来了,不用他,直接在RD的上升沿读取
//定义输出端口
output reg AD_CS; //片选,决定能否开始接收数据
output reg AD_RD; //输出的时钟,用于在其上升沿读出AD数据
output reg AD_RESET; //AD706在每次上电的时候复位一次
output reg AD_CONVERT; //启动AD转化
output reg [2:0] AD_OS; //默认设置为000,不设置采样倍率
output reg [127:0] AD_CH ; //稳定的8个通道的16进制值(00001111_11111111),用于发送给转换模式
//--------------------------------------------------------------------
//--定义内部端口
//-------------------------------------------------------------------
reg [29:0] time_cnt; //保证可以记录13ms的时间,用于关断模式的上电后复位
reg [29:0] time_cnt_n; //时间计数器下一状态
reg [7:0] fsm_time_cnt; //有限状态机时序计时器,实际只需要5位就行
reg [7:0] fsm_time_cnt_n;
reg [2:0] AD_RD_cnt; //打出2-1-2的节拍,用于读取数据
reg [2:0] AD_RD_cnt_n;
reg AD_CS_n;
reg AD_RD_n;
reg AD_RESET_n;
reg AD_CONVERT_n;
wire [2:0] AD_OS_n;
reg [127:0] AD_CH_reg; //不稳定的8个通道的16进制值,用于暂存
reg [127:0] AD_CH_reg_n; //不稳定的8个通道的16进制电压值,用于暂存
reg [127:0] AD_CH_n;
reg [2:0] fsm_s; //有限状态机当前状态 一共4态
reg [2:0] fsm_s_n; //有限状态机下一状态
reg [3:0] CH_cnt; //记录采集到的通道数,计数到8就清零
reg [3:0] CH_cnt_n; //记录采集到的通道数,计数到8就清零
//--------------------------------------------------------------------
//--定义参数
//-------------------------------------------------------------------
parameter AD_RESET_num=30'd650_000, //650000*20=13ms
Idle =3'd0, //状态机初始态
Convert =3'd1, //convert置0态
Wait_busy1 =3'd2, //等待busy置为1态
Wait_busy0 =3'd3, //等待busy置为0态
Data_IN =3'd4, //读取8通道的AD数据态
AD_END =3'd5; //数据结束态
//--------------------------------------------------------------------
//--逻辑实现
//-------------------------------------------------------------------
//时序电路,----------50Mhz AD复位计数器-------------------
always @(posedge clk_50M or negedge rst_n)
begin
if(!rst_n)
AD_OS<=3'b0;
else
AD_OS<=AD_OS_n;
end
//组合电路
assign AD_OS_n=3'b000; //不过采样(貌似就是不滤波)
//时序电路,----------50Mhz AD复位计数器-------------------
always @(posedge clk_50M or negedge rst_n)
begin
if(!rst_n)
time_cnt<=30'b0;
else
time_cnt<=time_cnt_n;
end
//组合电路
always @(*)
begin
if(time_cnt<=AD_RESET_num)
time_cnt_n=time_cnt+1'b1;
else
time_cnt_n=time_cnt;
end
//时序电路,----------等待AD706初上电复位-------------------
always @(posedge clk_50M or negedge rst_n)
begin
if(!rst_n)
AD_RESET<=1'b0;
else
AD_RESET<=AD_RESET_n;
end
//组合电路
always @(*)
begin
if(time_cnt<=AD_RESET_num) //时间到13ms,就拉低,不再拉回来了
AD_RESET_n=1'b1;
else
AD_RESET_n=1'b0;
end
//时序电路,----------有限状态机状态切换-------------------
always @(posedge clk_50M or negedge rst_n)
begin
if(!rst_n)
fsm_s<=Idle; //默认是空闲状态
else
fsm_s<=fsm_s_n;
end
//组合电路 ---有限状态机核心
always@(*) //状态机内部仅仅执行状态的跳转,不进行状态的内部操作
begin
if(AD_RESET==1) //状态机复位
fsm_s_n=Idle;
else
begin
case(fsm_s)
Idle: //初始化态(空闲态),等待AD_CONVERT为0
if(fsm_time_cnt==8'd5) //5*20ns=100ns 其实40ns就行,保险一些Debug
fsm_s_n=Convert; //进入下一态
else
fsm_s_n=fsm_s; //保持当前状态
Convert: //convert为0 的时间
if(fsm_time_cnt==8'd2) //2*20ns=40ns
fsm_s_n=Wait_busy1; //进入下一态
else
fsm_s_n=fsm_s; //保持当前状态
Wait_busy1: //等待busy为1,也就是目前busy为0
if(fsm_time_cnt==8'd5) //至少5*20ns=100ns,只要小于20us就行
fsm_s_n=Wait_busy0; //进入到下一个初始态
else
fsm_s_n=fsm_s; //保持当前状态
Wait_busy0: //此时busy为1,等待busy变成0进入下一态
if(AD_BUSY==1) //至少2*20ns=40ns
fsm_s_n=fsm_s; //AD_BUSY等于1就保持,1大概会200个时钟
else //直到AD_BUSY为0
fsm_s_n=Data_IN;
Data_IN: //3个时钟有一个通道,共需要8*60=480ns
if(fsm_time_cnt==8'd24||CH_cnt==8) //至少24*20ns=480ns,产生8个读取上升沿后,就不再读取数据
fsm_s_n=AD_END; //进入到下一个初始态
else
fsm_s_n=fsm_s;
AD_END:
fsm_s_n=Idle; //直接回到空闲态
default:fsm_s_n=Idle; //默认在空闲态
endcase
end
end
//时序电路,----------fsm_time_cnt//有限状态机时序计时器--------
always @(posedge clk_50M or negedge rst_n)
begin
if(!rst_n)
fsm_time_cnt<=8'b0;
else
fsm_time_cnt<=fsm_time_cnt_n;
end
//组合电路
always @(*)
begin
if(fsm_s==fsm_s_n&&AD_RESET==0) //只计数在每个状态时AD1.1Mhz的周期数
fsm_time_cnt_n=fsm_time_cnt+8'b1; //记录每个状态的脉冲数
else //改变状态时或者AD_RESET为1时清零
fsm_time_cnt_n=8'b0;
end
//时序电路,----------AD_CONVERT-------------------
always @(posedge clk_50M or negedge rst_n)
begin
if(!rst_n)
AD_CONVERT<=1'b0;
else
AD_CONVERT<=AD_CONVERT_n;
end
//组合电路
always @(*)
begin
if(fsm_s!=Convert||AD_RESET==1) //只要不在Convert态或者AD_RESET为1
AD_CONVERT_n=1'b1; //就把AD_CONVERT_n置为1
else //仅仅在Convert态时才为0
AD_CONVERT_n=1'b0;
end
//时序电路----------Data_IN态中的AD_CS-------------------
always @(posedge clk_50M or negedge rst_n)
begin
if(!rst_n)
AD_CS<=1'b0;
else
AD_CS<=AD_CS_n;
end
//组合电路
always @(*)
begin
if(fsm_s==Data_IN&&AD_RESET!=1)
AD_CS_n=1'b0; //CS拉低,开启片选可以读取数据
else
AD_CS_n=1'b1; //关闭片选,不可以读取数据
end
//时序电路----------Data_IN态中的AD_RD_cnt打拍子-------------------
always @(posedge clk_50M or negedge rst_n) //通过AD_RD交替变换产生2-1-2拍的时钟
begin
if(!rst_n)
AD_RD_cnt<=2'b0;
else
AD_RD_cnt<=AD_RD_cnt_n;
end
//组合电路
always @(*)
begin
if(AD_RESET==1) //复位
AD_RD_cnt_n=2'b0;
else if(fsm_s==Data_IN&&AD_RD_cnt==2'd2) //3个时钟一个循环,0-1,1-2,2-0
AD_RD_cnt_n=2'b0; //重新计数
else if(fsm_s==Data_IN)
AD_RD_cnt_n=AD_RD_cnt+2'b1; //每个时钟加1
else
AD_RD_cnt_n=AD_RD_cnt;
end
//时序电路----------Data_IN态中的AD_RD-------------------
always @(posedge clk_50M or negedge rst_n) //通过AD_RD交替变换产生2-1-2拍的时钟
begin
if(!rst_n)
AD_RD<=1'b0;
else
AD_RD<=AD_RD_n;
end
//组合电路
always @(*)
begin
if(AD_RESET==1) //复位
AD_RD_n=1'b1; //默认高
else if(fsm_s==Data_IN&&AD_RD_cnt==2'd2) //高电平一个时钟
AD_RD_n=1'b1;
else //低电平两个时钟
AD_RD_n=1'b0;
end
//时序电路----------Data_IN态中的CH_cnt-------------------
always @(posedge clk_50M or negedge rst_n) //通过AD_RD交替变换产生2-1-2拍的时钟
begin
if(!rst_n)
CH_cnt<=1'b0;
else
CH_cnt<=CH_cnt_n;
end
//组合电路
always @(*)
begin
if(AD_RESET==1||CH_cnt==8) //软件复位或者记录了8个上升沿就复位计数值为0
CH_cnt_n=4'b0; //默认采集了0个通道
else if(fsm_s==Data_IN&&(~AD_RD)&AD_RD_n) //上升沿加1
CH_cnt_n=CH_cnt+1'b1;
else //低电平两个时钟
CH_cnt_n=CH_cnt;
end
//时序电路----------Data_IN态中的AD_CH-------------------
always @(posedge clk_50M or negedge rst_n) //通过AD_RD交替变换产生2-1-2拍的时钟
begin
if(!rst_n)
AD_CH_reg<=1'b0;
else
AD_CH_reg<=AD_CH_reg_n;
end
//组合电路
always @(*)
begin
if(AD_RESET==1) //复位
AD_CH_reg_n=128'b0; //默认数据0
else if(fsm_s==Data_IN&&(~AD_RD)&AD_RD_n) //上升沿读一字节
AD_CH_reg_n={AD_data,AD_CH_reg[127:16]}; //先读的是低16位
else //非上升沿保持数据
AD_CH_reg_n=AD_CH_reg;
end
//时序电路----------AD_END态用于发送出处理后的8通道AD数据-------------------
always @(posedge clk_50M or negedge rst_n)
begin
if(!rst_n)
AD_CH<=128'b0; //默认是默认读0
else
AD_CH<=AD_CH_n;
end
//组合电路
always @(*)
begin
if(AD_RESET==1) //复位
AD_CH_n=128'b0; //默认数据0
else if(fsm_s==AD_END) //此时上8通道AD数据读取完毕,保存
AD_CH_n=AD_CH_reg; //稳定存储转换过来的数据
else
AD_CH_n=AD_CH;
end
endmodule //成对
2.1、BCD译码模块:用for循环实现移位加三法(还有更好的方法,不采用循环,需要的可以百度或者留言)
//2020.7.12
//BCD译码 移位加三法
module BCD
(
//输入16进制
clk_50M,
Num, //0-5000的值
rst_n, //硬件复位
AD_RESET, //软件复位
//输出10进制BCD
dec
);
//--------------------------------------------------------------------
//--定义外部端口端口
//-------------------------------------------------------------------
input clk_50M; //input output 默认类型都是wire型
input [15:0] Num;
input rst_n;
input AD_RESET;
output reg[19:0] dec;
//--------------------------------------------------------------------
//--定义内部端口
//-------------------------------------------------------------------
reg [35:0] z; //65535转为bcd需要5*4=20bits,再加上本身的16bit共需要36bits
reg [19:0] dec_n; //dec的bcd码的寄存器
reg [5:0] i; //循环次数
//--------------------------------------------------------------------
//--定义参数
//-------------------------------------------------------------------
//--------------------------------------------------------------------
//--逻辑实现
//-------------------------------------------------------------------
//**************BCD码转换***************
//时序电路
always @(posedge clk_50M or negedge rst_n)
begin
if(!rst_n)
dec<=20'b0; //
else
dec<=dec_n;
end
//组合电路
always @(*)
begin
if(AD_RESET) //软件复位
dec_n=20'b0;
else
dec_n=z[35:16];
end
//组合逻辑 //一个时钟可以进行完毕吧?
always @ (*)
begin
z = 35'b0; //置 0
z[15:0] = Num; //读入低 16位
// repeat (16) //很难综合,别用
for(i=6'd0;i<6'd16;i=i+1'b1)
begin
if(z[19:16 ]>4'd4) //大于 4 就加 3,加3后不可能溢出的,所以不用向后进位
z[19:16] = z[19:16] + 2'b11;
if(z[23:20]>4'd4)
z[23:20] = z[23:20] + 2'b11;
if(z[27:24]>4'd4) //大于 4 就加 3
z[27:24] = z[27:24] + 2'b11;
if(z[31:28]>4'd4)
z[31:28] = z[31:28] + 2'b11;
if(z[35:32]>4'd4)
z[35:32] = z[35:32] + 2'b11;
z[35:1] = z[34:0]; //整体左移一位
end
end
endmodule
3、串口发送前数据的准备模块AD_to_Uar:用于把模块2转换的数据排列好,接下来可以发送给串口发送模块 4,最终实现数据的串口发送。
//把AD的数据分成一个一个的字节发送 2020.7.12
//下接Uart_tx_Module
module AD_to_Uart //输入输出端口声明,和模块定义,只有下面这里是逗号
(
//输入端口
clk_50M,
rst_n, //硬件复位
AD_RESET, //软件复位
//fpga内部输入
CH1_BCD, //8个通道的显示的电压值 20位
CH2_BCD, //8个通道的显示的电压值
CH3_BCD, //8个通道的显示的电压值
CH4_BCD, //8个通道的显示的电压值
CH5_BCD, //8个通道的显示的电压值
CH6_BCD, //8个通道的显示的电压值
CH7_BCD, //8个通道的显示的电压值
CH8_BCD, //8个通道的显示的电压值
CH1_Sig, //八个通道数据的正负号
CH2_Sig,
CH3_Sig,
CH4_Sig,
CH5_Sig,
CH6_Sig,
CH7_Sig,
CH8_Sig,
//输出端口
//fpga内部输出
ctl, //数据控
data_out //数据出
);
//---------------------------------------------------------------------------
//-- 外部端口声明
//---------------------------------------------------------------------------
input clk_50M;
input AD_RESET;
input rst_n;
input [19:0] CH1_BCD; //8个通道的显示的电压值 20位
input [19:0] CH2_BCD; //8个通道的显示的电压值
input [19:0] CH3_BCD; //8个通道的显示的电压值
input [19:0] CH4_BCD; //8个通道的显示的电压值
input [19:0] CH5_BCD; //8个通道的显示的电压值
input [19:0] CH6_BCD; //8个通道的显示的电压值
input [19:0] CH7_BCD; //8个通道的显示的电压值
input [19:0] CH8_BCD; //8个通道的显示的电压值
input [7:0] CH1_Sig; //八个通道数据的正负号
input [7:0] CH2_Sig;
input [7:0] CH3_Sig;
input [7:0] CH4_Sig;
input [7:0] CH5_Sig;
input [7:0] CH6_Sig;
input [7:0] CH7_Sig;
input [7:0] CH8_Sig;
output reg ctl;
output reg[7:0] data_out;
//---------------------------------------------------------------------------
//-- 内部端口声明
//---------------------------------------------------------------------------
reg ctl_n;
reg [13:0] time_cnt; //计时器脉冲数 比实际多预留一位
reg [13:0] time_cnt_n; //下一时刻的计时器脉冲式
reg [ 6:0] date_byte_cnt; //总共不超过128个字节 数据索引
reg [ 6:0] date_byte_cnt_n; //总共不超过128个字节
//--------------------------------------------------------------------
//--定义参数
//-------------------------------------------------------------------
//波特率115200,发送一个字节(10位)需要大概4300个脉冲(50Mhz),这里取5300个(足够发送12.2112位了)保证数据发送完毕
parameter byte_time=13'd5300; //
//--------------------------------------------------------------------
//--逻辑实现
//-------------------------------------------------------------------
//**************发送时间间隔计数器***************
//时序电路
always @(posedge clk_50M or negedge rst_n)
begin
if(!rst_n)
time_cnt<=13'b0;
else
time_cnt<=time_cnt_n;
end
//组合电路
always @(*)
begin
if(AD_RESET||time_cnt==byte_time) //软件复位或者发送完毕一个字节复位
time_cnt_n=13'b0;
else
time_cnt_n=time_cnt+1'b1;
end
//**************串口发送控制位***************
//时序电路
always @(posedge clk_50M or negedge rst_n)
begin
if(!rst_n)
ctl<=1'b0;
else
ctl<=ctl_n;
end
//组合电路
always @(*)
begin
if(!AD_RESET&&time_cnt==byte_time) //每到达一次时间就置位发送标志位为1一次
ctl_n=1'b1;
else
ctl_n=1'b0;
end
//**************发送数据包的数据索引***************
//时序电路
always @(posedge clk_50M or negedge rst_n)
begin
if(!rst_n)
date_byte_cnt<=7'b0;
else
date_byte_cnt<=date_byte_cnt_n;
end
//组合电路
always @(*)
begin
if(!AD_RESET&&time_cnt==byte_time) //依次移动数组索引
date_byte_cnt_n=date_byte_cnt+1'b1;
else if(AD_RESET||date_byte_cnt==7'd114) //1-114是数据字节,到了就复位,如果到115的话,会产生一位defalut
date_byte_cnt_n=7'b0;
else
date_byte_cnt_n=date_byte_cnt;
end
//组合电路,根据哪个是高电平,决定串口输出的是哪个数据
always @(*)
begin
case(date_byte_cnt) //不需要break
7'd1:data_out <= 8'd65; //存储字符 A
7'd2:data_out <= 8'd68; //存储字符 D
7'd3:data_out <= 8'd49; //存储字符 1
7'd4:data_out <= 8'd58; //存储字符 :
7'd5:data_out <= CH1_Sig; //存储字符 正负
7'd6:data_out <= CH1_BCD[19:16] + 8'd48; //存储字符 个位
7'd7:data_out <= 8'd46; //存储字符 .
7'd8:data_out <= CH1_BCD[15:12] + 8'd48; //存储字符 小数点后一位
7'd9:data_out <= CH1_BCD[11:8] + 8'd48; //存储字符 小数点后二位
7'd10:data_out <= CH1_BCD[7:4] + 8'd48; //存储字符 小数点后三位
7'd11:data_out <= CH1_BCD[3:0] + 8'd48; //存储字符 小数点后四位
7'd12:data_out <= 8'd86; //存储字符 V
7'd13:data_out <= 8'd32; //存储字符 空格
7'd14:data_out <= 8'd32; //存储字符 空格
7'd15:data_out <= 8'd65; //存储字符 A
7'd16:data_out <= 8'd68; //存储字符 D
7'd17:data_out <= 8'd50; //存储字符 2
7'd18:data_out <= 8'd58; //存储字符 :
7'd19:data_out <= CH2_Sig; //存储字符 正负
7'd20:data_out <= CH2_BCD[19:16] + 8'd48; //存储字符 个位
7'd21:data_out <= 8'd46; //存储字符 .
7'd22:data_out <= CH2_BCD[15:12] + 8'd48; //存储字符 小数点后一位
7'd23:data_out <= CH2_BCD[11:8] + 8'd48; //存储字符 小数点后二位
7'd24:data_out <= CH2_BCD[7:4] + 8'd48; //存储字符 小数点后三位
7'd25:data_out <= CH2_BCD[3:0] + 8'd48; //存储字符 小数点后四位
7'd26:data_out <= 8'd86; //存储字符 V
7'd27:data_out <= 8'd32; //存储字符 空格
7'd28:data_out <= 8'd32; //存储字符 空格
7'd29:data_out <= 8'd65; //存储字符 A
7'd30:data_out <= 8'd68; //存储字符 D
7'd31:data_out <= 8'd51; //存储字符 3
7'd32:data_out <= 8'd58; //存储字符 :
7'd33:data_out <= CH3_Sig; //存储字符 正负
7'd34:data_out <= CH3_BCD[19:16] + 8'd48; //存储字符 个位
7'd35:data_out <= 8'd46; //存储字符 .
7'd36:data_out <= CH3_BCD[15:12] + 8'd48; //存储字符 小数点后一位
7'd37:data_out <= CH3_BCD[11:8] +8'd48; //存储字符 小数点后二位
7'd38:data_out <= CH3_BCD[7:4] + 8'd48; //存储字符 小数点后三位
7'd39:data_out <= CH3_BCD[3:0] + 8'd48; //存储字符 小数点后四位
7'd40:data_out <= 8'd86; //存储字符 V
7'd41:data_out <= 8'd32; //存储字符 空格
7'd42:data_out <= 8'd32; //存储字符 空格
7'd43:data_out <= 8'd65; //存储字符 A
7'd44:data_out <= 8'd68; //存储字符 D
7'd45:data_out <= 8'd52; //存储字符 4
7'd46:data_out <= 8'd58; //存储字符 :
7'd47:data_out <= CH4_Sig; //存储字符 正负
7'd48:data_out <= CH4_BCD[19:16] + 8'd48; //存储字符 个位
7'd49:data_out <= 8'd46; //存储字符 .
7'd50:data_out <= CH4_BCD[15:12] + 8'd48; //存储字符 小数点后一位
7'd51:data_out <= CH4_BCD[11:8] + 8'd48; //存储字符 小数点后二位
7'd52:data_out <= CH4_BCD[7:4] + 8'd48; //存储字符 小数点后三位
7'd53:data_out <= CH4_BCD[3:0] + 8'd48; //存储字符 小数点后四位
7'd54:data_out <= 8'd86; //存储字符 V
7'd55:data_out <= 8'd32; //存储字符 空格
7'd56:data_out <= 32; //存储字符 空格
7'd57:data_out <= 8'd65; //存储字符 A
7'd58:data_out <= 8'd68; //存储字符 D
7'd59:data_out <= 8'd53; //存储字符 5
7'd60:data_out <= 8'd58; //存储字符 :
7'd61:data_out <= CH5_Sig; //存储字符 正负
7'd62:data_out <= CH5_BCD[19:16] + 8'd48; //存储字符 个位
7'd63:data_out <= 8'd46; //存储字符 .
7'd64:data_out <= CH5_BCD[15:12] + 8'd48; //存储字符 小数点后一位
7'd65:data_out <= CH5_BCD[11:8] + 8'd48; //存储字符 小数点后二位
7'd66:data_out <= CH5_BCD[7:4] + 8'd48; //存储字符 小数点后三位
7'd67:data_out <= CH5_BCD[3:0] + 8'd48; //存储字符 小数点后四位
7'd68:data_out <= 8'd86; //存储字符 V
7'd69:data_out <= 8'd32; //存储字符 空格
7'd70:data_out <= 8'd32; //存储字符 空格
7'd71:data_out <= 8'd65; //存储字符 A
7'd72:data_out <= 8'd68; //存储字符 D
7'd73:data_out <= 8'd54; //存储字符 6
7'd74:data_out <= 8'd58; //存储字符 :
7'd75:data_out <= CH6_Sig; //存储字符 正负
7'd76:data_out <= CH6_BCD[19:16] + 8'd48; //存储字符 个位
7'd77:data_out <= 8'd46; //存储字符 .
7'd78:data_out <= CH6_BCD[15:12] + 8'd48; //存储字符 小数点后一位
7'd79:data_out <= CH6_BCD[11:8] + 8'd48; //存储字符 小数点后二位
7'd80:data_out <= CH6_BCD[7:4] + 8'd48; //存储字符 小数点后三位
7'd81:data_out <= CH6_BCD[3:0] + 8'd48; //存储字符 小数点后四位
7'd82:data_out <= 8'd86; //存储字符 V
7'd83:data_out <= 8'd32; //存储字符 空格
7'd84:data_out <= 8'd32; //存储字符 空格
7'd85:data_out <= 8'd65; //存储字符 A
7'd86:data_out <= 8'd68; //存储字符 D
7'd87:data_out <= 8'd55; //存储字符 7
7'd88:data_out <= 8'd58; //存储字符 :
7'd89:data_out <= CH7_Sig; //存储字符 正负
7'd90:data_out <= CH7_BCD[19:16] + 8'd48; //存储字符 个位
7'd91:data_out <= 8'd46; //存储字符 .
7'd92:data_out <= CH7_BCD[15:12] + 8'd48; //存储字符 小数点后一位
7'd93:data_out <= CH7_BCD[11:8] + 8'd48; //存储字符 小数点后二位
7'd94:data_out <= CH7_BCD[7:4] + 8'd48; //存储字符 小数点后三位
7'd95:data_out <= CH7_BCD[3:0] + 8'd48; //存储字符 小数点后四位
7'd96:data_out <= 8'd86; //存储字符 V
7'd97:data_out <= 8'd32; //存储字符 空格
7'd98:data_out <= 8'd32; //存储字符 空格
7'd99: data_out<= 8'd65; //存储字符 A
7'd100:data_out<= 8'd68; //存储字符 D
7'd101:data_out<= 8'd56; //存储字符 8
7'd102:data_out<= 8'd58; //存储字符 :
7'd103:data_out<= CH8_Sig; //存储字符 正负
7'd104:data_out<= CH8_BCD[19:16] + 8'd48; //存储字符 个位
7'd105:data_out<= 8'd46; //存储字符 .
7'd106:data_out<= CH8_BCD[15:12] + 8'd48; //存储字符 小数点后一位
7'd107:data_out<= CH8_BCD[11:8] + 8'd48; //存储字符 小数点后二位
7'd108:data_out<= CH8_BCD[7:4] + 8'd48; //存储字符 小数点后三位
7'd109:data_out<= CH8_BCD[3:0] + 8'd48; //存储字符 小数点后四位
7'd110:data_out<= 8'd86; //存储字符 V
7'd111:data_out<= 8'd32 ; //存储字符 空格
7'd112:data_out<= 8'd32 ; //存储字符 空格
7'd113:data_out<= 8'd10; //换行符
7'd114:data_out<= 8'd13 ; //回车符
default:data_out=8'd48; //默认发送0,但是应该永远不会发
endcase
end
endmodule
4、串口发送模块:波特率采用115200,用于把模块3中排好的数据发送到串口助手显示,注意发送的是ascl码,而且是字符发送方式,不是十六进制发送方式,16进制发送方式会更简单,但是上位机需要额外的处理才能显示出AD的电压值。
// 2020.7.9
module Uart_tx_Module //输入输出端口声明,和模块定义,只有下面这里是逗号
(
//输入端口
clk_50M,
rst_n,
//fpga内部输入
ctl, //数据控
data_in, //数据入
//输出端口
TXD //输出的端口
);
//--------------------------------------------------------------------
//--定义外部端口端口
//-------------------------------------------------------------------
input clk_50M;
input rst_n;
input ctl; //输入控制位
input [7:0] data_in; //数据进入
output reg TXD;
//--------------------------------------------------------------------
//--定义内部端口
//-------------------------------------------------------------------
reg ctl_flag; //输入控制位寄存器
reg ctl_flag_n; //输入控制位寄存器
reg [8:0] time_cnt; //计数器
reg [8:0] time_cnt_n;
reg [7:0] data_byte;
reg [7:0] data_byte_n;
reg [3:0] cnt_FSM; //有限状态机状态轮换(发送的数据位数)//0-10计数
reg [3:0] cnt_FSM_n;
//--------------------------------------------------------------------
//--定义参数
//-------------------------------------------------------------------
parameter bps_num=9'd434; //434个脉冲是波特率115200的一个脉冲
//--------------------------------------------------------------------
//--逻辑实现
//-------------------------------------------------------------------
//**************发送控制位***************
//时序电路
always @(posedge clk_50M or negedge rst_n)
begin
if(!rst_n)
ctl_flag<=1'b0; //
else
ctl_flag<=ctl_flag_n;
end
//组合电路,
always @(*)
begin
if(ctl==1) //仅仅会有一个时钟的ctl为1,控制数据开始执行一系列发送操作,直到发送完一个字节。
ctl_flag_n=1'b1;
else if(cnt_FSM==4'd10) //从开始位到停止位一共十位,需要记到10
ctl_flag_n=1'b0; //数据发送完毕,置位0
else
ctl_flag_n=ctl_flag;
end
//***************波特率产生***************
//时序电路
always @(posedge clk_50M or negedge rst_n)
begin
if(!rst_n)
time_cnt<=1'b0; //
else
time_cnt<=time_cnt_n;
end
//组合电路,
always @(*)
begin
if(time_cnt==bps_num) //到达一个波特率的时间了
time_cnt_n=1'b0;
else
time_cnt_n=time_cnt+1'b1;
end
//***************位数计算***************
//时序电路
always @(posedge clk_50M or negedge rst_n)
begin
if(!rst_n)
cnt_FSM<=4'b0; //
else
cnt_FSM<=cnt_FSM_n;
end
//组合电路,
always @(*)
begin
if(time_cnt==bps_num&&ctl_flag==1) //到达一个波特率的时间了且发送数据位已经满足
cnt_FSM_n=cnt_FSM+1'b1; //就把位数寄存器加1
else if(cnt_FSM==4'd10) //到达10就清零,一个字节发送完毕
cnt_FSM_n=4'b0;
else
cnt_FSM_n=cnt_FSM; //否则保持
end
//***************待发送数据存储***************
//时序电路
always @(posedge clk_50M or negedge rst_n)
begin
if(!rst_n)
data_byte<=8'hff; //默认是停止位
else
data_byte<=data_byte_n;
end
//组合电路,
always @(*)
begin
if(ctl==1) //一个时钟的控制位来了,就读入一个时钟的数据,然后一直保持到读完9个位
data_byte_n=data_in; //读一个字节数据
else if(cnt_FSM==4'd10) //到达9就清零,就把发送的数据置位为1(停止位)
data_byte_n=8'hff;
else
data_byte_n=data_byte; //否则保持
end
//***************数据发送***************
//组合电路,依次发送0-7位
always @(*) //
begin
case(cnt_FSM) //不需要break,
4'b0001:TXD=0; //第一位是起始位,起始位为0
4'b0010:TXD=data_byte[0]; //数据1
4'b0011:TXD=data_byte[1]; //数据2
4'b0100:TXD=data_byte[2]; //...3
4'b0101:TXD=data_byte[3]; //...4
4'b0110:TXD=data_byte[4]; //...5
4'b0111:TXD=data_byte[5]; //...6
4'b1000:TXD=data_byte[6]; //...7
4'b1001:TXD=data_byte[7]; //...8
default:TXD=1'b1; //默认发送高电平,停止位
endcase
end
endmodule