基于小脚丫FPGA实现DDS任意波形发生器/本地控制
2022寒假在家练,基于小脚丫FPGA实现DDS任意波形发生器(本地控制)
标签
FPGA
DDS
2022寒假在家练
上杉绘梨衣
更新2022-03-03
北京理工大学
1116

项目介绍

1 通过板上的高速DAC(10bits/125Msps)配合FPGA内部DDS的逻辑,
生成波形可调(正弦波、三角波、方波)、频率可调(DC-)、幅度
可调的波形


2 生成模拟信号的频率范围为DC-20MHz,调节精度为1Hz

3 生成模拟信号的幅度为最大1Vpp,调节范围为0.1V-1V

4 在OLED上显示当前波形的形状、波形的频率以及幅度

5 利用板上旋转编码器和按键能够对波形进行切换、进行参数调节

设计思路

本次实验中使用的模块包括OLED显示模块,DDS模块,旋钮模块,分频器模块,锁相环模块。

Fmv4LR23VhSmQXNMXKB7QWa_HCV6

硬件介绍

本次使用的是硬禾学堂提供的基于小脚丫FPGA的电赛训练平台。使用的综合软件是Lattic Diamond。

FqJA6G1aC5VR69gnw4aRhk9aVlZi

由于疫情原因对于波形检测使用了硬禾学堂提供的便携示波器

Fj1ATL6SAFclWWPm_wtjH7yzTsMO

实现的功能以及图片展示

FvcyzFgrwIp1WB6M_6kNNHQwENz8

FuItriVBUdMzXkNuRKCFwY2_X2QG

Fu2WN6yFdMQ-UdvUT4tm8bcl7HtZ

FoGn8GubRTz0jdggrcmXqYk4U8nJ

主要代码片段及说明

顶层模块  

通过顶层代码连接各个模块并传递参数。Verilog部分代码如下:

module TOP_1(
    input                    clk_in,        //系统时钟
    input                    rst_n_in,    //系统复位,低有效
    input                    key_a,            //旋转编码器A管脚
    input                    key_b,            //旋转编码器B管脚
    input                    change,    //切换按钮 频率 幅度
    input                   way, //切换按钮 波形图像

    output                oled_rst,    //OLCD液晶屏复位
    output                oled_dcn,    //OLCD数据指令控制
    output                oled_clk,    //OLCD时钟信号
    output                oled_dat,    //OLCD数据信号
    output                         dac_clk,
    output    [9:0]                dac_data
);//////略///
dds_main u1(
    .clk(clkop),
    .dac_data(dac_data),
    .dac_clk(dac_clk),
    .check(check_1),
    .frequence(F),
    .range(range)
);


posedge_check posedge_check_u5(
    .clk(clk_in),
    .rst_n(rst_n_in),
    .check(way),

    .pos_check(pos)
);    

a120M a120M_u3 (
            .CLKI(clk_in), 
            .CLKOP(clkop)
    );

OLED_12864    OLED_12864_u1 (
            .clk     (clk_in) ,        
            .rst_n   (rst_n_in),    
            .data(oleddata),
            .data1(data1),
            .state1(state1),
            .way(way_1),

            .oled_csn(oled_csn),            
            .oled_rst(oled_rst),            
            .oled_dcn(oled_dcn),    
            .oled_clk(oled_clk),    
            .oled_dat(oled_dat)
);

TEMP_1 TEMP_1_u2 (
    .clk(clk_in),
    .res(rst_n_in),
    .indata1(key1),
    .indata2(key2),
    .change(sum),

    .data(oleddata),
    .state1(state1),
    .data1(data1)
);

XUANNIU_1 XUANNIU_1_u3(
                    .clk(clk_in),            //系统时钟
                    .rst_n(rst_n_in),        //系统复位,低有效
                    .key_a(key_a),            //旋转编码器A管脚
                    .key_b(key_b),            //旋转编码器B管脚
                    .clk_500us(clk_500us),

                    .key1(key1),  
                    .key2(key2),
                    .L_pulse(L_pulse),
                    .R_pulse(R_pulse)

);

DIVIDE_1 #(.WIDTH(32),.N(6)) u4 ( 
            .clk(clk_in),
            .rst_n(rst_n_in),  

            .clkout(clk_500us)
            );

