FPGA结合ADC的SPI配置实战

#新星杯·14天创作挑战营·第11期#

一、引言:为什么需要SPI控制DAC?

在FPGA开发中,DAC(数模转换器) 是连接数字世界与模拟世界的关键模块,而 SPI(串行外设接口) 因其简单高效的特性,成为控制DAC的常用协议。本文将以一个真实的Verilog模块为例,进行分享学习

二、代码功能概述:这个模块能做什么?

1. 核心功能

  • 多通道DAC控制:支持8个独立通道(en_ch_i[7:0])和2级配置(en_cfg_i[1:0])。

  • 灵活参数配置

    • REF_WORD:启用DAC内部参考电压(默认0x08000000)。

    • CLR_MODE:定义DAC清除模式(0-清零,1-中值,2-满量程,3-忽略CLR引脚)。

    • DELAY_SCLK:SPI时钟分频系数(需为偶数,如4、6、8)。

2. 接口信号

信号方向描述
clk_i输入系统时钟(如100MHz)
start_i输入启动信号(单周期脉冲)
scs_n_o输出SPI片选(低有效)
sda_o输出SPI数据线
dac_done_o输出DAC完成信号(高有效)

三、代码解析:从状态机到SPI时序🔥

1. 状态机设计(核心!)

模块采用 7状态独热码(One-Hot)状态机,流程如下:

2. SPI时钟生成逻辑

  • 分频原理:通过计数器 sclk_count 实现 DELAY_SCLK 分频。

  • 关键代码

always @(posedge clk_i) begin
    if (sclk_count >= (DELAY_SCLK-1)) 
        sclk_count <= 0;
    else 
        sclk_count <= sclk_count + 1;
    // 生成边沿标志
    if (sclk_count == (DELAY_SCLK/2-1)) 
        clken_r <= 1;  // 上升沿
    else if (sclk_count == (DELAY_SCLK-1)) 
        clken_f <= 1;  // 下降沿
end

3. 状态机时序逻辑

  • 关键代码

always @(posedge clk_i or negedge rst_n_i) begin  
    if (!rst_n_i)  
        dac_csm <= IDLE;  
    else  
        dac_csm <= #3 dac_nsm;  // 仿真延迟(不可综合)  
end  

always @(*) begin  
    case (dac_csm)  
        IDLE:    dac_nsm = WAIT;  
        WAIT:    dac_nsm = (start_i) ? START : WAIT;  
        START:   dac_nsm = (en_r==0) ? NEXT : (clken_f && dy_num[1]) ? SEND : START;  
        SEND:    dac_nsm = (clken_r && da_count==0) ? END_TURN : SEND;  
        END_TURN:dac_nsm = (clken_f) ? NEXT : END_TURN;  
        NEXT:    dac_nsm = (ch_count>9) ? DONE : START;  
        DONE:    dac_nsm = WAIT;  
        default: dac_nsm = IDLE;  
    endcase  
end  

4.数据发送逻辑

关键代码

always @(posedge clk_i or negedge rst_n_i) begin  
    if (!rst_n_i) begin  
        ch_count <= 0;  
        scs_n_r <= 1;  
        sda_r <= 0;  
        da_send <= 0;  
    end else begin  
        case (dac_nsm)  
            START: begin  
                da_send <= {1'b0, da_send_d1};  // 填充 1 位后发送  
                da_count <= 32;  
            end  
            SEND: begin  
                sda_r <= da_send[da_count];     // 逐位发送  
                da_count <= da_count - clken_r;  
            end  
        endcase  
    end  
end  

五、常见问题解答(Q&A)❓

Q:为什么 da_send 是33位?

  • A:DAC8568芯片要求32位数据,但代码中添加了1位填充位(补0),总长度33位。

相关推荐

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值