基于FPGA(verilog 编写)的DDS信号发生器(指定频率,相移,幅度,数字键盘输入调节)

        时隔两年重新开始写博客,以后也会经常更新,感谢大家的支持!!

        DDS信号发生器采用直接数字频率合成(Direct Digital Synthesis,简称DDS)技术,把信号发生器的频率稳定度、准确度提高到与基准频率相同的水平,并且可以在很宽的频率范围内进行精细的频率调节。采用这种方法设计的信号源可工作于调制状态,可对输出电平进行调节,也可输出各种波形。在某一次课程设计中,需要设计一个DDS信号发生器,以下是题目要求:

基于DDS技术利用硬件描述语言设计并制作一个数字式移相信号发生器。

 (1)基本要求

     a.频率范围:1Hz~4kHz,频率步进为1Hz,输出频率可预置。

     b.A、B两路正弦信号输出,10位输出数据宽度。

     c.相位差范围为0~359°,步进为1.0°,相位差值可预置。

     d.数字显示预置的频率(10进制)、相位差值。

 (2)发挥部分

     a.修改设计,增加幅度控制电路(如可以用一乘法器控制输出幅度)。

     b.输出幅度峰峰值0.1~3.0V,步距0.1V。

首先介绍DDS的原理。如果想要采样一个正弦波,首先需要对正弦波数据进行采样,每周期为 M 个采样点,分别记为 1~M。 对应每次参考时钟 fc , 输出一个采样点,输出图中所示的一个周期的正弦,需要 M 个时钟周期,则输出的波形频率为 fout =fc/M。对于这种情况, 每次时钟到来时,相位累加器加 1 ,则就会在第 i 个时钟周期输出 第 i 个采样点( i = 1~M) ,第 M + 1 个时钟输出第 1 个采样点,以此循坏,这时的相位累加器实 际上是步进为 1 的模 M 计数器。 如果每次时钟到来时, 总是间隔一个采样点输出,即相位累 加器的步进为 2 ,这时在第 i 个周期输出第 2i 个采样点,输出波形如图 1. 2 的波形 b,显然波 形 b 的频率是 a 的 2 倍,即 f b = 2 f a 。

本次采用查表法进行波形数据的读取,调用ROM IP核,ROM IP核调用方法如下:

  1. 首先打开Quartus,新建一个工程(新建工程不过多赘述),左上角Tools->MegaWizard Plug-In Manager 

选择第一个Creat a new custom....,在Memory Compiler 中选择 ROM:1-PORT,右边选择自己的输出文件,名称为rom,文件语言默认选择Verilog,这里建议所有的IP核尽量存放在一个单独的文件夹内,方便后续管理

选择next,输出位宽选择10bits,1024个words,后续一路next后选择ROM的波形数据(.mif),波形数据选择可以使用波形数据生成器(资源链接分享在文章末尾)。然后一路next直到finish。之后可以在模块中例化ROM IP即可。

本次DDS信号发生器的为模块化,自顶向下的层次化设计,具体RTL视图如下:

整体为4个模块,系统时钟分频模块,矩阵键盘模块,DDS信号数据模块,数码管驱动模块。为了将DAC抛出的数据能够有两个不同相位的输出,DA模块有DAch的通道选择,可以使用二选一数据选择器,当DAch为低电平时,抛出未进行相移的波形,当DAch为高电平时,抛出进行相移后的波形。伪代码设计如下,其中Pword为相位控制字,ROMADDR为ROM的寻址信号。

always @(posedge clk or negedge rst) begin
    if(!rst)
        ROMADDR <= 10'd0;
    else if(DAch == 1'b0)                                    //相位控制
        ROMADDR <= frechange[31:22] + Pword;
    else
        ROMADDR <= frechange[31:22];
end

矩阵键盘扫描以及按下一个数字数码管自动移位的代码设计思路,矩阵键盘扫描原理不过多赘述,请参考其他优秀的文章,可以指定一个较大的位宽,比如八位数码管,每位数码管显示的最大数据为9,而显示9则需要4个位宽,则八位数码管需要32个位宽的寄存器,才能保证每一位数据的正常显示,可以使用一个计数器进行按键按下次数的计数,按键按下一下,计数器加1,再按下,计数器再加1,以次类推,对按键计数器进行状态机代码编写,当cnt == 1时,显示第一位数字,当cnt==2时,将之前的数字进行左移,然后将本次输入的数字放到最低位。具体伪代码设计如下:

always@(posedge sys_clk or negedge sys_rstn) begin  // 处理按键事件
    if(!sys_rstn)
        ;// todo
    else 
      case(key_count)
          4'd1:value = {4'd0,4'd0,4'd0,keyboard_val};
          4'd2:begin value[7:4]   <= value[3:0]; value[3:0]     <= keyboard_val;end
          4'd3:begin value[11:8]  <= value[7:4]; value[7:4]     <= value[3:0];value[3:0]     <= keyboard_val;end
          4'd4:begin value[15:12] <= value[11:8];value[11:8]    <= value[7:4]; value[7:4]    <= value[3:0];value[3:0]    <= keyboard_val;end
          4'd5:begin value[19:16] <= value[15:12];value[15:12]  <= value[11:8];value[11:8]   <= value[7:4]; value[7:4]   <= value[3:0];value[3:0] <= keyboard_val;end
          default value <= value;
      endcase
