13 玩转STM32数模转换器 (DAC)

13.1 介绍一下

数字量转换成模拟量的过程叫做数模转换,完成这种功能的电路叫做数模转换器。说白了就是将离散的数字信 转换为连续变量的模拟信 的一种器件。

这玩意主要由数字寄存器、模拟电子开关、位权 络、求和运算放大器和基准电压源(或恒流源)等组成的。主要用存于数字寄存器的数字量的各位数码,分别控制对应位的模拟电子开关,使数码为1的位在位权 络上产生与其位权成正比的电流值,再由运算放大器对各电流值求和,并转换成电压值 。
换成图也就是下面这个小东西:

13.2 STM32f429 DAC结构

STM32F429内部含有两个12 位电压输出数模转换器。DAC 可以按 8 位或 12 位模式进行配置,而且也是可以和DMA配合使用的,在12位模式中,数据可以是左对齐也可以是右对齐。

DAC 有两个输出通道,每个通道各有一个转换器。

两个 DAC 转换器:各对应一个输出通道:

12 位模式下数据采用左对齐或右对齐
生成噪声波、生成三角波
DAC 双通道单独或同时转换
每个通道都具有 DMA 功能
通过外部触发信 进行转换

DAC 各个引脚功能定义:

13.3 STM32f429 DAC功能

1、DAC 转换

两个DAC分别对应一个独立的通道,当使用时需要将 DAC_CR 寄存器中的相应 ENx 位置 1,即可使能对应 DAC 通道。
在使能DAC通道之后,可以通过写DAC_DHRx 寄存器或通过触发信 来启动一次DAC转换。

(1)写DAC_DHRx 寄存器启动DAC转换

如果未选择硬件触发(DAC CR中的TENx位复位),那么经过一个APB1时钟周期后,DAC_DHRx中存储的数据将自动转移到DAC_DORx。当DAC_DORx加载了DAC_DHRx内容时,模拟输出电压将在一段时间t_SETTING后可用,具体时间取决于电源电压和模拟输出负载。
DAC_DORx 无法直接写入,任何数据都必须通过加载DAC_DHRx 寄存器才能传输到 DAC 通道x。

(2)硬件触发启动DAC转换

如果选择硬件触发(置位 DAC_CR 寄存器中的 TENx 位)可通过外部事件(定时计数器、外部中断线)触发转换。
当触发条件到来,将在三个APB1时钟周期后,将DAC_DHRx 寄存器的内容转移到DAC_DORx寄存器。
DAC外部触发源:

2、DAC 数据格式

1)对于 DAC 单通道 x,有三种可能的方式:

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单通道数据对齐方式(下图):

2)对于 DAC双通道 x,有三种可能的方式:

8 位右对齐:将 DAC 1 通道的数据加载到 DAC_DHR8RD [7:0] 位(存储到DHR1[11:4] 位),将 DAC 2 通道的数据加载到 DAC_DHR8RD [15:8] 位(存储到 DHR2[11:4] 位)

12 位左对齐:将 DAC 1 通道的数据加载到 DAC_DHR12RD [15:4] 位(存储到 DHR1[11:0] 位),将 DAC 2 通道的数据加载到 DAC_DHR12RD [31:20] 位(存储到 DHR2[11:0] 位)

12 位右对齐:将 DAC 1 通道的数据加载到 DAC_DHR12RD [11:0] 位(存储到 DHR1[11:0] 位),将 DAC 2 通道的数据加载到 DAC_DHR12RD [27:16] 位(存储到 DHR2[11:0] 位)

3、DMA 请求

每个 DAC 通道都具有 DMA 功能。两个 DMA 通道用于处理 DAC 通道的 DMA 请求。

4、生成噪声

将 WAVEx[1:0] 置为 “01”即可选择生成噪声,使用 LFSR(线性反馈移位寄存器)可以生成可变振幅的伪噪声。
LFSR中的预加载值为0xAAA。

5、生成三角波

将 WAVEx[1:0] 置为“10”即可选择 DAC 生成三角波。

6、DAC 双通道转换

DAC控制器有三个双寄存器: DHR8RD、DHR12RD 和 DHR12LD,可以访问一个寄存器同时驱动两个 DAC 通道,从而有效利用两个 DAC 通道总线带宽。通过两个DAC 通道和这三个双寄存器可以实现 11 种转换模式:
独立触发(不产生波形)
独立触发(生成单个 LFSR)
独立触发(生成不同 LFSR)
独立触发(生成单个三角波)
独立触发(生成不同三角波)
同步软件启动
同步触发(不产生波形)
同步触发(生成单个 LFSR)
同步触发(生成不同 LFSR)
同步触发(生成单个三角波)
同步触发(生成不同三角波)