endmodule

OLED模块

通过SPI协议向OLED屏幕书写数据,OLED模块中连接着ASCII码模块,可以通过旋钮模块进行动态调节数据,显示生成波形的频率和幅度。Verilog部分代码如下(部分代码参考电子森林开原代码):

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

    input        [3:0]    sw,
    input                key_a,
    input                 key_b,
    input        [63:0]    data,
    input         [7:0]    data1,
    input                state1,
    input    [1:0]        way,

    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数据信号
);////// 略///
MAIN:begin
6'd0 :    begin state <= INIT; end
6'd1 :    begin y_p <= 8'hb0; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16;  char <= "                ";state <= SCAN; end
6'd2 :    begin y_p <= 8'hb1; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16;  char <= "                ";state <= SCAN; end                                                
6'd3 :    begin y_p <= 8'hb2; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16; char <= "                ";state <= SCAN; end
6'd4 :    begin y_p <= 8'hb3; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16; char <= "                ";state <= SCAN; end
6'd5 :    begin y_p <= 8'hb4; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16; char <= "                ";state <= SCAN; end
6'd6 :    begin y_p <= 8'hb5; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16; char <= "                ";state <= SCAN; end
6'd7 :    begin y_p <= 8'hb6; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16; char <= "                ";state <= SCAN; end
6'd8 :    begin y_p <= 8'hb7; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16; char <= "                ";state <= SCAN; end                            
6'd9 :    begin y_p <= 8'hb0; x_ph <= 8'h10; x_pl <= 8'h00; mem_hanzi_num <= 8'd0; state <= CHINESE; end
6'd10:    begin y_p <= 8'hb0; x_ph <= 8'h11; x_pl <= 8'h00; mem_hanzi_num <= 8'd2; state <= CHINESE; end
6'd11:    begin y_p <= 8'hb0; x_ph <= 8'h12; x_pl <= 8'h00; mem_hanzi_num <= 8'd4; state <= CHINESE; end
6'd12:    begin y_p <= 8'hb0; x_ph <= 8'h13; x_pl <= 8'h00; mem_hanzi_num <= 8'd6; state <= CHINESE; end
6'd13:	  if(way == 2'b00) begin
                y_p <= 8'hb0; x_ph <= 8'h15; x_pl <= 8'h00; mem_sin_num <= 8'd0; state <= SIN;
            end
          else if(way == 2'b01) begin
                y_p <= 8'hb0; x_ph <= 8'h15; x_pl <= 8'h00; mem_sin_num <= 8'd8; state <= SIN;
          end    
          else if(way == 2'b10) begin
                y_p <= 8'hb0; x_ph <= 8'h15; x_pl <= 8'h00; mem_sin_num <= 8'd16; state <= SIN;
          end    
          else if(way == 2'b11) begin
                y_p <= 8'hb0; x_ph <= 8'h15; x_pl <= 8'h00; mem_sin_num <= 8'd24; state <= SIN;
          end    
6'd14:  begin y_p <= 8'hb3; x_ph <= 8'h10; x_pl <= 8'h00; mem_hanzi_num <= 8'd8; state <= CHINESE; end
6'd15:  begin y_p <= 8'hb3; x_ph <= 8'h11; x_pl <= 8'h00; mem_hanzi_num <= 8'd10; state <= CHINESE; end
6'd16:    begin y_p <= 8'hb5; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd10;  char <= {data,"HZ"};state <= SCAN; end        
6'd17:  begin y_p <= 8'hb6; x_ph <= 8'h10; x_pl <= 8'h00; mem_hanzi_num <= 8'd12; state <= CHINESE; end
6'd18:  begin y_p <= 8'hb6; x_ph <= 8'h11; x_pl <= 8'h00; mem_hanzi_num <= 8'd14; state <= CHINESE; end
6'd19:  if(state1 == 1)    begin
            y_p <= 8'hb7; x_ph <= 8'h12; x_pl <= 8'h00; num <= 5'd4;  char <= {"0.",data1,"v"};state <= SCAN;
        end
        else if(state1 == 0)begin
			y_p <= 8'hb7; x_ph <= 8'h12; x_pl <= 8'h00; num <= 5'd4;  char <= {"  ",data1,"v"};state <= SCAN;
        end
6'd21:    begin cnt_main <= 6'd9; end
endcase       

ASCII码转换模块

通过旋钮进行自加自减,最后通过assign进行拼接,完成OLED动态显示过程中的变量参数设计。Verilog部分代码如下:

module TEMP_1
(
    clk,
    res,
    indata1,
    indata2,
    change,

    data,
    state1,
    data1
);////// 略///
always@(posedge clk or negedge res) begin
        if(!res)begin
            temp[0] <= 8'b0011_0000;
            temp[1] <= 8'b0011_0000;
            temp[2] <= 8'b0011_0001;
            temp[3] <= 8'b0011_0010;
            temp[4] <= 8'b0011_0000;
            temp[5] <= 8'b0011_0000;
            temp[6] <= 8'b0011_0000;
            temp[7] <= 8'b0011_0000;
            temp1 <= 8'b0011_0001;
            state1 <= 1;
        end
        else if (change == 1)begin
            if(indata2 == 1'b1)begin
                if(temp[7] > 8'b0011_0000)begin
                    temp[7] <= temp[7] - 1'b1;
                end
                else if(temp[7] == 8'b0011_0000)begin
                    temp[7] <= 8'b0011_1001;
                    if(temp[6] > 8'b0011_0000)begin
                       temp[6] <= temp[6] - 1'b1;
                    end
                    else if(temp[6] == 8'b0011_0000)begin
                        temp[6] <= 8'b0011_1001;
                        if(temp[5] > 8'b0011_0000)begin
                            temp[5] <= temp[5] - 1'b1;
                        end
                        else if(temp[5] == 8'b0011_0000)begin
                            temp[5] <= 8'b0011_1001;
                            if(temp[4] > 8'b0011_0000)begin
                                temp[4] <= temp[4] - 1'b1;
                            end
                            else if(temp[4] == 8'b0011_0000)begin
                                temp[4] <=8'b0011_1001;
                                if(temp[3] > 8'b0011_0000)begin
                                    temp[3] <= temp[3] - 1'b1;
                                end
                                else if(temp[3] == 8'b0011_0000)begin
                                    temp[3] <= 8'b0011_1001;
                                    if(temp[2] > 8'b0011_0000)begin
                                        temp[2] <= temp[2] - 1'b1;
                                    end
                                    else if(temp[2] == 8'b0011_0000)begin
                                        temp[2] <= 8'b0011_1001;
                                        if(temp[1] > 8'b0011_0000)begin
                                            temp[1] <= temp[1] - 1'b1;
                                        end
                                        else if(temp[1] == 8'b0011_0000)begin
                                            temp[1] <= 8'b0011_1001;
                                            if(temp[0] > 8'b0011_0000)begin
                                                temp[0] <= temp[0] - 1'b1;
                                            end
                                        end
                                    end
                                end
                            end
                        end
                    end    
                end
            end////// 略///
assign data1 = temp1;
assign outdata1 = temp[0];
assign outdata2 = temp[1];
assign outdata3 = temp[2];
assign outdata4 = temp[3];
assign outdata5 = temp[4];
assign outdata6 = temp[5];
assign outdata7 = temp[6];
assign outdata8 = temp[7];
assign data = {outdata1,outdata2,outdata3,outdata4,outdata5,outdata6,outdata7,outdata8};

DDS模块

通过相位累加器完成频率的控制Verilog代码如下(部分参考电子森林开源代码):

module dds_main(
    clk,
    frequence,    
    check,
    range,

    dac_data,
    dac_clk
);input clk;input [1:0] check;output [9:0] dac_data;        output dac_clk;input [31:0] frequence;input [3:0] range;
wire [3:0] range_1;
assign range_1 = range;


wire [31:0]    next_phase;
wire [7:0]    phase;
reg [31:0]    a;

// 相位累加器
assign next_phase = (32'h00000024 + frequence * 32'h24) + a;

always@(posedge clk)
    a <= next_phase;

assign phase = a[31:24];
wire [9:0]    sine_data;

lookup_tables u_lookup_tables(phase,check,range_1,sine_data);

assign dac_data = sine_data;
assign dac_clk = ~clk;

endmodule

正弦波 三角波 方波的波表以及幅度调节模块

通过制作的波表使DAC可以输出想要的波形,此模块中还含有波形幅度控制模块,可以配合旋钮进行波形幅度的控制。Verilog部分代码如下(部分参考电子森林开源代码):

module lookup_tables(
    phase, 
    check,
    range,

    sin_out
);////// 略///
always @(sel or sine_table_out or phase)
begin
    if(check == 2'b00) begin
        case(sel)
        2'b00:     begin
                sine_onecycle_amp = 9'h12C+sine_table_out[8:0];
                address = phase[5:0];
                end
        2'b01:     begin
                sine_onecycle_amp = 9'h12C+sine_table_out[8:0];
                address = ~phase[5:0];
                end
        2'b10:     begin
                sine_onecycle_amp = 9'h12C-sine_table_out[8:0];
                address = phase[5:0];
                end
        2'b11:     begin
                sine_onecycle_amp = 9'h12C-sine_table_out[8:0];
                address = ~ phase[5:0];
                end
        endcase
    end
    else if(check == 2'b01) begin
        case(sel)
            2'b00:    begin
                    sine_onecycle_amp = sine_table_out[8:0];
                    address1 = phase[7:0];
                    end
            2'b01:    begin
                    sine_onecycle_amp = sine_table_out[8:0];
                    address1 = phase[7:0];
                    end
            2'b10:    begin
                    sine_onecycle_amp = 9'd315 + sine_table_out[8:0];
                    address1 = phase[7:0];
                    end
            2'b11:    begin
                    sine_onecycle_amp = 9'd315 + sine_table_out[8:0];
                    address1 = phase[7:0];
                    end
        endcase
    end
    else if(check == 2'b10) begin
        case(sel)
            2'b00:    begin
                    sine_onecycle_amp = sine_table_out[8:0];
                    address2 = phase[7:0];
                    end
            2'b01:    begin
                    sine_onecycle_amp = 9'd315 + sine_table_out[8:0];
                    address2 = phase[7:0];
                    end
            2'b10:    begin
                    sine_onecycle_amp = 9'd315 + sine_table_out[8:0];
                    address2 = phase[7:0];
                    end
            2'b11:    begin
                    sine_onecycle_amp = sine_table_out[8:0];
                    address2 = phase[7:0];
                    end
        endcase
    end
end////// 略///
module sin_table(address,address1,address2,sin,check);
output [8:0] sin;
input  [5:0] address;
input  [7:0] address1;
input  [7:0] address2;
input  [1:0] check;

reg [9:0] state;
reg    [8:0] sin;

localparam SIN = 10'h1, Triangle = 10'h2, Square = 10'h4;


always @(address)
    begin
        if(check == 2'b00)
            state <= SIN;
        else if(check == 2'b01)
            state <= Square;
        else if(check == 2'b10)
            state <= Triangle;

        case(state)
            SIN:begin
                    case(address)    
                    6'd0:     sin=9'd    0    ;
                    6'd1:    sin=9'd    7    ;
                    6'd2:    sin=9'd    15    ;
                    6'd3:    sin=9'd    3    ;
                    6'd4:    sin=9'd    29    ;
                    6'd5:    sin=9'd    36    ;
                    6'd6:    sin=9'd    44    ;
                    6'd7:    sin=9'd    51    ;////// 略///
endmodule

旋钮模块以及分频模块

借鉴电子森林开源代码,这里就不过多描述了。

资源占用

FkOxzPIrYh8lH11BiFUuIuZvf34o

遇到的主要难题及解决方法

在DDS模块中,频率的控制和幅度的控制需要统一,所以要同步控制三种波形,需要提前计算或者一步一步尝试。

在OLED模块部分,一开始没有什么思路,通过学习电子森林开源代码了解了SPI协议的书写,完善了动态输出过程中ASCII转换的问题,在后面的学习中发现可以通过左移加三算法进行快速计算,相比我自己写出来的部分要好很多,但由于时间较紧张就没有进行完善。

未来的计划

希望在将来可以多参加FPGA此类活动,增加设计思路和经验,熟悉Verilog的各种使用,用更少的资源完成需要的任务。

 

附件下载
jed可直接烧录文件.zip
Project.zip
RTL.zip
团队介绍
北京理工大学-李绍祎
团队成员
上杉绘梨衣
上杉绘梨衣
评论
0 / 100
查看更多
目录
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2023 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号