2021暑假在家一起练-尚建业-基于STEP-XO2 FPGA设计ADC数字电压表
通过电子森林已有的一些代码,理解基本原理之后,利用他们设计出一个ADC数字电压表
标签
FPGA
显示
尚建业
更新2021-09-15
1695

项目任务需求

  1.旋转电位计可以产生0-3.3V的电压
  2.利用板上的串行ADC对电压进行转换
  3.将电压值在板上的OLED屏幕上显示出来
 
系统实现过程:

       由于是第一次接触FPGA和Verilog,才起步学习了很多FPGA和Verilog的基本知识,随后又通过电子森林给出的一些实例和代码,自己分析逐渐掌握了diamond这个软件和Verilog。

       在与群里同学交流之后我也弄懂了这个项目的设计思路。

下面就是我使用这块开发板所实现的功能:

根据改开发板项目要求,要完成任务需要用到OLED、电位计,SPI接口8位串行ADC 电路

FvlkynHrZhwJjzMQwYixGcoo-FCs

 

实现的思路:

  1、旋转电位计可以由人工完成

  2、由ADC电路对于产生的模拟电压进行数字化

  3、通过bin_to_bcd模块将电压值转化为BCD编码

  4、将BCD编码传至OLED模块并将其显示出来

  5、最后综合整理,完成顶层文件的编写

 

ADC模块实现

   该模块直接使用了电子森林里面的源代码,该模块负责完成ADC数据的采样,并将其传输给后面的模块,便于后面进行BCD码转换。

module ADS7868
(
input				clk,		//系统时钟
input				rst_n,  	//系统复位,低有效

output	reg			adc_cs,		//SPI总线CS
output	reg			adc_clk,	//SPI总线SCK
input				adc_dat,	//SPI总线SDA

output	reg			adc_done,	//ADC采样完成标志
output	reg [7:0]	adc_data	//ADC采样数据
);

localparam	HIGH = 1'b1;
localparam	LOW  = 1'b0;

