《嵌入式-STM32开发指南》第二部分 基础篇 - 第8章 模拟输入输出-DAC

2.1 DAC工作原理

2.1.1 DAC介绍

数字/模拟转换模块(DAC)是12位数字输入,电压输出的数字/模拟转换器。DAC可以配置为8位或12位模式,也可以与DMA控制器配合使用。DAC工作在12位模式时,数据可以设置成左对齐或右对齐。DAC模块有2个输出通道,每个通道都有单独的转换器。在双DAC模式下,2个通道可以独立地进行转换,也可以同时进行转换并同步地更新2个通道的输出。DAC可以通过引脚输入参考电压VREF+ 以获得更精确的转换结果。

《嵌入式-STM32开发指南》第二部分 基础篇 - 第8章 模拟输入输出-DAC_第1张图片

图1 DAC 通道模块框图

2.1.2 DAC主要特征

● 2个DAC转换器:每个转换器对应1个输出通道
● 8位或者12位单调输出
● 12位模式下数据左对齐或者右对齐
● 同步更新功能
● 噪声波形生成
● 三角波形生成
● 双DAC通道同时或者分别转换
● 每个通道都有DMA功能
● 外部触发转换
● 输入参考电压VREF+

表1 DAC 引脚

《嵌入式-STM32开发指南》第二部分 基础篇 - 第8章 模拟输入输出-DAC_第2张图片

【注意】一旦使能DACx通道,相应的GPIO引脚(PA4或者PA5)就会自动与DAC的模拟输出相连(DAC_OUTx)。为了避免寄生的干扰和额外的功耗,引脚PA4或者PA5在之前应当设置成模拟输入(AIN)。

2.1.4 DAC功能描述

 使能DAC通道
将DAC_CR寄存器的ENx位置’1’ 即可打开对DAC通道x 的供电。经过一段启动时间tWAKEUP,DAC通道x 即被使能。
注意: ENx位只会使能DAC通道x的模拟部分,即便该位被置’0’,DAC通道x的数字部分仍然工作。

** 使能DAC输出缓存 **
DAC集成了2个输出缓存,可以用来减少输出阻抗,无需外部运放即可直接驱动外部负载。每个DAC通道输出缓存可以通过设置DAC_CR寄存器的BOFFx位来使能或者关闭。

** DAC输出电压 **
数字输入经过DAC被线性地转换为模拟电压输出,
其范围为0到VREF+ 。
任一DAC通道引脚上的输出电压满足下面的关系:
DAC输出 = VREF x (DOR / 4095) 。

 DAC数据格式
根据选择的配置模式,数据按照下文所述写入指定的寄存器:
─ 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_DHRyyyx寄存器的操作,经过相应的移位后,写入的数据被转存到DHRx寄存器中(DHRx是内部的数据保存寄存器x) 。随后,DHRx寄存器的内容或被自动地传送到DORx寄存器,或通过软件触发或外部事件触发被传送到DORx寄存器。

《嵌入式-STM32开发指南》第二部分 基础篇 - 第8章 模拟输入输出-DAC_第3张图片

图2单 DAC 通道模式的数据寄存器

 DAC转换
不能直接对寄存器DAC_DORx写入数据,任何输出到DAC通道x 的数据都必须写入DAC_DHRx寄存器(数据实际写入DAC_DHR8Rx、DAC_DHR12Lx、DAC_DHR12Rx、DAC_DHR8RD、DAC_DHR12LD、或者DAC_DHR12RD寄存器)。

如果没有选中硬件触发(寄存器DAC_CR1的TENx位置’0’),存入寄存器DAC_DHRx的数据会在一个APB1 时钟周期后自动传至寄存器DAC_DORx 。如果选中硬件触发(寄存器DAC_CR1 的TENx位置’1’),数据传输在触发发生以后3个APB1 时钟周期后完成。

一旦数据从DAC_DHRx寄存器装入DAC_DORx寄存器,在经过时间tSETTLING 之后,输出即有效,这段时间的长短依电源电压和模拟输出负载的不同会有所变化。

《嵌入式-STM32开发指南》第二部分 基础篇 - 第8章 模拟输入输出-DAC_第4张图片

图3 TEN=0触发使能时转换的的时间图

 选择DAC触发
如果TENx位被置1,DAC转换可以由某外部事件触发(定时器计数器、外部中断线)。配置控制位TSELx[2:0] 可以选择8个触发事件之一触发DAC转换。

表2外部触发

《嵌入式-STM32开发指南》第二部分 基础篇 - 第8章 模拟输入输出-DAC_第5张图片

每次DAC接口侦测到来自选中的定时器TRGO输出,或者外部中断线9的上升沿,最近存放在寄存器DAC_DHRx中的数据会被传送到寄存器DAC_DORx中。在3个APB1 时钟周期后,寄存器 DAC_DORx更新为新值。

如果选择软件触发,一旦SWTRIG位置’1’ ,转换即开始。在数据从DAC_DHRx寄存器传送到DAC_DORx寄存器后,SWTRIG位由硬件自动清’0’。

2.1 DAC寄存器描述

我们介绍一下要实现 DAC 的通道 1 输出,需要用到的一些寄存器。首先是 DAC控制寄存器 DAC_CR,该寄存器的各位描述如图4所示。

《嵌入式-STM32开发指南》第二部分 基础篇 - 第8章 模拟输入输出-DAC_第6张图片

图4寄存器 DAC_CR 各位描述

《嵌入式-STM32开发指南》第二部分 基础篇 - 第8章 模拟输入输出-DAC_第7张图片

图5寄存器 DAC_CR 低八位详细描述

DAC_CR 的低 16 位用于控制通道 1,而高 16 位用于控制通道 2,我们这里仅列出比较重要的最低 8 位的详细描述,如图5所示。

首先,我们来看 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。通道 2 的情况和通道 1 一模一样,这里就不不细说了。在 DAC_CR 设置好之后, DAC 就可以正常工作了, 我们仅需要再设置 DAC 的数据保持寄存器的值,就可以在 DAC 输出通道得到你想要的电压了(对应 IO 口设置为模拟输入)。本章,我们用的是 DAC 通道 1 的 12 位右对齐数据保持寄存器: DAC_DHR12R1,该寄存器各位描述如图6所示。

《嵌入式-STM32开发指南》第二部分 基础篇 - 第8章 模拟输入输出-DAC_第8张图片

图6寄存器 DAC_DHR12R1 各位描述

该寄存器用来设置 DAC 输出,通过写入 12 位数据到该寄存器,就可以在 DAC 输出通道 1(PA4)得到我们所要的结果。

2.2 DAC具体代码分析

本章我们将使用库函数的方法来设置 DAC 模块的通道 1 来输出模拟电压,其详细设置步骤如下:
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)

跟前面一样,首先我们来看看参数设置结构体类型 DAC_InitTypeDef 的定义:

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);

 正弦波频率计算方式
原系统时钟周期 T S y s c l k = 1 / 72 M T_{Sysclk} = 1/72M TSysclk=1/72M: (单位:秒)
因为定时时钟分频: T I M _ P = 0 TIM\_P = 0 TIM_P=0
所以定时时钟周期: T T I M = T S y s c l k ∗ ( T I M _ P r e s c a l e r + 1 ) = 1 / 72 M T_{TIM} = T_{Sysclk} * (TIM\_Prescaler + 1) = 1/72M TTIM=TSysclk(TIM_Prescaler+1)=1/72M(单位:秒)
因为设置的定时更新周期: T I M _ P e r i o d = 9 TIM\_Period = 9 TIM_Period=9
所以定时器更新周期: T u p d a t e = T T I M ∗ ( T I M _ P e r i o d + 1 ) = 20 / 72 M T_{update} = T_{TIM} * (TIM\_Period +1) = 20/72M Tupdate=TTIM(TIM_Period+1)=20/72M
而DAC数据更新率等于定时器更新速率:即DAC的数据更新周期为:
D A C u p d a t e = T u p d a t e = 20 / 72 M DAC_{update} = T_{update} = 20/72M DACupdate=Tupdate=20/72M
本实验有32个数据点,则正弦波的周期为: T s i n = D A C u p d a t e ∗ 点 数 = 640 / 72 M T_{sin} = DAC_{update} * 点数 = 640/72M Tsin=DACupdate=640/72M
最后求的的正弦波的频率为: f s i n = 1 / T s i n = 112500 H z f_{sin} = 1/T_{sin}= 112500Hz fsin=1/Tsin=112500Hz
化简公式:
f s i n = 1 / T S y s c l k / ( T I M _ P r e s c a l e r + 1 ) / ( T I M _ P e r i o d + 1 ) / 点 数 f_{sin} = 1/T_{Sysclk} / (TIM\_Prescaler +1 ) / (TIM\_Period + 1) / 点数 fsin=1/TSysclk/(TIM_Prescaler+1)/(TIM_Period+1)/

本章参考代码

IAR版本完整代码
KEIL版本完整代码

你可能感兴趣的:(《嵌入式》STM32开发指南)