STM32数模转换器(DAC)简析

发布时间:2023-08-03  

STM32F4xx系列提供的DAC模块是12 位电压输出数模转换器。DAC可以按 8 位或 12 位模式进行配置,并且可与DMA控制器配合使用。在 12 位模式下,数据可以采用左对齐或右对齐。DAC有两个输出通道,每个通道各有一个转换器。在DAC双通道模式下,每个通道可以单独进行转换;当两个通道组合在一起同步执行更新操作时,也可以同时进行转换。可通过一个输入参考电压引脚VREF+(与ADC共享)来提高分辨率。


DAC通道框图

图片

DAC引脚

图片

DAC通道使能

将 DAC_CR 寄存器中的相应 ENx 位置 1,即可接通对应 DAC 通道。经过一段启动时间tWAKEUP 后,DAC 通道被真正使能。

注意:ENx 位只会使能模拟 DAC Channelx 宏单元。即使 ENx 位复位, DAC Channelx 数字接口仍处于使能状态。

DAC输出缓冲器使能

DAC 集成了两个输出缓冲器,可用来降低输出阻抗并在不增加外部运算放大器的情况下直接驱动外部负载。通过 DAC_CR 寄存器中的相应 BOFFx 位,可使能或禁止各 DAC 通道输出缓冲器。

DAC数据格式

DAC同ADC一样,数据分为8 位右对齐、12 位左对齐和12 位右对齐,为方便数据写入和精度要求,一般采用12 位右对齐格式。

DAC转换

DAC_DORx 无法直接写入,任何数据都必须通过加载 DAC_DHRx 寄存器(写入DAC_DHR8Rx、DAC_DHR12Lx、DAC_DHR12Rx、DAC_DHR8RD、DAC_DHR12LD 或DAC_DHR12LD)才能传输到 DAC 通道 x。

如果未选择硬件触发(DAC_CR 寄存器中的 TENx 位复位),那么经过一个 APB1 时钟周期后,DAC_DHRx 寄存器中存储的数据将自动转移到 DAC_DORx 寄存器。但是,如果选择硬件触发(置位 DAC_CR 寄存器中的 TENx 位)且触发条件到来,将在三个 APB1 时钟周期后进行转移。

当 DAC_DORx 加载了 DAC_DHRx 内容时,模拟输出电压将在一段时间 t SETTLING 后可用,具体时间取决于电源电压和模拟输出负载。

DAC输出电压

经过线性转换后,数字输入会转换为0到VREF+之间的输出电压。

各DAC通道引脚的模拟输出电压通过以下公式确定:

DACoutput = VREF* DOR/4095

DAC触发选择

如果 TENx 控制位置 1,可通过外部事件(定时计数器、外部中断线)触发转换。TSELx[2:0]控制位将决定通过 8 个可能事件中的哪一个来触发转换,如下表所示:

图片

每当 DAC 接口在所选定时器 TRGO 输出或所选外部中断线 9 上检测到上升沿时,DAC_DHRx寄存器中存储的最后一个数据即会转移到DAC_DORx 寄存器中。发生触发后再经过三个APB1 周期,DAC_DORx 寄存器将会得到更新。

如果选择软件触发,一旦 SWTRIG 位置 1,转换即会开始。DAC_DHRx 寄存器内容加载到DAC_DORx 寄存器中后,SWTRIG 即由硬件复位。

DMA请求

每个 DAC 通道都具有 DMA 功能。两个 DMA 通道用于处理 DAC 通道的 DMA 请求。这里不做详细介绍。

生成波形

可以根据需要,配置相应的寄存器生成噪声、三角波,也可以不生成波形。

下面了解下DAC相关的寄存器:

DAC 控制寄存器 (DAC_CR)

图片

位 29 DMAUDRIE2:DAC 2 通道 DMA 下溢中断使能 (DAC channel2 DMA underrun interrupt enable)

此位由软件置 1 和清零。

0:禁止 DAC 2 通道 DMA 下溢中断

