“暑期一起练”--利用ADC制作一个数字电压表
利用ADC制作一个数字电压表 利用板上的串行ADC对电压进行转换 将电压值在板上的OLED屏幕上显示出来
标签
FPGA
stepfpga
暑假一起练
Upton
更新2021-09-05
1391

PART1  硬件介绍

  • 核心器件:Lattice LCMXO2-4000HC-4MG132

    • 132脚BGA封装,引脚间距0.5mm,芯片尺寸8mm x 8mm;
    • 上电瞬时启动,启动时间<1ms;
    • 4320个LUT资源, 96Kbit 用户闪存,92Kbit RAM;
    • 2+2路PLL+DLL;
    • 嵌入式功能块(硬核):一路SPI、一路定时器、2路I2C
    • 支持DDR/DDR2/LPDDR存储器;
    • 104个可热插拔I/O;
    • 内核电压2.5-3.3V;
  • 板载资源:

    • 两位7段数码管;
    • 两个RGB三色LED;
    • 8路用户LED;
    • 4路拨码开关;
    • 4路按键;
  • 36个用户可扩展I/O(其中包括一路SPI硬核接口和一路I2C硬核接口)

  • 支持的开发工具Lattice Diamond

  • 支持MICO32/8软核处理器

  • 板上集成FPGA编程器

  • 一路Micro USB接口

     

v2.2%E7%AE%A1%E8%84%9A%E5%88%86%E9%85%8D.png?cache=

 

 

PART2 任务分析

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

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

1.顶层设计

202108202239899.png

2.主要使用的器件分析

  1. SSD1306驱动的oled
  2. ads7868

 

PART3 代码构建

3.1 adc数据采集(ads7868)

3.1.1 ads7868

 

8 位 280KSPS SPI接口的串行ADC,可以采集电位计上的电压

 

参考资料ADS7868 数据表, 产品信息与支持 | TI.com.cn

[简易电压表设计电子森林] (eetree.cn)

3.1.2 ads7868管脚说明

202108202240096.png

从上图可以看出:SDO信号在SCLK的节拍下传输数据,当SCLK下降沿时SDO更新数据输出,当驱动程序编程时我们要在上升沿采样数据可以得到稳定的输出。

3.1.3 ads7868串行通信时序

202108202240832.png

  1. SCLK空闲为高电平,CPOL=1,上升沿(第二个边沿)采样,CPHA = 1 spi模式3
  2. CS信号拉低有效,经过12个时钟完成一次ADC转换并采样,采样回来的数据前3位无效,接下来为MSB~MSB-7(有效数据),再接下来为无效数据。

3.1.4 构建驱动

参考ti官网手册

image-20210820233229738

想要达到280ksps,需要时钟频率为3.4MHz,此项目采用3MHz的adc主频,6M/25=240ksps,所以先构建一个时钟分频module,产生6MHz的频率。

时钟分频

module clock (	clk,rst_n,clkout);

input 	clk,rst_n;
output	clkout;

parameter	WIDTH	= 3;
parameter	N	= 2;

reg 	[WIDTH-1:0]	cnt_p,cnt_n;
reg			clk_p,clk_n;

always @ (posedge clk or negedge rst_n )
begin
    if(!rst_n)
        cnt_p<=0;
    else if (cnt_p==(N-1))
        cnt_p<=0;
    else
        cnt_p<=cnt_p+1;
end

always @ (posedge clk or negedge rst_n)
begin
    if(!rst_n)
        clk_p<=0;
    else if (cnt_p<(N>>1))
        clk_p<=0;
    else
        clk_p<=1;
end

always @ (negedge clk or negedge rst_n)
begin
    if(!rst_n)
        cnt_n<=0;
    else if (cnt_n==(N-1))
        cnt_n<=0;
    else
        cnt_n<=cnt_n+1;
end

always @ (negedge clk)
begin
    if(!rst_n)
        clk_n<=0;
    else if (cnt_n<(N>>1))
        clk_n<=0;
    else
        clk_n<=1;
