2021暑假一起练-用ADC设计数字电压表
基于ADC将旋转电位计得到的电压进行转换,然后在用左移加3的方法将二进制数转换成BCD码,最后将电压信息传送到OLED 屏幕上显示出来
标签
FPGA
测试
显示
lll
更新2021-09-11
986

                                                数字电压表

摘要:基于ADC将旋转电位计得到的电压进行转换,然后在用左移加3的方法将二进制数转换成BCD码,最后将电压信息传送到OLED 屏幕上显示出来

1项目背景

项目要求 - 利用ADC制作一个数字电压表

  1. 旋转电位计可以产生0-3.3V的电压
  2. 利用板上的串行ADC对电压进行转换
  3. 将电压值在板上的OLED屏幕上显示出来
  4. 项目原理图

Fvkan66TzIJvsjBV8-zRdZhbwfs0

Fldu2ZBuQdLCHcozn5ZvdOek9NNu

实物图:

FkJWZDpDBikdhB9udXKnrEJ-ofYP

2部分代码展示

ADC信号采集//采用电子森林模板

模数转换器即A/D转换器,或简称ADC,通常是指一个将模拟信号转变为数字信号的电子元件。通常的模数转换器是将一个输入电压信号转换为一个输出的数字信号。由于数字信号本身不具有实际意义,仅仅表示一个相对大小。故任何一个模数转换器都需要一个参考模拟量作为转换的标准,比较常见的参考标准为最大的可转换信号大小。而输出的数字量则表示输入信号相对于参考信号的大小。模数转换一般要经过采样、量化和编码这几个步骤:采样是指用每隔一定时间的信号样值序列来代替原来在时间上连续的信号,也就是在时间上将模拟信号离散化,量化是用有限个幅度值近似原来连续变化的幅度值,把模拟信号的连续幅度变为有限数量的有一定间隔的离散值而编码则是按照一定的规律,把量化后的值用二进制数字表示,然后转换成二值或多值的数字信号流。

module ADC081S101_driver

(

input                    clk,      //系统时钟

input                    rst_n,        //系统复位,低有效

input                    adc_dat,    //SPI总线SDA

output reg            adc_cs,//SPI总线CS

output reg            adc_clk,     //SPI总线SCK

output wire          oled_csn,   //OLCD液晶屏使能

output wire          oled_rst,    //OLCD液晶屏复位

output wire          oled_dcn,  //OLCD数据指令控制

output wire          oled_clk,    //OLCD时钟信号

output wire          oled_dat    //OLCD数据信号

);


reg            adc_done; //ADC采样完成标志

reg [7:0]    adc_data;  //ADC采样数据

reg [7:0] ss ;


parameter HIGH =1;

parameter LOW = 0;


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;

      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//采集电压

BCD转换

量化运算 N = 256 * Vin / Vref,那么逆向运算为Vin = N * Vref / 256,其中Vref = 3.3V,所以Vin = N * 0.0129所以我们需要用FPGA计算adc_data * 0.0129的结果,然后为了使用十进制的显示,先将结果进行BCD转码,然后显示在OLED 屏幕上。为了方便计算直接乘以129,得到的数据经过BCD转码后小数点左移4位即可,将二进制数转换成BCD码的形式,采用左移加三的算法: 1、左移要转换的二进制码1位 2、左移之后,BCD码分别置于百位、十位、个位 3、如果移位后所在的BCD码列大于或等于5,则对该值加3 4、继续左移的过程直至全部移位完成

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];

       ss = shift_reg[35:28];//电压的前2位

      end 

end//BCD

OLED 显示部分

SSD1306驱动的128*32分辨率的OLED屏幕,从功能上可以划分成两部分,驱动芯片电路 和 OLED点阵硬件.我们驱动该OLED屏幕显示,实际是与驱动芯片SSD1306通信,让SSD1306控制OLED点阵显示。SSD1306相当于一个中介所以我们需要了解SSD1306的功能、寄存器、总线、驱动流程等参数或工作方式,根据SSD1306的工作方式通信即可。

FlHgronhbOX3H9KZ1mEOl270Acdp

采用电子森林模板直接将要显示的电压的信息与原模块中的sw相匹配完成屏幕显示

OLED12832 U1

(

      .clk(clk),         

      .rst_n(rst_n),         

      .sw(ss),      //将电压数值传输到sw中.

      .oled_csn (oled_csn),

      .oled_rst(oled_rst),

      .oled_dcn(oled_dcn),

      .oled_clk(oled_clk),     

      .oled_dat(oled_dat)    

);

OLED 主函数

MAIN:begin

                           

                                  if(cnt_main >= 5'd6) cnt_main <= 5'd5;

                                  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 <= "OLED TEST  .    ";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 <= "                ";state <= SCAN; end

                                       5'd5:    begin y_p <= 8'hb0; x_ph <= 8'h15; x_pl <= 8'h00; num <= 5'd 1; char <= sw[7:4]; state <= SCAN; end

                                       5'd6:    begin y_p <= 8'hb0; x_ph <= 8'h16; x_pl <= 8'h00; num <= 5'd 1; char <= sw[3:0]; state <= SCAN; end


                                       default: state <= IDLE;

                                  endcase

                            end

最后附上TOP模块代码:

module ADC081S101_driver
(
input				clk,		//系统时钟
input				rst_n,  	//系统复位,低有效
input				adc_dat,	//SPI总线SDA
output reg			adc_cs,//SPI总线CS
output reg			adc_clk,	//SPI总线SCK
output	wire		oled_csn,	//OLCD液晶屏使能
output	wire		oled_rst,	//OLCD液晶屏复位
output	wire		oled_dcn,	//OLCD数据指令控制
output	wire		oled_clk,	//OLCD时钟信号
output	wire		oled_dat	//OLCD数据信号
);//直接以ADC081S101为主体,其中完成了ADC采集信号并完成BCD转化,并在其中调用例化的OLED模块即可

//OLED例化模块
OLED12832 U1
(
	.clk(clk),		
	.rst_n(rst_n),		
	.sw(ss),//将BCD转化后数据传入sw中		
	.oled_csn (oled_csn),
	.oled_rst(oled_rst),	
	.oled_dcn(oled_dcn),
	.oled_clk(oled_clk),	
	.oled_dat(oled_dat)	
);	

 

3总结和建议

Verilog语言之前从没学过,只是对C语言学了一会但也学的不是很好,自学这个Verilog感觉和C语言有很多相似的地方,但是在学习过程中还是遇到了很多问题就如在例化模块时开始用的reg定义变量但是会报错但是改成wire定义就可以了.虽然勉强完成了这个项目要求但是我感觉自己对Verilog语言还有很多地方不是很理解.搞这个项目让我感觉到了自己还有很多东西都不知道并且自己学过的很多知识都没有灵活的运用比如数电里学的一些关于FPGA的知识,学过了但一学期后需要用到的时候感觉就是基本上都忘了,通过这次的项目活动让我发现了自己在学习上的方式方法有问题导致没有真正学到东西,并且还有很多的知识都不是很理解,所以我需要调整自己的学习状态争取以后能够做到学以致用.

附件下载
DYB.zip
团队介绍
成都信息工程大学
团队成员
李政彤
成都信息工程大学大二学生
评论
0 / 100
查看更多
目录
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2023 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号