FPGA初级项目10——基于SPI的DAC芯片进行数模转换

embedded/2025/3/16 5:11:53/

FPGA初级项目10——基于SPI的DAC芯片进行数模转换

DAC芯片介绍


DAC 芯片(数字模拟转换器)是一种将数字信号转换为连续模拟信号(如电压或电流)的集成电路,广泛应用于电子系统中,连接数字世界与模拟世界。
 

核心功能


数字到模拟转换:将离散的二进制数字信号(如 0 和 1 的组合)转换为连续的模拟信号,使计算机、微控制器等数字设备能够驱动扬声器、显示器、传感器等模拟设备。
应用领域:音频处理(音乐播放器、手机)、视频显示(CRT 显示器)、通信系统(无线信号调制)、工业控制(执行器驱动)等。

关键性能指标


需要明确:输出电压由参考电压决定,输入的数字量起到一个分压作用。
分辨率:表示 DAC 能区分的最小电压变化,通常以位数衡量(如 16 位、24 位)。位数越高,输出越精细。
精度:包括微分非线性(DNL)和积分非线性(INL),反映输出与理想值的偏差,精度越高,信号失真越小。
建立时间:从输入变化到输出稳定所需的时间,高速应用(如通信)需短建立时间。
动态范围:最大信号与噪声的比值,决定 DAC 处理强弱信号的能力(单位:dB)。

典型应用场景


音频设备:如手机、耳机、Hi-Fi 播放器中的 DAC 芯片(如 AK4490、CS43131),直接影响音质。
工业控制:将数字指令转换为模拟信号控制电机、阀门等执行器(如 DAC0832)。
通信系统:生成调制信号用于无线传输。
医疗设备:控制成像设备或监测仪器的模拟输出。


选用芯片

这次使用的DAC芯片为TLV5618,其具体参数为:12位的分辨率;1Msps转换速率;双通道模拟输出(可使用两个通道A,B输出电压);最高可达到20M赫兹时钟输出
且经过换算关系有:输出电压U = 输入数字量code;



问题分析


1. 该模块书写思路与上一篇文章ADC转换大体上一致,同样我们写的是DAC芯片的驱动电路。我们所需明确的学习目标有:学会读芯片手册了解相关信息;学习线性序列机LSM的思想;


2. 根据芯片的时序要求,DAC芯片在SCLK的下降沿采样DIN上的数值(已经稳定),DIN数值在SCLK上升沿变化;值得注意的是:DIN的D15-D12为配置位(配置转换速率与功率;已经相应的通道选择);D11-D0才是正常数据位的传输。

3. 因为TLV5618芯片为双通道。那么如果我想要两个通道同时输出不同的电压值该怎么办呢?例如通道A输出1V,通道B输出2V。解决思路就是利用TLV5618芯片内部结构自带的buffer缓冲器!通过相应的通道选择模式,第一步先将2V输入buffer;第二步再将1V输入通道A,同时buffer输入给通道B。


4. 再有的就是要严格按照芯片的时序要求来设置驱动电路。考虑相关的建立时间与保持时间,例如在开始阶段,将CS电平拉低,DIN,SCLK电平拉高,这样可满足时序要求;同样的在结束阶段,在SCLK最后一个上升沿来临时,要保证CS为低电平,否则很有可能采样不到最后一个数据(芯片手册要求)。

5. 同时还有一些辅助信号的撰写(模仿上篇文章的思路),例如使能信号,采样开始与采样停止信号的触发条件等。(详情看代码)

好的,解决完上述问题,我们来代码


代码展示