1:使能 DAC 2 通道 DMA 下溢中断

位 28 DMAEN2:DAC 2 通道 DMA 使能 (DAC channel2 DMA enable)

此位由软件置 1 和清零。

0:禁止 DAC 2 通道 DMA 模式

1:使能 DAC 2 通道 DMA 模式

位 27:24 MAMP2[3:0]:DAC 2 通道掩码/振幅选择器 (DAC channel2 mask/amplitude selector)

这些位由软件写入,用于在生成噪声波模式下选择掩码,或者在生成三角波模式下选择振幅。

0000:不屏蔽 LFSR 的位 0/三角波振幅等于 1

0001:不屏蔽 LFSR 的位 [1:0]/三角波振幅等于 3

0010:不屏蔽 LFSR 的位 [2:0]/三角波振幅等于 7

0011:不屏蔽 LFSR 的位 [3:0]/三角波振幅等于 15

0100:不屏蔽 LFSR 的位 [4:0]/三角波振幅等于 31

0101:不屏蔽 LFSR 的位 [5:0]/三角波振幅等于 63

0110:不屏蔽 LFSR 的位 [6:0]/三角波振幅等于 127

0111:不屏蔽 LFSR 的位 [7:0]/三角波振幅等于 255

1000:不屏蔽 LFSR 的位 [8:0]/三角波振幅等于 511

1001:不屏蔽 LFSR 的位 [9:0]/三角波振幅等于 1023

1010:不屏蔽 LFSR 的位 [10:0]/三角波振幅等于 2047

=1011:不屏蔽 LFSR 的位 [11:0]/三角波振幅等于 4095

位 23:22 WAVE2[1:0]:DAC 2 通道噪声/三角波生成使能 (DAC channel2 noise/triangle wave generation enable)

这些位由软件置 1 或清零。

00:禁止生成波

01:使能生成噪声波

1x:使能生成三角波

注意:只在位 TEN2 = 1 (使能 DAC 2 通道触发)时使用

位 21:19 TSEL2[2:0]:DAC 2 通道触发器选择 (DAC channel2 trigger selection)

这些位用于选择 DAC 2 通道的外部触发事件

000:定时器 6 TRGO 事件

001:定时器 8 TRGO 事件

010:定时器 7 TRGO 事件

011:定时器 5 TRGO 事件

100:定时器 2 TRGO 事件

101:定时器 4 TRGO 事件

110:外部中断线 9

111:软件触发

注意:只在位 TEN2 = 1 (使能 DAC 2 通道触发)时使用。

位 18 TEN2:DAC 2 通道触发使能 (DAC channel2 trigger enable)

此位由软件置 1 和清零,以使能/禁止 DAC 2 通道触发

0:禁止 DAC 2 通道触发,写入 DAC_DHRx 寄存器的数据在一个 APB1 时钟周期之后转移到 DAC_DOR2 寄存器

1:使能 DAC 2 通道触发,DAC_DHRx 寄存器的数据在三个 APB1 时钟周期之后转移到DAC_DOR2 寄存器

注意:如果选择软件触发,DAC_DHRx 寄存器 的内容只需一个 APB1 时钟周期即可转移到DAC_DOR2 寄存器。

位 17 BOFF2:DAC 2 通道输出缓冲器禁止 (DAC channel2 output buffer disable)

此位由软件置 1 和清零,以使能/禁止 DAC 2 通道输出缓冲器。

0:使能 DAC 2 通道输出缓冲器

1:禁止 DAC 2 通道输出缓冲器

位 16 EN2:DAC 2 通道使能 (DAC channel2 enable)

此位由软件置 1 和清零,以使能/禁止 DAC 2 通道。

0:禁止 DAC 2 通道

1:使能 DAC 2 通道

位 13 DMAUDRIE1:DAC 1 通道 DMA 下溢中断使能 (DAC channel1 DMA Underrun Interrupt enable)

此位由软件置 1 和清零。

0:禁止 DAC 1 通道 DMA 下溢中断

