赞
踩
对于高职教师来说,必不可少的一个任务就是参加企业实践。这个暑假,本人也没闲着,报名参加了上海市电子信息类教师企业实践。7月8日到13日,有幸来到美丽的泉城济南,远离了上海的酷暑,走进了百科荣创科技发展有限公司。在这短短的一周时间里,我结合自己的教学经验和企业的需求,对一个简易函数信号发生器的嵌入式项目做了教学内容的转换。接下来,就用博客的形式,把这次转换后的成果分享出来,如果您觉得有用,还望多多点赞和转发!
针对本项目,笔者根据自己的理解,力求做到循序渐进和逐步深入,计划用6篇文章来展开,本文是第二篇,我们先来聊聊STM32的DAC,并做一个基本输出的实验。
DAC为数字/模拟转换模块,顾名思义,它的作用就是把输入的数字编码,转换成对应的模拟电压输出,它的功能与ADC相反。在常见的数字信号系统中,大部分传感器信号被化成电压信号,而ADC把电压模拟信号转换成易于计算机存储、处理的数字编码,由计算机处理完成后,再由DAC输出电压模拟信号,该电压模拟信号常常用来驱动某些执行器件,使人类易于感知。如音频信号的采集及还原就是这样一个过程。
STM32具有片上DAC外设,它的分辨率可配置为8位或12位的数字输入信号,具有两个DAC输出通道,这两个通道互不影响,每个通道都可以使用DMA功能,都具有出错检测能力,可外部触发。图4是STM32的DAC功能框图。
整个DAC模块围绕框图下方的“数字至模拟转换器x”展开,它的左边分别是参考电源的引脚:VDDA、VSSA及VREF+,其中STM32的DAC规定了它的参考电压输入范围为2.4~3.3V。“数字至模拟转换器x”的输入为DAC的数据寄存器“DORx”的数字编码,经过它转换得的模拟信号由图中右侧的“DAC_OUTx”输出。而数据寄存器“DORx”又受“控制逻辑”支配,它可以控制数据寄存器加入一些伪噪声信号或配置产生三角波信号。
图中的左上角为DAC的触发源,DAC根据触发源的信号来进行DAC转换,其作用就相当于DAC转换器的开关,它可以配置的触发源为外部中断源触发、定时器触发或软件控制触发。如本项目实验中需要控制三角波和正弦波的频率,就需要定时器定时触发DAC进行数据转换。
与ADC外设类似,DAC也使用VREF+ 引脚作为参考电压,在设计原理图的时候一般把VSSA 接地,把VREF+和VDDA接3.3V,可得到DAC的输出电压范围为:0~3.3V。如果想让输出的电压范围变宽,可以在外部加一个电压调理电路,把0~3.3V的DAC输出抬升到特定的范围即可。
框图中的“数字至模拟转换器x”是核心部件,整个DAC外设都围绕它而展开。它以左边的VREF+作为参考电源,以DAC的数据寄存器“DORx”的数字编码作为输入,经过它转换得的模拟信号由右侧的“DAC_OUTx”通道输出。其中各个部件中的“x”是指设备的标号,在STM32中具有2个这样的DAC部件,每个DAC有1个对应的输出通道连接到特定的引脚(PA4-通道1,PA5-通道2)。为避免干扰,使用DAC功能时,DAC通道引脚需要被配置成模拟输入功能(AIN)。
在使用DAC时,不能直接对上述DORx寄存器写入数据,任何输出到DAC通道x的数据都必须写入到DHRx寄存器中(其中包含DHR8Rx、DHR12Lx等,根据数据对齐方向和分辨率的情况写入到对应的寄存器中)。
数据被写入到DHRx寄存器后,DAC会根据触发配置进行处理,若使用硬件触发,则DHRx中的数据会在3个APB1时钟周期后传输至DORx,DORx随之输出相应的模拟电压到输出通道;若DAC设置为外部事件触发,可以使用定时器(TIMx_TRGO)、EXTI_9信号或软件触发(SWTRIGx)这几种方式控制数据DAC转换的时机,例如使用定时器触发,配合不同时刻的DHRx数据,可实现DAC输出三角波和正弦波的功能。
在ST的标准库中,把控制DAC相关的各种配置封装到了结构体 DAC_InitTypeDef 中,该结构体的主要成员见代码清单1。
- //---------------------------------------------
- // 代码清单1:DAC_InitTypeDef结构体
- //---------------------------------------------
-
- typedef struct {
- //DAC触发方式
- uint32_t DAC_Trigger;
-
- //是否自动输出噪声或三角波
- uint32_t DAC_WaveGeneration;
-
- //选择噪声生成器的低通滤波或三角波的幅值
- uint32_t DAC_LFSRUnmask_TriangleAmplitude;
-
- //选择是否使能输出缓冲器
- uint32_t DAC_OutputBuffer;
- } DAC_InitTypeDef;
(1)DAC_Trigger
本成员用于配置DAC的触发模式,当DAC产生相应的触发事件时,才会把DHRx寄存器的值转移到DORx寄存器中进行转换。本结构体成员可以选择的触发模式如下:
(2)DAC_WaveGeneration
本成员用于设置是否使用DAC输出伪噪声或三角波:
(3)DAC_LFSRUnmask_TriangleAmplitude
(4)DAC_OutputBuffer
本结构体成员用于控制是否使能 DAC 的输出缓冲(
DAC_OutputBuffer_Enable/Disable),使能了 DAC 的输出缓冲后可以减小输出阻抗,适合直接驱动一些外部负载。
代码清单2里是几个与DAC有关的库函数,下面简要介绍一下这几个函数:
- //--------------------------------------
- // 代码清单2:几个常用的DAC库函数
- //--------------------------------------
-
- //使能DAC通道1
- DAC_Cmd(DAC_Channel_1, ENABLE);
-
- //12位右对齐数据格式设置DAC值
- DAC_SetChannel1Data(DAC_Align_12b_R, 0);
-
- //读出DAC通道1最后一次转换的数值
- DAC_GetDataOutputValue(DAC_Channel_1);
我们先来做一个DAC的基本输出实验,使用DAC通道1输出模拟电压,然后通过万用表对该输出电压进行测量,看是否符合我们设定的值。此外,还可以通过按键对该输出电压进行设置。
如图6所示,在按键的基础工程上新增针对DAC的驱动文件 dac.c 和 dac.h,在 main.c 文件里编写主控制逻辑。
(1)dac.h源码剖析
该文件源码见代码清单3,主要是DAC初始化和功能函数的声明,每个函数的功能和参数将在剖析 dac.c 源码时解读。
- //---------------------------------------------------
- // 代码清单3:dac.h
- //---------------------------------------------------
-
- #ifndef _DAC_H_
- #define _DAC_H_
-
- #include "sys.h"
-
- //---------------------------------------------------
- // 函数声明
- //---------------------------------------------------
- void DAC1_Init(void); //DAC通道1初始化
- void DAC1_Set_Vol(uint16_t vol); //设置DAC通道1输出电压
-
- #endif
(2)dac.c源码剖析
该文件就是所有DAC驱动函数的定义,如代码清单4所示。
- /**
- ************************************************************************
- * 代码清单4:dac.c
- * 描 述:DAC初始化、驱动
- * 平 台:百科荣创STM32F407核心板
- * 作 者:老耿
- * 日 期:2024-7-10
- * 固 件 库:ST3.5.0
- * 版 本:V1.0
- * 说 明:
- * 修改记录:无
- ************************************************************************
- **/
-
- //必要的头文件
- #include "dac.h"
-
- /**
- ************************************************************************
- * 函 数 名:DAC1_Init
- * 功 能:DAC通道1的配置
- * 入口参数:无
- * 出口参数:无
- * 说 明:DAC通道1对应PA4引脚,注意要配置成模拟输入
- ************************************************************************
- **/
- void DAC1_Init(void)
- {
- //定义必要的初始化结构体
- GPIO_InitTypeDef gpio_initstruct;
- DAC_InitTypeDef dac_initstruct;
-
- //打开GPIOA和DAC的外设时钟
- RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
- RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE);
-
- //PA4初始化为模拟输入
- gpio_initstruct.GPIO_Pin = GPIO_Pin_4;
- gpio_initstruct.GPIO_Mode = GPIO_Mode_AN;
- gpio_initstruct.GPIO_PuPd = GPIO_PuPd_DOWN;
- GPIO_Init(GPIOA, &gpio_initstruct);
-
- //不使用触发功能
- dac_initstruct.DAC_Trigger = DAC_Trigger_None;
- //不使用波形发生
- dac_initstruct.DAC_WaveGeneration = DAC_WaveGeneration_None;
- //屏蔽、幅值设置
- dac_initstruct.DAC_LFSRUnmask_TriangleAmplitude = DAC_LFSRUnmask_Bit0;
- //DAC1输出缓存关闭
- dac_initstruct.DAC_OutputBuffer = DAC_OutputBuffer_Disable;
- //初始化DAC通道1
- DAC_Init(DAC_Channel_1,&dac_initstruct);
-
- //使能DAC通道1
- DAC_Cmd(DAC_Channel_1, ENABLE);
- //12位右对齐数据格式设置DAC值
- DAC_SetChannel1Data(DAC_Align_12b_R, 0);
- }
-
- /**
- ************************************************************************
- * 函 数 名:DAC1_Set_Vol
- * 功 能:设置通道1输出电压
- * 入口参数:vol --- 0~3300,代表0~3.3V
- * 出口参数:无
- * 说 明:无
- ************************************************************************
- **/
- void DAC1_Set_Vol(uint16_t vol)
- {
- double temp = vol;
- temp /= 1000;
- temp = temp*4096/3.3;
- DAC_SetChannel1Data(DAC_Align_12b_R, temp);//12位右对齐数据格式设置DAC值
- }
(3)main.c源码剖析
主程序比较简单,如代码清单5所示,在完成必要的初始化之后,检测到S4按下,就累加DAC的值,检测到S3按下,就递减DAC的值。
- /******************************************************
- * 代码清单5:main.c
- * 项 目:DAC基本输出
- * 任务描述:使用DAC通道1输出模拟电压,然后通过万用表测量
- * 该输出电压进行测量,看是否符合我们设定的值。
- * 此外,还可以通过按键对该输出电压进行设置。
- * 实验平台:百科荣创STM32F407开发板
- * 作 者:老耿
- * 日 期:2024/07/10
- ******************************************************/
-
- //------------------------------------------------------
- // 必要的头文件
- //------------------------------------------------------
- #include "sys.h"
- #include "delay.h"
- #include "dac.h"
- #include "key.h"
-
- //------------------------------------------------------
- // 主函数
- //------------------------------------------------------
- int main(void)
- {
- uint16_t dacval = 0; //存放DAC值
- uint8_t key; //存放键值
-
- //设置系统中断优先级分组2
- NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
-
- Delay_Init(); //延时初始化
- Key_Init(); //按键初始化
- DAC1_Init(); //DAC通道1初始化
-
- //设置DAC初始值为0
- DAC_SetChannel1Data(DAC_Align_12b_R, dacval);
-
- while(1)
- {
- key = Key_Scan(0); //检测按下的键
-
- if(key == 4) //如果按下S4
- { //DAC值累加200,约0.16V
- if(dacval < 4000)
- dacval += 200;
- DAC_SetChannel1Data(DAC_Align_12b_R, dacval);
- }
- else if(key == 3) //如果按下S3
- { //DAC值递减200
- if(dacval > 200)
- dacval -= 200;
- else
- dacval = 0;
- DAC_SetChannel1Data(DAC_Align_12b_R, dacval);
- }
- }
- }
将万用表按照图7所示去测量DAC的输出电压,每单击一次S4按键,DAC值增加200,换算成电压值即:200*3.3/4096≈0.161V。同理,每单击一次S3按键,DAC值减少200。具体效果见文末的演示视频。
简易信号发生器项目的前奏,先搞定DAC基本输出
(第二部分完,共六部分)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。