DAC原理与应用详解

第一部分:stm32

1、问题:什么是DAC?
回答:数模转换器
2、stm32 DAC简介
STM32 的 DAC 模块(数字/模拟转换模块)是 12 位数字输入,电压输出型的 DAC。DAC 可以配置为 8 位或 12 位模式,也可以与 DMA 控制器配合使用。DAC 工作在 12 位模式时,数据可以设置成左对齐或右对齐。DAC 模块有 2 个输出通道,每个通道都有单独的转换器。在双 DAC模式下,2 个通道可以独立地进行转换,也可以同时进行转换并同步地更新 2 个通道的输出。DAC 可以通过引脚输入参考电压 VREF+以获得更精确的转换结果。
3、STM32 的 DAC 模块主要特点有:
① 2 个 DAC 转换器:每个转换器对应 1 个输出通道
② 8 位或者 12 位单调输出
③ 12 位模式下数据左对齐或者右对齐
④ 同步更新功能
⑤ 噪声波形生成
⑥ 三角波形生成
⑦ 双 DAC 通道同时或者分别转换
⑧ 每个通道都有 DMA 功能
4、单个 DAC 通道的框图如图所示:
在这里插入图片描述
图中 VDDA 和 VSSA 为 DAC 模块模拟部分的供电,而 Vref+则是 DAC 模块的参考电压。DAC_OUTx 就是 DAC 的输出通道了。
从图中可以看出,DAC 输出是受 DORx 寄存器直接控制的,但是我们不能直接往 DORx寄存器写入数据,而是通过 DHRx 间接的传给 DORx 寄存器,实现对 DAC 输出的控制。前面我们提到,STM32 的 DAC 支持 8/12 位模式,8 位模式的时候是固定的右对齐的,而 12 位模式又可以设置左对齐/右对齐。
单 DAC 通道 x,总共有 3 种情况:
① 8 位数据右对齐:用户将数据写入 DAC_DHR8Rx[7:0]位(实际是存入 DHRx[11:4]位)。
② 12 位数据左对齐:用户将数据写入 DAC_DHR12Lx[15:4]位(实际是存入 DHRx[11:0]
位)。
③ 12 位数据右对齐:用户将数据写入 DAC_DHR12Rx[11:0]位(实际是存入 DHRx[11:0]
位)。
如果使用的是单 DAC 通道 1,采用 12 位右对齐格式,则采用第③种情况。
如果没有选中硬件触发(寄存器 DAC_CR1 的 TENx 位置’0’),存入寄存器 DAC_DHRx的数据会在一个 APB1 时钟周期后自动传至寄存器 DAC_DORx。如果选中硬件触发(寄存器DAC_CR1 的 TENx 位置’1’),数据传输在触发发生以后 3 个 APB1 时钟周期后完成。 一旦数据从 DAC_DHRx 寄存器装入 DAC_DORx 寄存器,在经过Tsettling时间之后,输出即有效,
这段时间的长短依电源电压和模拟输出负载的不同会有所变化。我们可以从 STM32F103ZET6的数据手册查到Tsettling的典型值为 3us,最大是 4us。所以 DAC 的转换速度最快是 250K 左右。
5、不使用硬件触发(TEN=0),其转换的时间框图如图所示:
在这里插入图片描述
当 DAC 的参考电压为 Vref+的时候,DAC 的输出电压是线性的从 0~Vref+,12 位模式下 DAC输出电压与 Vref+以及 DORx 的计算公式如下:
DACx 输出电压=Vref(DORx/4095)*
6、DAC相关寄存器
(1)DAC控制寄存器 DAC_CR
在这里插入图片描述
DAC_CR 的低 16 位用于控制通道 1,而高 16 位用于控制通道 2,我们这里仅列出比较重要的最低 8 位的详细描述
在这里插入图片描述
首先,我们来看 DAC 通道 1 使能位(EN1),该位用来控制 DAC 通道 1 使能的,如果用的 DAC 通道 1,该位设置为 1。
再看关闭 DAC 通道 1 输出缓存控制位(BOFF1),这里 STM32 的 DAC 输出缓存做的有些不好,如果使能的话,虽然输出能力强一点,但是输出没法到 0,这是个很严重的问题。不使用输出缓存。设置该位为 1。
DAC 通道 1 触发使能位(TEN1),该位用来控制是否使用触发,如果不使用触发,设置该位为 0。
DAC 通道 1 触发选择位(TSEL1[2:0]),如果没用到外部触发,设置这几个位为 0
DAC 通道 1 噪声/三角波生成使能位(WAVE1[1:0]),如果没用到波形发生器,设置为 0 。
DAC 通道 1 屏蔽/幅值选择器(MAMP[3:0]),这些位仅在使用了波形发生器的时候有用,如果没有用到波形发生器,设置为 0 。最后是 DAC 通道 1 DMA 使能位(DMAEN1),如果没有用到 DMA 功能,设置为 0。
在 DAC_CR 设置好之后,DAC 就可以正常工作了,我们仅需要再设置 DAC 的数据保持寄存器的值,就可以在 DAC 输出通道得到想要的电压了(对应 IO 口设置为模拟输入)
(2) DAC 通道 1 的 12 位右对齐数据保持寄存器:DAC_DHR12R1
在这里插入图片描述
(3)DAC通道1数据输出寄存器DAC_DOR1

