Verilog DDS 数字信号发生器:DAC 输出模拟波形与 ADC 回采观测

Verilog DDS 数字信号发生器:DAC 输出模拟波形与 ADC 回采观测

文章目录

一、项目概述

DDS 模块输出的是数字波形数据,而传统示波器只能观测模拟电压信号。因此需要 DAC 数模转换器,把数字波形转换成模拟波形,才可以使用示波器进行观测。

本工程使用 SPI 接口 DAC 芯片:MAX5383 / MAX5384 / MAX5385,为 8 bit 数模转换器,输出位宽与 DDS 模块输出的 8 bit 数字波形完全匹配。

MAX5383 系列为三线式 SPI 接口 DAC,只需要主机输出 MOSI 发送配置与波形数据;MISO 引脚无功能,芯片直接输出模拟电压,不会回传数字数据。

除硬件示波器观测模拟波形之外,本工程还实现数字波形回传 PC 上位机 的方案:

通过 ADC 模数转换器,把 DAC 输出的模拟波形重新采样变回数字信号,经由 UART-TX 发送到电脑,使用上位机波形软件查看数字波形。

器件选用 MAX1115 / MAX1116 ADC 芯片;ADC 接收到模拟输入后,内部需要一定转换等待时间,转换完成后通过 SPI 的 MISO 信号线,把采样得到的数字数据回传给 FPGA 主机。

本项目可以实现双重观测

  1. 硬件示波器:直接观测 DAC 输出原始模拟波形;
  2. PC 上位机:ADC 回采,串口接收数字波形,上位机软件观测波形。

拓展:基于这套 DDS+ADC 采集链路,还可以进一步实现 FPGA 片上简易示波器综合系统。

整体数据流框图

上位机下发串口指令 → rx 接收解析并行数据 → ctrl_rx 解析指令参数 → DDS (ctrl_rom1) 生成调制数字波形 → DAC (MAX5383) 数模转换输出模拟波形 → ADC (MAX1115) 模拟信号回采转换数字信号 → tx 串口上传至上位机 PC:

  • 模块分工说明
  1. ctrl_rom1:DDS 波形发生模块;完成正弦波选择、调相、调频、调幅,输出调制后的 8 bit 数字波形以及数据有效信号。
  2. ctrl_dac:DAC 专用 SPI 主机模块;驱动 MAX5383 系列,发送波形数字量,完成数模转换。
  3. ctrl_adc:ADC 专用 SPI 主机模块;驱动 MAX1115/MAX1116,启动采样转换,读取 ADC 采样得到的数字值。
  4. rx、ctrl_rx:串口接收、帧解析,解析波形选择、相位、频率、幅度配置参数。
  5. tx:UART 发送模块,把 ADC 采样数据回传给 PC 上位机。
  6. top 顶层:完成所有子模块例化,区分两套独立 SPI 接口,分别供给 DAC、ADC 使用。

二、关键器件要点

  1. MAX5383(8 bit SPI‑DAC)
  • 三线 SPI,只需要主机发送 MOSI,无 MISO 回传;
  • 输入数字位宽 8 bit,和 DDS 输出数据位宽对齐;
  • 在 SPI 通信完成之后,输出对应的模拟电压波形,供示波器探头测量。
  • 下面是数据输入格式:
  1. MAX1115 / MAX1116(ADC)
  • 模拟输入来自 DAC 输出波形;
  • 启动转换之后需要等待芯片内部转换时间(7500 ns);
  • 转换结束,通过 MISO 向 FPGA 主机回传采样数字结果;
  • 采样得到数字量经由 TX 串口发送到 PC,实现软件示波器效果。

三、模块代码实现

ctrl_rom1 DDS 波形发生模块