end
    
assign clkout = (N==1)?clk:(N[0])?(clk_p&clk_n):clk_p;

endmodule

参考资料:[5.时钟分频 [STEP FPGA开源社区]](https://www.stepfpga.com/doc/5.时钟分频)

构建25位计数器

reg [7:0] cnt; 

always @(posedge clkout or negedge rst_n)
    if(!rst_n)
        cnt <= 1'b0;
    else if(cnt >= 8'd24)
        cnt <= 1'b0;
    else
        cnt <= cnt + 1'b1;

构建adc采样代码

always @(posedge clkout or negedge rst_n)
    if(!rst_n)
    begin
        adc_cs <= 1;
        adc_clk <= 1;
    end
    else
        if(En)
        begin
            case(cnt)
                8'd0 :
                begin
                    adc_cs <= 1;
                    adc_clk <= 1;
                end
                8'd1 :
                begin
                    adc_cs <= 0;
                    adc_clk <= 1;
                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 :
                begin
                    adc_cs <= 0;
                    adc_clk <= 0;
                end
                8'd3 :
                begin
                    adc_cs <= 0;
                    adc_clk <= 1;
                end 
                8'd5 :
                begin
                    adc_cs <= 0;
                    adc_clk <= 1;
                end 
                8'd7 :
                begin
                    adc_cs <= 0;
                    adc_clk <= 1;
                end 
                8'd9 :
                begin
                    adc_cs <= 0;
                    adc_clk <= 1;
                    data[7] <= ADC_Din;
                end 
                8'd11 :
                begin
                    adc_cs <= 0;
                    adc_clk <= 1;
                    data[6] <= ADC_Din;
                end 
                8'd13 :
                begin
                    adc_cs <= 0;
                    adc_clk <= 1;
                    data[5] <= ADC_Din;
                end 
                8'd15 :
                begin
                    adc_cs <= 0;
                    adc_clk <= 1;
                    data[4] <= ADC_Din;
                end 
                8'd17 :
                begin
                    adc_cs <= 0;
                    adc_clk <= 1;
                    data[3] <= ADC_Din;
                end 
                8'd19 :
                begin
                    adc_cs <= 0;
                    adc_clk <= 1;
                    data[2] <= ADC_Din;
                end 
                8'd21 :
                begin
                    adc_cs <= 0;
                    adc_clk <= 1;
                    data[1] <= ADC_Din;
                end 
                8'd23 :
                begin
                    adc_cs <= 1;
                    adc_clk <= 1;
                    data[0] <= ADC_Din;
                end 
                
                default :
                begin
                    adc_cs <= 1;
                    adc_clk <= 1;
                end

            endcase
        end

 

3.2 OLED图形化信息显示

 

OLED显示模块借鉴电子森林应用案例及参考代码OLED显示模块相关代码,修改显示模式为垂直显示模式。

使用三贝线上取模软件进行对字库的取模(字体大小为长32,宽16)

image-20210820235308892

参考资料[oled_spi_verilog 电子森林] (eetree.cn)

代码过长,摘取主要部分

MAIN:
begin
    if(cnt_main == 5'd8)
        cnt_main <= 5'd1;
    else
        cnt_main <= cnt_main + 1'b1;
    case(cnt_main)
        5'd0:
            begin
                state <= INIT;
            end

        5'd1:
            begin
                x_ph <= 8'h7f;
                x_pl <= 8'h00;
                y_ph <= 8'h03;
                y_pl <= 8'h00;
                num1 <= 8'd8;
                char <= 8'd88;
                state <= SCAN;
            end
        5'd2:
            begin
                x_ph <= 8'h7f;
                x_pl <= 8'h10;
                y_ph <= 8'h03;
                y_pl <= 8'h00;
                num1 <= 8'd8;
                char <= dac_data[19:16]<<3;
                state <= SCAN;
            end
        5'd3:
            begin
                x_ph <= 8'h7f;
                x_pl <= 8'h20;
                y_ph <= 8'h03;
                y_pl <= 8'h00;
                num1 <= 8'd8;
                char <= 8'd80;
                state <= SCAN;
            end
        5'd4:
            begin
                x_ph <= 8'h7f;
                x_pl <= 8'h30;
                y_ph <= 8'h03;
                y_pl <= 8'h00;
                num1 <= 8'd8;
                char <= dac_data[15:12]<<3;
                state <= SCAN;
            end
        5'd5:
            begin
                x_ph <= 8'h7f;
                x_pl <= 8'h40;
                y_ph <= 8'h03;
                y_pl <= 8'h00;
                num1 <= 8'd8;
                char <= dac_data[11:8]<<3;
                state <= SCAN;
            end
        5'd6:
            begin
                x_ph <= 8'h7f;
                x_pl <= 8'h50;
                y_ph <= 8'h03;
                y_pl <= 8'h00;
                num1 <= 8'd8;
                char <= 8'd96;
                state <= SCAN;
            end
        5'd7:
            begin
                x_ph <= 8'h7f;
                x_pl <= 8'h60;
                y_ph <= 8'h03;
                y_pl <= 8'h00;
                num1 <= 8'd8;
                char <= 8'd88;
                state <= SCAN;
            end

        5'd8:
            begin
                x_ph <= 8'h7f;
                x_pl <= 8'h70;
                y_ph <= 8'h03;
                y_pl <= 8'h00;
                num1 <= 8'd8;
                char <= 8'd88;
                state <= SCAN;
            end
        default:
            state <= IDLE;
    endcase
end

字库

always@(posedge rst_n)
begin
            //0
            mem1[ 0] = {8'h00, 8'h00, 8'h00, 8'h00, 8'h00, 8'hF0, 8'h1F, 8'h00}; 
            mem1[ 1] = {8'h00, 8'hFE, 8'hFF, 8'h00, 8'h00, 8'hFF, 8'hFF, 8'h03}; 
            mem1[ 2] = {8'h80, 8'h07, 8'hC0, 8'h03, 8'hC0, 8'h01, 8'h00, 8'h07};
            mem1[ 3] = {8'hC0, 8'h00, 8'h00, 8'h06, 8'hC0, 8'h00, 8'h00, 8'h0E};
            mem1[ 4] = {8'hC0, 8'h00, 8'h00, 8'h0E, 8'hC0, 8'h00, 8'h00, 8'h06}; 
            mem1[ 5] = {8'hC0, 8'h03, 8'h00, 8'h07, 8'h80, 8'h0F, 8'hE0, 8'h03}; 
            mem1[ 6] = {8'h00, 8'hFF, 8'hFF, 8'h01, 8'h00, 8'hFC, 8'h7F, 8'h00};
            mem1[ 7] = {8'h00, 8'h00, 8'h00, 8'h00, 8'h00, 8'h00, 8'h00, 8'h00};
            //1
            mem1[ 8] = {8'h00, 8'h00, 8'h00, 8'h00, 8'h00, 8'h00, 8'h00, 8'h00}; 
            mem1[ 9] = {8'h00, 8'h00, 8'h00, 8'h00, 8'h00, 8'h1C, 8'h00, 8'h00}; 
            mem1[10] = {8'h00, 8'h0C, 8'h00, 8'h00, 8'h00, 8'h06, 8'h00, 8'h00};
            mem1[11] = {8'h00, 8'h03, 8'h00, 8'h00, 8'hC0, 8'hFF, 8'hFF, 8'h07};
            mem1[12] = {8'hC0, 8'hFF, 8'hFF, 8'h07, 8'hC0, 8'hFF, 8'hFF, 8'h07};
            mem1[13] = {8'h00, 8'h00, 8'h00, 8'h00, 8'h00, 8'h00, 8'h00, 8'h00};
            mem1[14] = {8'h00, 8'h00, 8'h00, 8'h00, 8'h00, 8'h00, 8'h00, 8'h00};
            mem1[15] = {8'h00, 8'h00, 8'h00, 8'h00, 8'h00, 8'h00, 8'h00, 8'h00};
            //2
            mem1[16] = {8'h00, 8'h00, 8'h00, 8'h00, 8'h00, 8'h04, 8'h00, 8'h00};
            mem1[17] = {8'h00, 8'h07, 8'h00, 8'h07, 8'h80, 8'h07, 8'h80, 8'h07};
            mem1[18] = {8'hC0, 8'h03, 8'hC0, 8'h07, 8'hC0, 8'h01, 8'hE0, 8'h07};
            mem1[19] = {8'hC0, 8'h00, 8'h70, 8'h06, 8'hC0, 8'h00, 8'h38, 8'h06};
            mem1[20] = {8'hC0, 8'h00, 8'h1E, 8'h06, 8'hC0, 8'h00, 8'h0F, 8'h06};
            mem1[21] = {8'hC0, 8'hC1, 8'h03, 8'h06, 8'h80, 8'hFF, 8'h01, 8'h06};
            mem1[22] = {8'h80, 8'hFF, 8'h00, 8'h06, 8'h00, 8'h1E, 8'h00, 8'h06};
            mem1[23] = {8'h00, 8'h00, 8'h00, 8'h00, 8'h00, 8'h00, 8'h00, 8'h00};
            //3
            mem1[24] = {8'h00, 8'h00, 8'h00, 8'h00, 8'h00, 8'h00, 8'h40, 8'h00};
            mem1[25] = {8'h00, 8'h06, 8'hC0, 8'h01, 8'h00, 8'h0F, 8'hE0, 8'h03};
            mem1[26] = {8'h80, 8'h03, 8'h80, 8'h07, 8'hC0, 8'h01, 8'h00, 8'h07};
            mem1[27] = {8'hC0, 8'h80, 8'h01, 8'h06, 8'hC0, 8'h80, 8'h01, 8'h0E};
            mem1[28] = {8'hC0, 8'h80, 8'h01, 8'h0E, 8'hC0, 8'hC0, 8'h03, 8'h06};
            mem1[29] = {8'hC0, 8'hC1, 8'h03, 8'h07, 8'h80, 8'h7F, 8'hDF, 8'h03};
            mem1[30] = {8'h80, 8'h7F, 8'hFE, 8'h03, 8'h00, 8'h1E, 8'hFC, 8'h00};
            mem1[31] = {8'h00, 8'h00, 8'h00, 8'h00, 8'h00, 8'h00, 8'h00, 8'h00};
            //4
            mem1[32] = {8'h00, 8'h00, 8'h70, 8'h00, 8'h00, 8'h00, 8'h78, 8'h00};
            mem1[33] = {8'h00, 8'h00, 8'h7E, 8'h00, 8'h00, 8'h00, 8'h7F, 8'h00};
            mem1[34] = {8'h00, 8'hC0, 8'h73, 8'h00, 8'h00, 8'hE0, 8'h71, 8'h00};
            mem1[35] = {8'h00, 8'h70, 8'h70, 8'h00, 8'h00, 8'h3C, 8'h70, 8'h00};
            mem1[36] = {8'h00, 8'h1E, 8'h70, 8'h00, 8'h80, 8'h07, 8'h70, 8'h00};
            mem1[37] = {8'hC0, 8'hFF, 8'hFF, 8'h07, 8'hC0, 8'hFF, 8'hFF, 8'h07};
            mem1[38] = {8'h00, 8'h00, 8'h70, 8'h00, 8'h00, 8'h00, 8'h70, 8'h00};
            mem1[39] = {8'h00, 8'h00, 8'h70, 8'h00, 8'h00, 8'h00, 8'h00, 8'h00};
            //5
            mem1[40] = {8'h00, 8'h00, 8'h00, 8'h00, 8'h00, 8'h80, 8'hC1, 8'h01};
            mem1[41] = {8'h00, 8'hF8, 8'hE1, 8'h03, 8'hC0, 8'hFF, 8'h81, 8'h07};
            mem1[42] = {8'hC0, 8'hCF, 8'h00, 8'h06, 8'hC0, 8'h60, 8'h00, 8'h0E};
            mem1[43] = {8'hC0, 8'h60, 8'h00, 8'h0E, 8'hC0, 8'h60, 8'h00, 8'h0E};
            mem1[44] = {8'hC0, 8'h60, 8'h00, 8'h0E, 8'hC0, 8'hE0, 8'h00, 8'h07};
            mem1[45] = {8'hC0, 8'hC0, 8'h01, 8'h07, 8'hC0, 8'hC0, 8'hE7, 8'h03};
            mem1[46] = {8'hC0, 8'h80, 8'hFF, 8'h01, 8'h00, 8'h00, 8'hFE, 8'h00};
            mem1[47] = {8'h00, 8'h00, 8'h00, 8'h00, 8'h00, 8'h00, 8'h00, 8'h00};
            //6
            mem1[48] = {8'h00, 8'h00, 8'h00, 8'h00, 8'h00, 8'h00, 8'h7C, 8'h00};
            mem1[49] = {8'h00, 8'h00, 8'hFF, 8'h01, 8'h00, 8'hC0, 8'hFF, 8'h03};
            mem1[50] = {8'h00, 8'hF0, 8'h83, 8'h07, 8'h00, 8'hF8, 8'h01, 8'h07};
            mem1[51] = {8'h00, 8'hFE, 8'h00, 8'h06, 8'h00, 8'hCF, 8'h00, 8'h0E};
            mem1[52] = {8'hC0, 8'hC7, 8'h00, 8'h0E, 8'hC0, 8'hC1, 8'h00, 8'h0E};
            mem1[53] = {8'hC0, 8'hC0, 8'h00, 8'h06, 8'h00, 8'hC0, 8'h01, 8'h07};
            mem1[54] = {8'h00, 8'h80, 8'hFF, 8'h03, 8'h00, 8'h00, 8'hFF, 8'h01};
            mem1[55] = {8'h00, 8'h00, 8'hFE, 8'h00, 8'h00, 8'h00, 8'h00, 8'h00};
            //7
            mem1[56] = {8'h00, 8'h00, 8'h00, 8'h00, 8'hC0, 8'h00, 8'h00, 8'h00};
            mem1[57] = {8'hC0, 8'h00, 8'h00, 8'h00, 8'hC0, 8'h00, 8'h00, 8'h00};
            mem1[58] = {8'hC0, 8'h00, 8'h00, 8'h04, 8'hC0, 8'h00, 8'h80, 8'h07};
            mem1[59] = {8'hC0, 8'h00, 8'hF0, 8'h07, 8'hC0, 8'h00, 8'hFE, 8'h01};
            mem1[60] = {8'hC0, 8'h80, 8'h3F, 8'h00, 8'hC0, 8'hE0, 8'h07, 8'h00};
            mem1[61] = {8'hC0, 8'hF8, 8'h00, 8'h00, 8'hC0, 8'h3E, 8'h00, 8'h00};
            mem1[62] = {8'hC0, 8'h0F, 8'h00, 8'h00, 8'hC0, 8'h03, 8'h00, 8'h00};
            mem1[63] = {8'hC0, 8'h01, 8'h00, 8'h00, 8'h00, 8'h00, 8'h00, 8'h00};
            //8
            mem1[64] = {8'h00, 8'h00, 8'h00, 8'h00, 8'h00, 8'h00, 8'hFC, 8'h01};
            mem1[65] = {8'h00, 8'h7F, 8'hFE, 8'h03, 8'h80, 8'h7F, 8'hCF, 8'h07};
            mem1[66] = {8'hC0, 8'hE1, 8'h07, 8'h07, 8'hC0, 8'hC0, 8'h03, 8'h06};
            mem1[67] = {8'hC0, 8'h80, 8'h01, 8'h0E, 8'hC0, 8'h80, 8'h01, 8'h0E};
            mem1[68] = {8'hC0, 8'h80, 8'h01, 8'h0E, 8'hC0, 8'hC0, 8'h03, 8'h06};
            mem1[69] = {8'hC0, 8'hE1, 8'h03, 8'h07, 8'h80, 8'h7F, 8'h8F, 8'h07};
            mem1[70] = {8'h00, 8'h7F, 8'hFE, 8'h03, 8'h00, 8'h1E, 8'hFC, 8'h01};
            mem1[71] = {8'h00, 8'h00, 8'h00, 8'h00, 8'h00, 8'h00, 8'h00, 8'h00};
            //9
            mem1[72] = {8'h00, 8'h00, 8'h00, 8'h00, 8'h00, 8'hFE, 8'h01, 8'h00};
            mem1[73] = {8'h00, 8'hFF, 8'h03, 8'h00, 8'h80, 8'h87, 8'h07, 8'h00};
            mem1[74] = {8'hC0, 8'h01, 8'h07, 8'h08, 8'hC0, 8'h01, 8'h06, 8'h0E};
            mem1[75] = {8'hC0, 8'h00, 8'h86, 8'h0F, 8'hC0, 8'h00, 8'hE6, 8'h03};
            mem1[76] = {8'hC0, 8'h00, 8'hFE, 8'h01, 8'hC0, 8'h01, 8'h7F, 8'h00};
            mem1[77] = {8'hC0, 8'h83, 8'h1F, 8'h00, 8'h80, 8'hFF, 8'h07, 8'h00};
            mem1[78] = {8'h00, 8'hFF, 8'h03, 8'h00, 8'h00, 8'h7E, 8'h00, 8'h00};
            mem1[79] = {8'h00, 8'h00, 8'h00, 8'h00, 8'h00, 8'h00, 8'h00, 8'h00};
        //.
            mem1[80] = {8'h00,8'h00,8'h00,8'h00,8'h00,8'h00,8'h00,8'h00};
            mem1[81] = {8'h00,8'h00,8'h00,8'h00,8'h00,8'h00,8'h00,8'h00};
            mem1[82] = {8'h00,8'h00,8'h00,8'h00,8'h00,8'h00,8'h00,8'h00};
            mem1[83] = {8'h00,8'h00,8'h00,8'h07,8'h00,8'h00,8'h00,8'h07};
            mem1[84] = {8'h00,8'h00,8'h00,8'h07,8'h00,8'h00,8'h00,8'h00};
            mem1[85] = {8'h00,8'h00,8'h00,8'h00,8'h00,8'h00,8'h00,8'h00};
            mem1[86] = {8'h00,8'h00,8'h00,8'h00,8'h00,8'h00,8'h00,8'h00};
            mem1[87] = {8'h00,8'h00,8'h00,8'h00,8'h00,8'h00,8'h00,8'h00};

        //sp
            mem1[88] = {8'h00,8'h00,8'h00,8'h00,8'h00,8'h00,8'h00,8'h00};
            mem1[89] = {8'h00,8'h00,8'h00,8'h00,8'h00,8'h00,8'h00,8'h00};
            mem1[90] = {8'h00,8'h00,8'h00,8'h00,8'h00,8'h00,8'h00,8'h00};
            mem1[91] = {8'h00,8'h00,8'h00,8'h00,8'h00,8'h00,8'h00,8'h00};
            mem1[92] = {8'h00,8'h00,8'h00,8'h00,8'h00,8'h00,8'h00,8'h00};
            mem1[93] = {8'h00,8'h00,8'h00,8'h00,8'h00,8'h00,8'h00,8'h00};
            mem1[94] = {8'h00,8'h00,8'h00,8'h00,8'h00,8'h00,8'h00,8'h00};
            mem1[95] = {8'h00,8'h00,8'h00,8'h00,8'h00,8'h00,8'h00,8'h00};

        //V
            mem1[96]  = {8'hC0,8'h00,8'h00,8'h00,8'hC0,8'h00,8'h00,8'h00};
            mem1[97]  = {8'hC0,8'h3F,8'h00,8'h00,8'hC0,8'h3F,8'h00,8'h00};
            mem1[98]  = {8'hC0,8'hC0,8'h3F,8'h00,8'hC0,8'hC0,8'h3F,8'h00};
            mem1[99]  = {8'h00,8'h00,8'hC0,8'h0F,8'h00,8'h00,8'hC0,8'h0F};
            mem1[100] = {8'h00,8'h00,8'hFC,8'h00,8'h00,8'h00,8'hFC,8'h00};
            mem1[101] = {8'hC0,8'hF0,8'h03,8'h00,8'hC0,8'hF0,8'h03,8'h00};
            mem1[102] = {8'hC0,8'h0F,8'h00,8'h00,8'hC0,8'h0F,8'h00,8'h00};
            mem1[103] = {8'hC0,8'h00,8'h00,8'h00,8'hC0,8'h00,8'h00,8'h00};


end

 

3.3 bin_to_bcd 转码

 

量化运算 N = 256 * Vin / Vref,

那么逆向运算为Vin = N * Vref / 256,其中Vref = 3.3V,所以Vin = N * 0.0129

wire [15:0] bin_code = data * 16'd129;

将二进制数转换成BCD码的形式,采用左移加三的算法:

  1. 左移要转换的二进制码1位
  2. 左移之后,BCD码分别置于百位、十位、个位
  3. 如果移位后所在的BCD码列大于或等于5,则对该值加3
  4. 继续左移的过程直至全部移位完成
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

最后得到20位的数据输出,每4位表示一个BCD码,所以有5位有效数据,这里我们还需要将小数点左移4位,计算出来的数应该是X.XXXX伏特,1个整数位和4个小数位,本代码只显示前3位

第一位数字

  char <= dac_data[19:16]<<3;

第二位数字

  char <= dac_data[15:12]<<3;

第三位数字

  char <= dac_data[11:8]<<3;

 

3.4顶层设计

 

module dac_top(
        input                  clk,
        input                  rst_n,
        input                  ADC_Din,

        output 			       clkout,
        output                 adc_clk,
        output                 adc_cs,
        output                 oled_csn,
        output                 oled_rst,
        output                 oled_dcn,
        output                 oled_clk,
        output                 oled_dat
    );


    wire [19:0]          bcd_code;
    wire                 adc_done;
    wire [7:0]           data;

    clock  clock(
                .clk              (clk           ),
                .rst_n            (rst_n         ),
                .clkout           (clkout        )
           );
    adc_Driver adc_Driver(
                .clkout           (clkout         ),
                .rst_n            (rst_n          ),
                .En               (1'b1           ),
                .ADC_Din          (ADC_Din        ),
                .adc_clk          (adc_clk        ),
                .adc_cs           (adc_cs         ),
                .data             (data           ),
                .adc_done         (adc_done       )
            );
    Control Control(
                .clk              (clk            ),
                .rst_n            (rst_n          ),
                .adc_done         (adc_done       ),
                .data             (data           ),
                .bcd_code         (bcd_code       )
            );
    OLED12832 OLED12832(
                .clkout           (clkout         ),
                .rst_n            (rst_n          ),
                .dac_data         (bcd_code       ),
                .oled_csn         (oled_csn       ),
                .oled_rst         (oled_rst       ),
                .oled_dcn         (oled_dcn       ),
                .oled_clk         (oled_clk       ),
                .oled_dat         (oled_dat       )
            );

endmodule
PART4 项目展示

 

image-20210822230429623

PART5 遇到的问题

1.adc芯片驱动编写问题:

在仔细查阅实例代码和电子森林的示例代码和反复尝试,终于根据数据手册构建出了adc的串行通信代码

通过网上搜寻各种各样的资料可以有效的帮助我们找到解决方案,同时我也体会到和同学一起交流学习也能显著提高自己

2.未使用ip核

 

      oled代码主要参考了寒假一起练项目四 - 电子森林 (eetree.cn)的代码,在此表示感谢

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