1:使能 DAC 1 通道 DMA 下溢中断

位 12 DMAEN1:DAC 1 通道 DMA 使能 (DAC channel1 DMA enable)

此位由软件置 1 和清零。

0:禁止 DAC 1 通道 DMA 模式

1:使能 DAC 1 通道 DMA 模式

位 11:8 MAMP1[3:0]:DAC 1 通道掩码/振幅选择器 (DAC channel1 mask/amplitude selector)

这些位由软件写入,用于在生成噪声波模式下选择掩码,或者在生成三角波模式下选择振幅。

0000:不屏蔽 LFSR 的位 0/三角波振幅等于 1

0001:不屏蔽 LFSR 的位 [1:0]/三角波振幅等于 3

0010:不屏蔽 LFSR 的位 [2:0]/三角波振幅等于 7

0011:不屏蔽 LFSR 的位 [3:0]/三角波振幅等于 15

0100:不屏蔽 LFSR 的位 [4:0]/三角波振幅等于 31

0101:不屏蔽 LFSR 的位 [5:0]/三角波振幅等于 63

0110:不屏蔽 LFSR 的位 [6:0]/三角波振幅等于 127

0111:不屏蔽 LFSR 的位 [7:0]/三角波振幅等于 255

1000:不屏蔽 LFSR 的位 [8:0]/三角波振幅等于 511

1001:不屏蔽 LFSR 的位 [9:0]/三角波振幅等于 1023

1010:不屏蔽 LFSR 的位 [10:0]/三角波振幅等于 2047

=1011:不屏蔽 LFSR 的位 [11:0]/三角波振幅等于 4095

位 7:6 WAVE1[1:0]:DAC 1 通道噪声/三角波生成使能 (DAC channel1 noise/triangle wave generation enable)

这些位将由软件置 1 和清零。

00:禁止生成波

01:使能生成噪声波

1x:使能生成三角波

注意:只在位 TEN1 = 1 (使能 DAC 1 通道触发)时使用。

位 5:3 TSEL1[2:0]:DAC 1 通道触发器选择 (DAC channel1 trigger selection)

这些位用于选择 DAC 1 通道的外部触发事件。

000:定时器 6 TRGO 事件

001:定时器 8 TRGO 事件

010:定时器 7 TRGO 事件

011:定时器 5 TRGO 事件

100:定时器 2 TRGO 事件

101:定时器 4 TRGO 事件

110:外部中断线 9

111:软件触发

注意:只在位 TEN1 = 1 (使能 DAC 1 通道触发)时使用。

位 2 TEN1:DAC 1 通道触发使能 (DAC channel1 trigger enable)

此位由软件置 1 和清零,以使能/禁止 DAC 1 通道触发。

0:禁止 DAC 1 通道触发,写入 DAC_DHRx 寄存器的数据在一个 APB1 时钟周期之后转移到 DAC_DOR1 寄存器

1:使能 DAC 1 通道触发,DAC_DHRx 寄存器的数据在三个 APB1 时钟周期之后转移到DAC_DOR1 寄存器

注意:如果选择软件触发, DAC_DHRx 寄存器的内容只需一个 APB1 时钟周期即可转移到DAC_DOR1 寄存器。

位 1 BOFF1:DAC 1 通道输出缓冲器禁止 (DAC channel1 output buffer disable)

此位由软件置 1 和清零,以使能/禁止 DAC 1 通道输出缓冲器。

0:使能 DAC 1 通道输出缓冲器

1:禁止 DAC 1 通道输出缓冲器

位 0 EN1:DAC 1 通道使能 (DAC channel1 enable)

此位由软件置 1 和清零,以使能/禁止 DAC 1 通道。

0:禁止 DAC 1 通道

1:使能 DAC 1 通道

DAC 软件触发寄存器 (DAC_SWTRIGR)

图片

位 1 SWTRIG2:DAC 2 通道软件触发 (DAC channel2 software trigger)

此位由软件置 1 和清零,以使能/禁止软件触发。

0:禁止软件触发

