当前位置:   article > 正文

我在百科荣创企业实践——简易函数信号发生器(2)

我在百科荣创企业实践——简易函数信号发生器(2)

        对于高职教师来说,必不可少的一个任务就是参加企业实践。这个暑假,本人也没闲着,报名参加了上海市电子信息类教师企业实践。7月8日到13日,有幸来到美丽的泉城济南,远离了上海的酷暑,走进了百科荣创科技发展有限公司。在这短短的一周时间里,我结合自己的教学经验和企业的需求,对一个简易函数信号发生器的嵌入式项目做了教学内容的转换。接下来,就用博客的形式,把这次转换后的成果分享出来,如果您觉得有用,还望多多点赞和转发!

        针对本项目,笔者根据自己的理解,力求做到循序渐进和逐步深入,计划用6篇文章来展开,本文是第二篇,我们先来聊聊STM32的DAC,并做一个基本输出的实验。

二、STM32的DAC

2.1 DAC简介

        DAC为数字/模拟转换模块,顾名思义,它的作用就是把输入的数字编码,转换成对应的模拟电压输出,它的功能与ADC相反。在常见的数字信号系统中,大部分传感器信号被化成电压信号,而ADC把电压模拟信号转换成易于计算机存储、处理的数字编码,由计算机处理完成后,再由DAC输出电压模拟信号,该电压模拟信号常常用来驱动某些执行器件,使人类易于感知。如音频信号的采集及还原就是这样一个过程。

2.2 STM32的DAC功能概览

        STM32具有片上DAC外设,它的分辨率可配置为8位或12位的数字输入信号,具有两个DAC输出通道,这两个通道互不影响,每个通道都可以使用DMA功能,都具有出错检测能力,可外部触发。图4是STM32的DAC功能框图。

图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进行数据转换。

2.3 参考电压

        与ADC外设类似,DAC也使用VREF+ 引脚作为参考电压,在设计原理图的时候一般把VSSA 接地,把VREF+和VDDA接3.3V,可得到DAC的输出电压范围为:0~3.3V。如果想让输出的电压范围变宽,可以在外部加一个电压调理电路,把0~3.3V的DAC输出抬升到特定的范围即可。

2.4 输出通道

        框图中的“数字至模拟转换器x”是核心部件,整个DAC外设都围绕它而展开。它以左边的VREF+作为参考电源,以DAC的数据寄存器“DORx”的数字编码作为输入,经过它转换得的模拟信号由右侧的“DAC_OUTx”通道输出。其中各个部件中的“x”是指设备的标号,在STM32中具有2个这样的DAC部件,每个DAC有1个对应的输出通道连接到特定的引脚(PA4-通道1,PA5-通道2)为避免干扰,使用DAC功能时,DAC通道引脚需要被配置成模拟输入功能(AIN)。

2.5 触发源及DHRx寄存器

        在使用DAC时,不能直接对上述DORx寄存器写入数据,任何输出到DAC通道x的数据都必须写入到DHRx寄存器中(其中包含DHR8Rx、DHR12Lx等,根据数据对齐方向和分辨率的情况写入到对应的寄存器中)。

        数据被写入到DHRx寄存器后,DAC会根据触发配置进行处理,若使用硬件触发,则DHRx中的数据会在3个APB1时钟周期后传输至DORx,DORx随之输出相应的模拟电压到输出通道;若DAC设置为外部事件触发,可以使用定时器(TIMx_TRGO)、EXTI_9信号或软件触发(SWTRIGx)这几种方式控制数据DAC转换的时机,例如使用定时器触发,配合不同时刻的DHRx数据,可实现DAC输出三角波和正弦波的功能。

2.6 DAC初始化结构体详解

        在ST的标准库中,把控制DAC相关的各种配置封装到了结构体 DAC_InitTypeDef 中,该结构体的主要成员见代码清单1。

  1. //---------------------------------------------
  2. // 代码清单1:DAC_InitTypeDef结构体
  3. //---------------------------------------------
  4. typedef struct {
  5. //DAC触发方式
  6. uint32_t DAC_Trigger;
  7. //是否自动输出噪声或三角波
  8. uint32_t DAC_WaveGeneration;
  9. //选择噪声生成器的低通滤波或三角波的幅值
  10. uint32_t DAC_LFSRUnmask_TriangleAmplitude;
  11. //选择是否使能输出缓冲器
  12. uint32_t DAC_OutputBuffer;
  13. } DAC_InitTypeDef;

(1)DAC_Trigger

        本成员用于配置DAC的触发模式,当DAC产生相应的触发事件时,才会把DHRx寄存器的值转移到DORx寄存器中进行转换。本结构体成员可以选择的触发模式如下:

  • 硬件触发模式(DAC_Trigger_None),DHRx寄存器内的数据会在3个APB1时钟周期内自动转换至DORx进行转换;
  • 定时器触发模式(DAC_Trigger_T2/4/5/6/7_TRGO),使用定时器2、4、5、6、7控制DHRx寄存器的数据按时间转移到DORx中进行转换,利用这种方式可以输出特定的波形;
  • EXTI_9 触发方式(DAC_Trigger_Ext_IT9),当产生EXTI_9 事件时(如GPIO中断事件),触发转换;
  • 软件触发模式(DAC_Trigger_Software),在本模式下,向DAC_SWTRIGR寄存器写入配置即可触发信号进行转换。

