2021暑期一起练-用小脚丫fpga完成数字电压表
用小脚丫fpga搭配adc芯片,旋转电位器,oled,一起实现了一个数字电压表的基本功能
标签
嵌入式系统
FPGA
数字逻辑
显示
叶火火
更新2021-09-25
729

一、项目要求

1.旋转电位计可以产生0-3.3V的电压
2.利用板上的串行ADC对电压进行转换
3.将电压值在板上的OLED屏幕上显示出来
 
二、设计思路
 
FhALxBxSLu8s0_sfNHi3gmiaqw8f
如上图所示,整个数字电压表由一个顶层模块和四个功能模块组成。四个功能模块分别是串行ADC驱动模块,数值转换模块、OLED显示模块和数码管显示模块。首先,我们要制作的是一个数字电压表,而电压值是一个模拟量,所以我们需要使用ADC(模数转换器)把模拟信号转换为数字信号。本次使用的板卡带有ADS7868芯片,通过编写驱动程序,我们可以驱动该芯片对电位计电压值进行采样,得到8位的采样数据。之后通过SPI总线与数字FPGA进行通信。当然,采样获得的8位数据是一个相对量度值,范围是0~255,而实际的电压值范围是0~3.3V,所以进行一个乘法运算,256对应3.3V,0对应0V。接下来,我们希望将电压值显示在OLED 屏幕上和数码管上。由于采样数据是二进制表示的,所以显示之前,先要进行数值转换,从bin码转换为bcd码。
 
三、代码展示

顶层模块 voltmeter 本部分综合了所有剩余的模块,控制整个项目的框架,其中ADC采样数据乘以0.0130时可以完整的显示电压表示数,从0.0-3.3V。

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

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

output  			seg1_sel,	//数码管位选
output  	[7:0]	        seg1_led,	//数码管段选
output  			seg2_sel,	//数码管位选
output  	[7:0]	        seg2_led,	//数码管段选

output				oled_csn,	//OLCD液晶屏使能
output				oled_rst,	//OLCD液晶屏复位
output				oled_dcn,	//OLCD数据指令控制
output				oled_clk,	//OLCD时钟信号
output				oled_dat	//OLCD数据信号
);

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.0130),这里我们直接乘以129,得到的数据经过BCD转码后小数点左移4位即可
wire [15:0]	bin_code = adc_data * 16'd130;
wire [19:0]	bcd_code;

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

//Segment led display module
segled 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
); 

OLEDshow u4
(
.clk                (clk            ),		//12MHz系统时钟
.rst_n	            (rst_n          ),	        //系统复位,低有效	
.data		    (bcd_code[19:12]),	
.oled_clk           (oled_clk       ),
.oled_csn           (oled_csn       ),
.oled_dat           (oled_dat       ),
.oled_dcn           (oled_dcn       ),
.oled_rst           (oled_rst       )
);

endmodule

 

ADC采样模块 ADS7868 本模块完全采用了电子森林里ADC采样的实例模块,也是方便了我们程序的编写,直接拿来用就可以了。这里也希望大家能多多挖掘各网站上对自己完成项目有用的知识,很多已经完成了的固定用法的模块我们就可以直接享用前人的成果了,在此也感谢创作者。

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

 

转码模块 bintobcd 本模块负责将ADC提供的二进制代码转为十进制,方便后续模块的显示。

module bintobcd
(
input						rst_n,	//系统复位,低有效
input		[15:0]			bin_code,	//需要进行BCD转码的二进制数据
output	reg	[19:0]			bcd_code	//转码后的BCD码型数据输出
);