13.4 DAC典型应用步骤

1、使能DAC时钟:

开启PA口时钟和ADC1时钟,设置PA1为模拟输入。

RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE);

使能模拟信 输入GPIO时钟,假设是GPIOA,根据实际情况调整:

RCC_AHB1PeriphClockCmd (RCC_AHB1Periph_GPIOA, ENABLE);

2、初始化模拟信 输入的GPIO引脚为模拟方式:

GPIO_Init();

3、初始化DAC通道参数。

void DAC_Init(uint32_t DAC_Channel, DAC_InitTypeDef* DAC_InitStruct) ;

4、使能DAC通道

void DAC_Cmd(uint32_t DAC_Channel, FunctionalState NewState);

5、启动DAC转换

(1)通过写DAC_DHRx 寄存器启动DAC转换:

void DAC_SetChannel1Data(uint32_t DAC_Align, uint16_t Data) ; 或void DAC_SetChannel2Data(uint32_t DAC_Align, uint16_t Data) ;或void DAC_SetDualChannelData(uint32_t DAC_Align, uint16_t Data2, uint16_t Data1) ;或者通过触发信 启动DAC转换

13.5 常用库函数

DAC相关的函数和宏都被定义在两个文件中:
头文件:stm32f4xx_dac.h
源文件:stm32f4xx_dac.c

1、DAC初始化函数

void DAC_Init(uint32_t DAC_Channel, DAC_InitTypeDef* DAC_InitStruct);

参数1:uint32_t DAC_Channel,是DAC通道宏定义,定义在stm32f4xx_dac.h中:

#define DAC_Channel_1 ((uint32_t)0x00000000) //通道1#define DAC_Channel_2 ((uint32_t)0x00000010) //通道2

参数2:DAC_InitTypeDef* DAC_InitStruct,是DAC初始化结构体指针,自定义的结构体定义在stm32f4xx_dac.h中:

