这一节我们来学习一下DAC的原理以及实战用法!!!

一、DAC简介

DAC(Digital to analog converter),即数字模拟转换器,是将数字信号转换为模拟信号的电路。其输入为数字信号输出为模拟信号。在常见的数字信号系统中,大部分传感器信号被化成电压信号,而 ADC 把电压模拟信号转换成易于计算机存储、处理的数字编码,由计算机处理完成后,再由 DAC 输出电压模拟信号,该电压模拟信号常用来驱动某些执行器件,使人类易于感知,如音频广播信号的采集及还原就是这样一个过程。

二、基本原理

数模转换器(DAC)的核心功能是将二进制数据转换为模拟电压或电流。其工作原理是通过解析输入的二进制编码,生成对应的连续或分段模拟信号输出。转换质量主要受制于器件的分辨率(量化精度)和刷新频率,其中高分辨率与快速更新能力可显著提升信号保真度。从电路实现原理划分,DAC主要采用电流合成电阻分压两种基础架构。

三 、常见的电路类型

1. 开关树型: 简单粗暴,由电阻分压器和树状的开关网络组成。
Alt
这些开关分别受 3 位输入d0,d1,d2 控制,所以有:
在这里插入图片描述

进一步看,对于 n 位二进制输入的开关树型 DAC,输出为:
在这里插入图片描述

总结: 开关树型 DAC 特点是电阻种类单一,根据欧姆定理U=IR,在输出端基本不取电流的情况下,对开关导通电阻要求不高,但缺点是用的开关太多。

2、权电阻网络型: 它由权电阻、4 个模拟开关和 1 个求和放大器组成。
在这里插入图片描述
由图可知是反相求和放大器,电路结构:由运算放大器和多个输入电阻、一个反馈电阻组成。输入电压通过各自的输入电阻连接到运算放大器的反相输入端,同相输入端接地,输出端通过反馈电阻连接到反相输入端。
为了方便电路的计算 ,先来学习一下运算放大器的虚短虚断特性基尔霍夫定理

虚短虚断:“虚短”指运算放大器的两个输入端在理想情况下电位相等,就像它们之间有一个短路连接一样,但实际上并没有物理上的短路(V+ = V-)。“虚断”顾名思义就是输入端断开(即V+和V-端流入运放的电流等于0)指运算放大器的输入端在理想情况下对地呈现开路状态,即输入阻抗无限大,几乎不会有电流流入输入端,简化了电路计算。

基尔霍夫电流定律:一个节点的电流和为0,即在集总参数电路中,任一节点在任一时刻,流入该节点的电流代数和等于流出该节点的电流代数和。
因此:
由基尔霍夫定理得:流入反相输入端的电流等于流出反相输入端的电流。各输入电流在反相输入端相加后通过反馈电阻形成输出电流,进而在输出端得到与输入电压总和成比例的输出电压。
在这里插入图片描述
输入放大器电流等于0:
公式中i是求和

又因为虚短,V- = V+ = 0,所以:
在这里插入图片描述
反馈电阻为:RF = R/2,因此:
在这里插入图片描述
除了以上两种类型:还有倒T型电阻网络、权电流型,这里不再作介绍,有需要自行参考以下链接文章进行学习。DAC电路类型解析

四、DAC的主要参数

1、分辨率和精度
分辨率:DAC的分辨率是指其可以表示的模拟值的数量。对于6bitDAC,它能够表示2^6=64个不同的电压或电流值。
精度:DAC的精度是指其转换结果与理想值之间的接近程度。通常以最大误差(如满量程的百分比)来表示。
选择规则:
根据应用对精度的要求来选择DAC。例如,音频应用可能需要较高的精度,而一些工业控制应用可能对精度要求较低。
对于需要高精度输出的应用,可以选择具有更高bit数的DAC,例如12bit或16bit,以提高分辨率和精度。

2、转换速度
转换速度:DAC的转换速度是指它能够处理输入数据并输出模拟信号的速度。
选择规则:对于需要快速响应的应用,如高频信号处理,应选择转换速度较快的DAC。对于静态或低频信号的应用,转换速度不是主要考虑因素。

3、输出范围
输出范围:DAC的输出范围是指它能够产生的模拟信号的最大和最小值。
选择规则:根据应用对模拟信号范围的需求来选择DAC,确保DAC的输出范围能够覆盖应用所需的模拟信号范围。

五、STM32的DAC

