2021暑假一起练-用小脚丫FPGA做了一个数字电压表
本项目是2021暑假一起练,利用基于Lattice XO2的小脚丫FPGA平台,采用串行ADC制作一个数字电压表,将电压值显示在OLED屏幕上。
标签
FPGA
数字逻辑
Lattice Diamond
Faded0811
更新2021-09-15
1560

基于小脚丫FPGA的数字电压表

 

一、项目要求:

1.旋转电位计可以产生0-3.3V的电压;

2.利用板上的串行ADC对电压进行转换;

3.将电压值在板上的OLED屏幕上显示出来。

 

二、设计思路:

本次使用的硬件为基于Lattice XO2-4000HC FPGA的综合技能训练版,开发环境为Diamond 3.12,编程语言为Verilog。

因为是第一次接触FPGA和Verilog编程

 

我们可以手动操控旋转电位计,然后由ADC电路将产生的模拟电压采集并数字化,再通过ADS7868和bin_to_bcd模块将电压值又二进制码转化为BCD码,再将BCD码输入至OLED和LED模块从而显示电压值,最后完成顶层模块。

 

以下为该项目的思路图和逻辑电路图。

 

思路图:

FtfftXiJf92Iv0buiWiqRoxIvgBJ

 

逻辑电路图:

FhtSdz122SHOgFi2Srohp5XtONVw

 

三、模块代码

在硬禾学堂和电子森林上我找到很多模块的案例,这些案例的代码可以用来借鉴,再通过自己的学习理解,结合项目要求进行修改调整,最终实现了数字电压表的功能。

 

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

 

二进制码转BCD码模块:

该模块是将上面ADC数据采集模块的数据由二进制码转化为BCD码。

module bin_to_bcd

(

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

 

LED显示模块:

本模块是将上面转来的BCD码又转化为两个数码管上的数字显示,即带小数点后一位的电压值。

module Seg_led

(

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显示模块:

该模块主要参考了电子森林:OLED驱动说明及Verilog代码实例其中的OLED驱动模块,并结合本项目的项目要求作出调整,使做出来的数字电压表能读出小数点后三位的电压值。

module OLED12832

(

       input                           clk,         //12MHz系统时钟

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

      

       input             [15:0]      data,              //

      

       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 <= "wzh-------------";state <= SCAN; end

                                                 5'd2:       begin y_p <= 8'hb1; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16; char <= "  Volt meter:   ";state <= SCAN; end

                                                 5'd3:       begin y_p <= 8'hb2; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16; char <= "       .    V   ";state <= SCAN; end

                                                 5'd4:       begin y_p <= 8'hb3; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16; char <= "-------------wzh";state <= SCAN; end                                                                                                                              

                                                 5'd5:       begin y_p <= 8'hb2; x_ph <= 8'h13; x_pl <= 8'h00; num <= 5'd 1; char <=data[15:12]; state <= SCAN; end

                                                 5'd6:       begin y_p <= 8'hb2; x_ph <= 8'h14; x_pl <= 8'h00; num <= 5'd 1; char <=data[11:8]; state <= SCAN; end                                              

                                                 5'd7:       begin y_p <= 8'hb2; x_ph <= 8'h14; x_pl <= 8'h10; num <= 5'd 1; char <=data[7:4]; state <= SCAN; end                    

                                                 5'd8:       begin y_p <= 8'hb2; x_ph <= 8'h15; x_pl <= 8'h00; num <= 5'd 1; char <=data[3:0]; 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

 

顶层模块:

顶层模块通过实例化各个模块,综合了各个模块相对独立的功能,也就是所有底层模块输入的总和,最终完成了数字电压表的运作。

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采样数据

);




wire [15:0]      bin_code = adc_data * 16'd130;

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码型数据输出

);




//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

);




OLED12832 u4

(

.clk                (clk            ),          //12MHz系统时钟

.rst_n                  (rst_n          ),         //系统复位,低有效    

.data                  (bcd_code[19:4]),  

.oled_clk           (oled_clk       ),

.oled_csn           (oled_csn       ),

.oled_dat           (oled_dat       ),

.oled_dcn           (oled_dcn       ),

.oled_rst           (oled_rst       )

);




endmodule

 

四、结果展示

Fu7Opvb7HFVaSj0vM5LSzgFFIH6Y

五、心得体会

这是我第一次接触FPGA,自己其实还是有很多不明白的地方,但FPGA本身就有很好的学习平台,硬禾学堂和电子森林为我的学习提供了很多很好的讲解和案例,我在学习了之后,再在自己的理解基础上,借助其代码和思路进行封装以及符合项目要求的修改,最后完成了本次项目。同时,这也是我第一次接触Verilog编程语言以及运用Lattice Diamond编程软件,通过自己查找学习和别人的指点,我又了解掌握了很多知识,并且Verilog对于我算是一种新的编程语言,要学会转变自己思考的方式,这也是很重要的一点,很感谢硬禾学堂和电子森林这样的平台提供了很好的资源,我自己也是通过这次的项目锻炼了自己,丰富了自己!

附件下载
wzh_fpga.zip
这是整个工程文件
volt_1_impl1.jed
这是jed文件
代码.zip
这是代码
团队介绍
北京理工大学
团队成员
王子豪
学校:北京理工大学 专业:电子信息工程
评论
0 / 100
查看更多
目录
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号