硬件介绍: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