end

第二个较难点就是如何将输入的数字转换成DDS的频率控制、相位控制以及幅度控制。其实这个点不难,熟悉C语言的应该知道,如何取多位数的指定的个位十位百位,这里进行逆运算就可以了,将上述存储在value寄存器中的数据进行运算,具体伪代码如下,具体看对应控制字的赋值。

always @(posedge sys_clk or negedge sys_rstn) begin   // 状态机选择
    if (!sys_rstn) begin   //默认开始调节频率
      Fword   <= 22'd3436426;
      Pword   <= 10'd0; 
      Aword   <= 10'd1023;
      LED_OUT <= 1'b0;
    end
    else
      case (mode_select)
        FwordState:begin      // 频率控制
          if(key_pressed_flag && keyboard_val == KEY_SURE)begin  // 按下确认按键,将对应控制字进行输出
            LED_OUT <= 1'b1;
            Fword   <= (value[19:16]*10000 + value[15:12]*1000 + value[11:8] * 100 + value[7:4]*10 + value[3:0])*100000/1164;
          end
          else
            LED_OUT <=1'b0;
        end
        PwordState:begin    // 相位控制
          if(key_pressed_flag && keyboard_val == KEY_SURE)begin    // 按下确认按键,将对应控制字进行输出
            LED_OUT <= 1'b1;
            Pword   <= (value[11:8] * 100 + value[7:4]*10 + value[3:0])*1023/360;
          end
          else
            LED_OUT <=1'b0;
        end
        AwordState:begin
          if(key_pressed_flag && keyboard_val == KEY_SURE)begin    // 按下确认按键,将对应控制字进行输出
            LED_OUT <= 1'b1;
            Aword   <= (value[7:4]*10 + value[3:0])*1023/33;
          end
          else
            LED_OUT <=1'b0;
        end
        default :
          ;
      endcase
end

整体代码如下,觉得有用的可以三连支持哟,感谢!!!

/*
Author    				: 	 Junhan Lv
Email Address         	:    1322069095@qq.com
Filename             	:    top.v
Data                 	:    2024-9-1
Description           	:    DDS TOP Connection.
Modification History    	:
Data            Author       Version         Change Description
=======================================================
24/9/1        Junhan Lv    1.0              Original
*/


module top (
    input wire          sys_clk,                // 系统时钟
    input wire          sys_rstn,            // 系统复位
    input wire  [1:0]   mode_select,        // 模式选择,通过拨码开关
    input wire  [1:0]   wave_selevt,
    input       [3:0]   row,                  // 矩阵键盘 行
    output wire         LED_OUT,                 
    output wire [3:0]   col,                 // 矩阵键盘 列
    output wire [2:0]   sel,
    output wire [7:0]   seg,
    output wire         DAch,               // 通道选择
    output wire         DAclk,
    output wire         DAwr,
    output wire [9:0]   data_out            // 波形数据输出
);


wire [21:0]value   ;    // 按键键值
wire [3:0] key_count;    // 按键按下次数
wire       nedge    ;    

wire [9:0] ROMADDR  ;   // ROM 地址查找
wire [21:0]Fword    ;
wire [9:0] Pword    ;
wire [9:0] Aword    ;   


keyboard keyboard_1 (
    .sys_clk                 ( sys_clk             ),
    .sys_rstn                ( sys_rstn            ),
    .row                     ( row                 ),
    .col                     ( col                 ),
    .mode_select             ( mode_select         ),
    .nedge                   ( nedge               ),
    .value                   ( value               ),
    .key_count               ( key_count           ),
    .LED_OUT                 ( LED_OUT             ),
    .Fword                   ( Fword               ),
    .Pword                   ( Pword               ),
    .Aword                   ( Aword               )
);

seg_driver seg_driver_1 (
    .clk                     ( sys_clk             ),
    .rst_n                   ( sys_rstn            ),
    .value                   ( value               ),
    .key_count               ( key_count           ),
    .nedge                   ( nedge               ),
    .sel                     ( sel                 ),
    .seg                     ( seg                 ),
    .mode_select             ( mode_select         )
);

dds dds_1(
    .clk                     ( sys_clk             ),
    .rst                     ( sys_rstn            ),
    .DAch                    ( DAch                ),
    .wave_select             ( wave_selevt         ),
    .Fword                   ( Fword               ),
    .Pword                   ( Pword               ),
    .Aword                   ( Aword               ),
    .ROMADDR                 ( ROMADDR             ),
    .dataout                 ( data_out            )
);

div_freq  div_freq_1 (
    .clk                     ( sys_clk             ),
    .DAch                    ( DAch                ),
    .DAclk                   ( DAclk               ),
    .DAwr                    ( DAwr                )
);




endmodule



module div_freq (
    input  clk,

    output wire         DAch,
    output wire         DAclk,
    output wire         DAwr
);

wire[15:0]   DOUT;
reg [15:0] cnt = 0;
 

always @(posedge clk) begin
    if (cnt == 16'b1111_1111_1111_1111)
        cnt <= 0;
    else 
        cnt <= cnt+1'b1;
end


assign DOUT = cnt;

assign DAclk = DOUT[1];
assign DAwr  = DOUT[1];
assign DAch  = DOUT[2];


endmodule

/*
Author    				: 	 Junhan Lv
Email Address         	:    1322069095@qq.com
Filename             	:    keyboard.v
Data                 	:    2024-9-1
Description           	:    keyboard 
Modification History    	:
Data            Author       Version         Change Description
=======================================================
24/9/1        Junhan Lv    1.0              Original
*/


module keyboard(
  input                 sys_clk,
  input                 sys_rstn,
  input      [3:0]      row,                 // 矩阵键盘 行
  input  wire[1:0]      mode_select,         // 模式选择
  output reg [3:0]      col,                 // 矩阵键盘 列
  output reg [21:0]     value,        // 键盘值
  output reg [3:0]      key_count,            // 在确定按钮按下前,进行按键按下计数操作
  output wire           nedge,
  output reg            LED_OUT, 
  output reg [21:0]     Fword,
  output reg [9:0]      Pword,
  output reg [9:0]      Aword
);
 
//++++++++++++++++++++++++++++++++++++++
// 分频部分 开始
//++++++++++++++++++++++++++++++++++++++

parameter FwordState = 2'b01;  // 频率控制字模式
parameter PwordState = 2'b10;  // 相位控制字模式
parameter AwordState = 2'b11;  // 幅度控制字模式

reg [17:0] cnt;                         // 去抖动计数器
reg [3:0]  keyboard_val;

reg                 key_r0;  //同步信号(滤波作用,滤除小于一个周期的抖动)
reg                 key_r1;  //打拍

//同步
always @(posedge sys_clk) begin
    key_r0<=key_pressed_flag;
end

//打拍
always @(posedge sys_clk) begin
    key_r1<=key_r0;
end


assign nedge = ~key_r0 & key_r1;  //检测到下降沿拉高


always @ (posedge sys_clk, negedge sys_rstn)
  if (!sys_rstn)
    cnt <= 0;
  else
    cnt <= cnt + 1'b1;
 
wire key_clk = cnt[17];                 //  T =(2^20/50M = 20.97152)ms 
//--------------------------------------
// 分频部分 结束
//--------------------------------------
 
 
//++++++++++++++++++++++++++++++++++++++
// 状态机部分 开始
//++++++++++++++++++++++++++++++++++++++
// 状态数较少,独热码编码
parameter NO_KEY_PRESSED = 6'b000_001;  // 没有按键按下  
parameter SCAN_COL0      = 6'b000_010;  // 扫描第0列 
parameter SCAN_COL1      = 6'b000_100;  // 扫描第1列 
parameter SCAN_COL2      = 6'b001_000;  // 扫描第2列 
parameter SCAN_COL3      = 6'b010_000;  // 扫描第3列 
parameter KEY_PRESSED    = 6'b100_000;  // 有按键按下
parameter KEY_SURE       = 4'hE      ;  // 确认按键
parameter KEYCLEAR       = 4'hF      ;  // 清楚按键
parameter KEYMODE        = 4'hD      ;  // 模式选择按键




reg [5:0] current_state, next_state;    // 现态、次态
 
always @ (posedge key_clk, negedge sys_rstn)
  if (!sys_rstn)
    current_state <= NO_KEY_PRESSED;
  else
    current_state <= next_state;
 
// 根据条件转移状态
always @(*)
  case (current_state)
    NO_KEY_PRESSED :                    // 没有按键按下
        if (row != 4'hF)
          next_state = SCAN_COL0;
        else
          next_state = NO_KEY_PRESSED;
    SCAN_COL0 :                         // 扫描第0列 
        if (row != 4'hF)
          next_state = KEY_PRESSED;
        else
          next_state = SCAN_COL1;
    SCAN_COL1 :                         // 扫描第1列 
        if (row != 4'hF)
          next_state = KEY_PRESSED;
        else
          next_state = SCAN_COL2;    
    SCAN_COL2 :                         // 扫描第2列
        if (row != 4'hF)
          next_state = KEY_PRESSED;
        else
          next_state = SCAN_COL3;
    SCAN_COL3 :                         // 扫描第3列
        if (row != 4'hF)
          next_state = KEY_PRESSED;
        else
          next_state = NO_KEY_PRESSED;
    KEY_PRESSED :                       // 有按键按下
        if (row != 4'hF)
          next_state = KEY_PRESSED;
        else
          next_state = NO_KEY_PRESSED;                      
  endcase
 
reg       key_pressed_flag;             // 键盘按下标志
reg [3:0] col_val, row_val;             // 列值、行值



// 根据次态,给相应寄存器赋值
always @ (posedge key_clk, negedge sys_rstn)
  if (!sys_rstn)
  begin
    col              <= 4'h0;
    key_pressed_flag <=    0;
  end
  else
    case (next_state)
      NO_KEY_PRESSED :                  // 没有按键按下
      begin
        col              <= 4'h0;
        key_pressed_flag <=    0;       // 清键盘按下标志
      end
      SCAN_COL0 :                       // 扫描第0列
        col <= 4'b1110;
      SCAN_COL1 :                       // 扫描第1列
        col <= 4'b1101;
      SCAN_COL2 :                       // 扫描第2列
        col <= 4'b1011;
      SCAN_COL3 :                       // 扫描第3列
        col <= 4'b0111;
      KEY_PRESSED :                     // 有按键按下
      begin
        col_val          <= col;                // 锁存列值
        row_val          <= row;                // 锁存行值
        key_pressed_flag <= 1;                  // 置键盘按下标志
      end
    endcase
//--------------------------------------
// 状态机部分 结束
//--------------------------------------
 
 
//++++++++++++++++++++++++++++++++++++++
// 扫描行列值部分 开始
//++++++++++++++++++++++++++++++++++++++
always @ (posedge key_clk, negedge sys_rstn)
  if (!sys_rstn)begin 
    keyboard_val <= 4'h0;
  end 
  else
    if (key_pressed_flag) begin 
      case ({col_val, row_val})
        8'b1110_1110 : keyboard_val <= KEYMODE;
        8'b1110_1101 : keyboard_val <= KEY_SURE;      //4'hE  // 确定按键
        8'b1110_1011 : keyboard_val <= 4'h0;
        8'b1110_0111 : keyboard_val <= KEYCLEAR;        // 清除按键
         
        8'b1101_1110 : keyboard_val <= 4'hC;
        8'b1101_1101 : keyboard_val <= 4'h9;
        8'b1101_1011 : keyboard_val <= 4'h8;
        8'b1101_0111 : keyboard_val <= 4'h7;
         
        8'b1011_1110 : keyboard_val <= 4'hB;
        8'b1011_1101 : keyboard_val <= 4'h6;
        8'b1011_1011 : keyboard_val <= 4'h5;
        8'b1011_0111 : keyboard_val <= 4'h4;
         
        8'b0111_1110 : keyboard_val <= 4'hA; 
        8'b0111_1101 : keyboard_val <= 4'h3;
        8'b0111_1011 : keyboard_val <= 4'h2;
        8'b0111_0111 : keyboard_val <= 4'h1;        
      endcase
    end

//--------------------------------------
//  扫描行列值部分 结束
//--------------------------------------

always @(posedge sys_clk or negedge sys_rstn ) begin
  if(!sys_rstn)
    key_count <= 0;
  else if(nedge)
    key_count <= key_count + 1'b1;
  else if (key_count >= 9 || keyboard_val == KEYCLEAR) 
    key_count <= 0;
  else
    key_count <= key_count;
end 


always@(posedge sys_clk or negedge sys_rstn) begin  // 处理按键事件
    if(!sys_rstn)begin
        value <= 22'b0;
    end
    else if(nedge && keyboard_val != KEY_SURE && keyboard_val != KEYCLEAR && keyboard_val != KEYMODE)begin   // 判断是否按键按下,除开确认按键和清除按键
      case(key_count)
          4'd1:value = {4'd0,4'd0,4'd0,keyboard_val};
          4'd2:begin value[7:4]   <= value[3:0]; value[3:0]     <= keyboard_val;end
          4'd3:begin value[11:8]  <= value[7:4]; value[7:4]     <= value[3:0];value[3:0]     <= keyboard_val;end
          4'd4:begin value[15:12] <= value[11:8];value[11:8]    <= value[7:4]; value[7:4]    <= value[3:0];value[3:0]    <= keyboard_val;end
          4'd5:begin value[19:16] <= value[15:12];value[15:12]  <= value[11:8];value[11:8]   <= value[7:4]; value[7:4]   <= value[3:0];value[3:0] <= keyboard_val;end
          default value <= value;
      endcase
    end
    else if (nedge && keyboard_val == KEYCLEAR) begin // 清除按键,将所有value清除
        value <= 22'b0;
    end
    else
      value[21:20] <= mode_select; // 模式赋值
end




always @(posedge sys_clk or negedge sys_rstn) begin   // 状态机选择
    if (!sys_rstn) begin   //默认开始调节频率
      Fword   <= 22'd3436426;
      Pword   <= 10'd0; 
      Aword   <= 10'd1023;
      LED_OUT <= 1'b0;
    end
    else
      case (mode_select)
        FwordState:begin      // 频率控制
          if(key_pressed_flag && keyboard_val == KEY_SURE)begin  // 按下确认按键,将对应控制字进行输出
            LED_OUT <= 1'b1;
            Fword   <= (value[19:16]*10000 + value[15:12]*1000 + value[11:8] * 100 + value[7:4]*10 + value[3:0])*100000/1164;
          end
          else
            LED_OUT <=1'b0;
        end
        PwordState:begin    // 相位控制
          if(key_pressed_flag && keyboard_val == KEY_SURE)begin    // 按下确认按键,将对应控制字进行输出
            LED_OUT <= 1'b1;
            Pword   <= (value[11:8] * 100 + value[7:4]*10 + value[3:0])*1023/360;
          end
          else
            LED_OUT <=1'b0;
        end
        AwordState:begin
          if(key_pressed_flag && keyboard_val == KEY_SURE)begin    // 按下确认按键,将对应控制字进行输出
            LED_OUT <= 1'b1;
            Aword   <= (value[7:4]*10 + value[3:0])*1023/33;
          end
          else
            LED_OUT <=1'b0;
        end
        default :
          ;
      endcase
end


endmodule

module seg_driver (
    input  wire          clk,
    input  wire          rst_n,
    input  wire [21:0]   value,
    input  wire [3:0]    key_count,
    input  wire          nedge,
    input  wire [1:0]    mode_select,
    output reg  [2:0]    sel,
    output reg  [7:0]    seg
);
reg [3:0]       seg_flag;
reg             dot;  

//10ms计时器---用来切换数码管位选,以达到轮流显示时间的各位(肉眼可以看到动态的时间计数)
reg [19:0]      cnt;
wire            add_cnt;
wire            end_cnt;




parameter       MAX_CNT =50_000    ,
                ZERO    =7'b011_1111,
                ONE     =7'b000_0110,
                TWO     =7'b101_1011,
                THREE   =7'b100_1111,
                FOUR    =7'b110_0110,
                FIVE    =7'b110_1101,
                SIX     =7'b111_1101,
                SEVEN   =7'b000_0111,
                EIGHT   =7'b111_1111,
                NINE    =7'b110_1111,
                KEY_SURE = 4'hE;



//计时器
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        cnt<=0;
    end
    else if(add_cnt) begin
        if (end_cnt) begin
            cnt<=0;
        end
        else
            cnt<=cnt+1'b1;
    end
end
assign add_cnt=1'b1;
assign end_cnt=add_cnt&&cnt==MAX_CNT-1;


//切换数码管位选
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        sel<=3'b000;
    end
    else if(cnt==MAX_CNT-1) begin
        sel<=sel+1'b1;              // 动态切换数码管
    end
end  


//切换数码管段选
always @(*) begin
    begin
        case (sel)
            3'b111: begin seg_flag<=value[3:0];                               dot <=0;            end  
            3'b110: begin seg_flag<=value[7:4];      if(mode_select == 2'b11) dot <=1;   else dot <= 0;         end  
            3'b101: begin seg_flag<=value[11:8];                              dot <=0;            end  
            3'b100: begin seg_flag<=value[15:12];                             dot <=0;            end  
            3'b011: begin seg_flag<=value[19:16];                             dot <=0;            end  
            3'b001: begin seg_flag<=value[21:20];                             dot <=0;            end 
            3'b010: begin seg_flag<=0;                                        dot <=0;            end  
            3'b000: begin seg_flag<=key_count;                                dot <=0;            end   
            default :seg_flag<=0;
        endcase
    end
end


//段选译码
always @(*) begin
    begin
    case (seg_flag)     
        0:  seg<={dot,ZERO}    ;
        1:  seg<={dot,ONE}     ;
        2:  seg<={dot,TWO}     ;
        3:  seg<={dot,THREE}   ;
        4:  seg<={dot,FOUR}    ;
        5:  seg<={dot,FIVE}    ;
        6:  seg<={dot,SIX}     ;
        7:  seg<={dot,SEVEN}   ;
        8:  seg<={dot,EIGHT}   ;
        9:  seg<={dot,NINE}    ;
        default: seg<=8'b0000_0000;
    endcase
    end
end




endmodule //seg_driver



module dds(
    input clk,
    input rst,
    input wire  [1:0] wave_select,     // 波形切换
    input wire        DAch,           // 波形通道选择
    input wire  [21:0]Fword,
    input wire  [9:0] Pword,
    input wire  [9:0] Aword,
    input wire  [7:0] CNT8 ,
    output reg  [9:0] ROMADDR,       // 输出ROM地址
    output wire [9:0] dataout        // 输出对应波形
);

parameter SINWAVE       = 2'b00;    // 正弦波
parameter SAWTOOTHWAVE  = 2'b01;   // 锯齿波
parameter SQUAREWAVE    = 2'b10;  //方波
parameter TRIANGLEWAVE  = 2'b11;  // 三角波


reg [9:0] wavedata;

wire[9:0] sindata;
wire[9:0] sawtoothdata;
wire[9:0] squaredata;
wire[9:0] triangledata;



rom	rom_inst0 (                                             // 正弦波数据    
	.address                 ( ROMADDR             ),
	.clock                   ( clk                 ),
	.q                       ( sindata             )
);


rom2 rom_inst1 (                                             // 三角波数据    
	.address                 ( ROMADDR             ),
	.clock                   ( clk                 ),
	.q                       ( triangledata        )
);


rom3 rom_inst2 (                                             // 方波数据    
	.address                 ( ROMADDR             ),
	.clock                   ( clk                 ),
	.q                       ( squaredata          )
);


rom4 rom_inst3 (                                             // 锯齿波数据    
	.address                 ( ROMADDR             ),
	.clock                   ( clk                 ),
	.q                       ( sawtoothdata        )
);



always @(*) begin           // 波形选择
    case (wave_select)
        SINWAVE:        wavedata <= sindata;
        SAWTOOTHWAVE:   wavedata <= sawtoothdata;
        SQUAREWAVE:     wavedata <= squaredata;
        TRIANGLEWAVE:   wavedata <= triangledata;
    default:
        wavedata <= triangledata;
    endcase
end


//相位寄存器:
reg [31:0]frechange; 

always @(posedge clk or negedge rst) begin
   if(!rst)
       frechange <= 32'd0; 
   else
       frechange <= frechange + Fword;
end


always @(posedge clk or negedge rst) begin
    if(!rst)
        ROMADDR <= 10'd0;
    else if(DAch == 1'b0)                                    //相位控制
        ROMADDR <= frechange[31:22] + Pword;
    else
        ROMADDR <= frechange[31:22];
end

wire [9:0] data;
assign data = wavedata;

//调幅
reg [19:0] AMdata;
always@(posedge clk)
   if(!rst)
       AMdata <= 1'd0;
   else
       AMdata <= data * Aword;

assign dataout = AMdata[19:10];
 

endmodule

  • 23
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值