1、DAC等效电路
在这里插入图片描述
(1)可以看作是一个数字控制的电压源以及一个输出阻抗。
(2)DAC的输出阻抗恒定,与数字输入无关。
在这里插入图片描述
(3)当输出缓存区关闭时,DACINT和DACOUT通过Rb连接,当S1和S2打开时,输出阻抗是Rb+Ra = 2Ra。
(4)当输出缓存区打开时,运算放大器被配置为一个带有Av = -1的反相放大器,由于反馈回路,输出阻抗几乎为零。

由于stm32F10X中只有正参考电压,无负参考电压,故负参考电压默认为接地,又根据运算放大器的理想特性虚短虚断,得出对应结果,虚断:i进 = i出,虚短:相当于正反相短接,V+ = V-。

2、DAC Speed
(1)当在DAC输出上使能输出缓冲区,速率由输出缓冲区的性能决定。
(2)当DAC输出上失能输出缓冲区,输出速度仅遵循RC常数,该常数由输出阻抗Rdac = 2Ra和DACOUT焊盘上的容性负载决定的。以下是DAC转换的最小时间计算。
在这里插入图片描述
3、失能DAC输出缓存,在外部接运算放大器实现
在这里插入图片描述
(1)当使能缓冲区,DAC的转换时间是由缓冲区定。
(2)当失能缓冲区,DAC的转换时间取决于输出阻抗和DACOUT焊盘的电容特性决定,以上电路是可以忽略电容特性。
(3)在上面电路中,RC常数有一个很小的影响,主要还是看运算放大器和DAC数字数据更新速率。反馈电阻R1必须为STM32的片上Rdac,否则会产生DAC增益误差。

4、DAC数据更新(DHR保持寄存器->>DOR输出寄存器)
(1)首先,DAC的数字数据先写入到保持寄存器(DHR),然后将保持寄存器的数据移动至输出寄存器(DOR)。
(2)DAC数据一般保存在RAM中,由CPU负责从RAM搬运到DAC。
(3)对比第二点,若使用DMA进行数据传送,可以释放CPU的压力,提高数据更新性能。
(4)如果想要实现高速的DAC输出,建议使用DMA+定时器触发的方式完成数据传输。
(5)数据从内存到DAC的传输速率影响因素:1、DAC时钟周期(APB(高级高性能总线)/AHB(高级外设总线))2、从内存到DAC的DMA传输周期。3、触发机制本身。

5、DAC基础(基于STM32F10x)
(1)DAC模块只存在于大容量产品(256-512K)、互联型产品中。
(2)DAC简介
在这里插入图片描述
(3)DAC主要特征
在这里插入图片描述
(4)DAC模块框图
在这里插入图片描述
由上图可知,实际主要是操作DHRx(数据保持寄存器)设置DAC值,不能直接操作DORx(数据输出寄存器)。
软件触发或者外部事件触发、DMA触发使能、噪声模式、三角波模式,以上均是通过DAC CR控制寄存器的配置实现。

(5)DAC DHRx寄存器中的数据格式
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

一般是选择右对齐方式使用。为什么一般选择右对齐的方式?因为右对齐比较直观,数据直接放在低12位,直接与硬件兼容,代码上无需进行移位,减少了代码的复杂度和潜在错误,若使用左对齐,可能需要进行移位操作。

(6)DAC触发源
在这里插入图片描述 三种DAC触发转换方式: 自动触发、软件触发、外部事件触发

在这里插入图片描述这是TEN = 0时,即自动触发,一个APB1周期后,DHR将数据移至DOR,然后在经过tSETTLING时间稳定后有效可用,这个时间长短依据电源电压和模拟输出负载的不同会有所变化。反之,TEN = 1时,即是外部触发,外部事件触发是三个APB1周期后,DHR将数据移至DOR;软件触发是一个APB1周期后,DHR将数据移至DOR。

(7)DMA控制DAC数据搬运
在这里插入图片描述必须是使用外部事件触发DAC,无法使用软件触发,触发后产生DMA请求,通过DMA的方式直接将某地址的数据移至DHRx,同时可以设置地址自增,将提前实现波形的数据搬运至DHRx,可现实正弦波、三角波等波形的输出,使用此方式可以减轻CPU的负担,效率也会更高。

(8)噪声和相关波形的生成
在这里插入图片描述线性反馈寄存器LFSR可以参考:线性移位寄存器算法分析
线性反馈移位寄存器算法的简介,如上图为例:
主要是通过一个异或门和一个或非门组成,每次整体右移一位,然后选取特定位作为异或门的输入,其输出作为反馈值重新输入到移位寄存器的最高位,然后不断地更新移位寄存器的值,图示描述是线性反馈的,具有一定的规律。
噪声:最终的结果是按特定的算法产生幅值不同的电压,波形看起来像噪声。
应用场景:1、可用于电路噪声抑制能力的输入。2、测试控制系统、通信系统的抗噪声能力和鲁棒性。