typedef struct{uint32_t DAC_Trigger; //设置触发方式uint32_t DAC_WaveGeneration; // 设置波形生成uint32_t DAC_LFSRUnmask_TriangleAmplitude; //设置LFSR掩码值或三角波最大振幅uint32_t DAC_OutputBuffer; //设置输出缓冲器}DAC_InitTypeDef;例如:DAC_InitTypeDef DAC_InitStructure;DAC_InitStructure.DAC_Trigger = DAC_Trigger_None;//不使用触发方式,软件启动转换DAC_InitStructure.DAC_WaveGeneration = DAC_WaveGeneration_None; //不使用波形发生器DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Enable; //不使用DAC输出缓冲DAC_InitStructure.DAC_LFSRUnmask_TriangleAmplitude=DAC_LFSRUnmask_Bit0;//屏蔽幅值DAC_Init(DAC_Channel_1, &DAC_InitStructure);

2、DAC使能函数

void DAC_Cmd(uint32_t DAC_Channel, FunctionalState NewState);
参数1uint32_t DAC_Channel,DAC通道宏定义。 参数2:FunctionalStateNewState,使能或禁止ADC,定义如下: ENABLE:使能ADC;DISABLE:禁止ADC。

3、单通道输出,写DAC通道1输出数据寄存器函数

void DAC_SetChannel1Data(uint32_t DAC_Align, uint16_t Data) ;

参数1:uint32_t DAC_Align, DAC数据对齐方式,定义如下:

#define DAC_Align_12b_R ((uint32_t)0x00000000)//12位右对齐形式#define DAC_Align_12b_L ((uint32_t)0x00000004) //12位左对齐形式#define DAC_Align_8b_R ((uint32_t)0x00000008) //8位右对齐形式

参数2:uint16_t Data,DAC输出的数据。

4、单通道输出,写DAC通道2输出数据寄存器函数

void DAC_SetChannel1Data(uint32_t DAC_Align, uint16_t Data) ; void DAC_Cmd(uint32_t DAC_Channel, FunctionalState NewState);

5、双通道输出,写DAC输出数据寄存器函数

void DAC_SetDualChannelData(uint32_t DAC_Align, uint16_t Data2, uint16_t Data1) ;

参数1:同单通道输出定义形式。
参数2和参数3:DAC通道2和DAC通道1输出数据。

6、DAC软件触发函数

void DAC_SoftwareTriggerCmd(uint32_t DAC_Channel, FunctionalState NewState)参数1:uint32_t DAC_Channel,DAC通道宏定义。参数2: FunctionalState NewState,使能或禁止ADC。

13.6 应用

使用DAC通道1生成一个频率为1KHz的正弦波。
使用定时器T2,在每次定时溢出中断中采样数组数据,并写入DAC_DHRx,定时溢出频率为20KHz。
DAC使用单通道模式,输出不使用触发,使能输出缓冲功能。在每次将数据写入DAC_DHRx后,经过一个APB1 时钟周期,DAC_DHRx 寄存器中存储的数据将自动转移到DAC_DORx,经输出缓冲器,在PA4引脚上产生对应输出电压。

1、编程要点

(1)、使能DAC和复用引脚GPIO的工作时钟。
(2)、初始化DAC通道1相关GPIO引脚为模拟方式。
(3)、根据要求,初始化ADC1。
(4)、初始化定时器
(5)、使能DAC。
(6)、在定时器一处中断中写DAC_DHRx。

2、DAC初始化功能

#include <math.h>//因为要使用sin函数生成正弦表,需要包含math.h头文件#define Pi 3.1415926#define f 1000//正弦波频率 1KHz#define fs 20000//采样频率 20KHzuint16_t Sine_Table[20];void DAC_Config (void){uint16_t i = 0; /* 生成正弦波形数据,右对齐*/for (i = 0; i < 20; i++){Sine_Table [i] = (uint16_t )((float)4095*(1+sin(2*i*Pi* f /fs))/2);}DAC_Mode_Init ();//配置DAC功能TIM_Config();//配置定时器功能}

3、DAC配置程序

static void DAC_Mode_Config(void){GPIO_InitTypeDef GPIO_InitStructure;DAC_InitTypeDef DAC_InitStructure;/*-------------------第1步--------------------*//* 使能GPIOA时钟 */RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); /* 使能DAC时钟 */ RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE);/*-------------------第2步--------------------*/ /* DAC的GPIO配置,模拟 */GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;GPIO_Init(GPIOA, &GPIO_InitStructure);/*-------------------第3步--------------------*//* 配置DAC 通道1 */ DAC_InitStructure.DAC_Trigger = DAC_Trigger_None;//不使用触发DAC_InitStructure.DAC_WaveGeneration = DAC_WaveGeneration_None; //不使用波形发生器DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Enable; //使用DAC输出缓冲DAC_InitStructure.DAC_LFSRUnmask_TriangleAmplitude=DAC_LFSRUnmask_Bit0; //屏蔽幅值 设置DAC_Init(DAC_Channel_1, &DAC_InitStructure);/*-------------------第4步--------------------*//* 使能通道1 由PA4输出 */DAC_Cmd(DAC_Channel_1, ENABLE);}

4、定时器配置程序

static void TIM_Config(void){TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; NVIC_InitTypeDef NVIC_InitStructure; /* 使能TIM2时钟,TIM2CLK 为90M */RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);/* TIM2基本定时器配置 */// TIM_TimeBaseStructInit(&TIM_TimeBaseStructure); TIM_TimeBaseStructure.TIM_Period = 4500-1; //采样频率 = 20KHz TIM_TimeBaseStructure.TIM_Prescaler = 0x0; //预分频,不分频 90M / (0+1) = 90MTIM_TimeBaseStructure.TIM_ClockDivision = 0x0; //时钟分频系数TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //向上计数模式TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);/* 配置TIM2中断 */NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);NVIC_InitStructure.NVIC_IRQChannel=TIM2_IRQn;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;NVIC_InitStructure.NVIC_IRQChannelSubPriority=0;NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE; NVIC_Init(&NVIC_InitStructure); TIM_ITConfig(TIM2, TIM_IT_Update,ENABLE); /* 使能TIM2 */TIM_Cmd(TIM2, ENABLE);}

5、定时器中断服务程序

void TIM2_IRQHandler(void){static u16 Index;if(TIM_GetITStatus(TIM2, TIM_IT_Update)==SET){TIM_ClearITPendingBit(TIM2, TIM_IT_Update);if(Index <19)//采样边界控制Index ++;elseIndex =0;//写DAC通道1的12位右对齐数据寄存器,从而启动一次DAC转换DAC_SetChannel1Data(DAC_Align_12b_R, Sine_Table [Index]); }}

具体的可以参考其他官方或个人优秀的代码。

声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!

上一篇 2022年10月3日
下一篇 2022年10月3日

相关推荐