首页 > 其他分享 >STM32CubeMX教程16 DAC - 输出3.3V内任意电压

STM32CubeMX教程16 DAC - 输出3.3V内任意电压

时间:2024-01-17 09:12:19浏览次数:26  
标签:输出 HAL 16 DAC STM32CubeMX DacValue GPIO

1、准备材料

开发板(正点原子stm32f407探索者开发板V2.4

STM32CubeMX软件(Version 6.10.0

keil µVision5 IDE(MDK-Arm

ST-LINK/V2驱动

野火DAP仿真器

XCOM V2.6串口助手

2、实验目标

使用STM32CubeMX软件配置STM32F407开发板的DAC OUT1实现输出0-3.3V任意模拟电压,然后用ADC1_IN5单通道采集DAC输出的电压,并利用USART1输出信息用于验证

3、实验流程

3.0、前提知识

STM32F407有一个DAC,该DAC拥有两个输出通道OUT1/2,每个通道均可以输出0~VREF+范围内电压、噪声波或三角波型

DAC集成了两个输出缓冲器,可用来降低输出阻抗并在不增加外部运算放大器的情况下直接驱动外部负载,该参数可以在STM32CubeMX中DAC参数配置页面配置,一般选择Enable

DAC输出的触发源一共有8个触发源,可以通过设置DAC控制寄存器DAC_CR的TSEL[2:0]位来决定触发源,其中外部引脚触发源在STM32CubeMX中需要勾选Mode中的External Trigger才可以选择,具体如下表所示 (注释1)

DAC的数字转模拟主要是利用片上的12位电压输出数模转换器来实现的,而这个12位电压输出数模转换器的输入数据为数据输出寄存器DORx中的内容,但是用户不能直接将数据写入数据输出寄存器DORx中,而是需要将数据输入数据保持寄存器DHRx中,然后等待触发源到来/一个时钟周期后,数据将自动从DHRx中转移到DORx中

由于DHRx寄存器位32位寄存器,而我们写入的数据为8/12位的,因此存在数据对齐的问题,采用不同的对齐方式需要将数据写入对应对齐方式的数据保存寄存器中,如下图所示为DAC单/双通道模式下的数据对齐模式,每种模式对应1/2个寄存器(注释1)

举个例子:

本实验采取DAC1单通道模式12位右对齐,因此笔者需要将数据写入 DAC_DHR12R1 寄存器中,而该寄存器的偏址从手册上可以看到为0x80

我们写入DAC寄存器数据时使用的函数为HAL_DAC_SetValue(&hdac,DAC_CHANNEL_1,DAC_ALIGN_12B_R,DacValue),其中DAC_ALIGN_12B_R值为0,因此最后将DacValue值写入了地址为DAC基址+0x00000008UL偏址的 DAC_DHR12R1 寄存器,上述描述如下图所示

DAC输出引脚输出的电压值由DACoutput = VREF+ * DOR / 4095公式计算,通常 VREF+直接与VDDA短接,因此DAC通道输出的电压范围为0-3.3V,如下图所示为DAC通道框图 (注释1)

3.1、CubeMX相关配置

3.1.0、工程基本配置

打开STM32CubeMX软件,单击ACCESS TO MCU SELECTOR选择开发板MCU(选择你使用开发板的主控MCU型号),选中MCU型号后单击页面右上角Start Project开始工程,具体如下图所示

开始工程之后在配置主页面System Core/RCC中配置HSE/LSE晶振,在System Core/SYS中配置Debug模式,具体如下图所示

详细工程建立内容读者可以阅读“STM32CubeMX教程1 工程建立

3.1.1、时钟树配置

系统时钟使用8MHz外部高速时钟HSE,HCLK、PCLK1和PCLK2均设置为STM32F407能达到的最高时钟频率,具体如下图所示

3.1.2、外设参数配置

本实验需要需要初始化开发板上KEY2和KEY0用户按键,具体配置步骤请阅读“STM32CubeMX教程3 GPIO输入 - 按键响应

本实验需要需要初始化USART1作为输出信息渠道,具体配置步骤请阅读“STM32CubeMX教程9 USART/UART 异步通信

本实验需要配置TIM3 100ms更新事件作为ADC1_IN5通道采集触发源的ADC采集,因此需要初始化TIM3和ADC1_IN5,具体配置步骤请阅读“STM32CubeMX教程13 ADC - 单通道转换”,如下图所示为配置简图

接下来配置DAC OUT1,在Pinout & Configuration页面左边功能分类栏目Analog中单击其中DAC,在Mode中勾选OUT1 Configuration

在DAC Out1 Settings中使能 Output Buffer Trigger 选择默认None,这里不需要触发源,也就是说当DAC启动后DAC就会一直输出下去而不是在每次一触发源来到的时候才输出,具体配置如下图所示

3.1.3、外设中断配置

DAC只有两个关于DMA的下溢事件的中断源,本实验尚不设计DMA,因此这里无需开启DAC的任何中断

但是DAC的输出电压需要由ADC1_IN5来采集,因此这里勾选ADC的全局中断,并设置合适的中断优先级,具体配置如下图所示

3.2、生成代码

3.2.0、配置Project Manager页面

单击进入Project Manager页面,在左边Project分栏中修改工程名称、工程目录和工具链,然后在Code Generator中勾选“Gnerate peripheral initialization as a pair of 'c/h' files per peripheral”,最后单击页面右上角GENERATE CODE生成工程,具体如下图所示

详细Project Manager配置内容读者可以阅读“STM32CubeMX教程1 工程建立”实验3.4.3小节

3.2.1、外设初始化调用流程

在生成的工程代码主函数中增加了MX_DAC_Init()函数,该函数对启用的DAC触发方式、输出缓存进行了配置

然后调用HAL_DAC_Init()函数对DAC进行了初始化,并调用了HAL_DAC_MspInit()函数

在HAL_DAC_MspInit()函数中对DAC OUT1的输出引脚PA4做了引脚复用配置,并且使能了DAC的时钟,如果配置了中断,在该函数中还会出现中断优先级及中断使能相关代码

上述DAC初始化调用流程如下图所示

3.2.2、外设中断调用流程

本实验只开启了ADC的全局中断,ADC全局中断调用流程请阅读“STM32CubeMX教程13 ADC - 单通道转换”4.2.2小节

3.2.3、添加其他必要代码

在adc.c中重新实现ADC采集完毕中断回调函数HAL_ADC_ConvCpltCallback(),与”STM32CubeMX教程13 ADC - 单通道转换”实验内容一致,目的就是获取ADC1_IN5通道采集值并通过USART1输出,具体代码如下所示

void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc)
{
    if(hadc->Instance == ADC1)
    {
        uint32_t val=HAL_ADC_GetValue(&hadc1);
        uint32_t Volt=(3300*val)>>12;
        printf("val:%d, Volt:%d\r\n",val,Volt);
    }
}

在主函数中启动DAC输出,并设置默认的DAC输出值,然后启动定时器和ADC采集,并在主循环中实现按下按键KEY2将DAC输出值增加500,按下按键KEY0将DAC输出值减少500,具体代码如下图所示

源代码如下所示

/*主循环外代码*/
printf("Reset\r\n");
HAL_DAC_Start(&hdac,DAC_CHANNEL_1);
uint32_t DacValue=1000;
HAL_DAC_SetValue(&hdac,DAC_CHANNEL_1,DAC_ALIGN_12B_R,DacValue);
HAL_ADC_Start_IT(&hadc1);
HAL_TIM_Base_Start(&htim3);


/*主循环中代码*/
/*按键KEY2被按下*/
if(HAL_GPIO_ReadPin(KEY2_GPIO_Port,KEY2_Pin) == GPIO_PIN_RESET)
{
    HAL_Delay(50);
    if(HAL_GPIO_ReadPin(KEY2_GPIO_Port,KEY2_Pin) == GPIO_PIN_RESET)
    {
        printf("---now DacValue is: %d---\r\n", DacValue);
        DacValue += 500;
        if(DacValue>4095) DacValue=4095;
        HAL_DAC_SetValue(&hdac,DAC_CHANNEL_1,DAC_ALIGN_12B_R,DacValue);
        while(!HAL_GPIO_ReadPin(KEY2_GPIO_Port,KEY2_Pin));
    }
}


/*按键KEY0被按下*/
if(HAL_GPIO_ReadPin(KEY0_GPIO_Port,KEY0_Pin) == GPIO_PIN_RESET)
{
    HAL_Delay(50);
    if(HAL_GPIO_ReadPin(KEY0_GPIO_Port,KEY0_Pin) == GPIO_PIN_RESET)
    {
        printf("---now DacValue is: %d---\r\n", DacValue);
        DacValue -= 500;
        HAL_DAC_SetValue(&hdac,DAC_CHANNEL_1,DAC_ALIGN_12B_R,DacValue);
        while(!HAL_GPIO_ReadPin(KEY0_GPIO_Port,KEY0_Pin));
    }
}

值得提醒的是本实验使用DAC1_IN5(PA5)采集DAC OUT1(PA4)输出值,需要使用跳线帽/杜邦线将PA5和PA4两个引脚短接

4、常用函数

/*DAC软件启动输出函数*/
HAL_StatusTypeDef HAL_DAC_Start(DAC_HandleTypeDef *hdac, uint32_t Channel)

/*DAC软件停止输出函数*/
HAL_StatusTypeDef HAL_DAC_Stop(DAC_HandleTypeDef *hdac, uint32_t Channel)

/*DAC输出值设置*/
HAL_StatusTypeDef HAL_DAC_SetValue(DAC_HandleTypeDef *hdac, uint32_t Channel, uint32_t Alignment, uint32_t Data)

5、烧录验证

烧录程序,开发板上电后打开串口助手,可以看到间隔100ms输出一次采集到的DAC OU1输出值,默认输出值为1000,按下KEY2按键后输出值增加500,按下KEY0按键后输出值减少500,可以从串口输出信息看到采集到的值和我们预想的效果一致,如下图所示为整个过程串口输出信息

6、注释详解

注释1:图片来源STM32F4xx 中文参考手册

更多内容请浏览 STM32CubeMX+STM32F4系列教程文章汇总贴

标签:输出,HAL,16,DAC,STM32CubeMX,DacValue,GPIO
From: https://www.cnblogs.com/lc-guo/p/17965467

相关文章

  • 20230116python基本语法day1
    20230116python基本语法day1代码看一行写一行。菜鸟教程python3成为自己尊重自己欣赏的自己。注意点:python中,#TODO待处理,显示为黄色,这边的问题要在最后解决掉,这很重要。在java中可能是//TODO    解释器的作用是运行文件,给代码解释文件。......
  • 已知IP地址是192.168.5.121,子网掩码是255.255.255.248,求网络地址和主机地址
    方法1首先,将Ip地址转换为二进制:192.168.5.121转换为二进制:11000000.10101000.00000101.01111001然后将子网掩码转换成二进制:255.255.255.248转换为二进制:11111111.11111111.11111111.11111000网络地址=IP地址&子网掩码192.168.5.121&255.255.255.248=192.168.5.12......
  • 英文闲话 2024-01-16
    Iwokeupat6on1-13,thedayofecfinal.IcheckedmyphoneandsurprisinglyfoundthatLolasentmeaemail.Afterannalyzingwhatshetalkedaboutcarefully,IrepliedherwithwhatIthought.InadditionIpraisedWeimingLakeandshowedmyappreciatio......
  • 吴师兄学算法day07 双指针 16. 最接近的三数之和
    题目:16. 最接近的三数之和易错点:比较的时候,要减去target在算结果我的代码:classSolution:defthreeSumClosest(self,nums:List[int],target:int)->int:nums=sorted(nums)max_res=float('inf')min_res=float('-inf')......
  • 2024.1.16做题纪要
    硬币多少有些人类智慧了。。。。。题解写的还行。具体就是每次把当前这一位代表的质数\(i\)向后每隔\(i\)个数除上\(i\)。这一位肯定是一个质数,因为若是合数则前面一定会被除上质数。Kaiserredux#include<bits/stdc++.h>longlongnum[1100000];longlonganswe......
  • 2024-1-16
    0这里是我的博客平台。接下来的博客时间将以学习编程为主题,多多关照。1学习编程的目标——学会编程,实在的插足新领域自夸。#include<stdio.h>intmain(void){charc;do{c=getchar();}while(c!='');printf("\a");printf("helloC\n");p......
  • 1.16寒假每日总结7
     对接口的参数进行合法性校验。  如果不符合参数校验,会报错,但是不合符接口文档要求,所以要进行异常处理 ......
  • C++学习日记 2024-1-16
    开始学习C++几天了,之前没有记录,从现在开始,记录一下学习过程复习与回忆:1.引用与指针共同优点:只用引用与指针,在传递参数时,可以减少拷贝,减少内存消耗,提高效率指针优点:指针比引用更强大,所有引用能做的事,指针都能做,指针缺点:危险,指针可以为空,指针指向地址,同一地址可以......
  • 2024.1.16
    写些不知道写在哪的东西。看了一下THUSC2023Day1T1。有一个长度为\(n\)的序列,\(q\)次操作,形如区间加和询问区间内至少几次区间±1使得区间内所有数等于\(x\)结论是把区间两边接上\(x\),答案是差分序列的绝对值之和除以\(2\)是这样的,考虑区间加在差分序列上的改变,即为一......
  • 2024.1.16日报
    今天继续学习spark,不过今天有些特殊,因为有些同学回来了,大伙在一起交流了一下总体上考研的居多,所以自己也有些犹豫到底是要考研还是就业,需要深入的思考一下 总结:RDD是一个数据集的表示,不仅表示了数据集,还表示了这个数据集从哪来,如何计算,主要属性包括:分区列表计算函数依赖关系......