2021年暑假一起练-用adc做了数字电压表
基于adc做了一个数字电压表.实现了OLED. ADS7868的驱动和除法器模块.
标签
FPGA
数字逻辑
显示
57SSG
更新2021-09-10
773

ADC数字电压表

摘要:基于adc做了一个数字电压表.实现了OLED. ADC7868的驱动和除法器模块.

设计目标:

利用ADC制作一个数字电压表

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

项目思路:FPGA从ADC7868处得到二进制值.FPGA将得到的二进制值转换为对应的的电压值.由于小数不好计算.所以得到的电压值是三位的十进制数.再将得到的十进制数通过除法器模块得到个位.十位和百位.接着将得到的个位.十位和百位数显示在OLED上.

实施方案:

1.将ADC7868的数据手册和电子森林中的串行ADC电压计项目综合起来看,完成adc7868的驱动代码.

Fjm1H82dIBHxDU45OoGgXY60qEq0

module ADS7868 (

         input clk,         //系统时钟

         input rst,         //系统复位信号

         output reg adc_cs, //adc片选信号

         output reg adc_clk,//adc芯片同步时钟

         input adc_dat,     //adc芯片输出引脚    

         output reg [8:0] adc_data          //电压对应的二进制值

);

localparam HIGH = 1'd1;

localparam LOW = 1'd0;

reg [7:0] data;              //用于在传输数据时,缓存数据

reg [2:0] cnt_t = 0;           //用于分频器时钟计数

localparam [2:0] cnt_025ms = 3;

reg [4:0] cnt_c = 0;           //数据传输计数

reg clk_div = 1;             //分频器输出

always @(posedge clk or negedge rst) begin

         if(!rst) cnt_t <= 0;

         else begin

                  cnt_t <= cnt_t + 1'd1;

                  if(cnt_t >= cnt_025ms) begin  //clk_div周期为0.5ms,频率为2MHz

                          clk_div <= ~clk_div;

                          cnt_t <= 1'd0;

                  end

         end

end