/*
此模块为了将ADC采样的数据转换为我们常用的十进制显示而存在,
主要知识涉及数学中不同制式数据的转换,详细原理这里不做介绍,去百度搜索<FPGA 二进制转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

 

数码管显示 segled 用于在两个数码管上显示采集到的ADC数据,这也是可以在电子森林上找到的,不过多解释。

module segled
(
input 		[3:0]	seg_data,	//seg_data input
input				seg_dot,	//segment dot control
output				seg_sel,	//segment com port
output reg	[7:0]	seg_led		//MSB~LSB = DP,G,F,E,D,C,B,A
);

always@(seg_data)
	case(seg_data)
		4'h0: seg_led = {seg_dot,7'h3f};  //  0
		4'h1: seg_led = {seg_dot,7'h06};  //  1
		4'h2: seg_led = {seg_dot,7'h5b};  //  2
		4'h3: seg_led = {seg_dot,7'h4f};  //  3
		4'h4: seg_led = {seg_dot,7'h66};  //  4
		4'h5: seg_led = {seg_dot,7'h6d};  //  5
		4'h6: seg_led = {seg_dot,7'h7d};  //  6
		4'h7: seg_led = {seg_dot,7'h07};  //  7
		4'h8: seg_led = {seg_dot,7'h7f};  //  8
		4'h9: seg_led = {seg_dot,7'h6f};  //  9
		4'ha: seg_led = {seg_dot,7'h77};  //  A
		4'hb: seg_led = {seg_dot,7'h7C};  //  b
		4'hc: seg_led = {seg_dot,7'h39};  //  C
		4'hd: seg_led = {seg_dot,7'h5e};  //  d
		4'he: seg_led = {seg_dot,7'h79};  //  E
		4'hf: seg_led = {seg_dot,7'h71};  //  F
		default: seg_led = {seg_dot,7'h00};
	endcase

assign seg_sel = 1'b0;	//共阴极,使能

endmodule 

 

OLED显示 OLEDshow 负责把采集到的ADC数据显示在OLED屏上。此款开发板的OLED显示屏也是SPI形式通信的,我们可以找到使用它的基础程序,然后按照项目需求,把采集到的十进制电压值显示上去就可以了。此处只提供核心部分修改代码。

			MAIN:begin
				if(cnt_main >= 5'd5) 
					cnt_main <= 5'd5;
				else 
					cnt_main <= cnt_main + 1'b1;
					
				case(cnt_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 <= "voltage:        ";	state <= SCAN; end
					5'd2:	begin y_p <= 8'hb1; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16; char <= "                ";	state <= SCAN; end
					5'd3:	begin y_p <= 8'hb2; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16; char <= "                ";	state <= SCAN; end
					5'd4:	begin y_p <= 8'hb3; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16; char <= "Designed by YERUI";	state <= SCAN; end
					//保留状态机23 因为之前可能会有别的程序的残留显示 需要刷屏清空一下
					5'd5:	begin y_p <= 8'hb0; x_ph <= 8'h15; x_pl <= 8'h00; num <= 5'd5; char <= showhex ; state <= SCAN; end	//D

					default: state <= IDLE;
				endcase
			end
四,实物结果:
 
如下图所示,通过旋转电位器实现了数字电位器,在LED数字管和OLED上显示出了实际电压的变化,具体过程请参看视频。
Fvz2p6xGpWVp_cTOp9Mt7OxEy8lU

五.项目总结:

      完成本次项目很简单,其主要原因是很多有用的、难的代码其实硬禾学堂和电子森林里都已经提供了现成的范例,我们只需要进行封装和适量的修改就可以了。比如说ADC的采集与OLED的通信等,已经为我们提供了很好的例程,很多是可以直接拿来用的,对于一个FPGA和Verilog的初学者来说这是很友好的。通过本次项目的学习,我大致了解了Verilog和FPGA的部分知识,为后面学校开设的课程进行了一个先导性的学习。同时,完成本次项目,还提高了我的信息搜集能力,能够在网上找到很多自己需要的东西,这是很有用的。剩余时间我又完成了项目二,FPGA制作音乐播放器,也从中学到了很多关于这方面的知识。

      通过本次项目,我主要了解和掌握了:

  • ADC及其工作模式;
  • SPI通信模式;
  • OLED的显示模式,看懂FPGA写OLED;
  • 状态机和序列机;

      对于我以后的fpga软件的编程起到了很好的作用。

 
软硬件
电路图
附件下载
VolTable_impl1.jed
VolTable.rar
团队介绍
审核老师您好,我是上海安路信息科技有限公司的一名软件工程师,我叫叶蕊,主要从事EDA软件方面的研发,但对于FPGA的应用缺乏了解,所以希望通过这个机会来提高自己对FPGA应用的了解。
团队成员
叶火火
评论
0 / 100
查看更多
目录
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2023 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号