1:使能软件触发

注意:一旦 DAC_DHR2 寄存器值加载到 DAC_DOR2 寄存器中,该位即会由硬件清零(一个APB1 时钟周期之后)。

位 0 SWTRIG1:DAC 1 通道软件触发 (DAC channel1 software trigger)

此位由软件置 1 和清零,以使能/禁止软件触发。

0:禁止软件触发

1:使能软件触发

注意:一旦 DAC_DHR1 寄存器值加载到 DAC_DOR1 寄存器中,该位即会由硬件清零(一个APB1 时钟周期之后)。

DAC 1 通道 12 位右对齐数据保持寄存器 (DAC_DHR12R1)

图片

位 11:0 DACC1DHR[11:0]:DAC 1 通道 12 位右对齐数据 (DAC channel1 12-bit right-aligned data)

这些位由软件写入,用于为 DAC 1 通道指定 12 位数据。

下面开始编写DAC1初始化函数和数据写入函数

void DAC1_Init()

{

  //1. 开PA时钟

  RCC- >AHB1ENR  |= 1< < 0;

  //2. 模式:模拟

  GPIOA- >MODER  |= 3< < 8;


  //3. 开DAC1时钟(DAC1和DAC2共用一个时钟)

  RCC- >APB1ENR   |= 1< < 29;

  DAC- >CR  &= 0XFFFF0000;

  DAC- >CR  |= 7< < 3;      //软件触发,不生产波形

  DAC- >CR  |= 0< < 1;      //使能输出缓冲驱动器

  DAC- >CR  |= 1< < 0;      //使能通道

  Delay_us(10);

}



//使能软件触发函数

//一旦 DAC_DHR1寄存器值加载到DAC_DOR1寄存器中,该位即会由硬件清零(一个APB1时钟周期之后)

void DAC1_Trigger()

{

  DAC- >SWTRIGR |= 1< < 0;

}



//把数据写入12位右对齐数据保持寄存器 

void DAC1_SendDat(u16 *dat)

{

  DAC- >DHR12R1 = *dat;    

}

接下来编写测试函数


u16 DAC_buf[1024] = {0};



void DAC1_GetSine()//把DAC_buf赋值为正弦波的数组,用于模拟呼吸灯

{

  u16 i = 0;


  for(i=0;i< 256;i++)  //0-π/2

  {

    DAC_buf[i] = (u16)((1.65+1.65*sin(3.14/512*i))*4096/3.3);

  }

  for(i=1;i<=256;i++)  //π/2~π

  {

    DAC_buf[255+i] = DAC_buf[256-i];

  }


  for(i=1;i< 256;i++)  //π~3/2π

  {

    DAC_buf[511+i] = 4096 - DAC_buf[i];

  }


  for(i=1;i< 256;i++)  //3/2π~2π

  {

    DAC_buf[767+i] = DAC_buf[768-i];

  }

}

#include "stm32f4xx.h"

#include "delay.h"

#include "math.h"

#include "dac.h"



int main()

{

  u32 i=0;

  DAC1_Init();

  DAC1_GetSine();


  while(1)

  {

    for(i=0;i< 1024;i++)

    {

      DAC1_SendDat(&DAC_buf[i]);

      DAC1_Trigger();

      Delay_ms(5);

    }

  }

}

对于STM32来说,并不是所有型号都有;就算有DAC,资源也非常有限。而STM32所有的芯片都有PWM输出,并且PWM输出通道很多,资源丰富。因此,我们可以使用PWM来实现DAC的输出从而节省成本。

void DAC1_Init()

{

  //1. 开PA时钟

  RCC- >AHB1ENR  |= 1< < 0;

  //2. 模式:模拟

  GPIOA- >MODER  |= 3< < 8;


  //3. 开DAC1时钟(DAC1和DAC2共用一个时钟)

  RCC- >APB1ENR   |= 1< < 29;

  DAC- >CR  &= 0XFFFF0000;

  DAC- >CR  |= 7< < 3;      //软件触发,不生产波形

  DAC- >CR  |= 0< < 1;      //使能输出缓冲驱动器

  DAC- >CR  |= 1< < 0;      //使能通道

  Delay_us(10);

}



