当前位置:   article > 正文

基于FPGA的数字电压表设计

基于fpga的数字电压表设计

基于FPGA的数字电压表设计
本文代码数据处理模块参照大佬设计:https://blog.csdn.net/aodan3459/article/details/102011527?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522159469213619725222417809%2522%252C%2522scm%2522%253A%252220140713.130102334…%2522%257D&request_id=159469213619725222417809&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2blogfirst_rank_v2~rank_blog_default-2-102011527.pc_v2_rank_blog_default&utm_term=FPGA%E6%95%B0%E5%AD%97%E7%94%B5%E5%8E%8B%E8%A1%A8,
感谢大佬,侵删。

参考文献:
1.《现代数字电子技术基础实践》陈龙 牛小燕 马学条 杨柳 编著 机械工业出版社
2.《EDA Technology and Verilog HDL》黄继业 郑兴 黄汐威 潘松 编著 清华大学出版社
3.《Verilog HDL与FPGA数字系统设计》罗杰 主编 机械工业出版社

开发板资料:
1.AX301B_UG.pdf
2.高速ADDA使用指南REV1.3.pdf
3.黑金Altera开发板AX301_AX4010Verilog实例教程.pdf
以上资料均来自黑金动力社区:http://www.heijin.org/
*

一、设计指标:
(1)使用ALINX AX301 FPGA开发平台。
(2)使用ALINX AN108 AD/DA模块。
(3)通过DA部分AD9708芯片产生固定模拟电压信号。
(4)通过AD部分AD9280芯片将模拟信号转化为数字信号。
(5)数码管显示所测电压值,分辨率0.01V,并且负电压可以显示负号。
(6)量程为 -4.00到4.00V。

二、系统框图:
1.
三、代码部分:
1.顶层模块:
使用“例化”方式连接各个模块。

module adc9280_top(
						//input
						sys_clk,		//系统时钟50MHz
						rst_n,		//复位信号
                  data_ad,		//AD输出信号
                     
                  //output									
                  clk_adc,		//ADC时钟
						clk_dac,		//DAC时钟
						data_da,		//DA输出信号
                  wei_slec,	//数码管位选信号
                  duan_slec	//数码管段选信号
                  );
						
input sys_clk;
input rst_n;
input [7:0] data_ad;

output clk_adc;
output clk_dac;
output [7:0] data_da;
output [3:0] wei_slec;
output [7:0] duan_slec;

wire [7:0] pre_data;
wire [11:0] cout;

//分频器模块
clk_10M	u1(
				//input
				.sys_clk(sys_clk),
				.rst_n(rst_n),
				
				//ouput
				.clk_10M(clk_10M)
				);

//ADC采样模块			
ADC9280	u2(
				//input
				.sys_clk(sys_clk),
				.data_ad(data_ad),
				
				//ouput
				.pre_data(pre_data)
				);

//数据处理模块
data_ad_pro	u3(
					//input 
					.sys_clk(sys_clk),
					.rst_n(rst_n),
					.pre_data(pre_data),
                 
					//output
					.cout(cout)
					);

//数码显示译码模块				
display	u4(
				//input 
				.sys_clk(sys_clk),
				.rst_n(rst_n),
				.cout(cout),
                 
				//output			
				.slec_wei(wei_slec),
				.slec_duan(duan_slec)
				);

//信号发生模块
signal_generator u5(
							//input
							.sys_clk(sys_clk),
							.rst_n(rst_n),
							
							//output
							.data_da(data_da)
						);

assign clk_adc = clk_10M;
assign clk_dac = sys_clk;
endmodule
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83

2.分频器模块:
AD9280最大采样率为32MSPS,使用10MHz时钟比较合适。

module clk_10M(
						//input
						sys_clk,		//输系统时钟50MHz
						rst_n,		//复位信号
						
						//ouput	
						clk_10M		//输出分频后的时钟10MHz
					);

input sys_clk;
input rst_n;

output clk_10M;

reg clk_10M;
reg [2:0]cnt;		//计数信号