在这里插入图片描述
7、DAC库函数配置步骤:
1)开启 PA 口时钟,设置 PA4 为模拟输入。
STM32F103ZET6 的 DAC 通道 1 在 PA4 上,所以,我们先要使能 PORTA 的时钟,然后设置 PA4 为模拟输入。DAC 本身是输出,但是为什么端口要设置为模拟输入模式呢?因为一但使能 DACx 通道之后,相应的 GPIO 引脚(PA4 或者 PA5)会自动与 DAC 的模拟输出相连,设置为输入,是为了避免额外的干扰。
使能 GPIOA 时钟:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE ); //使能 PORTA 时钟
设置 PA1 为模拟输入只需要设置初始化参数即可:
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //模拟输入
2)使能 DAC1 时钟。
同其他外设一样,要想使用,必须先开启相应的时钟。STM32 的 DAC 模块时钟是由 APB1提供的,所以我们调用函数 RCC_APB1PeriphClockCmd()设置 DAC 模块的时钟使能。
RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE ); //使能 DAC 通道时钟
3)初始化 DAC,设置 DAC 的工作模式。
该部分设置全部通过 DAC_CR 设置实现,包括:DAC 通道 1 使能、DAC 通道 1 输出缓存关闭、不使用触发、不使用波形发生器等设置。这里 DMA 初始化是通过函数 DAC_Init 完成的:
void DAC_Init(uint32_t DAC_Channel, DAC_InitTypeDef DAC_InitStruct)*

typedef struct
{
 uint32_t DAC_Trigger; 
 uint32_t DAC_WaveGeneration; 
 uint32_t DAC_LFSRUnmask_TriangleAmplitude; 
 uint32_t DAC_OutputBuffer; 
}DAC_InitTypeDef;

第一个参数 DAC_Trigger 用来设置是否使用触发功能,如果不使用触发功能,其值为 DAC_Trigger_None。
第二个参数 DAC_WaveGeneratio 用来设置是否使用波形发生,如果不使用,其值为 DAC_WaveGeneration_None。
第三个参数 DAC_LFSRUnmask_TriangleAmplitude 用来设置屏蔽/幅值选择器,这个变量只在使用波形发生器的时候才有用,如果设置为 0 ,其值为 DAC_LFSRUnmask_Bit0。
第四个参数 DAC_OutputBuffer 是用来设置输出缓存控制位,如果不使用输出缓存,其值为 DAC_OutputBuffer_Disable。
例子:

DAC_InitTypeDef DAC_InitType;
DAC_InitType.DAC_Trigger=DAC_Trigger_None; //不使用触发功能 TEN1=0
DAC_InitType.DAC_WaveGeneration=DAC_WaveGeneration_None;//不使用波形发生
DAC_InitType.DAC_LFSRUnmask_TriangleAmplitude=DAC_LFSRUnmask_Bit0;
DAC_InitType.DAC_OutputBuffer=DAC_OutputBuffer_Disable ; //DAC1 输出缓存关闭
DAC_Init(DAC_Channel_1,&DAC_InitType); //初始化 DAC 通道 1

4)使能 DAC 转换通道
初始化 DAC 之后,使能 DAC 转换通道,库函数方法是:
DAC_Cmd(DAC_Channel_1, ENABLE); //使能 DAC1
5)设置 DAC 的输出值。
通过前面 4 个步骤的设置,DAC 就可以开始工作了,如果使用 12 位右对齐数据格式,通过设置 DHR12R1,就可以在 DAC 输出引脚(PA4)得到不同的电压值了。库函数的函数是:
DAC_SetChannel1Data(DAC_Align_12b_R, 0);
第一个参数设置对齐方式,可以为 12 位右对齐 DAC_Align_12b_R,12 位左对齐DAC_Align_12b_L 以及 8 位右对齐DAC_Align_8b_R 方式。
第二个参数就是 DAC 的输入值了,初始化设置为 0。
读出 DAC 的数值,函数是:
DAC_GetDataOutputValue(DAC_Channel_1);
8、程序:
dac.c

#include "dac.h"
//DAC 通道 1 输出初始化
void Dac1_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
DAC_InitTypeDef DAC_InitType;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE ); //①使能 PA 时钟
 RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE ); //②使能 DAC 时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4; // 端口配置
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //模拟输入
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure); //①初始化 GPIOA
GPIO_SetBits(GPIOA,GPIO_Pin_4) ; //PA.4 输出高
DAC_InitType.DAC_Trigger=DAC_Trigger_None; //不使用触发功能
DAC_InitType.DAC_WaveGeneration=DAC_WaveGeneration_None;//不使用波形发生
DAC_InitType.DAC_LFSRUnmask_TriangleAmplitude=DAC_LFSRUnmask_Bit0;
DAC_InitType.DAC_OutputBuffer=DAC_OutputBuffer_Disable ; //DAC1 输出缓存关
 DAC_Init(DAC_Channel_1,&DAC_InitType); //③初始化 DAC 通道 1
DAC_Cmd(DAC_Channel_1, ENABLE); //④使能 DAC1
 DAC_SetChannel1Data(DAC_Align_12b_R, 0); //⑤12 位右对齐,设置 DAC 初始值
}
//设置通道 1 输出电压
//vol:0~3300,代表 0~3.3V
void Dac1_Set_Vol(u16 vol)
{
float temp=vol;
temp/=1000;
temp=temp*4096/3.3;
DAC_SetChannel1Data(DAC_Align_12b_R,temp);// 12 位右对齐设置 DAC 值 }

main.c

int main(void)
{
u16 adcx;
float temp;
u8 t=0;
u16 dacval=0;
u8 key;
delay_init(); //延时函数初始化 
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置 NVIC 中断分组 2 
uart_init(115200); //串口初始化波特率为 115200
KEY_Init(); //初始化按键程序
LED_Init(); //LED 端口初始化
LCD_Init(); //LCD 初始化
usmart_dev.init(72); //初始化 USMART
Adc_Init(); //ADC 初始化
Dac1_Init(); //DAC 初始化
POINT_COLOR=RED;//设置字体为红色
LCD_ShowString(30,50,200,16,16,"ELITE STM32");
LCD_ShowString(30,70,200,16,16,"DAC TEST");
LCD_ShowString(30,90,200,16,16,"ATOM@ALIENTEK");
LCD_ShowString(30,110,200,16,16,"2015/1/15");
LCD_ShowString(30,130,200,16,16,"WK_UP:+ KEY1:-");
//显示提示信息 
POINT_COLOR=BLUE;//设置字体为蓝色
LCD_ShowString(60,150,200,16,16,"DAC VAL:"); 
LCD_ShowString(60,170,200,16,16,"DAC VOL:0.000V"); 
LCD_ShowString(60,190,200,16,16,"ADC VOL:0.000V");
DAC_SetChannel1Data(DAC_Align_12b_R, 0); //初始值为 0 
while(1)
{
t++;
key=KEY_Scan(0); 
if(key== WKUP_PRES) {
if(dacval<4000)dacval+=200;
DAC_SetChannel1Data(DAC_Align_12b_R, dacval); //设置 DAC 值
}else if(key== KEY1_PRES) {
if(dacval>200)dacval-=200;
else dacval=0;
DAC_SetChannel1Data(DAC_Align_12b_R, dacval); //设置 DAC 值 }
if(t==10||key==KEY1_PRES||key==WKUP_PRES) 
{ 
adcx=DAC_GetDataOutputValue(DAC_Channel_1); //读取前面设置 DAC 的值
LCD_ShowxNum(124,150,adcx,4,16,0); //显示 DAC 寄存器值
temp=(float)adcx*(3.3/4096); //得到 DAC 电压值
adcx=temp;
LCD_ShowxNum(124,170,temp,1,16,0); //显示电压值整数部分
temp-=adcx;
temp*=1000;
LCD_ShowxNum(140,170,temp,3,16,0X80); //显示电压值的小数部分
adcx=Get_Adc_Average(ADC_Channel_1,10); //得到 ADC 转换值 
temp=(float)adcx*(3.3/4096); //得到 ADC 电压值
adcx=temp;
LCD_ShowxNum(124,190,temp,1,16,0); //显示电压值整数部分
temp-=adcx;
temp*=1000;
LCD_ShowxNum(140,190,temp,3,16,0X80); //显示电压值的小数部分
LED0=!LED0; 
t=0;
} 
delay_ms(10);
} }

第二部分:51单片机

1、DAC0832内部结构及引脚
在这里插入图片描述
2、DAC0832引脚的说明
在这里插入图片描述

51单片机部分参考文章链接

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