第二十六章 DAC实验
[mw_shl_code=c,true]1.硬件平台:正点原子探索者STM32F407开发板
2.软件平台:MDK5.1
3.固件库版本:V1.4.0
[/mw_shl_code]
上几章,我们介绍了
STM32F4的
ADC使用,本章我们将向大家介绍
STM32F4的
DAC功能。在本章中,我们将利用按键(或
USMART)控制
STM32F4内部
DAC1来输出电压,通过
ADC1的通道
5采集
DAC的输出电压,在
LCD模块上面显示
ADC获取到的电压值以及
DAC的设定输出电压值等信息。本章将分为如下几个部分:
26.1 STM32F4 DAC简介
26.2 硬件设计
26.3 软件设计
26.4 下载验证
26.1
STM32F4 DAC简介
STM32F4的
DAC模块
(数字
/模拟转换模块
)是
12位数字输入,电压输出型的
DAC。
DAC可以配置为
8位或
12位模式,也可以与
DMA控制器配合使用。
DAC工作在
12位模式时,数据可以设置成左对齐或右对齐。
DAC模块有
2个输出通道,每个通道都有单独的转换器。在双
DAC模式下,
2个通道可以独立地进行转换,也可以同时进行转换并同步地更新
2个通道的输出。
DAC可以通过引脚输入参考电压
Vref+(通
ADC共用)以获得更精确的转换结果。
STM32F4的
DAC模块主要特点有:
①
2个
DAC转换器:每个转换器对应
1个输出通道
②
8位或者
12位单调输出
③
12位模式下数据左对齐或者右对齐
④ 同步更新功能
⑤ 噪声波形生成
⑥ 三角波形生成
⑦ 双
DAC通道同时或者分别转换
⑧ 每个通道都有
DMA功能
单个
DAC通道的框图如图
26.1.1所示:
图
26.1.1 DAC通道模块框图
图中
VDDA和
VSSA为
DAC模块模拟部分的供电,而
Vref+则是
DAC模块的参考电压。
DAC_OUTx就是
DAC的输出通道了(对应
PA4或者
PA5引脚)。
从图
26.1.1可以看出,
DAC输出是受
DORx寄存器直接控制的,但是我们不能直接往
DORx寄存器写入数据,而是通过
DHRx间接的传给
DORx寄存器,实现对
DAC输出的控制。前面我们提到,
STM32F4的
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寄存器,在经过时间
之后,输出即有效,这段时间的长短依电源电压和模拟输出负载的不同会有所变化。我们可以从
STM32F407ZGT6的数据手册查到
的典型值为
3us,最大是
6us。所以
DAC的转换速度最快是
333K左右。
本章我们将不使用硬件触发(
TEN=0),其转换的时间框图如图
26.1.2所示:
图
26.1.2 TEN=0时
DAC模块转换时间框图
当
DAC的参考电压为
Vref+的时候,
DAC的输出电压是线性的从
0~Vref+,
12位模式下
DAC输出电压与
Vref+以及
DORx的计算公式如下:
DACx输出电压
=Vref*(
DORx/4095)
接下来,我们介绍一下要实现
DAC的通道
1输出,需要用到的一些寄存器。首先是
DAC控制寄存器
DAC_CR,该寄存器的各位描述如图
26.1.3所示:
图
26.1.3 寄存器
DAC_CR各位描述
DAC_CR的低
16位用于控制通道
1,而高
16位用于控制通道
2,我们这里仅列出比较重要的最低
8位的详细描述,如图
26.1.4所示:
图
26.1.4 寄存器
DAC_CR低八位详细描述
首先,我们来看
DAC通道
1使能位
(EN1),该位用来控制
DAC通道
1使能的,本章我们就是用的
DAC通道
1,所以该位设置为
1。
再看关闭
DAC通道
1输出缓存控制位(
BOFF1),这里
STM32F4的
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,该寄存器各位描述如图
26.1.5所示:
图
26.1.5 寄存器
DAC_DHR12R1各位描述
该寄存器用来设置
DAC输出,通过写入
12位数据到该寄存器,就可以在
DAC输出通道
1(
PA4)得到我们所要的结果。
通过以上介绍,我们了解了
STM32F4实现
DAC输出的相关设置,本章我们将使用
DAC模块的通道
1来输出模拟电压。这里我们用到的库函数以及相关定义分布在文件
stm32f4xx_dac.c以及头文件
stm32f4xx_dac.h中。实现上面功能的详细设置步骤如下:
1)开启PA口时钟,设置PA4为模拟输入。
STM32F407ZGT6的
DAC通道
1是接在
PA4上的,所以,我们先要使能
GPIOA的时钟,然后设置
PA4为模拟输入。
这里需要特别说明一下,虽然
DAC引脚设置为输入,但是
STM32F4内部会连接在
DAC模拟输出上
,这在我们引脚复用映射章节有讲解。
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);//使能
GPIOA时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;//模拟输入
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN;//下拉
GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化
对于
DAC通道与引脚对应关系,这在
STM32F4的数据手册引脚表上有列出,如下图:
图
26.1.6 DAC通道引脚对应关系
2)使能DAC1时钟。
同其他外设一样,要想使用,必须先开启相应的时钟。
STM32F4的
DAC模块时钟是由
APB1提供的,所以我们先要在通过调用函数
RCC_APB1PeriphClockCmd来使能
DAC1时钟。
RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE);//使能
DAC时钟
3)初始化DAC,设置DAC的工作模式。
该部分设置全部通过
DAC_CR设置实现,包括:
DAC通道
1使能、
DAC通道
1输出缓存关闭、不使用触发、不使用波形发生器等设置。这里
DAC初始化是通过函数
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); //使能
DAC通道
1
5)设置DAC的输出值。
通过前面
4个步骤的设置,
DAC就可以开始工作了,我们使用
12位右对齐数据格式,所以我们通过设置
DHR12R1,就可以在
DAC输出引脚(
PA4)得到不同的电压值了。设置
DHR12R1的库函数是:
DAC_SetChannel1Data(DAC_Align_12b_R, 0);
//12位右对齐数据格式设置
DAC值
第一个参数设置对齐方式,可以为
12位右对齐
DAC_Align_12b_R,
12位左对齐
DAC_Align_12b_L以及
8位右对齐
DAC_Align_8b_R方式。
第二个参数就是
DAC的输入值了,这个很好理解,初始化设置为
0。
这里,还可以读出
DAC对应通道最后一次转换的数值,函数是:
DAC_GetDataOutputValue(DAC_Channel_1);
设置和读出一一对应很好理解,这里就不多讲解了。
最后,再提醒一下大家,本例程,我们使用的是
3.3V的参考电压,即
Vref+连接
VDDA。
通过以上几个步骤的设置,我们就能正常的使用
STM32F4的
DAC通道
1来输出不同的模拟电压了。
26.2 硬件设计
本章用到的硬件资源有:
1) 指示灯
DS0
2) KEY_UP和
KEY1按键
3) 串口
4) TFTLCD模块
5) ADC
6) DAC
本章,我们使用
DAC通道
1输出模拟电压,然后通过
ADC1的通道
1对该输出电压进行读取,并显示在
LCD模块上面,
DAC的输出电压,我们通过按键(或
USMART)进行设置。
我们需要用到
ADC采集
DAC的输出电压,所以需要在硬件上把他们短接起来。
ADC和
DAC的连接原理图如图
26.2.1所示:
图
26.2.1 ADC、
DAC与
STM32F4连接原理图
P12是多功能端口,我们只需要通过跳线帽短接
P14的
ADC和
DAC,就可以开始做本章实验了。如图
26.2.2所示:
图
26.2.2 硬件连接示意图
26.3 软件设计
打开本章实验工程可以发现,我们相比
ADC实验,在库函数中主要是添加了
dac支持的相关文件
stm32f4xx_dac.c以及包含头文件
stm32f4xx_dac.h。同时我们在
HARDWARE分组下面新建了
dac.c源文件以及包含对应的头文件
dac.h。这两个文件用来存放我们编写的
ADC相关函数和定义。打开
dac.c,代码如下:
//DAC通道
1输出初始化
void Dac1_Init(void)
{
GPIO_InitTypeDef
GPIO_InitStructure;
DAC_InitTypeDef DAC_InitType;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);//①使能
PA时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE);//②使能
DAC时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;//模拟输入
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN;//下拉
GPIO_Init(GPIOA, &GPIO_InitStructure);//①初始化
GPIO
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 ;/输出缓存关闭
DAC_Init(DAC_Channel_1,&DAC_InitType); //③初始化
DAC通道
1
DAC_Cmd(DAC_Channel_1, ENABLE);
//④使能
DAC通道
1
DAC_SetChannel1Data(DAC_Align_12b_R, 0);
//⑤
12位右对齐数据格式
}
//设置通道
1输出电压
//vol:0~3300,代表
0~3.3V
void Dac1_Set_Vol(u16 vol)
{
double
temp=vol;
temp/=1000;
temp=temp*4096/3.3;
DAC_SetChannel1Data(DAC_Align_12b_R,temp);//12位右对齐数据格式
}
此部分代码就
2个函数,
Dac1_Init函数用于初始化
DAC通道
1。这里基本上是按我们上面的步骤来初始化的,我们用序号①
~⑤已经标示这些步骤。经过这个初始化之后,我们就可以正常使用
DAC通道
1了。第二个函数
Dac1_Set_Vol,用于设置
DAC通道
1的输出电压,实际就是将电压值转换为
DAC输入值。
其他头文件代码就比较简单,这里我们不做过多讲解,接下来我们来看看主函数代码:
int main(void)
{
u16
adcx;
float
temp;
u8 t=0, key;
u16
dacval=0;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组
2
delay_init(168); //初始化延时函数
uart_init(115200); //初始化串口波特率为
115200
LED_Init(); //初始化
LED
LCD_Init(); //LCD初始化
Adc_Init();
//adc初始化
KEY_Init();
//按键初始化
Dac1_Init(); //DAC通道
1初始化
POINT_COLOR=RED;
LCD_ShowString(30,50,200,16,16,"Explorer
STM32F4");
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,"2014/5/6");
LCD_ShowString(30,130,200,16,16,"WK_UP:+ KEY1:-");
POINT_COLOR=BLUE;//设置字体为蓝 {MOD}
LCD_ShowString(30,150,200,16,16,"DAC
VAL:");
LCD_ShowString(30,170,200,16,16,"DAC
VOL:0.000V");
LCD_ShowString(30,190,200,16,16,"ADC
VOL:0.000V");
DAC_SetChannel1Data(DAC_Align_12b_R,dacval);//初始值为
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==2)
{
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)
//WKUP/KEY1按下了
,或者定时时间到了
{
adcx=DAC_GetDataOutputValue(DAC_Channel_1);//读取前面设置
DAC的值
LCD_ShowxNum(94,150,adcx,4,16,0); //显示
DAC寄存器值
temp=(float)adcx*(3.3/4096); //得到
DAC电压值
adcx=temp;
LCD_ShowxNum(94,170,temp,1,16,0); //显示电压值整数部分
temp-=adcx;
temp*=1000;
LCD_ShowxNum(110,170,temp,3,16,0X80);
//显示电压值的小数部分
adcx=Get_Adc_Average(ADC_Channel_5,10); //得到
ADC转换值
temp=(float)adcx*(3.3/4096); //得到
ADC电压值
adcx=temp;
LCD_ShowxNum(94,190,temp,1,16,0); //显示电压值整数部分
temp-=adcx;
temp*=1000;
LCD_ShowxNum(110,190,temp,3,16,0X80);
//显示电压值的小数部分
LED0=!LED0;
t=0;
}
delay_ms(10);
}
}
此部分代码,我们先对需要用到的模块进行初始化,然后显示一些提示信息,本章我们通过
KEY_UP(
WKUP按键)和
KEY1(也就是上下键)来实现对
DAC输出的幅值控制。按下
KEY_UP增加,按
KEY1减小。同时在
LCD上面显示
DHR12R1寄存器的值、
DAC设计输出电压以及
ADC采集到的
DAC输出电压。
26.4 下载验证
在代码编译成功之后,我们通过下载代码到
ALIENTEK探索者
STM32F4开发板上,可以看到
LCD显示如图
26.4.1所示:
图
26.4.1 DAC实验测试图
同时伴随
DS0的不停闪烁,提示程序在运行。此时,我们通过按
KEY_UP按键,可以看到输出电压增大,按
KEY1则变小。
实验详细手册和源码下载地址:http://www.openedv.com/posts/list/41586.htm
正点原子探索者STM32F407开发板购买地址:http://item.taobao.com/item.htm?id=41855882779
一周热门 更多>