(2)DAC_WaveGeneration

        本成员用于设置是否使用DAC输出伪噪声或三角波:

  • DAC_WaveGeneration_None/Noise/Triangle:使用伪噪声和三角波输出时,DAC都会把LFSR寄存器的值叠加到DHRx数值上,产生伪噪声和三角波,若希望产生自定义的输出时,直接配置为DAC_WaveGeneration_None即可。

(3)DAC_LFSRUnmask_TriangleAmplitude

  • 当使用伪噪声或三角波输出时要叠加到 DHRx 的值,非噪声或三角波输出模式下,本配置无效。
  • 使用伪噪声输出时,把本结构体成员赋值为DAC_LFSRUnmask_Bit0~DAC_LFSRUnmask_Bit11_0 等宏即可;
  • 使用三角波输出时,本结构体设置三角波的最大幅值,可选择为DAC_TriangleAmplitude_1~DAC_TriangleAmplitude_4096 等宏,如图5所示,DAC在DHRx值的基础上升,达到最大幅度时下降,形成三角波的输出。
图5 DAC初始化中的三角波控制示意

(4)DAC_OutputBuffer

        本结构体成员用于控制是否使能 DAC 的输出缓冲(
DAC_OutputBuffer_Enable/Disable),使能了 DAC 的输出缓冲后可以减小输出阻抗,适合直接驱动一些外部负载。

2.7 与DAC相关的库函数

        代码清单2里是几个与DAC有关的库函数,下面简要介绍一下这几个函数:

  • DAC_Cmd() 函数用来使能DAC转换通道;
  • DAC开始工作后,我们就可以使用 DAC_SetChannel1Data() 函数在DAC的输出通道1(PA4引脚)得到不同的电压值了。第一个参数设置对齐方式,可以为12位右对齐 DAC_Align_12b_R、12位左对齐 DAC_Align_12b_L 以及8位右对齐 DAC_Align_8b_R 方式。第二个参数就是DAC的输入值了,这个很好理解,初始可以设置为0。
  • DAC_GetDataOutputValue() 函数看名字也很好理解,是获取DAC指定通道上的值。注意,读到的值是最近一次转换的值。
  1. //--------------------------------------
  2. // 代码清单2:几个常用的DAC库函数
  3. //--------------------------------------
  4. //使能DAC通道1
  5. DAC_Cmd(DAC_Channel_1, ENABLE);
  6. //12位右对齐数据格式设置DAC值
  7. DAC_SetChannel1Data(DAC_Align_12b_R, 0);
  8. //读出DAC通道1最后一次转换的数值
  9. DAC_GetDataOutputValue(DAC_Channel_1);

三、DAC基本输出

        我们先来做一个DAC的基本输出实验,使用DAC通道1输出模拟电压,然后通过万用表对该输出电压进行测量,看是否符合我们设定的值。此外,还可以通过按键对该输出电压进行设置。

3.1 工程文件清单

        如图6所示,在按键的基础工程上新增针对DAC的驱动文件 dac.c 和 dac.h,在 main.c 文件里编写主控制逻辑。

图6 DAC基本输出工程文件清单

3.2 工程代码剖析

(1)dac.h源码剖析

        该文件源码见代码清单3,主要是DAC初始化和功能函数的声明,每个函数的功能和参数将在剖析 dac.c 源码时解读。

  1. //---------------------------------------------------
  2. // 代码清单3:dac.h
  3. //---------------------------------------------------
  4. #ifndef _DAC_H_
  5. #define _DAC_H_
  6. #include "sys.h"
  7. //---------------------------------------------------
  8. // 函数声明
  9. //---------------------------------------------------
  10. void DAC1_Init(void); //DAC通道1初始化
  11. void DAC1_Set_Vol(uint16_t vol); //设置DAC通道1输出电压
  12. #endif

