一、引言:为什么需要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位。
相关推荐: