DAC的主要作用就是将数字信号变为模拟信号输出,数字/ 模拟转换模块(DAC) 是12位数字输入,电压输出的数字/ 模拟转换器。DAC可以配置为8 位或12位模式,也可以与DMA控制器配合使用。DAC工作在12位模式时,数据可以设置成左对齐或右对齐。DAC可以通过引脚输入参考电压VDD以获得更精确的转换结果。
数字输入经过DAC被线性地转换为模拟电压输出,其范围为0到VDD。 任一DAC通道引脚上的输出电压满足下面的关系:
DAC输出 = VREFx (DOR / 4095)
下面我们就从软硬件2个方面对如何配置时钟DAC模块进行详细的分析:
硬件准备:
硬件方面采用PA4作为DAC输出管脚,内部存储单元输出数字量。
软件准备:
打开keil编译环境,设置系统工程树:
我们需要编写DAC的配置子函数DAC.C,按键中断我们可以直接调用在第二节编写的按键中断驱动。实验第一步就是给2个数字波形,我们设置2个波形,一个为正弦波,一个为梯形波。并且赋值为一个数组:
1
2
3
4
5
6 |
const uint16_t Sine12bit[32] = { 2047, 2447, 2831, 3185, 3498, 3750, 3939, 4056, 4095, 4056, 3939, 3750, 3495, 3185, 2831, 2447, 2047, 1647, 1263, 909, 599, 344, 155, 38, 0, 38, 155, 344, 599, 909, 1263, 1647}; const uint8_t Escalator8bit[6] = {0x0, 0x33, 0x66, 0x99, 0xCC, 0xFF}; |
实验中为了快速对数据进行调用,并且减少多CPU的负担,我们采用DMA进行数据的传输,把我们的赋值数组直接放入DMA_MemoryBaseAddr内,因此可以输入如下:
1
2 |
</pre> DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&Sine12bit; |
1
2
3 |
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&Escalator8bit; <pre> |
当然两种不同的波形我们需要进行选择,选择DAC对哪一个进行转换,选择我们可以通过采用按键中断进行选择,按下后改变一次波形选择,那么按键中断的子函数可以编写为如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 |
void EXTI4_15_IRQHandler( void ) { if (EXTI_GetITStatus(EXTI_Line7) != RESET) { /* 改变波形 */ WaveChange = !WaveChange; /* 改变选择的波形 */ SelectedWavesForm = !SelectedWavesForm; /*清除按键中断*/ EXTI_ClearITPendingBit(EXTI_Line7); } } |
DAC我们必须对其所采用的硬件进行配置,首先我们使能DAC和DMA的时钟,然后配置DAC输出管脚,上面说过,我们采用PA4作为输出管脚,采用其模拟数字功能,因此需要对IO管脚进行配置,具体代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 |
void DAC_Config( void ) { GPIO_InitTypeDef GPIO_InitStructure; /* DMA1 时钟使能 */ RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); /* DAC 外设时钟使能*/ RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE); /* GPIOA 时钟使能 */ RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE); /*配置 PA.04 为模拟输出 */ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; GPIO_Init(GPIOA, &GPIO_InitStructure); } |
回到主函数,在主函数里,我们需要设置DAC的通道触发方式,这个参数在stm32f0xx_dac.h文件中通过结构体进行了描述:
1
2
3
4
5
6
7 |
typedef struct { uint32_t DAC_Trigger; /*!<指定选定的DAC通道的外部触发*/ uint32_t DAC_OutputBuffer; /*!< 指定的DAC通道的输出缓冲是否被启用或禁用。*/ }DAC_InitTypeDef; |
对上面两个产生进行配置,我们采用TIM2做为触发源:
1
2
3
4 |
/* DAC 通道1配置 */ DAC_InitStructure.DAC_Trigger = DAC_Trigger_T2_TRGO; DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Enable; |
DAC的触发源在STM32F051参考手册中有详细的说明,我们可以首先看看DAC的功能结构图,如下图所示,可以看出其可以通过多个触发源进行触发转换:
详细的触发源列表可以归纳如下:
有效设置了TIM2定时器作为触发源,下面我们就需要来配置TIM定时器,配置TIM2定时器在之前通用定时器的运用中详细讲解过,大家可以回忆,这样里面我们按照要求如下配置:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 |
/* TIM2 外设时钟使能*/ RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); /* 定时器基础配置 */ TIM_TimeBaseStructInit(&TIM_TimeBaseStructure); TIM_TimeBaseStructure.TIM_Period = 0xFF; TIM_TimeBaseStructure.TIM_Prescaler = 0x0; TIM_TimeBaseStructure.TIM_ClockDivision = 0x0; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); /* TIM2触发输出模式选择 */ TIM_SelectOutputTrigger(TIM2, TIM_TRGOSource_Update); /* TIM2使能 */ TIM_Cmd(TIM2, ENABLE); |
配置完定时器后,我们需要把DMA连通DAC输入端,首先初始化DMA,使能通道,代码如下所示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27 |
/* DMA 通道3配置 */ DMA_DeInit(DMA1_Channel3); DMA_InitStructure.DMA_PeripheralBaseAddr = DAC_DHR12R1_ADDRESS; DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&Sine12bit; DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; DMA_InitStructure.DMA_BufferSize = 32; DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; DMA_InitStructure.DMA_Priority = DMA_Priority_High; DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; DMA_Init(DMA1_Channel3, &DMA_InitStructure); /* 使能DMA1通道3 */ DMA_Cmd(DMA1_Channel3, ENABLE); /* DAC 通道1初始化 */ DAC_Init(DAC_Channel_1, &DAC_InitStructure); /* 使能DAC 通道1: 当 DAC 通道1被使能 , PA.04 自动与DAC转换器相连. */ DAC_Cmd(DAC_Channel_1, ENABLE); /* 使能DAC通道1的DMA */ DAC_DMACmd(DAC_Channel_1, ENABLE); |