(2)dac.c源码剖析

        该文件就是所有DAC驱动函数的定义,如代码清单4所示。

  1. /**
  2. ************************************************************************
  3. * 代码清单4:dac.c
  4. * 描 述:DAC初始化、驱动
  5. * 平 台:百科荣创STM32F407核心板
  6. * 作 者:老耿
  7. * 日 期:2024-7-10
  8. * 固 件 库:ST3.5.0
  9. * 版 本:V1.0
  10. * 说 明:
  11. * 修改记录:无
  12. ************************************************************************
  13. **/
  14. //必要的头文件
  15. #include "dac.h"
  16. /**
  17. ************************************************************************
  18. * 函 数 名:DAC1_Init
  19. * 功 能:DAC通道1的配置
  20. * 入口参数:无
  21. * 出口参数:无
  22. * 说 明:DAC通道1对应PA4引脚,注意要配置成模拟输入
  23. ************************************************************************
  24. **/
  25. void DAC1_Init(void)
  26. {
  27. //定义必要的初始化结构体
  28. GPIO_InitTypeDef gpio_initstruct;
  29. DAC_InitTypeDef dac_initstruct;
  30. //打开GPIOA和DAC的外设时钟
  31. RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
  32. RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE);
  33. //PA4初始化为模拟输入
  34. gpio_initstruct.GPIO_Pin = GPIO_Pin_4;
  35. gpio_initstruct.GPIO_Mode = GPIO_Mode_AN;
  36. gpio_initstruct.GPIO_PuPd = GPIO_PuPd_DOWN;
  37. GPIO_Init(GPIOA, &gpio_initstruct);
  38. //不使用触发功能
  39. dac_initstruct.DAC_Trigger = DAC_Trigger_None;
  40. //不使用波形发生
  41. dac_initstruct.DAC_WaveGeneration = DAC_WaveGeneration_None;
  42. //屏蔽、幅值设置
  43. dac_initstruct.DAC_LFSRUnmask_TriangleAmplitude = DAC_LFSRUnmask_Bit0;
  44. //DAC1输出缓存关闭
  45. dac_initstruct.DAC_OutputBuffer = DAC_OutputBuffer_Disable;
  46. //初始化DAC通道1
  47. DAC_Init(DAC_Channel_1,&dac_initstruct);
  48. //使能DAC通道1
  49. DAC_Cmd(DAC_Channel_1, ENABLE);
  50. //12位右对齐数据格式设置DAC值
  51. DAC_SetChannel1Data(DAC_Align_12b_R, 0);
  52. }
  53. /**
  54. ************************************************************************
  55. * 函 数 名:DAC1_Set_Vol
  56. * 功 能:设置通道1输出电压
  57. * 入口参数:vol --- 0~3300,代表0~3.3V
  58. * 出口参数:无
  59. * 说 明:无
  60. ************************************************************************
  61. **/
  62. void DAC1_Set_Vol(uint16_t vol)
  63. {
  64. double temp = vol;
  65. temp /= 1000;
  66. temp = temp*4096/3.3;
  67. DAC_SetChannel1Data(DAC_Align_12b_R, temp);//12位右对齐数据格式设置DAC值
  68. }

(3)main.c源码剖析

        主程序比较简单,如代码清单5所示,在完成必要的初始化之后,检测到S4按下,就累加DAC的值,检测到S3按下,就递减DAC的值。

  1. /******************************************************
  2. * 代码清单5:main.c
  3. * 项 目:DAC基本输出
  4. * 任务描述:使用DAC通道1输出模拟电压,然后通过万用表测量
  5. * 该输出电压进行测量,看是否符合我们设定的值。
  6. * 此外,还可以通过按键对该输出电压进行设置。
  7. * 实验平台:百科荣创STM32F407开发板
  8. * 作 者:老耿
  9. * 日 期:2024/07/10
  10. ******************************************************/
  11. //------------------------------------------------------
  12. // 必要的头文件
  13. //------------------------------------------------------
  14. #include "sys.h"
  15. #include "delay.h"
  16. #include "dac.h"
  17. #include "key.h"
  18. //------------------------------------------------------
  19. // 主函数
  20. //------------------------------------------------------
  21. int main(void)
  22. {
  23. uint16_t dacval = 0; //存放DAC值
  24. uint8_t key; //存放键值
  25. //设置系统中断优先级分组2
  26. NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
  27. Delay_Init(); //延时初始化
  28. Key_Init(); //按键初始化
  29. DAC1_Init(); //DAC通道1初始化
  30. //设置DAC初始值为0
  31. DAC_SetChannel1Data(DAC_Align_12b_R, dacval);
  32. while(1)
  33. {
  34. key = Key_Scan(0); //检测按下的键
  35. if(key == 4) //如果按下S4
  36. { //DAC值累加200,约0.16V
  37. if(dacval < 4000)
  38. dacval += 200;
  39. DAC_SetChannel1Data(DAC_Align_12b_R, dacval);
  40. }
  41. else if(key == 3) //如果按下S3
  42. { //DAC值递减200
  43. if(dacval > 200)
  44. dacval -= 200;
  45. else
  46. dacval = 0;
  47. DAC_SetChannel1Data(DAC_Align_12b_R, dacval);
  48. }
  49. }
  50. }

3.3 验证与测试

        将万用表按照图7所示去测量DAC的输出电压,每单击一次S4按键,DAC值增加200,换算成电压值即:200*3.3/4096≈0.161V。同理,每单击一次S3按键,DAC值减少200。具体效果见文末的演示视频。

图7 使用万用表测量DAC输出电压

简易信号发生器项目的前奏,先搞定DAC基本输出

(第二部分完,共六部分)

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家小花儿/article/detail/847586
推荐阅读
相关标签
  

闽ICP备14008679号