在这里插入图片描述应用场景:LED调光、用于测试等等。
总结:噪声和三角波是通过硬件方式生成的,无需CPU干预,提升效率,如果要生成正弦波、梯形波等波形,由于没有硬件支持,那就需要CPU的模拟生成相应的波形,具体可以通过DMA方式实现。

6、DAC的实战用法(STM32Cubemx(V6.7.0)HAL库 )
1、当我们的时钟源、debug方式设置完成后,进行设置我们的DAC。
2、在stm32cubemx创建好工程后,对Analog下的DAC进行配置。在Analog中选择DAC设置,然后选择OUT1 Configuration通道1(OUT2 Configuration通道2),此操作作用是激活DAC。
在这里插入图片描述或者在芯片GUI里面找到PA4(PA5)引脚,选择DAC_OUT1(DAC_OUT2),如下图
在这里插入图片描述3、具体参数配置如下:
在这里插入图片描述(1)OUT1/2 Configuration: 对应的激活两个DAC输出通道。
(2)External Trigger: 外部中断EXTI9触发,使用外部中断9来触发DAC.
(3)Output Buffer: 使能DAC输出缓存。
(4)Trigger: 选择触发源来触发DAC的转换,配置控制位TSELx[2:0]可以选择8个触发事件之一,具体触发源如下图。
在这里插入图片描述
在STM32cubemx上要配置EXTI9的外部中断事件触发需要选择“External Trigger”,否则的话是没有External line 9这个选项的。
在这里插入图片描述
其中有六个是定时器触发分别是:TIM2、互联型TIM3(大容量TIM8)、TIM4、TIM5、TIM6、TIM7,还有两个是EXTI Line 9和软件触发。DAC接口每次检测到来自外部中断事件,在3个APB1时钟周期后,近存放在DAC_DHRx中的数据就会被送到DAC_DORx寄存器中;如果是选择软件触发,一旦SWTRIGx位置“1”,一个APB1时钟周期后,近存放在DAC_DHRx中的数据就会被送到DAC_DORx寄存器中,然后SWTRIGx位由硬件自动清0。

4、DMA配置:
在这里插入图片描述
如上所述,在DAC申请DMA请求的情况,如果两个DAC都同时触发申请了DMA请求,但是第一个的DMA请求还没响应完成,则第二个DMA请求就会被干掉。DMA请求是外部触发(非软件触发),然后产生一个DMA请求,然后将数据从DHRx>>DORx的,执行HAL_DAC_Start_DMA()函数把RAM中的变量数据搬运到DHRx中且需要外部事件触发,软件触发和自动触发也无效。
在这里插入图片描述
(1)Add: 选择激活DAC1/DAC2的DMA通道。
(2)Mode: 选择正常触发/循环触发,正常触发是执行完停止,需重新调用函数;循环触发是在每次执行完后继续执行,以此往复。
(3)Increment Address: 地址自增,每次执行完后,对应的内存地址自增,这样子可以实现连续不同电压的输出。
(4)Data Width: 数据长度,即每次DMA要传输的数据长度,Word:字(32bit),Half Word:半字(16bit),Byte:字节(1bit)。

5、实现正弦波的DAC输出:
实现方式: 采集TIM+DMA方式实现,TIM产生更新事件触发DAC转换,DMA方式实现DHRx->DORx的数据搬运操作,DMA选择循坏模式,TIM2作为触发源,其中PSC=0,ARR=19。
(1)利用正弦函数V=sin(t)+1,得出V的输出范围是[0:2],扩展到DAC的输出电压范围为[0:3.3],所以得出公式:V=3.3*(sin(t)+1)/2。
(2)映射到寄存器的输出范围[0:4095],Regdata = 4095*(sin(t)+1)/2,其中 t = [0:2π],取32个点已经较好的还原正弦波形,增量=1/32=0.03125即每次要增加的间隔,如4095*(0+1)/2、4095*(0.03125+1)/2、4095*(0.0625+1)/2、4095*(0.09375+1)/2、…、4095*(-0.03125+1)/2、4095*(0+1)/2以此类推的增加,最终得出如下波形:
在这里插入图片描述
对应32个点的数据:2048, 2460, 2856, 3218, 3532, 3786, 3969, 4072, 4093, 4031, 3887, 3668, 3382, 3042, 2661, 2255, 1841, 1435, 1054, 714, 428, 209, 65, 3, 24, 127, 310, 564, 878, 1240, 1636, 2048
(3)在代码中设置一个数组,将32的数据作为正弦波的数据表:

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */
const uint32_t data[32] = { 2048  , 2460  , 2856  , 3218  , 3532  , 3786  , 3969  , 4072  ,
    4093  , 4031  , 3887  , 3668  , 3382  , 3042  , 2661  , 2255  ,
    1841  , 1435  , 1054  , 714   , 428   , 209   , 65    , 3     ,
    24    , 127   , 310   , 564   , 878   , 1240  , 1636  , 2048
};
/* USER CODE END PV */