reg [7:0] cnt; //计数器
always @(posedge clk or negedge rst_n)
	if(!rst_n) cnt <= 1'b0;
	else if(cnt >= 8'd34) cnt <= 1'b0;
	else cnt <= cnt + 1'b1;
	
reg [7:0] data;
always @(posedge clk or negedge rst_n)
	if(!rst_n) begin
		adc_cs <= HIGH; adc_clk <= HIGH; 
		data <= 1'b0; adc_data <= 1'b0; adc_done <= LOW;
	end else case(cnt)
		8'd0 :  begin adc_cs <= HIGH; adc_clk <= HIGH; end
		8'd1 :  begin adc_cs <= LOW;  adc_clk <= HIGH; end
		8'd2,8'd4,8'd6,8'd8,8'd10,8'd12,8'd14,8'd16,
		8'd18,8'd20,8'd22,8'd24,8'd26,8'd28,8'd30,8'd32:	
				begin adc_cs <= LOW;  adc_clk <= LOW;  end
		8'd3 :  begin adc_cs <= LOW;  adc_clk <= HIGH; end 						//0
		8'd5 :  begin adc_cs <= LOW;  adc_clk <= HIGH; end 						//1
		8'd7 :  begin adc_cs <= LOW;  adc_clk <= HIGH; end 						//2
		8'd9 :  begin adc_cs <= LOW;  adc_clk <= HIGH; data[7] <= adc_dat; end 	//3
		8'd11 : begin adc_cs <= LOW;  adc_clk <= HIGH; data[6] <= adc_dat; end	//4
		8'd13 : begin adc_cs <= LOW;  adc_clk <= HIGH; data[5] <= adc_dat; end 	//5
		8'd15 : begin adc_cs <= LOW;  adc_clk <= HIGH; data[4] <= adc_dat; end 	//6
		8'd17 : begin adc_cs <= LOW;  adc_clk <= HIGH; data[3] <= adc_dat; end 	//7
		8'd19 : begin adc_cs <= LOW;  adc_clk <= HIGH; data[2] <= adc_dat; end 	//8
		8'd21 : begin adc_cs <= LOW;  adc_clk <= HIGH; data[1] <= adc_dat; end 	//9
		8'd23 : begin adc_cs <= LOW;  adc_clk <= HIGH; data[0] <= adc_dat; end 	//10
		8'd25 : begin adc_cs <= LOW;  adc_clk <= HIGH; adc_data <= data; end 	//11
		8'd27 : begin adc_cs <= LOW;  adc_clk <= HIGH; adc_done <= HIGH; end 	//12
		8'd29 : begin adc_cs <= LOW;  adc_clk <= HIGH; adc_done <= LOW; end 	//13
		8'd31 : begin adc_cs <= LOW;  adc_clk <= HIGH; end 						//14
		8'd33 : begin adc_cs <= LOW;  adc_clk <= HIGH; end 						//15
		8'd34 : begin adc_cs <= HIGH;  adc_clk <= HIGH; end
		default : begin adc_cs <= HIGH;  adc_clk <= HIGH;  end
	endcase

endmodule

 

bin_to_bcd模块

  该模块是将前面ADC采样的数据转化为BCD编码,便于在OLED屏幕上进行实现,该模块也是使用电子森林已有的代码。

module bin_to_bcd					//此模块为了将ADC采样的数据转换为我们常用的十进制显示而存在
(
input						rst_n,	//系统复位,低有效
input		[15:0]			bin_code,	//需要进行BCD转码的二进制数据
output	reg	[19:0]			bcd_code	//转码后的BCD码型数据输出
);

reg		[35:0]		shift_reg; 

always@(bin_code or rst_n)begin
	shift_reg = {20'h0,bin_code};
	if(!rst_n) bcd_code = 0; 
	else begin 
		repeat(16) begin //循环16次  
			//BCD码各位数据作满5加3操作,
			if (shift_reg[19:16] >= 5) shift_reg[19:16] = shift_reg[19:16] + 2'b11;
			if (shift_reg[23:20] >= 5) shift_reg[23:20] = shift_reg[23:20] + 2'b11;
			if (shift_reg[27:24] >= 5) shift_reg[27:24] = shift_reg[27:24] + 2'b11;
			if (shift_reg[31:28] >= 5) shift_reg[31:28] = shift_reg[31:28] + 2'b11;
			if (shift_reg[35:32] >= 5) shift_reg[35:32] = shift_reg[35:32] + 2'b11;
			shift_reg = shift_reg << 1; 
		end
		bcd_code = shift_reg[35:16];   
	end  
end

endmodule

 

OLED模块

  该模块负责将前面的BCD编码进行显示,该模块电子森林也已给出,但是需要自己修改

wire [19:0] bcd_code;//将BCD数据与oled相连
	wire [7:0] odin1;
	wire [7:0] odin2;
	wire [7:0] odin3;
	wire [7:0] odin4;
    

	assign odin3={4'b0,bcd_code[19:16]}; //实现数据相通
	assign odin2={4'b0,bcd_code[15:12]};
	assign odin1={4'b0,bcd_code[11:8]};		
	assign odin4={4'b0,bcd_code[7:4]};

  在这里我们声明了odin1、2、3、4,用来存放前面的BCD码,并在后面将他们显示在屏幕上

MAIN:begin
						if(cnt_main >= 5'd8) cnt_main <= 5'd2;//将电压显示在第二行,显示到小数点后三位
						else cnt_main <= cnt_main + 1'b1;
						case(cnt_main)	//MAIN状态
							5'd0:	begin state <= INIT; end
							5'd1:	begin y_p <= 8'hb0; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16; char <= "SJY's design     ";state <= SCAN; end
							5'd2:	begin y_p <= 8'hb1; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16; char <= {odin3,8'd46,odin2,odin1,odin4," V "};state <= SCAN; end
							5'd3:	begin y_p <= 8'hb2; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16; char <= " it is correct    ";state <= SCAN; end
							5'd4:	begin y_p <= 8'hb3; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16; char <= "OLED TEST       ";state <= SCAN; end
							
							5'd5:	begin y_p <= 8'hb0; x_ph <= 8'h15; x_pl <= 8'h00; num <= 5'd 1; char <="OLED TEST       "; state <= SCAN; end
							5'd6:	begin y_p <= 8'hb1; x_ph <= 8'h15; x_pl <= 8'h00; num <= 5'd 1; char <= "OLED TEST       "; state <= SCAN; end
							5'd7:	begin y_p <= 8'hb2; x_ph <= 8'h15; x_pl <= 8'h00; num <= 5'd 1; char <= "OLED TEST       "; state <= SCAN; end
							5'd8:	begin y_p <= 8'hb3; x_ph <= 8'h15; x_pl <= 8'h00; num <= 5'd 1; char <= "OLED TEST       "; state <= SCAN; end

							default: state <= IDLE;
						endcase
					end

  在MAIN中,我们将BCD码显示在了第二行,由于声明了4个odin,我们可以实现将电压显示在小数点后三位,比数码管上的电压精确许多。

顶层模块及例化

  由于我是第一次接触FPGA,这个模块我学习了很久,花费了许多时间,并自己编写了OLED模块的例化,其他模块参考了电子森林的代码

module TOP
(
    	clk,		
    	rst_n,   	
    	adc_dat,	
  		adc_cs,		
    	adc_clk,	
      	seg1_sel,	
      	seg1_led,	
     	seg2_sel,	
    	seg2_led,	

    	oled_csn,	
  		oled_rst,	
 		oled_dcn,	
   		oled_clk,	
   		oled_dat	
);

input			clk;		
    input			rst_n;   	
//ADC鎬荤嚎
    input			adc_dat;
    output			adc_cs;
    output			adc_clk;

    output  			seg1_sel;	
    output  	[7:0]	seg1_led;	
    output  			seg2_sel;	
    output  	[7:0]	seg2_led;	

    output			oled_csn;	
    output			oled_rst;	
    output			oled_dcn;	
    output			oled_clk;	
    output			oled_dat;	


wire adc_done;
wire [7:0] adc_data;
//ADC功能,例化
ADS7868 u2
(
.clk				(clk			),	//系统时钟
.rst_n				(rst_n			),	//系统复位,低有效
.adc_cs				(adc_cs			),	//SPI总线CS
.adc_clk			(adc_clk		),	//SPI总线SCK
.adc_dat			(adc_dat		),	//SPI总线SDA
.adc_done			(adc_done		),	//ADC采样完成标志
.adc_data			(adc_data		)	//ADC采样数据
);

//将ADC采样数据按规则转换为电压数据(乘以0.0129),这里我们直接乘以129,得到的数据经过BCD转码后小数点左移4位即可
wire [15:0]	bin_code = adc_data * 16'd129;
wire [19:0]	bcd_code;

//将处理后的ADC数据进行BCD转码,例化
bin_to_bcd u3
(
.rst_n				(rst_n			),	//系统复位,低有效
.bin_code			(bin_code		),	//需要进行BCD转码的二进制数据
.bcd_code			(bcd_code		)	//转码后的BCD码型数据输出
);

OLED12832 u4
 (
    .clk				(clk        ),	
    .rst_n				(rst_n		),	
    .oled_csn	        (oled_csn  ),   
    .oled_rst	        (oled_rst  ),   
	.oled_dcn	        (oled_dcn  ),   
	.oled_clk	        (oled_clk  ),   
   .oled_dat           (oled_dat  ),   
    .bcd_code			(bcd_code	)   
);


//Segment led display module
Seg_led seg[1:0] 
(
.seg_data			(bcd_code[19:12]	),	//seg_data input
.seg_dot			({1'b1,1'b0}		),	//segment dot control
.seg_sel			({seg1_sel,seg2_sel}),	//segment com port
.seg_led			({seg1_led,seg2_led})	//MSB~LSB = DP,G,F,E,D,C,B,A
); 



endmodule

项目总结

      这是我第一次接触FPGA和Verilog语言,因此项目开始前我做了一些准备,看网上的Verilog学习视频,向自己的同学请教,自己分析一些代码实例,为项目开始之后自己独立完成做了铺垫。同时这个过程还锻炼了我的自学能力,加强了我对于Verilog这一语言的   掌握能力,在分析顶层例化中,增强了我的思维能力。

     

完成项目过程中所遇到的问题

   整个项目最困扰我的是各个模块之间数据的交互,可能对于其他同学很简单,但对于刚上手FPGA的我,虽然有电子森林的源代码,但是我并不清楚这之间缺少什么,需要什么才能让他们运转起来实现整个功能。为此我也咨询了之前参加过FPGA的同学,通过与他们一同分析给出的代码,逐渐了解了基本原理,我也就自然而然的清楚了这些代码少的是什么,也顺其自然的将顶层例化编写出来了。

   整体来说本次活动难度并不大,用心就能完成,丰富了我的知识,了解了FPGA,增强了我的信心。同时我也学会了掌握diamond软件的基本能力。

 

项目感悟:

  这次能够参加电子森林的寒假一起练这个项目活动 我觉得很荣幸 也很感谢这个项目让我学到了很多,从什么都没接触过的小萌新,到一步步获取知识,完成项目,收获满满。很感谢这个平台所提供的多种资源,更便于我们去学习和成长。后面也会积极学习,向各位大佬们靠近。

 

 

 

软硬件
电路图
附件下载
Verilog代码.rar
Verilog代码
stepsjy.rar
工程文件
stepsjy_impl1.jed
jed文件
团队介绍
北京理工大学信息与电子学院
团队成员
尚建业
北京理工大学信息与电子学院大四
评论
0 / 100
查看更多
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号