//定义端口输入输出
module DAC_driver(Clk,Rst_n,DAC_DATA,Set_Go,Set_Done,DAC_CS_N,DAC_SCLK,DAC_DIN
);input Clk;input Rst_n;input [15:0]DAC_DATA;input Set_Go;output reg Set_Done;output reg DAC_CS_N;output reg DAC_SCLK;output reg DAC_DIN;parameter CLOCK_FREQ = 50_000_000;parameter SCLK_EREQ = 12_500_000;parameter MCNT_DIV_CNT = CLOCK_FREQ / (SCLK_EREQ * 2) - 1;reg [7:0]DIV_CNT;reg [5:0]LSM_CNT;reg [15:0]r_DAC_DATA;//一个暂存器,将用户输入的数据暂时存储     
always@(posedge Clk)
if(Set_Go)r_DAC_DATA <= DAC_DATA;
elser_DAC_DATA <= r_DAC_DATA;//使能信号的确立  reg Set_En;
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)Set_En <= 1'd0;
else if(Set_Go)Set_En <= 1'd1;
else if((LSM_CNT == 6'd33) && (DIV_CNT == MCNT_DIV_CNT))Set_En <= 1'd0;
elseSet_En <= Set_En;//最小时间单元
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)DIV_CNT <= 0;
else if(Set_En)beginif(DIV_CNT == MCNT_DIV_CNT) DIV_CNT <= 0;else DIV_CNT <= DIV_CNT + 1'd1;end
elseDIV_CNT <= 0;//序列计数器
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)LSM_CNT <= 6'd0;
else if(DIV_CNT == MCNT_DIV_CNT)beginif(LSM_CNT <= 6'd33)LSM_CNT <= 6'd0;elseLSM_CNT <= LSM_CNT + 1'd1;end
elseLSM_CNT <= LSM_CNT;//线性序列机的驱动输出
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)beginDAC_SCLK <= 1'd1;DAC_DIN <= 1'd1;DAC_CS_N <= 1'd1; end
else if(DIV_CNT == MCNT_DIV_CNT)begincase(LSM_CNT)0 : begin  DAC_CS_N <= 1'd0; DAC_DIN <= r_DAC_DATA[15]; DAC_SCLK <= 1'd1; end1 : begin  DAC_SCLK <= 1'd0; end2 : begin  DAC_SCLK <= 1'd1; DAC_DIN <= r_DAC_DATA[14]; end3 : begin  DAC_SCLK <= 1'd0; end4 : begin  DAC_SCLK <= 1'd1; DAC_DIN <= r_DAC_DATA[13]; end5 : begin  DAC_SCLK <= 1'd0; end6 : begin  DAC_SCLK <= 1'd1; DAC_DIN <= r_DAC_DATA[12]; end7 : begin  DAC_SCLK <= 1'd0; end8 : begin  DAC_SCLK <= 1'd1; DAC_DIN <= r_DAC_DATA[11]; end9 : begin  DAC_SCLK <= 1'd0; end10 : begin  DAC_SCLK <= 1'd1; DAC_DIN <= r_DAC_DATA[10]; end11 : begin  DAC_SCLK <= 1'd0; end12 : begin  DAC_SCLK <= 1'd1; DAC_DIN <= r_DAC_DATA[9]; end13 : begin  DAC_SCLK <= 1'd0; end14 : begin  DAC_SCLK <= 1'd1; DAC_DIN <= r_DAC_DATA[8]; end15 : begin  DAC_SCLK <= 1'd0; end16 : begin  DAC_SCLK <= 1'd1; DAC_DIN <= r_DAC_DATA[7]; end17 : begin  DAC_SCLK <= 1'd0; end18 : begin  DAC_SCLK <= 1'd1; DAC_DIN <= r_DAC_DATA[6]; end19 : begin  DAC_SCLK <= 1'd0; end20 : begin  DAC_SCLK <= 1'd1; DAC_DIN <= r_DAC_DATA[5]; end21 : begin  DAC_SCLK <= 1'd0; end22 : begin  DAC_SCLK <= 1'd1; DAC_DIN <= r_DAC_DATA[4]; end23 : begin  DAC_SCLK <= 1'd0; end24 : begin  DAC_SCLK <= 1'd1; DAC_DIN <= r_DAC_DATA[3]; end25 : begin  DAC_SCLK <= 1'd0; end26 : begin  DAC_SCLK <= 1'd1; DAC_DIN <= r_DAC_DATA[2]; end27 : begin  DAC_SCLK <= 1'd0; end28 : begin  DAC_SCLK <= 1'd1; DAC_DIN <= r_DAC_DATA[1]; end29 : begin  DAC_SCLK <= 1'd0; end30 : begin  DAC_SCLK <= 1'd1; DAC_DIN <= r_DAC_DATA[0]; end31 : begin  DAC_SCLK <= 1'd0; end32 : begin  DAC_SCLK <= 1'd1; end33 : begin  DAC_CS_N <= 1'd1; enddefault: DAC_CS_N <= 1'd1;endcaseend//产生结束信号 
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)Set_Done <= 0;
else if((LSM_CNT == 6'd33) && (DIV_CNT == MCNT_DIV_CNT))Set_Done <= 1'd1;
elseSet_Done <= 1'd0;endmodule


综合出来的底层系统逻辑图schematic如下:



http://www.ppmy.cn/embedded/172969.html

相关文章

大语言模型微调和大语言模型应用的区别?

1. 基本概念 微调&#xff08;Fine-tuning&#xff09; 定义&#xff1a;微调是指在预训练大语言模型的基础上&#xff0c;通过在特定领域或任务的数据上进一步训练&#xff0c;从而使模型在该特定任务上表现更优。 目的&#xff1a;适应具体的任务需求&#xff0c;比如法律文…

Linux——信号

前言&#xff1a;信号和信号量没有任何关系 操作系统开机即处于死循环状态&#xff0c;直到给操作系统发送信号时&#xff0c;才会开始执行相应操作。 1. 信号的产生 给进程产生信号的信号源是非常多的&#xff01; 1.1 键盘产生信号 打开VS&#xff0c;运行一个进程&#…

虚拟展览馆小程序:数字艺术与文化展示的新形式探索

虚拟展览馆小程序:数字艺术与文化展示的新形式探索 一、传统展览的痛点:物理空间的局限与数字化的必然 在传统的艺术与文化展览中,观众往往需要跨越地理距离、排队数小时才能进入展馆,而许多珍贵展品因保护需求无法长期展出。数据显示,全球90%以上的博物馆藏品常年沉睡于…

图像分类数据集

《动手学深度学习》-3.5-学习笔记 # 通过ToTensor实例将图像数据从PIL类型变换成32位浮点数格式&#xff0c; # 并除以255使得所有像素的数值均在0&#xff5e;1之间 trans transforms.ToTensor()#用于将图像数据从 PIL 图像格式&#xff08;Python Imaging Library&#xff…

Spring Boot与Apache Ignite集成:构建高性能分布式缓存和计算平台

1. 前言 1.1 什么是Apache Ignite Apache Ignite是一个高性能的分布式内存计算平台,支持内存缓存、分布式计算、流处理和机器学习等功能。它提供了低延迟的数据访问和强大的计算能力,适用于需要高性能和可扩展性的应用。 1.2 为什么选择Apache Ignite 高性能:Ignite利用内…

C++11多线程,锁与条件变量

&#x1f351;个人主页&#xff1a;Jupiter. &#x1f680; 所属专栏&#xff1a;C学习笔记 欢迎大家点赞收藏评论&#x1f60a; 目录 线程库1. thread类的简单介绍1.1constructor构造函数1.2 线程函数参数2. 原子性操作库(atomic)3. lock_guard与unique_lock3.1 mutex的种类3.…

Python :数据模型

一. 什么是数据模型&#xff1f; Python数据模型是Python对象系统的抽象&#xff0c;通过一组特殊方法​&#xff08;如__init__、__len__等&#xff09;和协议​&#xff08;如迭代协议、上下文管理协议&#xff09;&#xff0c;定义了对象如何与语言的内置功能&#xff08;如…

SQLark 实战 | 如何从Excel、csv、txt等外部文件进行数据导入

数据导入导出是应用开发者在平时开发中最常用的操作之一&#xff0c;SQLark 里提供了方便的图形化界面来完成导入导出。本文先和大家分享如何从 Excel、csv、txt 等外部文件导入数据到数据库表中。 &#x1f449; 前往 SQLark 官网&#xff1a;www.sqlark.com 下载全功能免费版…