verilog 复制代码
//完成正弦波(01)的波形选择、调相、调频、调幅
module ctrl_rom1 (
    input   wire            clk         ,
    input   wire            rst_n       , 
    input   wire    [7:0]   wave        ,//波形选择        
    input   wire    [7:0]   phal        ,//初始相位
    input   wire    [7:0]   ferq        ,//频率参数
    input   wire    [7:0]   ampl        ,//调幅参数
    input   wire            done_cmd    ,//指令更新信号
    input   wire            done_tx     ,//tx单字节发送完成信号
    output  wire    [7:0]   data_wave1  ,//调制后信号
    output  wire            wave1_vld    //数据有效信号
);
    //状态
    localparam  IDLE = 1,
                REG  = 2,
                WAVE = 3,
                PHAL = 4,
                FERQ = 5,
                AMPL = 6,
                DATA = 7,
                WAIT = 8;
    reg [3:0]   state_c,state_n;
    //内部信号
    //信号寄存
    reg [7:0] wave_reg;
    reg [7:0] phal_reg;
    reg [7:0] ferq_reg;
    reg [7:0] ampl_reg;
    reg [7:0] data_reg;//输出信号寄存
    reg [3:0] cnt_freq;//频率计数    
    //rom
    reg  [8:0] address;
    wire [7:0] q      ;
    //状态机第一段
    always @(posedge clk or negedge rst_n) begin
        if (rst_n == 0) begin
            state_c <= IDLE;
        end
        else begin
            state_c <= state_n;
        end
    end
    //状态机第二段 
    always @(*) begin
        if (rst_n == 0) begin
            state_n = IDLE;
        end
        else begin
            case (state_c)
                IDLE:begin
                    if (done_cmd) begin
                        state_n = REG;
                    end
                    else begin
                        state_n = state_c;
                    end
                end
                REG :state_n = WAVE;//一个周期用来进行参数寄存
                WAVE:begin//一个周期进行波形选择
                    if (done_cmd) begin
                        state_n = REG;
                    end
                    else if (wave_reg != 8'h01) begin
                        state_n = IDLE;
                    end
                    else begin
                        state_n = PHAL;
                    end
                end
                PHAL:begin//数据没有更新的情况下直接跳,进行rom地址的初始化(相位初始化)
                    if (done_cmd) begin
                        state_n = REG;
                    end
                    else begin
                        state_n = FERQ;
                    end                    
                end
                FERQ:begin//频率计数到最大值进行采样完成频率调制
                    if (done_cmd) begin
                        state_n = REG;
                    end
                    else if ((ferq_reg[4] == 0 && cnt_freq == ferq_reg[3:0] - 1 ) || ferq_reg[4] == 1)begin
                        state_n = AMPL;
                    end    
                    else begin
                        state_n = state_c;
                    end                    
                end
                AMPL:begin//一个周期完成调幅
                    if (done_cmd) begin
                        state_n = REG;
                    end
                    else begin
                        state_n = DATA;
                    end
                end
                DATA:begin//一个周期完成信号赋值
                    if (done_cmd) begin
                        state_n = REG;
                    end
                    else begin
                        state_n = WAIT;
                    end
                end
                WAIT:begin//一个周期的过渡
                    if (done_cmd) begin
                        state_n = REG;
                    end
                    else if(done_tx)begin
                        state_n = FERQ;
                    end
                    else begin
                        state_n = WAIT;
                    end
                end
                default:state_n = IDLE; 
            endcase
        end
    end
    //状态机第三段 
    always @(posedge clk or negedge rst_n) begin
        if (rst_n == 0) begin
            wave_reg <= 0;
            phal_reg <= 0;
            ferq_reg <= 0;
            ampl_reg <= 0;
            data_reg <= 0;
            cnt_freq <= 0;
            address  <= 0;
        end
        else begin
            case (state_c)
                IDLE: begin//等done_cmd,指令有效信号
                    wave_reg <= 0;
                    phal_reg <= 0;
                    ferq_reg <= 0;
                    ampl_reg <= 0;
                    data_reg <= 0;
                    cnt_freq <= 0;
                    address  <= 0;
                end
                REG :begin//进行指令寄存
                    wave_reg <= wave;
                    phal_reg <= phal;
                    ferq_reg <= ferq;
                    ampl_reg <= ampl;                    
                end
                WAVE:;//进行波形选择,只做了一个判断
                PHAL:begin//调相
                    address  <= phal_reg;
                end
                FERQ:begin//调频,调大(周期缩小)、调小(周期展宽)
                    //频率计数
                    if (cnt_freq == ferq_reg[3:0] - 1) begin
                        cnt_freq <= 0;
                    end
                    else begin
                        cnt_freq <= cnt_freq + 1;
                    end
                    //地址
                    if ((ferq_reg[4] == 1 && cnt_freq == ferq_reg[3:0] - 1 ) || ferq_reg[4] == 0) begin
                        address <= address + 1;
                    end
                end
                AMPL:begin//根据上一个状态选中的地址,由于读使能一直打开,q为开放状态可以直接读取
                    data_reg <= q/ampl_reg;
                end
                DATA:;//产生所谓的数据有效信号
                WAIT:;//过渡状态
                default:begin
                    wave_reg <= 0;
                    phal_reg <= 0;
                    ferq_reg <= 0;
                    ampl_reg <= 0;
                    data_reg <= 0;
                    cnt_freq <= 0;
                    address  <= 0;
                end 
            endcase
        end
    end
    //输出信号
    assign data_wave1 = data_reg;
    assign wave1_vld  = (state_c == DATA);
rom1 rom1_inst (
    .aclr       (~rst_n  ),
    .address    (address ),
    .clock      (clk     ),
    .q          (q       )
);
endmodule

ctrl_adc ADC SPI 驱动模块

verilog 复制代码
//ADC 的专用 SPI,用于接收 ADC 转换成功的数字信号
//1. 接口分析:协议开始信号,目前只回传数据,以字节为单位,因此长度设为 1?
//并未写数据,MOSI 处于闲置状态
module ctrl_adc(
    input   wire            clk         ,
    input   wire            rst_n       ,  
    input   wire            start_spi   ,//上层给到 SPI 接口的开始信号
    input   wire    [7:0]   data_len    ,//上层给到 SPI 接口的总传输长度
    input   wire    [7:0]   wr_lenth    ,//上层给到spi接口的要写(发送给从机的长度)
    output  wire    [7:0]   data_o_spi  ,//输出的数据
    output  wire            done_w      ,//握手信号
    output  wire            done_r      ,//握手信号
    output  wire            done_spi    ,//握手信号
    output  wire            ADC_SCK     ,//四线
    output  wire            ADC_CNVST   ,//四线
    output  wire            ADC_MOSI    ,//四线
    input   wire            ADC_MISO     //四线
);
    //参数
    parameter   CPOL  = 0 ,//时钟极性
                CPHA  = 0 ,//时钟相位
                delay = 6 ,//分频系数
                TIME  = 375;
    //状态定义
    localparam  IDLE  = 4'b0001,  
                START = 4'b0010,  
                DATA  = 4'b0100,    
                STOP  = 4'b1000;
    reg [3:0]   state_c,state_n;
    //状态转移
    wire    IDLE_START,
            START_DATA,
            DATA_STOP ,
            STOP_IDLE ;
    //内部信号
    reg     [8:0]   cnt_clk;//分频时钟计数
    reg     [7:0]   cnt_bit;//比特计数
    reg     [1:0]   start_reg   ;//输入寄存信号
    reg     [7:0]   data_reg    ;//输入寄存信号
    reg     [7:0]   data_len_r  ;//输入寄存信号
    reg     [7:0]   wr_lenth_r  ;//输入寄存信号    
    reg             SCK_R       ;//时钟寄存
    reg     [7:0]   data_temp   ;//输出数据的暂存信号
    reg             MOSI_R      ;//主机输出寄存
    reg             CS_N_R      ;//片选信号寄存
    //开始信号的寄存
    always @(posedge clk or negedge rst_n) begin
        if (rst_n == 0) begin
            start_reg <= 2'b00;
        end
        else begin
            start_reg <= {start_reg[0],start_spi};
        end
    end
    //状态机第一段
    always @(posedge clk or negedge rst_n) begin
        if (rst_n == 0) begin
            state_c <= IDLE;
        end
        else begin
            state_c <= state_n;
        end
    end
    //状态机第二段
    always @(*) begin
        if (rst_n == 0) begin
            state_n = IDLE;
        end
        else begin
            case (state_c)
                IDLE   :state_n = (IDLE_START) ? START : state_c ;
                START  :state_n = (START_DATA) ? DATA  : state_c ;
                DATA   :state_n = (DATA_STOP ) ? STOP  : state_c ;
                STOP   :state_n = (STOP_IDLE ) ? IDLE  : state_c ;
                default:state_n = IDLE; 
            endcase
        end
    end
    //状态转移条件
    assign IDLE_START = (state_c == IDLE ) && start_reg == 2'b01;
    assign START_DATA = (state_c == START) && cnt_clk == TIME - 1; 
    assign DATA_STOP  = (state_c == DATA ) && ((CPHA == 0 &&cnt_bit == data_len_r*8 - 1 && cnt_clk == delay - 1) || (CPHA == 1 &&cnt_bit == data_len_r*8  && cnt_clk == 0));
    assign STOP_IDLE  = (state_c == STOP ) && cnt_clk == delay - 1;    
    //状态机第三段
    always @(posedge clk or negedge rst_n) begin
        if (rst_n == 0) begin
            cnt_clk     <= 0   ;//分频时钟计数
            cnt_bit     <= 0   ;//比特计数
            CS_N_R      <= 1   ;//片选信号
            SCK_R       <= CPOL;//分频时钟信号
            data_reg    <= 0   ;//输入寄存信号
            data_len_r  <= 0   ;//输入寄存信号
            wr_lenth_r  <= 0   ;//输入寄存信号    
            data_temp   <= 0   ;//输出数据的暂存信号        
        end
        else begin
            case (state_c)
                IDLE   :begin//寄存、等待开始信号
                    cnt_clk     <= 0;//分频时钟计数
                    cnt_bit     <= 0;//比特计数
                    CS_N_R      <= 1;//片选信号
                    SCK_R       <= CPOL;
                    data_reg    <= 0;
                    data_len_r  <= data_len;//输入寄存信号
                    wr_lenth_r  <= wr_lenth;//输入寄存信号    
                    data_temp   <= 0;//输出数据的暂存信号        
                end
                START  :begin//拉低片选信号,并等待7.5us
                    if (cnt_clk ==  TIME) begin//分频时钟计数,也是一个bit计数器,多给一周期 
                        cnt_clk <= 0; 
                        CS_N_R  <= 1;                        
                    end
                    else begin
                        cnt_clk <= cnt_clk + 1;
                        CS_N_R  <= 0;//片选信号拉低
                    end
                    
                end
                DATA   :begin//数据的更新,接收,发送
                    if (cnt_clk == delay - 1) begin//分频时钟计数,也是一个bit计数器 
                        cnt_clk <= 0;                        
                    end
                    else begin
                        cnt_clk <= cnt_clk + 1;
                    end
                    if (cnt_clk == delay - 1) begin//比特计数 
                        if (cnt_bit == data_len_r*8) begin
                            cnt_bit <= 0;                                
                        end
                        else begin
                            cnt_bit <= cnt_bit + 1;
                        end
                    end
                    //时钟
                    if (cnt_clk == delay/2 || (cnt_clk == 0 && cnt_bit > 0 )) begin
                        SCK_R <= ~SCK_R;
                    end
                    
                    //根据极性来进行采样和寄存
                    case (CPHA)
                        0:begin//第一个沿进行采集
                            //数据发送
                            if (cnt_clk == 0) begin//数据已经在上一个字节末尾进行更新
                                MOSI_R <= data_reg[7 - cnt_bit];//高位优先
                            end
                            //数据接收/采集
                            if (cnt_clk == delay/2) begin
                                data_temp  <= {data_temp[6:0],ADC_MISO}; 
                            end
                        end
                        default:begin//第二个沿进行采集
                            //数据发送
                            if (cnt_clk == delay/2 ) begin
                                MOSI_R <= data_reg[7 - cnt_bit%8];//高位优先
                            end
                            //数据接收/采集
                            if (cnt_clk == 0) begin
                                data_temp  <= {data_temp[6:0],ADC_MISO}; 
                            end
                        end 
                    endcase
                end
                STOP   :begin//持续一个周期后拉高片选产生done_spi结束本次通信
                    if (cnt_clk == delay - 1) begin
                        cnt_clk <= 0;
                        CS_N_R  <= 1;                        
                    end
                    else begin
                        cnt_clk <= cnt_clk + 1;
                    end
                    cnt_bit     <= 0;
                    SCK_R       <= CPOL;                        
                end
                default:begin
                    cnt_clk     <= 0   ;//分频时钟计数
                    cnt_bit     <= 0   ;//比特计数
                    CS_N_R      <= 1   ;//片选信号
                    SCK_R       <= CPOL;//分频时钟信号
                    data_reg    <= 0   ;//输入寄存信号
                    data_len_r  <= 0   ;//输入寄存信号
                    wr_lenth_r  <= 0   ;//输入寄存信号    
                    data_temp   <= 0   ;//输出数据的暂存信号        
                end 
            endcase
        end
    end
    
    //输出信号
//    assign  data_o_spi = ((CPHA == 0 && cnt_clk == delay/2 && cnt_bit%8 == 7) || (CPHA == 1 && cnt_clk == delay - 1 && cnt_bit%8 == 7)) ? data_temp : data_o_spi  ;
    assign  data_o_spi = data_temp  ;//只在done_r信号来的时候才采样
    assign  done_r     = (CPHA == 0 && cnt_clk == delay/2 + 1 && cnt_bit%8 == 7 && cnt_bit > wr_lenth*8) || (CPHA == 1 && cnt_clk == 1 && cnt_bit%8 == 0 && cnt_bit > wr_lenth*8 && data_len > wr_lenth);
    assign  done_w     = (CPHA == 0 && cnt_clk == 1 && cnt_bit%8 == 7 && cnt_bit <= wr_lenth*8) || (CPHA == 1 && cnt_clk == delay/2 + 1 && cnt_bit%8 == 7 && cnt_bit <= wr_lenth*8) ;
//    assign  done_spi   = STOP_IDLE  ;
    assign done_spi = (state_c == STOP) && (cnt_clk == delay - 1);
    assign  ADC_SCK        = SCK_R      ;
    assign  ADC_CNVST      = CS_N_R     ;
    assign  ADC_MOSI       = MOSI_R     ;
endmodule

ctrl_dac DAC SPI 驱动模块

verilog 复制代码
//dac的专用spi
//1.接口分析:时钟、复位、上层给到spi接口的开始信号、数据、定义的总传输长度、要写(发送给从机的长度)、接口之间的四线结构
//输出的数据、以及读写、一次通信完成的握手信号
//2.四个时序图转换为代码
//之前是依托flash进行的帧解析,现在要使用dac,原理比flash更简单,只需要两字节的输出就可以了。
//由于之前是以单字节为基础写的,所以在赋值的时候有点问题
//如果完善一下,写一个针对dac的控制模块也可以,顺便省去额外不必要的信号。
module ctrl_dac(
    input   wire            clk         ,
    input   wire            rst_n       ,  
    input   wire            start_spi   ,//上层给到spi接口的开始信号
    input   wire    [15:0]  data_i_spi  ,//上层给到spi接口的(指令、地址、数据)
    input   wire    [15:0]  data_len    ,//上层给到spi接口的总传输长度
    input   wire    [15:0]  wr_lenth    ,//上层给到spi接口的要写(发送给从机的长度)
    output  wire    [15:0]  data_o_spi  ,//输出的数据
    output  wire            done_w      ,//握手信号
    output  wire            done_r      ,//握手信号
    output  wire            done_spi    ,//握手信号
    output  wire            DAC_SCK         ,//四线
    output  wire            DAC_CS_N        ,//四线
    output  wire            DAC_MOSI        ,//四线
    input   wire            DAC_MISO         //四线
);
    //参数
    parameter   CPOL  = 0 ,//时钟极性
                CPHA  = 0 ,//时钟相位
                delay = 6 ;//分频系数
    //状态定义
    localparam  IDLE  = 4'b0001,  
                START = 4'b0010,  
                DATA  = 4'b0100,    
                STOP  = 4'b1000;
    reg [3:0]   state_c,state_n;
    //状态转移
    wire    IDLE_START,
            START_DATA,
            DATA_STOP ,
            STOP_IDLE ;
    //内部信号
    reg     [2:0]   cnt_clk;//分频时钟计数
    reg     [15:0]  cnt_bit;//比特计数
    reg     [1:0]   start_reg   ;//输入寄存信号
    reg     [15:0]  data_reg    ;//输入寄存信号
    reg     [15:0]  data_len_r  ;//输入寄存信号
    reg     [15:0]  wr_lenth_r  ;//输入寄存信号
    reg             SCK_R       ;//时钟寄存
    reg     [15:0]  data_temp   ;//输出数据的暂存信号
    reg             MOSI_R      ;//主机输出寄存
    reg             CS_N_R      ;//片选信号寄存
    //开始信号的寄存
    always @(posedge clk or negedge rst_n) begin
        if (rst_n == 0) begin
            start_reg <= 2'b00;
        end
        else begin
            start_reg <= {start_reg[0],start_spi};
        end
    end
    //状态机第一段
    always @(posedge clk or negedge rst_n) begin
        if (rst_n == 0) begin
            state_c <= IDLE;
        end
        else begin
            state_c <= state_n;
        end
    end
    //状态机第二段
    always @(*) begin
        if (rst_n == 0) begin
            state_n = IDLE;
        end
        else begin
            case (state_c)
                IDLE   :state_n = (IDLE_START) ? START : state_c ;
                START  :state_n = (START_DATA) ? DATA  : state_c ;
                DATA   :state_n = (DATA_STOP ) ? STOP  : state_c ;
                STOP   :state_n = (STOP_IDLE ) ? IDLE  : state_c ;
                default:state_n = IDLE; 
            endcase
        end
    end
    //状态转移条件
    assign IDLE_START = (state_c == IDLE ) && start_reg == 2'b01;
    assign START_DATA = (state_c == START) ; 
    assign DATA_STOP  = (state_c == DATA ) && ((CPHA == 0 && cnt_bit == data_len_r*8 - 1 && cnt_clk == delay - 1) || (CPHA == 1 && cnt_bit == data_len_r*8  && cnt_clk == 0));
    assign STOP_IDLE  = (state_c == STOP ) && cnt_clk == delay - 1;    
    //状态机第三段
    always @(posedge clk or negedge rst_n) begin
        if (rst_n == 0) begin
            cnt_clk     <= 0   ;//分频时钟计数
            cnt_bit     <= 0   ;//比特计数
            CS_N_R      <= 1   ;//片选信号
            SCK_R       <= CPOL;//分频时钟信号
            data_reg    <= 0   ;//输入寄存信号
            data_len_r  <= 0   ;//输入寄存信号
            wr_lenth_r  <= 0   ;//输入寄存信号    
            data_temp   <= 0   ;//输出数据的暂存信号        
        end
        else begin
            case (state_c)
                IDLE   :begin//寄存、等待开始信号
                    cnt_clk     <= 0;//分频时钟计数
                    cnt_bit     <= 0;//比特计数
                    CS_N_R      <= 1;//片选信号
                    SCK_R       <= CPOL;
                    data_reg    <= data_i_spi;//输入寄存信号
                    data_len_r  <= data_len;//输入寄存信号
                    wr_lenth_r  <= wr_lenth;//输入寄存信号    
                    data_temp   <= 0;//输出数据的暂存信号        
                end
                START  :begin//拉低片选信号
                    CS_N_R      <= 0;//片选信号拉低
                end
                DATA   :begin//数据的更新,接收,发送
                    if (cnt_clk == delay - 1) begin//分频时钟计数,也是一个bit计数器 
                        cnt_clk <= 0;                        
                    end
                    else begin
                        cnt_clk <= cnt_clk + 1;
                    end
                    if (cnt_clk == delay - 1) begin//比特计数 
                        if (cnt_bit == data_len_r*8) begin
                            cnt_bit <= 0;                                
                        end
                        else begin
                            cnt_bit <= cnt_bit + 1;
                        end
                    end
                    //时钟
                    if (cnt_clk == delay/2 || (cnt_clk == 0 && cnt_bit > 0 )) begin
                        SCK_R <= ~SCK_R;
                    end
                    
                    //根据极性来进行采样和寄存
                    case (CPHA)
                        0:begin//第一个沿进行采集
                            //数据发送
                            if (cnt_clk == 0) begin//数据已经在上一个字节末尾进行更新
                                MOSI_R <= data_reg[15 - cnt_bit];//高位优先
                            end
                            //数据接收/采集
                            if (cnt_clk == delay/2) begin
                                data_temp  <= {data_temp[6:0],DAC_MISO}; 
                            end
                        end
                        default:begin//第二个沿进行采集
                            //数据发送
                            if (cnt_clk == delay/2 ) begin
                                MOSI_R <= data_reg[15 - cnt_bit%16];//高位优先
                            end
                            //数据接收/采集
                            if (cnt_clk == 0) begin
                                data_temp  <= {data_temp[6:0],DAC_MISO}; 
                            end
                        end 
                    endcase
                end
                STOP   :begin//持续一个周期后拉高片选产生done_spi结束本次通信
                    if (cnt_clk == delay - 1) begin
                        cnt_clk <= 0;
                        CS_N_R  <= 1;                        
                    end
                    else begin
                        cnt_clk <= cnt_clk + 1;
                    end
                    cnt_bit     <= 0;
                    SCK_R       <= CPOL;                        
                end
                default:begin
                    cnt_clk     <= 0   ;//分频时钟计数
                    cnt_bit     <= 0   ;//比特计数
                    CS_N_R      <= 1   ;//片选信号
                    SCK_R       <= CPOL;//分频时钟信号
                    data_reg    <= 0   ;//输入寄存信号
                    data_len_r  <= 0   ;//输入寄存信号
                    wr_lenth_r  <= 0   ;//输入寄存信号    
                    data_temp   <= 0   ;//输出数据的暂存信号        
                end 
            endcase
        end
    end
    //输出信号
//    assign  data_o_spi = ((CPHA == 0 && cnt_clk == delay/2 && cnt_bit%16 == 15) || (CPHA == 1 && cnt_clk == delay - 1 && cnt_bit%16 == 15)) ? data_temp : data_o_spi  ;
    assign  data_o_spi = data_temp  ;//只在done_r信号来的时候才采样
    assign  done_r     = (CPHA == 0 && cnt_clk == delay/2 + 1 && cnt_bit%16 == 15 && cnt_bit > wr_lenth*8) || (CPHA == 1 && cnt_clk == 1 && cnt_bit%16 == 0 && cnt_bit > wr_lenth*8 && data_len > wr_lenth);
    assign  done_w     = (CPHA == 0 && cnt_clk == 1 && cnt_bit%16 == 15 && cnt_bit <= wr_lenth*8) || (CPHA == 1 && cnt_clk == delay/2 + 1 && cnt_bit%16 == 15 && cnt_bit <= wr_lenth*8) ;
//    assign  done_spi   = STOP_IDLE  ;
    assign  done_spi = (state_c == STOP) && (cnt_clk == delay - 1);
    assign  DAC_SCK         = SCK_R      ;
    assign  DAC_CS_N        = CS_N_R     ;
    assign  DAC_MOSI        = MOSI_R     ;
endmodule

top 顶层模块

verilog 复制代码
//一个主机芯片可以有多个 SPI 接口,所以这里的 SCK 是分开的。
module top (
    input   wire            clk      ,
    input   wire            rst_n    ,
    input   wire            rx       ,//接收端:串行数据线  
//dac
    output  wire            DAC_SCK  ,
    output  wire            DAC_CS_N ,
    output  wire            DAC_MOSI ,
//adc                        
    output  wire            ADC_SCK  ,
    output  wire            ADC_CNVST,
    input   wire            ADC_MISO ,
    output  wire            tx        //输出的串行数据
);
    //rx
    wire    [7:0]   data_rx;
    wire            done_rx;
    //ctrl_rx
    wire    [7:0]   wave    ;
    wire    [7:0]   phal    ;
    wire    [7:0]   ferq    ;
    wire    [7:0]   ampl    ;
    wire            done_cmd;
    //ctrl_rom1
    wire    [7:0]   data_wave1;
    wire            wave1_vld ;
    //ctrl_rom2
    wire    [7:0]   data_wave2;
    wire            wave2_vld ;
    //ctrl_rom3
    wire    [7:0]   data_wave3;
    wire            wave3_vld ;
    //ctrl_rom4
    wire    [7:0]   data_wave4;
    wire            wave4_vld ;
    //tx
    wire            done_tx;
    wire            done_rom;
    //ctrl_dac
    wire    [7:0]   dac_data_len  ;  
    wire    [7:0]   dac_wr_lenth  ;  
    wire    [7:0]   dac_data_o_spi;  
    wire            dac_done_w    ;  
    wire            dac_done_r    ;  
    wire            dac_done_spi  ;
    wire            DAC_MISO      ;  
    //ctrl_adc
    wire    [7:0]   adc_data_len  ;  
    wire    [7:0]   adc_wr_lenth  ;  
    wire    [7:0]   adc_data_o_spi;  
    wire            adc_done_w    ;  
    wire            adc_done_r    ;  
    wire            adc_done_spi  ;  
    wire            ADC_MOSI;
    //
//    wire    [7:0]   data_out ;
//    wire            rd_done  ;
    //中间信号
    reg     [7:0]   data_wave;
    reg             wave_vld ;
    always @(*) begin
        if (rst_n == 0) begin
            data_wave = 0;
            wave_vld  = 0;          
        end
        else begin case (wave)
            8'h01:begin
                data_wave = data_wave1;
                wave_vld  = wave1_vld ;                  
            end
            8'h02:begin
                data_wave = data_wave2;
                wave_vld  = wave2_vld ;                  
            end
            8'h03:begin
                data_wave = data_wave3;
                wave_vld  = wave3_vld ;                  
            end
            default:begin
                data_wave = data_wave4;
                wave_vld  = wave4_vld ;                  
            end 
        endcase
        end
    end 
rx rx_inst(
    .clk      (clk    ),
    .rst_n    (rst_n  ),
    .rx       (rx     ),//接收端:串行数据线
    .data_rx  (data_rx),//并行数据
    .done_rx  (done_rx) //数据有效信号
);
ctrl_rx ctrl_rx_inst(
    .clk         (clk     ),
    .rst_n       (rst_n   ),  
    .data_rx     (data_rx ),//rx发送过来的单个指令
    .done_rx     (done_rx ),//数据有效信号
    .wave        (wave    ),//波形选择        
    .phal        (phal    ),//初始相位
    .ferq        (ferq    ),//频率参数
    .ampl        (ampl    ),//调幅参数
    .done_cmd    (done_cmd)
);
ctrl_rom1 ctrl_rom1_inst(
    .clk        (clk       ),
    .rst_n      (rst_n     ), 
    .wave       (wave      ),//波形选择        
    .phal       (phal      ),//初始相位
    .ferq       (ferq      ),//频率参数
    .ampl       (ampl      ),//调幅参数
    .done_cmd   (done_cmd  ),//指令更新信号
    .done_tx    (done_rom  ),//tx发送,提前更新数据
    .data_wave1 (data_wave1),//调制后信号
    .wave1_vld  (wave1_vld )  //数据有效信号
);
ctrl_rom2 ctrl_rom2_inst(
    .clk        (clk       ),
    .rst_n      (rst_n     ), 
    .wave       (wave      ),//波形选择        
    .phal       (phal      ),//初始相位
    .ferq       (ferq      ),//频率参数
    .ampl       (ampl      ),//调幅参数
    .done_cmd   (done_cmd  ),//指令更新信号
    .done_tx    (done_rom  ),//tx发送,提前更新数据
    .data_wave2 (data_wave2),//调制后信号
    .wave2_vld  (wave2_vld )  //数据有效信号
);
ctrl_rom3 ctrl_rom3_inst(
    .clk        (clk       ),
    .rst_n      (rst_n     ), 
    .wave       (wave      ),//波形选择        
    .phal       (phal      ),//初始相位
    .ferq       (ferq      ),//频率参数
    .ampl       (ampl      ),//调幅参数
    .done_cmd   (done_cmd  ),//指令更新信号
    .done_tx    (done_rom  ),//tx发送,提前更新数据
    .data_wave3 (data_wave3),//调制后信号
    .wave3_vld  (wave3_vld )  //数据有效信号
);
ctrl_rom4 ctrl_rom4_inst(
    .clk        (clk       ),
    .rst_n      (rst_n     ), 
    .wave       (wave      ),//波形选择        
    .phal       (phal      ),//初始相位
    .ferq       (ferq      ),//频率参数
    .ampl       (ampl      ),//调幅参数
    .done_cmd   (done_cmd  ),//指令更新信号
    .done_tx    (done_rom  ),//tx发送,提前更新数据
    .data_wave4 (data_wave4),//调制后信号
    .wave4_vld  (wave4_vld )  //数据有效信号
);
ctrl_dac ctrl_dac_inst(
    .clk        (clk                        ),
    .rst_n      (rst_n                      ),  
    .start_spi  (wave_vld                   ),//上层给到spi接口的开始信号
    .data_i_spi ({4'b0000,data_wave,4'b0000}),
    .data_len   (8'd2                       ),//上层给到spi接口的总传输长度
    .wr_lenth   (8'd2                       ),//上层给到spi接口的要写(发送给从机的长度)
    .data_o_spi (dac_data_o_spi             ),//输出的数据
    .done_w     (dac_done_w                 ),//握手信号
    .done_r     (dac_done_r                 ),//握手信号
    .done_spi   (dac_done_spi               ),//握手信号
    .DAC_SCK    (DAC_SCK                    ),//四线
    .DAC_CS_N   (DAC_CS_N                   ),//四线
    .DAC_MOSI   (DAC_MOSI                   ),//四线
    .DAC_MISO   (DAC_MISO                   ) //四线
);
ctrl_adc ctrl_adc_inst(
    .clk        (clk                        ),
    .rst_n      (rst_n                      ),  
    .start_spi  (dac_done_spi               ),//上层给到spi接口的开始信号
    .data_len   (8'd1                       ),//上层给到spi接口的总传输长度
    .wr_lenth   (8'd0                       ),//上层给到spi接口的要写(发送给从机的长度)
    .data_o_spi (adc_data_o_spi             ),//输出的数据
    .done_w     (adc_done_w                 ),//握手信号
    .done_r     (adc_done_r                 ),//握手信号
    .done_spi   (adc_done_spi               ),//握手信号
    .ADC_SCK    (ADC_SCK                    ),//四线
    .ADC_CNVST  (ADC_CNVST                  ),//四线
    .ADC_MOSI   (ADC_MOSI                   ),//四线
    .ADC_MISO   (ADC_MISO                   ) //四线
);
//ctrl_tx ctrl_tx_inst(
//    .clk        (clk   ),
//    .rst_n      (rst_n ), 
//    .data_o_spi (adc_data_o_spi),//spi的输出数据,也就是读出的
//    .done_r     (adc_done_r    ),//完成一次读数据的握手信号
//    .done_tx    (done_tx       ),//读数据控制读使能的信号
//    .done_spi   (adc_done_spi  ),//数据寄存fifo结束 
//    .data_out   (data_out),//tx的输入    
//    .rd_done    (rd_done ) //tx的开始
//);
tx tx_inst(
    .clk         (clk           ),
    .rst_n       (rst_n         ),
    .data        (adc_data_o_spi),//并行数据
    .data_done   (adc_done_r    ),//数据有效信号,和数据同时到来
    .tx          (tx            ),//输出的串行数据
    .done_rom    (done_rom      ),
    .done_tx     (done_tx       ) //表示这组数据发送完毕等待下一个数据到来  
);
endmodule

四、上板测试

  • 观测的时候,示波器的探头没稳定接触,存在少量毛刺。
  • 上位机观测现象。
相关推荐
深圳市恒锐丰科技杨生1 小时前
EG4318 / EG4319 / EG4320 铅酸电池三段式充电副边管理芯片|屹晶 EGmicro
嵌入式硬件·硬件工程
深圳市恒锐丰科技杨生1 小时前
EG1253 电流模式 PWM 控制器|屹晶 EGmicro
嵌入式硬件·硬件工程
远翔调光芯片^138287988721 小时前
ECP5702能芯科技PD取电芯片在市场上的优势有哪些?
开发语言·人工智能·单片机·嵌入式硬件·智能家居
时空自由民.2 小时前
WSL解决USB 串口连接问题与linux串口权限问题
linux·单片机
泡海椒2 小时前
规则引擎对比:JQuick-Java vs Drools 轻量场景选型对比
java·开发语言
泡泡鱼(敲代码中)2 小时前
Python 容器类型学习笔记:列表、元组、字典、集合
开发语言·笔记·python·pycharm
2501_933670793 小时前
平台招商运营校招能力模型:数据分析、SQL与增长场景拆解
开发语言
菜鸟~noob2333 小时前
【Bonjour】华为Peerium架构深度解析:从冯·诺依曼到百万处理器“一台计算机”,附MATLAB性能仿真【含matlab代码,可直接运行】
开发语言·matlab·华为·peerium
白远山4 小时前
24小时自助健身房系统开发实战:从需求分析到完整指南
java·开发语言·数据库·数据挖掘·需求分析