always @(posedge clk_div or negedge rst) begin

         if (!rst) begin

                  adc_cs <= HIGH;

                  adc_clk <= HIGH;

                  cnt_c <= 1'd0;

         end

         else begin         //adc_clk周期为1ms,频率为1MHz

                  if(cnt_c >= 5'd24) begin cnt_c <= 1'd0; adc_data <= (331 * data) >> 8;end //将从adc芯片得到的二进制数据转换为十进制数据

                  else cnt_c <= cnt_c + 1'd1;

                  case (cnt_c)

                          5'd0: begin adc_clk <= HIGH; adc_cs <= HIGH;end

                          5'd1: begin adc_cs <= LOW; adc_clk <= HIGH;end

                          5'd2,5'd4,5'd6,5'd8,5'd10,5'd12,5'd14,5'd16,5'd18,5'd20,5'd22:

                          begin adc_cs <= LOW;adc_clk <= LOW;end

                          5'd3:begin adc_cs <= LOW; adc_clk <= HIGH;end//0

                          5'd5:begin adc_cs <= LOW; adc_clk <= HIGH;end//1

                          5'd7:begin adc_cs <= LOW; adc_clk <= HIGH;end//2

                          5'd9:begin adc_cs <= LOW; adc_clk <= HIGH; data[7] <= adc_dat;end//3

                          5'd11:begin adc_cs <= LOW; adc_clk <= HIGH; data[6] <= adc_dat;end//4

                          5'd13:begin adc_cs <= LOW; adc_clk <= HIGH; data[5] <= adc_dat;end//5

                          5'd15:begin adc_cs <= LOW; adc_clk <= HIGH; data[4] <= adc_dat;end//6

                          5'd17:begin adc_cs <= LOW; adc_clk <= HIGH; data[3] <= adc_dat;end//7

                          5'd19:begin adc_cs <= LOW; adc_clk <= HIGH; data[2] <= adc_dat;end//8

                          5'd21:begin adc_cs <= LOW; adc_clk <= HIGH; data[1] <= adc_dat;end//9

                          5'd23:begin adc_cs <= LOW; adc_clk <= HIGH; data[0] <= adc_dat;end//10

                          5'd24:begin adc_cs <= HIGH; adc_clk <= HIGH;end

                          default: begin adc_cs <= HIGH; adc_clk <= HIGH;end

                  endcase

         end

end

endmodule

2.因为板子上的OLED的驱动芯片是SSD1306和电子森林中的OLED案例项目的相同,所以可以直接搬过来使用.我们只需要将main状态中的代码修改一下就可以了

// --------------------------------------------------------------------

// >>>>>>>>>>>>>>>>>>>>>>>>> COPYRIGHT NOTICE <<<<<<<<<<<<<<<<<<<<<<<<<

// --------------------------------------------------------------------

// Module: OLED12832

//

// Author: Step

//

// Description: OLED12832_Driver,使用8*8点阵字库,每行显示128/8=16个字符

//

// Web: www.stepfpga.com

//

// --------------------------------------------------------------------

// Code Revision History :

// --------------------------------------------------------------------

// Version: |Mod. Date:   |Changes Made:

// V1.0     |2015/11/11   |Initial ver

// --------------------------------------------------------------------

module OLED12832

(

         input                                   clk,            ////12MHz系统时钟

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




         input [3:0] ones,

         input [3:0] ten,

         input [1:0] hund,

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

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

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

         output      reg                     oled_clk,  //OLCD时钟信号

         output      reg                     oled_dat  //OLCD数据信号

);

         localparam INIT_DEPTH = 16'd25; //LCD初始化的命令的数量

         localparam IDLE = 6'h1, MAIN = 6'h2, INIT = 6'h4, SCAN = 6'h8, WRITE = 6'h10, DELAY = 6'h20;

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

         localparam DATA     = 1'b1, CMD = 1'b0;




        

         reg [7:0] cmd [24:0];

         reg [39:0] mem [122:0];

         reg   [7:0] y_p, x_ph, x_pl;

         reg   [(8*21-1):0] char;

         reg   [7:0] num, char_reg;                                  //

         reg   [4:0] cnt_main, cnt_init, cnt_scan, cnt_write;

         reg   [15:0]       num_delay, cnt_delay, cnt;

         reg   [5:0]        state, state_back;




         always@(posedge clk or negedge rst_n) begin

                  if(!rst_n) begin

                          cnt_main <= 1'b0; cnt_init <= 1'b0; cnt_scan <= 1'b0; cnt_write <= 1'b0;

                          y_p <= 1'b0; x_ph <= 1'b0; x_pl <= 1'b0;

                          num <= 1'b0; char <= 1'b0; char_reg <= 1'b0;

                          num_delay <= 16'd5; cnt_delay <= 1'b0; cnt <= 1'b0;

                          oled_csn <= HIGH; oled_rst <= HIGH; oled_dcn <= CMD; oled_clk <= HIGH; oled_dat <= LOW;

                          state <= IDLE; state_back <= IDLE;

                  end else begin

                          case(state)

                                   IDLE:begin

                                                     cnt_main <= 1'b0; cnt_init <= 1'b0; cnt_scan <= 1'b0; cnt_write <= 1'b0;

                                                     y_p <= 1'b0; x_ph <= 1'b0; x_pl <= 1'b0;

                                                     num <= 1'b0; char <= 1'b0; char_reg <= 1'b0;

                                                     num_delay <= 16'd5; cnt_delay <= 1'b0; cnt <= 1'b0;

                                                     oled_csn <= HIGH; oled_rst <= HIGH; oled_dcn <= CMD; oled_clk <= HIGH; oled_dat <= LOW;

                                                     state <= MAIN; state_back <= MAIN;

                                            end

                                   MAIN:begin

                                                     if(cnt_main >= 5'd8) 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 <= "Voltage:    V   ";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//消除GRAM中我们不想要的部分

                                                             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'h14; x_pl <= 8'h00; num <= 5'd 1; char <= hund; state <= SCAN; end

                                                             5'd6:         begin y_p <= 8'hb0; x_ph <= 8'h14; x_pl <= 8'h08; num <= 5'd 1; char <= "."; state <= SCAN; end

                                                             5'd7:         begin y_p <= 8'hb0; x_ph <= 8'h15; x_pl <= 8'h00; num <= 5'd 1; char <= ten; state <= SCAN; end

                                                             5'd8:         begin y_p <= 8'hb0; x_ph <= 8'h15; x_pl <= 8'h08; num <= 5'd 1; char <= ones; state <= SCAN; end




                                                             default: state <= IDLE;

                                                     endcase

                                            end

                                   INIT:begin        //初始化状态

                                                     case(cnt_init)

                                                             5'd0:         begin oled_rst <= LOW; cnt_init <= cnt_init + 1'b1; end         //复位有效

                                                             5'd1:         begin num_delay <= 16'd25000; state <= DELAY; state_back <= INIT; cnt_init <= cnt_init + 1'b1; end        //延时大于3us

                                                             5'd2:         begin oled_rst <= HIGH; cnt_init <= cnt_init + 1'b1; end  //复位恢复

                                                             5'd3:         begin num_delay <= 16'd25000; state <= DELAY; state_back <= INIT; cnt_init <= cnt_init + 1'b1; end        //延时大于220us

                                                             5'd4:         begin

                                                                                        if(cnt>=INIT_DEPTH) begin     //当25条指令及数据发出后,配置完成

                                                                                                cnt <= 1'b0;

                                                                                                cnt_init <= cnt_init + 1'b1;

                                                                                        end else begin

                                                                                                cnt <= cnt + 1'b1; num_delay <= 16'd5;

                                                                                                oled_dcn <= CMD; char_reg <= cmd[cnt]; state <= WRITE; state_back <= INIT;

                                                                                        end

                                                                               end

                                                             5'd5:         begin cnt_init <= 1'b0; state <= MAIN; end    //初始化完成,返回MAIN状态

                                                             default: state <= IDLE;

                                                     endcase

                                            end

                                   SCAN:begin     //刷屏状态,从RAM中读取数据刷屏

                                                     if(cnt_scan == 5'd11) begin

                                                             if(num) cnt_scan <= 5'd3;

                                                             else cnt_scan <= cnt_scan + 1'b1;

                                                     end else if(cnt_scan == 5'd12) cnt_scan <= 1'b0;

                                                     else cnt_scan <= cnt_scan + 1'b1;

                                                     case(cnt_scan)

                                                             5'd 0:        begin oled_dcn <= CMD; char_reg <= y_p; state <= WRITE; state_back <= SCAN; end            //定位列页地址

                                                             5'd 1:        begin oled_dcn <= CMD; char_reg <= x_pl; state <= WRITE; state_back <= SCAN; end   //定位行地址低位

                                                             5'd 2:        begin oled_dcn <= CMD; char_reg <= x_ph; state <= WRITE; state_back <= SCAN; end   //定位行地址高位




                                                             5'd 3:        begin num <= num - 1'b1;end

                                                             5'd 4:        begin oled_dcn <= DATA; char_reg <= 8'h00; state <= WRITE; state_back <= SCAN; end   //将5*8点阵编程8*8

                                                             5'd 5:        begin oled_dcn <= DATA; char_reg <= 8'h00; state <= WRITE; state_back <= SCAN; end   //将5*8点阵编程8*8

                                                             5'd 6:        begin oled_dcn <= DATA; char_reg <= 8'h00; state <= WRITE; state_back <= SCAN; end   //将5*8点阵编程8*8

                                                             5'd 7:        begin oled_dcn <= DATA; char_reg <= mem[char[(num*8)+:8]][39:32]; state <= WRITE; state_back <= SCAN; end

                                                             5'd 8:        begin oled_dcn <= DATA; char_reg <= mem[char[(num*8)+:8]][31:24]; state <= WRITE; state_back <= SCAN; end

                                                             5'd 9:        begin oled_dcn <= DATA; char_reg <= mem[char[(num*8)+:8]][23:16]; state <= WRITE; state_back <= SCAN; end

                                                             5'd10:      begin oled_dcn <= DATA; char_reg <= mem[char[(num*8)+:8]][15: 8]; state <= WRITE; state_back <= SCAN; end

                                                             5'd11:      begin oled_dcn <= DATA; char_reg <= mem[char[(num*8)+:8]][ 7: 0]; state <= WRITE; state_back <= SCAN; end

                                                             5'd12:      begin state <= MAIN; end

                                                             default: state <= IDLE;

                                                     endcase

                                            end

                                   WRITE:begin   //WRITE状态,将数据按照SPI时序发送给屏幕

                                                     if(cnt_write >= 5'd17) cnt_write <= 1'b0;

                                                     else cnt_write <= cnt_write + 1'b1;

                                                     case(cnt_write)

                                                             5'd 0:        begin oled_csn <= LOW; end  //9位数据最高位为命令数据控制位

                                                             5'd 1:        begin oled_clk <= LOW; oled_dat <= char_reg[7]; end         //先发高位数据

                                                             5'd 2:        begin oled_clk <= HIGH; end

                                                             5'd 3:        begin oled_clk <= LOW; oled_dat <= char_reg[6]; end

                                                             5'd 4:        begin oled_clk <= HIGH; end

                                                             5'd 5:        begin oled_clk <= LOW; oled_dat <= char_reg[5]; end

                                                             5'd 6:        begin oled_clk <= HIGH; end

                                                             5'd 7:        begin oled_clk <= LOW; oled_dat <= char_reg[4]; end

                                                             5'd 8:        begin oled_clk <= HIGH; end

                                                             5'd 9:        begin oled_clk <= LOW; oled_dat <= char_reg[3]; end

                                                             5'd10:      begin oled_clk <= HIGH; end

                                                             5'd11:      begin oled_clk <= LOW; oled_dat <= char_reg[2]; end

                                                             5'd12:      begin oled_clk <= HIGH; end

                                                             5'd13:      begin oled_clk <= LOW; oled_dat <= char_reg[1]; end

                                                             5'd14:      begin oled_clk <= HIGH; end

                                                             5'd15:      begin oled_clk <= LOW; oled_dat <= char_reg[0]; end         //后发低位数据

                                                             5'd16:      begin oled_clk <= HIGH; end

                                                             5'd17:      begin oled_csn <= HIGH; state <= DELAY; end        //

                                                             default: state <= IDLE;

                                                     endcase

                                            end

                                   DELAY:begin    //延时状态

                                                     if(cnt_delay >= num_delay) begin

                                                             cnt_delay <= 16'd0; state <= state_back;

                                                     end else cnt_delay <= cnt_delay + 1'b1;

                                            end

                                   default:state <= IDLE;

                          endcase

                  end

         end

//OLED配置指令数据

         always@(posedge rst_n)

                  begin

                          cmd[ 0] = {8'hae};

                          cmd[ 1] = {8'h00};

                          cmd[ 2] = {8'h10};

                          cmd[ 3] = {8'h00};

                          cmd[ 4] = {8'hb0};

                          cmd[ 5] = {8'h81};

                          cmd[ 6] = {8'hff};

                          cmd[ 7] = {8'ha1};

                          cmd[ 8] = {8'ha6};

                          cmd[ 9] = {8'ha8};

                          cmd[10] = {8'h1f};

                          cmd[11] = {8'hc8};

                          cmd[12] = {8'hd3};

                          cmd[13] = {8'h00};

                          cmd[14] = {8'hd5};

                          cmd[15] = {8'h80};

                          cmd[16] = {8'hd9};

                          cmd[17] = {8'h1f};

                          cmd[18] = {8'hda};

                          cmd[19] = {8'h00};

                          cmd[20] = {8'hdb};

                          cmd[21] = {8'h40};

                          cmd[22] = {8'h8d};

                          cmd[23] = {8'h14};

                          cmd[24] = {8'haf};

                  end




         //5*8点阵字库数据

         always@(posedge rst_n)

                  begin

                          mem[  0] = {8'h3E, 8'h51, 8'h49, 8'h45, 8'h3E};   // 48  0

                          mem[  1] = {8'h00, 8'h42, 8'h7F, 8'h40, 8'h00};   // 49  1

                          mem[  2] = {8'h42, 8'h61, 8'h51, 8'h49, 8'h46};   // 50  2

                          mem[  3] = {8'h21, 8'h41, 8'h45, 8'h4B, 8'h31};   // 51  3

                          mem[  4] = {8'h18, 8'h14, 8'h12, 8'h7F, 8'h10};   // 52  4

                          mem[  5] = {8'h27, 8'h45, 8'h45, 8'h45, 8'h39};   // 53  5

                          mem[  6] = {8'h3C, 8'h4A, 8'h49, 8'h49, 8'h30};   // 54  6

                          mem[  7] = {8'h01, 8'h71, 8'h09, 8'h05, 8'h03};   // 55  7

                          mem[  8] = {8'h36, 8'h49, 8'h49, 8'h49, 8'h36};   // 56  8

                          mem[  9] = {8'h06, 8'h49, 8'h49, 8'h29, 8'h1E};   // 57  9

                          mem[ 10] = {8'h7C, 8'h12, 8'h11, 8'h12, 8'h7C};   // 65  A

                           mem[ 11] = {8'h7F, 8'h49, 8'h49, 8'h49, 8'h36};   // 66  B

                          mem[ 12] = {8'h3E, 8'h41, 8'h41, 8'h41, 8'h22};   // 67  C

                          mem[ 13] = {8'h7F, 8'h41, 8'h41, 8'h22, 8'h1C};   // 68  D

                          mem[ 14] = {8'h7F, 8'h49, 8'h49, 8'h49, 8'h41};   // 69  E

                          mem[ 15] = {8'h7F, 8'h09, 8'h09, 8'h09, 8'h01};   // 70  F




                          mem[ 32] = {8'h00, 8'h00, 8'h00, 8'h00, 8'h00};   // 32  sp

                          mem[ 33] = {8'h00, 8'h00, 8'h2f, 8'h00, 8'h00};   // 33  ! 

                          mem[ 34] = {8'h00, 8'h07, 8'h00, 8'h07, 8'h00};   // 34 

                          mem[ 35] = {8'h14, 8'h7f, 8'h14, 8'h7f, 8'h14};   // 35  #

                          mem[ 36] = {8'h24, 8'h2a, 8'h7f, 8'h2a, 8'h12};   // 36  $

                          mem[ 37] = {8'h62, 8'h64, 8'h08, 8'h13, 8'h23};   // 37  %

                          mem[ 38] = {8'h36, 8'h49, 8'h55, 8'h22, 8'h50};   // 38  &

                          mem[ 39] = {8'h00, 8'h05, 8'h03, 8'h00, 8'h00};   // 39  '

                          mem[ 40] = {8'h00, 8'h1c, 8'h22, 8'h41, 8'h00};   // 40  (

                          mem[ 41] = {8'h00, 8'h41, 8'h22, 8'h1c, 8'h00};   // 41  )

                          mem[ 42] = {8'h14, 8'h08, 8'h3E, 8'h08, 8'h14};   // 42  *

                          mem[ 43] = {8'h08, 8'h08, 8'h3E, 8'h08, 8'h08};   // 43  +

                          mem[ 44] = {8'h00, 8'h00, 8'hA0, 8'h60, 8'h00};   // 44  ,

                          mem[ 45] = {8'h08, 8'h08, 8'h08, 8'h08, 8'h08};   // 45  -

                          mem[ 46] = {8'h00, 8'h60, 8'h60, 8'h00, 8'h00};   // 46  .

                          mem[ 47] = {8'h20, 8'h10, 8'h08, 8'h04, 8'h02};   // 47  /

                          mem[ 48] = {8'h3E, 8'h51, 8'h49, 8'h45, 8'h3E};   // 48  0

                          mem[ 49] = {8'h00, 8'h42, 8'h7F, 8'h40, 8'h00};   // 49  1

                          mem[ 50] = {8'h42, 8'h61, 8'h51, 8'h49, 8'h46};   // 50  2

                          mem[ 51] = {8'h21, 8'h41, 8'h45, 8'h4B, 8'h31};   // 51  3

                          mem[ 52] = {8'h18, 8'h14, 8'h12, 8'h7F, 8'h10};   // 52  4

                          mem[ 53] = {8'h27, 8'h45, 8'h45, 8'h45, 8'h39};   // 53  5

                          mem[ 54] = {8'h3C, 8'h4A, 8'h49, 8'h49, 8'h30};   // 54  6

                          mem[ 55] = {8'h01, 8'h71, 8'h09, 8'h05, 8'h03};   // 55  7

                          mem[ 56] = {8'h36, 8'h49, 8'h49, 8'h49, 8'h36};   // 56  8

                          mem[ 57] = {8'h06, 8'h49, 8'h49, 8'h29, 8'h1E};   // 57  9

                          mem[ 58] = {8'h00, 8'h36, 8'h36, 8'h00, 8'h00};   // 58  :

                          mem[ 59] = {8'h00, 8'h56, 8'h36, 8'h00, 8'h00};   // 59  ;

                          mem[ 60] = {8'h08, 8'h14, 8'h22, 8'h41, 8'h00};   // 60  <

                          mem[ 61] = {8'h14, 8'h14, 8'h14, 8'h14, 8'h14};   // 61  =

                          mem[ 62] = {8'h00, 8'h41, 8'h22, 8'h14, 8'h08};   // 62  >

                          mem[ 63] = {8'h02, 8'h01, 8'h51, 8'h09, 8'h06};   // 63  ?

                          mem[ 64] = {8'h32, 8'h49, 8'h59, 8'h51, 8'h3E};   // 64  @

                          mem[ 65] = {8'h7C, 8'h12, 8'h11, 8'h12, 8'h7C};   // 65  A

                          mem[ 66] = {8'h7F, 8'h49, 8'h49, 8'h49, 8'h36};   // 66  B

                          mem[ 67] = {8'h3E, 8'h41, 8'h41, 8'h41, 8'h22};   // 67  C

                          mem[ 68] = {8'h7F, 8'h41, 8'h41, 8'h22, 8'h1C};   // 68  D

                          mem[ 69] = {8'h7F, 8'h49, 8'h49, 8'h49, 8'h41};   // 69  E

                          mem[ 70] = {8'h7F, 8'h09, 8'h09, 8'h09, 8'h01};   // 70  F

                          mem[ 71] = {8'h3E, 8'h41, 8'h49, 8'h49, 8'h7A};   // 71  G

                          mem[ 72] = {8'h7F, 8'h08, 8'h08, 8'h08, 8'h7F};   // 72  H

                          mem[ 73] = {8'h00, 8'h41, 8'h7F, 8'h41, 8'h00};   // 73  I

                          mem[ 74] = {8'h20, 8'h40, 8'h41, 8'h3F, 8'h01};   // 74  J

                          mem[ 75] = {8'h7F, 8'h08, 8'h14, 8'h22, 8'h41};   // 75  K

                          mem[ 76] = {8'h7F, 8'h40, 8'h40, 8'h40, 8'h40};   // 76  L

                          mem[ 77] = {8'h7F, 8'h02, 8'h0C, 8'h02, 8'h7F};   // 77  M

                          mem[ 78] = {8'h7F, 8'h04, 8'h08, 8'h10, 8'h7F};   // 78  N

                          mem[ 79] = {8'h3E, 8'h41, 8'h41, 8'h41, 8'h3E};   // 79  O

                          mem[ 80] = {8'h7F, 8'h09, 8'h09, 8'h09, 8'h06};   // 80  P

                          mem[ 81] = {8'h3E, 8'h41, 8'h51, 8'h21, 8'h5E};   // 81  Q

                          mem[ 82] = {8'h7F, 8'h09, 8'h19, 8'h29, 8'h46};   // 82  R

                          mem[ 83] = {8'h46, 8'h49, 8'h49, 8'h49, 8'h31};   // 83  S

                          mem[ 84] = {8'h01, 8'h01, 8'h7F, 8'h01, 8'h01};   // 84  T

                          mem[ 85] = {8'h3F, 8'h40, 8'h40, 8'h40, 8'h3F};   // 85  U

                          mem[ 86] = {8'h1F, 8'h20, 8'h40, 8'h20, 8'h1F};   // 86  V

                          mem[ 87] = {8'h3F, 8'h40, 8'h38, 8'h40, 8'h3F};   // 87  W

                          mem[ 88] = {8'h63, 8'h14, 8'h08, 8'h14, 8'h63};   // 88  X

                          mem[ 89] = {8'h07, 8'h08, 8'h70, 8'h08, 8'h07};   // 89  Y

                          mem[ 90] = {8'h61, 8'h51, 8'h49, 8'h45, 8'h43};   // 90  Z

                          mem[ 91] = {8'h00, 8'h7F, 8'h41, 8'h41, 8'h00};   // 91  [

                          mem[ 92] = {8'h55, 8'h2A, 8'h55, 8'h2A, 8'h55};   // 92  .

                          mem[ 93] = {8'h00, 8'h41, 8'h41, 8'h7F, 8'h00};   // 93  ]

                          mem[ 94] = {8'h04, 8'h02, 8'h01, 8'h02, 8'h04};   // 94  ^

                          mem[ 95] = {8'h40, 8'h40, 8'h40, 8'h40, 8'h40};   // 95  _

                          mem[ 96] = {8'h00, 8'h01, 8'h02, 8'h04, 8'h00};   // 96  '

                          mem[ 97] = {8'h20, 8'h54, 8'h54, 8'h54, 8'h78};   // 97  a

                          mem[ 98] = {8'h7F, 8'h48, 8'h44, 8'h44, 8'h38};   // 98  b

                          mem[ 99] = {8'h38, 8'h44, 8'h44, 8'h44, 8'h20};   // 99  c

                          mem[100] = {8'h38, 8'h44, 8'h44, 8'h48, 8'h7F};   // 100 d

                          mem[101] = {8'h38, 8'h54, 8'h54, 8'h54, 8'h18};   // 101 e

                          mem[102] = {8'h08, 8'h7E, 8'h09, 8'h01, 8'h02};   // 102 f

                          mem[103] = {8'h18, 8'hA4, 8'hA4, 8'hA4, 8'h7C};   // 103 g

                          mem[104] = {8'h7F, 8'h08, 8'h04, 8'h04, 8'h78};   // 104 h

                          mem[105] = {8'h00, 8'h44, 8'h7D, 8'h40, 8'h00};   // 105 i

                          mem[106] = {8'h40, 8'h80, 8'h84, 8'h7D, 8'h00};   // 106 j

                          mem[107] = {8'h7F, 8'h10, 8'h28, 8'h44, 8'h00};   // 107 k

                          mem[108] = {8'h00, 8'h41, 8'h7F, 8'h40, 8'h00};   // 108 l

                          mem[109] = {8'h7C, 8'h04, 8'h18, 8'h04, 8'h78};   // 109 m

                          mem[110] = {8'h7C, 8'h08, 8'h04, 8'h04, 8'h78};   // 110 n

                          mem[111] = {8'h38, 8'h44, 8'h44, 8'h44, 8'h38};   // 111 o

                          mem[112] = {8'hFC, 8'h24, 8'h24, 8'h24, 8'h18};   // 112 p

                          mem[113] = {8'h18, 8'h24, 8'h24, 8'h18, 8'hFC};   // 113 q

                          mem[114] = {8'h7C, 8'h08, 8'h04, 8'h04, 8'h08};   // 114 r

                          mem[115] = {8'h48, 8'h54, 8'h54, 8'h54, 8'h20};   // 115 s

                          mem[116] = {8'h04, 8'h3F, 8'h44, 8'h40, 8'h20};   // 116 t

                          mem[117] = {8'h3C, 8'h40, 8'h40, 8'h20, 8'h7C};   // 117 u

                          mem[118] = {8'h1C, 8'h20, 8'h40, 8'h20, 8'h1C};   // 118 v

                          mem[119] = {8'h3C, 8'h40, 8'h30, 8'h40, 8'h3C};   // 119 w

                          mem[120] = {8'h44, 8'h28, 8'h10, 8'h28, 8'h44};   // 120 x

                          mem[121] = {8'h1C, 8'hA0, 8'hA0, 8'hA0, 8'h7C};   // 121 y

                          mem[122] = {8'h44, 8'h64, 8'h54, 8'h4C, 8'h44};   // 122 z

                  end
endmodule

3.除法器模块思路和代码

Fk6nyCHlbSZvb1Hw6fN8BBNC6mVS

module divi (

    input [8:0] dived,//被除数9位,最大为512 大于 331

    input [3:0] div,//除数最大为15,大于10

    output reg [5:0] res,//除法结果整数部分

    output reg  [3:0] rem//除法结果余数部分

);

//由于此次除数已定为10,所以我就没有从最高位一位一位的算,而是最高四位开始算,如果想要更好的适应性,应从最高位开始算.

//此代码缺点,如果除数小于等于为7,那么计算结果可能是错的.这就是没有从最高位开始一位一位计算导致的.

//如果你的被除数大于512可以将被除数的位数增大来使用.

reg [5:0] res_reg = 1'd0;//最大为64,大于33

reg [4:0] buffer = 0;




always @(dived) begin

         buffer = 5'b0;//初始化

         rem = dived % div;

    res_reg = (dived[8:5] >= div)?1:0;

    buffer = dived[8:5] % div;




         buffer = {buffer[3:0],dived[4]};

//    buffer = (buffer << 1) + dived[4];

    res_reg = res_reg << 1;

    res_reg = res_reg | ((buffer >= div)?1:0);

    buffer = buffer % div;




    buffer = {buffer[3:0],dived[3]};

    res_reg = res_reg << 1;

    res_reg = res_reg | ((buffer >= div)?1:0);

    buffer = buffer % div;




    buffer = {buffer[3:0],dived[2]};

    res_reg = res_reg << 1;

    res_reg = res_reg | ((buffer >= div)?1:0);

    buffer = buffer % div;




         buffer = {buffer[3:0],dived[1]};

    res_reg = res_reg << 1;

    res_reg = res_reg | ((buffer >= div)?1:0);

    buffer = buffer % div;




    buffer = {buffer[3:0],dived[0]};

    res_reg = res_reg << 1;

    res_reg = res_reg | ((buffer >= div)?1:0);

    res = res_reg;

end

endmodule

顶层模块代码:

module vol_measure(
	input clk,         //系统时钟
	input rst,         //系统复位信号
	output  adc_cs, //adc片选信号
	output  adc_clk,//adc芯片同步时钟
	input adc_dat,     //adc芯片输出引脚	
	output	oled_csn,  //OLED片选信号
	output	oled_rst,  //OLCD复位信号
	output	oled_dcn,  //OLCD命令/数据控制信号
	output	oled_clk,  //OLCD同步时钟信号
	output	oled_dat   //OLCD数据传输信号
);
//ADC 
wire [8:0] adc_data;
//OLED
wire [3:0] ones;
wire [3:0] ten;
wire [1:0] hund;
//divide
wire [5:0] buf1;


ADS7868 u1( //从ADS7868得到三位数的十进制电压值
	.clk (clk),
	.rst (rst),
	.adc_cs (adc_cs),
	.adc_clk (adc_clk),
	.adc_dat (adc_dat),
	.adc_data (adc_data)//三位十进制电压值
);
OLED12832 u2( //调用OLED显示电压值
	.clk (clk),
	.rst_n (rst),
	.ones (ones),
	.ten (ten),
	.hund (hund),
	.oled_csn (oled_csn),
	.oled_rst (oled_rst),
	.oled_dcn (oled_dcn),
	.oled_clk (oled_clk),
	.oled_dat (oled_dat)
);
divi u3( 
	.dived (adc_data),
	.div (4'd10),
	.res (buf1),//buf1为adc_data除10后的整数值
	.rem (ones)//ones 为adc_data的个位值
);
divi u4(//hund
	.dived ({3'b0,buf1}),
	.div (4'd10),
	.res (hund),//adc_data的百位值
	.rem (ten)//adc_data的十位值
);
endmodule

信号引脚对应图:

FoYNLPRNDgYYUMzSAbUx5lVVA4qa

操作说明:

  1. 将R_ADJ和J3的中间引脚通过跳线帽短接起来.通过旋转RV1旋钮来改变,电阻上的分压,从OLED上观察R_ADJ的电压值.
  2. 将外部小于3V的电压接在J3的中间引脚(注意共地),从OLED上观察外部电压值.
  3. 将A_out与J3的中间引脚通过跳线帽短接起来.可以测出DAC输出的电压. 

实物图:

FqAKV6ldTMq2ox9i-aFFDS2vtXtu

笔记:

1.Verilog的always中不能对wire类型的变量进行赋值,可以对reg类型变量赋值.在进程语句中对reg变量赋值后,在进程外使用assign关键字对wire变量更新.

2.Always进程在满足条件时,会一直运行.模块调用.always属于并行关系.

在testbench中,模块的输入如果是要在进程中自动赋值,那么使用reg类型,而模块的输出就使用wire类型.

3.Verilog中区分大小写.

4.Verilog模块例化端口规则:

输入端口(input):对模块内部而言,必须是wire类型,对模块外部而言,可以是wire也可以是reg.

输出端口(output):对模块内部而言,可以是wire和reg类型,对模块外部必须是wire类型.

输入/输出端口(inout):对模块内外而言都必须是wire类型.

注释:模块外部是指调用模块的模块,模块内部是指被调模块.

5.模块例化是用线将网络连接起来,是实时反应(如果一定要用函数来类比的话,就是一直运行的函数.不过我并不觉得这对理解有太大帮助).

6.报错误时,如果发现逻辑没问题,那么看一下关键字是否写对了.

7.一个寄存器变量,只能在一个always中赋值,不能交叉赋值

8.在没有除法器的FPGA芯片的编程中,Verilog中不能使用除法,否则会出现问题.但可以使用%指令

9.最好不要用一个宽变量来接收窄变量的值,因为二进制高位的值不确定.若没有办法,就用拼接操作符,来将二进制0和窄变量拼接为宽位值再赋给宽变量.

注:这里的窄和宽指的值和变量的二进制位数.

注意数值的表达形式,b.d.h,这三个.当发现计算结果出错时,最先检查数值的表达形式出错没.

参考文献:

  1. 基于FPGA的除法器设计(2019-07-15)

https://blog.csdn.net/weixin_42507186/article/details/95960089

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