一、引言:为什么需要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) beginif (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位。
相关推荐:
-
FPGA工程师必备:SPI协议终极指南
-
Verilog代码风格:从菜鸟到高手