//使能软件触发函数

//一旦 DAC_DHR1寄存器值加载到DAC_DOR1寄存器中,该位即会由硬件清零(一个APB1时钟周期之后)

void DAC1_Trigger()

{

  DAC- >SWTRIGR |= 1< < 0;

}



//把数据写入12位右对齐数据保持寄存器 

void DAC1_SendDat(u16 *dat)

{

  DAC- >DHR12R1 = *dat;    

}

接下来编写测试函数


u16 DAC_buf[1024] = {0};



void DAC1_GetSine()//把DAC_buf赋值为正弦波的数组,用于模拟呼吸灯

{

  u16 i = 0;


  for(i=0;i< 256;i++)  //0-π/2

  {

    DAC_buf[i] = (u16)((1.65+1.65*sin(3.14/512*i))*4096/3.3);

  }

  for(i=1;i<=256;i++)  //π/2~π

  {

    DAC_buf[255+i] = DAC_buf[256-i];

  }


  for(i=1;i< 256;i++)  //π~3/2π

  {

    DAC_buf[511+i] = 4096 - DAC_buf[i];

  }


  for(i=1;i< 256;i++)  //3/2π~2π

  {

    DAC_buf[767+i] = DAC_buf[768-i];

  }

}

#include "stm32f4xx.h"

#include "delay.h"

#include "math.h"

#include "dac.h"



int main()

{

  u32 i=0;

  DAC1_Init();

  DAC1_GetSine();


  while(1)

  {

    for(i=0;i< 1024;i++)

    {

      DAC1_SendDat(&DAC_buf[i]);

      DAC1_Trigger();

      Delay_ms(5);

    }

  }

}

对于STM32来说,并不是所有型号都有;就算有DAC,资源也非常有限。而STM32所有的芯片都有PWM输出,并且PWM输出通道很多,资源丰富。因此,我们可以使用PWM来实现DAC的输出从而节省成本。


文章来源于:电子工程世界    原文链接
本站所有转载文章系出于传递更多信息之目的,且明确注明来源,不希望被转载的媒体或个人可与我们联系,我们将立即进行删除处理。

我们与500+贴片厂合作,完美满足客户的定制需求。为品牌提供定制化的推广方案、专属产品特色页,多渠道推广,SEM/SEO精准营销以及与公众号的联合推广...详细>>

利用葫芦芯平台的卓越技术服务和新产品推广能力,原厂代理能轻松打入消费物联网(IOT)、信息与通信(ICT)、汽车及新能源汽车、工业自动化及工业物联网、装备及功率电子...详细>>

充分利用其强大的电子元器件采购流量,创新性地为这些物料提供了一个全新的窗口。我们的高效数字营销技术,不仅可以助你轻松识别与连接到需求方,更能够极大地提高“闲置物料”的处理能力,通过葫芦芯平台...详细>>

我们的目标很明确:构建一个全方位的半导体产业生态系统。成为一家全球领先的半导体互联网生态公司。目前,我们已成功打造了智能汽车、智能家居、大健康医疗、机器人和材料等五大生态领域。更为重要的是...详细>>

我们深知加工与定制类服务商的价值和重要性,因此,我们倾力为您提供最顶尖的营销资源。在我们的平台上,您可以直接接触到100万的研发工程师和采购工程师,以及10万的活跃客户群体...详细>>

凭借我们强大的专业流量和尖端的互联网数字营销技术,我们承诺为原厂提供免费的产品资料推广服务。无论是最新的资讯、技术动态还是创新产品,都可以通过我们的平台迅速传达给目标客户...详细>>

我们不止于将线索转化为潜在客户。葫芦芯平台致力于形成业务闭环,从引流、宣传到最终销售,全程跟进,确保每一个potential lead都得到妥善处理,从而大幅提高转化率。不仅如此...详细>>