(4)添加 HAL_TIM_Base_Start() 函数,启动定时器。
添加 HAL_DAC_Start_DMA()函数,启动 DAC的DMA输出,使用DMA方式只能使用外部触发。

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_TIM2_Init();
  MX_DAC_Init();
  /* USER CODE BEGIN 2 */
	HAL_DAC_Start_DMA(&hdac, DAC_CHANNEL_2, data, 32, DAC_ALIGN_12B_R);
	HAL_TIM_Base_Start(&htim2);
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
	 //HAL_DAC_Start(&hdac, DAC_CHANNEL_1);
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

(5)烧录后,然后实验现象是输出正弦波电压,如下:
111
(6)波形频率计算公式,例子如下:
在这里插入图片描述可能是由于定时器选择的内部时钟误差以及是用测量点外有其他电路的干扰的原因,导致实验测出来106k左右,待验证。

疑问点1: 改变示波器的触发电压,频率106k是否会发生变化,是否与理论112500hz对应?
答:改变示波器触发电压,正弦波波形未发生改变,与理论值不符,因此与此无关。
疑问点2: 在正弦波实验中,其多少触发频率内有效,即触发频率多少时实测与理论频率对应?
答:由于正弦波实验是通过TIM2的更新事件触发DMA请求的,因此在原基础上逐步减少TIM2的ARR值以改变频率,最终发现在触发3M左右,实测频率与理论频率对应,因此建议DAC触发频率不要大于3M,否则存在失效风险。

注意!!!:如果触发的时间太快,如果同时开启双通道的DAC DMA转换,会出现另一个通道无法输出的问题,原因是由于第一个外部触发到了,然后两个DAC被触发,产生两个DMA请求,第一个DMA请求还没有被响应,第二个DMA请求在第一个执行完前来了,所以就被丢掉。
在这里插入图片描述
6、实现三角波输出

(1)利用硬件自带的电路直接实现三角波输出,配置如下:
(2)在GUI中选择Wave generation mode为Triangle wave generation,然后选择需要的三角波幅值,我选择4095,同时选择为软件触发(其他触发源触发也可以)如下:
在这里插入图片描述
(3)添加 HAL_DAC_Start()函数,启动 DAC的输出,由于是硬件自动清0,所以放在while里面一直触发了,按需求选择自己所需的触发频率即可,如下:

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_TIM2_Init();
  MX_DAC_Init();
  /* USER CODE BEGIN 2 */

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
	 HAL_DAC_Start(&hdac, DAC_CHANNEL_1);
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

(4)实验效果图
在这里插入图片描述
三角波的特点:
在这里插入图片描述

7、实现噪声输出
(1)利用硬件自带的电路直接实现噪声输出,配置如下:
(2)在GUI中选择Wave generation mode为Noise wave generation,然后选择需要的噪声幅值,我选择12bit的,同时选择为软件触发(其他触发源触发也可以)如下:
在这里插入图片描述
(3)添加 HAL_DAC_Start()函数,启动 DAC的输出,由于是硬件自动清0,所以放在while里面一直触发了,选择自己所需的触发频率即可,代码与上面的三角波实现一样,如下:

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_TIM2_Init();
  MX_DAC_Init();
  /* USER CODE BEGIN 2 */

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
	 HAL_DAC_Start(&hdac, DAC_CHANNEL_1);
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

(4)实验效果图
在这里插入图片描述
总结:DAC的基本原理和用法已经介绍完毕,由于作者本人的能力水平有限,若有错误请大胆指正。其中文章有参考到相关文章以及引用相关图片,由于太多就不一一罗列了,若有侵权请联系作者,文章只用作学习用途,非商业性质。创作不易,请各位朋友们点个赞和关注。再次表示感谢!!!

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