always @(posedge sys_clk or negedge rst_n)
 if(!rst_n) begin
     cnt  <= 5'd0;
     clk_10M <= 1'b0;
 end
 else if(cnt == 5'd5)		//5分频,系统时钟每翻转5次,clk_10M翻转一次
 begin
     cnt <= 5'd0;
     clk_10M <= ~clk_10M;
 end
 else
     cnt <= cnt + 1'b1;
endmodule
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

3.ADC采样模块:

module ADC9280(
						//input
						sys_clk,		//输入系统时钟50MHz
						data_ad,		//输入AD9280转化的8位数字信号
						
						//output
						pre_data		//输出采样到的8位电压信号
					);

input sys_clk;					
input [7:0] data_ad;

output [7:0] pre_data;

reg [7:0]addata;

always @(posedge sys_clk)
begin
 addata <= data_ad;
end 

assign pre_data = addata;	
endmodule			
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

4.数据处理模块:
本次实验精度为0.01V,使用四位数码管作为显示(有一位为负号)。将AD9280的8位BCD数据按低四位与高四位分开编码,得到各自12位宽的数据,对两个编码进行相加,如代码中的cout[11:0] = L[11:0] + H[11:0],这里注意,高四位[11:8]、中四位[7:4]、低四位[3:0]。
假如AD9280得到的数据是8’b0110_1010转成16进制为8’h6a,从代码中可以看到,低四位4’ha:L <= 12’h050;高四位4’h6:H <= 12’h480;H+L = 12’h4D0,中位为D,大于9,向高位产生进位C1= 1,中位需要加6得到3,故cout[11:0]=12’h530,电压也就是5.30V,然后把530送给数码显示译码模块进行处理并显示。同理如果低位相加大于9也得向中位进位C0,此时的低位要变为加6后的值。

module data_ad_pro(
							//input
							sys_clk,		//输入系统时钟50MHz
							rst_n,		//输入复位信号
							pre_data,	//输入AD采样模块传来的数据
                        
							//output
							cout			//输出处理后的12位电压数据
                  );
input sys_clk;							
input rst_n;
input [7:0] pre_data;

output [11:0] cout;

//由于AD9280限制,需作电压转化,Vin = 5*Vad - 5
/************************************/
//低四位BCD编码
reg [11:0] L;
always @ (posedge sys_clk)
     case(pre_data[3:0]) //L = n * 12'h005(n=1、2、3、、f)
         4'h1:        L <= 12'h005;
         4'h2:        L <= 12'h010;
         4'h3:        L <= 12'h015;
         4'h4:        L <= 12'h020;
         4'h5:        L <= 12'h025;
         4'h6:        L <= 12'h030;
         4'h7:        L <= 12'h035;
         4'h8:        L <= 12'h040;
         4'h9:        L <= 12'h045;
         4'ha:        L <= 12'h050;
         4'hb:        L <= 12'h055;
         4'hc:        L <= 12'h060;
         4'hd:        L <= 12'h065;
         4'he:        L <= 12'h070;
         4'hf:        L <= 12'h075;
         default:     L <= 12'h000;
     endcase
 
/************************************/
//高四位BCD编码
reg [11:0] H;
always @ (posedge sys_clk)
     case(pre_data[7:4])  //H = n * 12'h080(n=1、2、3、、f)
         4'h1:        H <= 12'h080;  
         4'h2:        H <= 12'h160;  
         4'h3:        H <= 12'h240;  
         4'h4:        H <= 12'h320;  
         4'h5:        H <= 12'h400;  
         4'h6:        H <= 12'h480;  
         4'h7:        H <= 12'h560;  
         4'h8:        H <= 12'h640;  
         4'h9:        H <= 12'h720;  
         4'ha:        H <= 12'h800;  
         4'hb:        H <= 12'h880; 
         4'hc:        H <= 12'h960; 
         4'hd:        H <= 12'hA40; 
         4'he:        H <= 12'hB20;  
         4'hf:        H <= 12'hC00; 
         default:     H <= 12'h000; 
     endcase
	  
/************************************/
//判断低四位是否大于9并进位
reg c0;
always @ (posedge sys_clk)
begin
    if(H[3:0] + L[3:0] > 4'd9) 
         c0 <= 1;
     else
         c0 <= 0;
 end
 
/************************************/
//判断中间四位是否大于9并进位
reg c1;
always @(posedge sys_clk) 
begin
    if(H[7:4] + L[7:4] > 4'd9) 
         c1 <= 1;
     else
         c1 <= 0;
 end
 
/************************************/ 
//对进位进行计算,因为在0.00V校准的时候,AD默认会读取到1.30V的基准电压,因此需要减去这多出来的电压值
reg [11:0] cout; 
always @(c1 or c0)
begin
     case({c1,c0})
         2'b00: begin 
             cout[11:8] <= H[11:8] + L[11:8] -4'h1;
             cout[7:4] <= H[7:4] + L[7:4] - 4'h3;
             cout[3:0] <= H[3:0] + L[3:0];
         end 
         
         2'b01: begin 
             if((H[7:4] + L[7:4] + 4'b0001) > 9) begin  
                 cout[11:8] <= H[11:8] + L[11:8] + 4'b0001 -4'h1;
                 cout[7:4] <= H[7:4] + L[7:4] + 4'b0111 - 4'h3;//加上6并加上来自低位上的进位
                 cout[3:0] <= H[3:0] + L[3:0]+ 4'b0110;//加上6
             end
             else begin 
                 cout[11:8] <= H[11:8] + L[11:8] -4'h1;
                 cout[7:4] <= H[7:4] + L[7:4] + 4'b0001 - 4'h3;
                 cout[3:0] <= H[3:0] + L[3:0] + 4'b0110;
             end 
         end
         
         2'b10:begin 
             cout[11:8] <= H[11:8] + L[11:8] + 4'b0001 -4'h1;
             cout[7:4] <= H[7:4] + L[7:4] + 4'b0110 - 4'h3;
             cout[3:0] <= H[3:0] + L[3:0];
         end  
         
         2'b11:begin 
             cout[11:8] <= H[11:8] + L[11:8] + 4'b0001 -4'h1;
             cout[7:4] <= H[7:4] + L[7:4] + 4'b0110 - 4'h3;
             cout[3:0] <= H[3:0] + L[3:0] + 4'b0110;
         end 
     endcase
end	
endmodule
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123

5.数码显示译码模块:

module display(
						//input
						sys_clk,		//输系统时钟50MHz
                  rst_n,		//输入复位信号
                  cout,		//输入数据处理模块传来的12位电压数据
                        
                  //output	
                  slec_wei,		//输出数码管位选信号
                  slec_duan		//输出数码管段选信号
              );

input sys_clk;							
input rst_n;
input [11:0] cout;

output [3:0] slec_wei;
output [7:0] slec_duan;

parameter   SEG_NUM0 = 8'b1100_0000,		//数码管显示0
				SEG_NUM1 = 8'b1111_1001,		//数码管显示1
				SEG_NUM2 = 8'b1010_0100,		//数码管显示2
				SEG_NUM3 = 8'b1011_0000,		//数码管显示3
				SEG_NUM4 = 8'b1001_1001,		//数码管显示4
				SEG_NUM5 = 8'b1001_0010,		//数码管显示5
				SEG_NUM6 = 8'b1000_0010,		//数码管显示6
				SEG_NUM7 = 8'b1111_1000,		//数码管显示7
				SEG_NUM8 = 8'b1000_0000,		//数码管显示8
				SEG_NUM9 = 8'b1001_0000;		//数码管显示9

parameter T1MS = 16'd49999; //1ms计数

reg [11:0] data;

/**********************************************/
//将0_10V转为-5_5V
reg flag;		//判断是否为负电压,1表示是,0表示否
always @ (posedge sys_clk)
	if(cout < 12'h500)begin
			flag = 1;
			data = 12'h499 - cout;
	end
	else	begin
			flag = 0;
			data = cout - 12'h500;
	end		
	
/**********************************************/
//1ms计数器
reg [15:0] cnt; 

always @(posedge sys_clk or negedge rst_n)
 if(!rst_n) 
     cnt <= 16'd0;
 else if(cnt == T1MS) 
     cnt <= 16'd0;
 else
     cnt <= cnt + 1'b1;
	  
/**********************************************/
//数码管轮流导通	  
reg [2:0] i;
reg [3:0] slec_wei;
reg [7:0] slec_duan;

always @ (posedge sys_clk or negedge rst_n)
	if( !rst_n )begin
		i <= 4'd0;
		slec_wei <= 4'b0000;
	end
 else
	case( i )
	
	0:	if( cnt == T1MS ) i <= i + 1'b1;
		else begin
			  slec_wei  <= 4'b0111; //第一个数码选通	
			if(flag == 1)
			  slec_duan <= 8'b1011_1111;	//显示负号
			else
			slec_duan <= 8'b1111_1111;	//不显示
		end
			  
	1:	if( cnt == T1MS ) i <= i + 1'b1;
		else begin
			  slec_wei  <= 4'b1011; //第二个数码选通
			  slec_duan <= data0 + 8'b1000_0000;	//第一位是整数位,加上小数点
		end
		
	2:	if( cnt == T1MS ) i <= i + 1'b1;
		else begin
		     slec_wei  <= 4'b1101; //第三个数码选通
			  slec_duan <= data1;
		end
		
	3:	if( cnt == T1MS ) i <= 4'd0;
		else begin
		     slec_wei  <= 4'b1110; //第四个数码选通
		     slec_duan <= data2;
		end
		
	endcase
	  	
/*****************************************/    
reg [7:0] data0;

always @ (posedge sys_clk)
 case(data[11:8])    //进行编码 高
     4'h0: data0 <= SEG_NUM0;
     4'h1: data0 <= SEG_NUM1;
     4'h2: data0 <= SEG_NUM2;
     4'h3: data0 <= SEG_NUM3;
     4'h4: data0 <= SEG_NUM4;
     4'h5: data0 <= SEG_NUM5;
     4'h6: data0 <= SEG_NUM6;
     4'h7: data0 <= SEG_NUM7;
     4'h8: data0 <= SEG_NUM8;
     4'h9: data0 <= SEG_NUM9;
     default:data0 <= SEG_NUM0;
endcase

/*****************************************/    
reg [7:0] data1;

always @ (posedge sys_clk)
case(data[7:4])    //进行编码  中
     4'h0: data1 <= SEG_NUM0;
     4'h1: data1 <= SEG_NUM1;
     4'h2: data1 <= SEG_NUM2;
     4'h3: data1 <= SEG_NUM3;
     4'h4: data1 <= SEG_NUM4;
     4'h5: data1 <= SEG_NUM5;
     4'h6: data1 <= SEG_NUM6;
     4'h7: data1 <= SEG_NUM7;
     4'h8: data1 <= SEG_NUM8;
     4'h9: data1 <= SEG_NUM9;
     default:data1 <= SEG_NUM0;
endcase

/*****************************************/    
reg [7:0] data2;

always @ (posedge sys_clk)
case(data[3:0])    //进行编码  低
     4'h0: data2 <= SEG_NUM0;
     4'h1: data2 <= SEG_NUM1;
     4'h2: data2 <= SEG_NUM2;
     4'h3: data2 <= SEG_NUM3;
     4'h4: data2 <= SEG_NUM4;
     4'h5: data2 <= SEG_NUM5;
     4'h6: data2 <= SEG_NUM6;
     4'h7: data2 <= SEG_NUM7;
     4'h8: data2 <= SEG_NUM8;
     4'h9: data2 <= SEG_NUM9;
     default:data2 <= SEG_NUM0;
 endcase
 
/***********************************************/    
endmodule
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157

四、实验验证:
1.使用ALINX AX301 FPGA cycloneⅣ开发板:
在这里插入图片描述
2.使用ALINX AN108 AD/DA模块:
在这里插入图片描述
在这里插入图片描述
其接口定义:
在这里插入图片描述
3.引脚分配:
在这里插入图片描述
五、实验结果:
正电压:
测试1:
在这里插入图片描述
在这里插入图片描述
测试2:
在这里插入图片描述
在这里插入图片描述
测试3:
在这里插入图片描述
在这里插入图片描述
负电压:
测试1:
在这里插入图片描述
在这里插入图片描述
测试2:
在这里插入图片描述
在这里插入图片描述
测试3:
在这里插入图片描述
在这里插入图片描述
结果评估:
在正电压和负电压的测试下,误差没有超过0.1V,满足课程设计要求,在正电压的时候,误差很小,但是由于算法原因,FPGA逻辑运算的限制,在负电压作减法的时候,会出现一些误差,导致结果没有很精确。但是在误差允许范围内,这次实验是成功的!

六、注意事项:
在烧录前,一旦代码作了任何改动,都一定要编译一次,否则烧录的程序是改动前的程序。在插入AN108模块的时候,一定要对准对应IO口,否则模块AD/DA不起作用。校准电压值一定要进行0.00V校准,否则后面所有的校准都是无效的。VOLTAGE的值不能超过MINVOL和MAXVOL。

本次设计对应资源链接:https://download.csdn.net/download/weixin_43586860/12612981

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/462763
推荐阅读
相关标签
  

闽ICP备14008679号