【STM32】RTT-Studio中HAL库开发教程四:DAC+DMA输出波形

news2024/9/22 17:27:40

文章目录

    • 一、DAC介绍
    • 二、HAL库配置初始化
    • 三、RTT中初始化
    • 四、测试验证

一、DAC介绍

1.DAC作用

  • DAC(Digital-to-Analog Converter),即为数字/模拟转换模块,又称D/A转换器;
  • 作用就是把输入的数字编码,转换成对应的模拟电压输出,它的功能与ADC 相反。即为输出波形和输出固定电压

2.DAC介绍

  • 在常见的数字信号系统中,大部分传感器信号被化成电压信号,而ADC 把电压模拟信号转换成易于计算机存储、处理的数字编码,由计算机处理完成后,再由DAC 输出电压模拟信号,该电压模拟信号常常用来驱动某些执行器件,使人类易于感知。
  • STM32 具有片上DAC 外设,它的分辨率可配置为8 位或12 位的数字输入信号,具有两个DAC输出通道,这两个通道互不影响,每个通道都可以使用DMA 功能,都具有出错检测能力,可外部触发。
  • 在双DAC模式下,2个通道可以独立地进行转换,也可以同时进行转换并同步地更新2个通道的输出。DAC可以通过引脚输入参考电压VREF+ 以获得更精确的转换结果。
  • DAC工作在12位模式时,数据可以设置成左对齐或右对齐

3.DAC功能框图
在这里插入图片描述
(1)整个DAC 模块围绕框图下方的“数字至模拟转换器x”展开,它的左边分别是参考电源的引脚:VDDA、VSSA 及Vref+,其中STM32 的DAC 规定了它的参考电压Vref+ 输入范围为2.4——3.3V。
(2)“数字至模拟转换器x”的输入为DAC 的数据寄存器“DORx”的数字编码,经过它转换得的模拟信号由图中右侧的“DAC_OUTx”输出。而数据寄存器“DORx”又受“控制逻辑”支配,它可以控制数据寄存器加入一些伪噪声信号或配置产生三角波信号。
(3)图中的左上角为DAC 的触发源,DAC根据触发源的信号来进行DAC 转换,其作用就相当于DAC 转换器的开关,它可以配置的触发源为外部中断源触发、定时器触发或软件控制触发

4.参考电压
与ADC 外设类似,DAC 也使用VREF+ 引脚作为参考电压,在设计原理图的时候一般把VSSA 接地,把VREF+ 和VDDA 接3.3V,可得到DAC 的输出电压范围为:0~3.3V。

如果想让输出的电压范围变宽,可以在外部加一个电压调理电路,把0~3.3V 的DAC 输出抬升到特定的范围即可。

5.数模转换及输出通道
框图中的“数字至模拟转换器x”是核心部件,整个DAC 外设都围绕它而展开。它以左边的VREF+作为参考电源,以DAC 的数据寄存器“DORx”的数字编码作为输入,经过它转换得的模拟信号由右侧的“DAC_OUTx”通道输出。

其中各个部件中的“x”是指设备的标号,在STM32 中具有2 个这样的DAC 部件,每个DAC 有1 个对应的输出通道连接到特定的引脚,即:PA4-通道1,PA5-通道2,为避免干扰,使用DAC 功能时,DAC 通道引脚需要被配置成模拟输入功能(AIN)。

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

数据被写入到DHRx 寄存器后,DAC 会根据触发配置进行处理,若使用硬件触发,则DHRx 中的数据会在3 个APB1 时钟周期后传输至DORx,DORx 随之输出相应的模拟电压到输出通道;

若DAC 设置为外部事件触发,可以使用定时器(TIMx_TRGO)、EXTI_9 信号或软件触发(SWTRIGx)这几种方式控制数据DAC 转换的时机,例如使用定时器触发,配合不同时刻的DHRx 数据,可实现DAC 输出正弦波的功能。

二、HAL库配置初始化

1.配置时钟
在这里插入图片描述
在这里插入图片描述
2.开启DAC
在这里插入图片描述

  • Output Buffer:关闭后可输出更低的电压值
  • Trigger:触发方式,这里选择TIM2定时器触发
  • Wave generation mode:关闭后可输出自定义波形

3.开启DMA
在这里插入图片描述

  • Mode:选择循环模式
  • Data Width:发送数据长度,半字即可

4.配置定时器
在这里插入图片描述

  • 初始化定时器触发频率为1:84MHZ /(84 * 20) =50000HZ
  • 触发事件选择“更新触发事件”

5.配置中断优先级
在这里插入图片描述

三、RTT中初始化

1.board.h
在board.h中添加宏定义,使用DAC的驱动函数

#define BSP_USING_DAC1

2.board.c
在board.c中添加如下初始化代码,进行DAC引脚初始化。

/**
 * @brief DAC初始化
 * @param dacHandle
 */
void HAL_DAC_MspInit(DAC_HandleTypeDef* dacHandle)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    if (dacHandle->Instance == DAC)
    {
        /* DAC clock enable */
        __HAL_RCC_DAC_CLK_ENABLE();
        __HAL_RCC_GPIOA_CLK_ENABLE();

        /**DAC GPIO Configuration
         PA5     ------> DAC_OUT2
         */
        GPIO_InitStruct.Pin = GPIO_PIN_5;
        GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
        HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

        /* DAC DMA Init */
        /* DAC2 Init */
        hdma_dac2.Instance = DMA1_Stream6;
        hdma_dac2.Init.Channel = DMA_CHANNEL_7;
        hdma_dac2.Init.Direction = DMA_MEMORY_TO_PERIPH;
        hdma_dac2.Init.PeriphInc = DMA_PINC_DISABLE;
        hdma_dac2.Init.MemInc = DMA_MINC_ENABLE;
        hdma_dac2.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
        hdma_dac2.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
        hdma_dac2.Init.Mode = DMA_CIRCULAR;
        hdma_dac2.Init.Priority = DMA_PRIORITY_LOW;
        hdma_dac2.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
        if (HAL_DMA_Init(&hdma_dac2) != HAL_OK)
        {
            Error_Handler();
        }

        __HAL_LINKDMA(dacHandle, DMA_Handle2, hdma_dac2);
    }
}

3.dac.c

#include "dac.h"

// 方波缓冲波形
uint16_t signalSquareBuffer[DAC_SAMPLE_POINT] = {0};

/*======================================================##### 外部调用 #####==================================================*/
/**
 * @brief DAC输出DMA初始化
 */
void DAC_TIME_DMA_Init(void)
{
    DAC_ChannelConfTypeDef sConfig = {0};
    TIM_ClockConfigTypeDef sClockSourceConfig = {0};
    TIM_MasterConfigTypeDef sMasterConfig = {0};

    /* DMA controller clock enable */
    __HAL_RCC_GPIOA_CLK_ENABLE();
    __HAL_RCC_DMA1_CLK_ENABLE();

    /** DAC Initialization
     */
    hdac.Instance = DAC;
    if (HAL_DAC_Init(&hdac) != HAL_OK)
    {
        Error_Handler();
    }

    /** DAC channel OUT2 config
     */
    sConfig.DAC_Trigger = DAC_TRIGGER_T2_TRGO;
    sConfig.DAC_OutputBuffer = DAC_OUTPUTBUFFER_DISABLE;
    if (HAL_DAC_ConfigChannel(&hdac, &sConfig, DAC_CHANNEL_2) != HAL_OK)
    {
        Error_Handler();
    }

    htim2.Instance = TIM2;
    htim2.Init.Prescaler = 20 - 1;
    htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
    htim2.Init.Period = 84 - 1;
    htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
    htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
    if (HAL_TIM_Base_Init(&htim2) != HAL_OK)
    {
        Error_Handler();
    }
    sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
    if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK)
    {
        Error_Handler();
    }
    sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;
    sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
    if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)
    {
        Error_Handler();
    }

    /* DMA interrupt init */
    /* DMA1_Stream6_IRQn interrupt configuration */
    HAL_NVIC_SetPriority(DMA1_Stream6_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(DMA1_Stream6_IRQn);
}

/**
 * @brief 启动DAC的定时器和DMA输出
 */
void Start_Dac_Time_DMA(void)
{
    HAL_TIM_Base_Start(&htim2);
    HAL_DAC_Start_DMA(&hdac, DAC_CHANNEL_2, (uint32_t *)signalSquareBuffer, DAC_SAMPLE_POINT, DAC_ALIGN_12B_R);
}

/**
 * @brief 关闭DAC的定时器和DMA输出
 */
void Stop_Dac_Time_DMA(void)
{
    HAL_TIM_Base_Stop(&htim2);
    HAL_DAC_Stop_DMA(&hdac, DAC_CHANNEL_2);
}

/**
 * @brief 方波信号缓存填充
 * @param duty: 占空比设置0 ~ 100
 * @param vol : 电压设置 0-2.5V
 */
void signalSquare (uint16_t  duty, float vol)
{
    if (duty < 0)
        duty = 0;
    else if (duty > DAC_SAMPLE_POINT)
        duty = DAC_SAMPLE_POINT;

    if (vol > DAC_OUTPUT_MAX)
        vol = DAC_OUTPUT_MAX;
    else if (vol < 0)
        vol = 0;

    for (int i = 0; i < duty; i++)
    {
        signalSquareBuffer[i] = vol * DAC_REFERENCE_VALUE / DAC_OUTPUT_MAX;
    }

    for (int i = duty; i < DAC_SAMPLE_POINT; i++)
    {
        signalSquareBuffer[i] = 0;
    }
}

/**
 * @brief 输出方波参数设置
 * @param duty:占空比设置
 * @param vol :输出电压
 * @param fre :输出频率
 */
void I_DAC_Output(uint16_t duty, float vol, uint32_t fre)
{
    signalSquare(duty, vol);

    //计算PWM频率,所对应的自动重装载值   ---> ARR = 主频 / (预分频+1) / 预期PWM频率(Hz) - 1
    float prescaler = (TIMER_FREQ * 1.0) / (84 * DAC_SAMPLE_POINT * fre) - 1;

    //配置PSC预分频值
    __HAL_TIM_SET_PRESCALER(&htim2, prescaler);
}

/**
 * @brief 实时输出方波数据
 */
void I_DAC_Output_Signal(void)
{
    uint16_t pwr, pt, it;

    // 数据改变后再输出
    if (g_laser.param_flag == 1)
    {
        g_laser.param_flag = 0;
        pwr = g_laser.laser_pwr;
        pt = g_laser.laser_pt;
        it = g_laser.laser_it;
        I_DAC_Output(100.0 * pt / (pt + it), pwr * I_DAC_MAX / I_PWR_MAX, 1000.0 / (pt + it));
    }
}
/*=====================================================#######  END  #######=================================================*/

4.dac.h

#ifndef APPLICATIONS_SYSTEM_INC_DAC_H_
#define APPLICATIONS_SYSTEM_INC_DAC_H_

#include <rtthread.h>

/**====================================================###### 宏定义 ######==================================================*/
#define  DAC_OUTPUT_MAX         (3.3f)          // DAC输出最大值
#define  I_DAC_MAX              (2.5f)          // I_DAC输出最大电压
#define  I_PWR_MAX              850             // I_DAC输出功率最大值
#define  DAC_REFERENCE_VALUE    4095            // DAC参考值
#define  TIMER_FREQ             84000000        // 定时器主频
#define  DAC_SAMPLE_POINT       100             // 采样点个数
/**====================================================#######  END  #######=================================================*/

/**=================================================##### 函数及变量声明 #####===============================================*/
extern void DAC_TIME_DMA_Init(void);                                    // DAC输出DMA初始化
extern void Start_Dac_Time_DMA(void);                                   // 启动DAC的定时器和DMA输出
extern void Stop_Dac_Time_DMA(void);                                    // 关闭DAC的定时器和DMA输出
extern void signalSquare (uint16_t  duty, float vol);                   // 方波信号缓存填充
extern void I_DAC_Output(uint16_t duty, float vol, uint32_t pscVal);    // 输出方波参数设置
extern void I_DAC_Output_Signal(void);                                  // 实时输出方波数据
/**====================================================#######  END  #######=================================================*/

#endif /* APPLICATIONS_SYSTEM_INC_DAC_H_ */

5.其他波形函数

//   正弦波信号缓存填充
void signalSin(void)
{
    for (int i = 0; i < 100; i++)
    {
        signalSinBuffer[i] = ((sin(i * 2 * PI / 100) + 1) * (4096 / 2));
    }
}

//   三角波信号缓存填充,duty: 对称性设置 0 ~ 100
void signalRamp(uint16_t duty)
{
    if(duty < 0)    duty = 0;
    if (duty > 100) duty = 100;

    for (int i = 0; i < duty; i++)
    {
        signalRampBuffer[i] = i * 4095 / duty;
    }

    for (int i = duty; i < 100; i++)
    {
        signalRampBuffer[i] = (100 - i) * 4095 / (100 - duty);
    }
}

四、测试验证

1.main.c测试

int main(void)
{
    DAC_TIME_DMA_Init();        // DAC输出DMA初始化
    Start_Dac_Time_DMA();       // 启动DAC的定时器和DMA输出

    while (1)
    {
    	I_DAC_Output(50, 5, 500);
        rt_thread_mdelay(1);
    }

    return RT_EOK;
}

总结:通过使用示波器测试输出的波形,可以正常观察到方波信号,并且可以更改方波的幅度、频率和占空比。同时上面还附带了其他波形生成的函数以供使用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1932819.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

昇思25天学习打卡营第16天 | Vision Transformer图像分类

昇思25天学习打卡营第16天 | Vision Transformer图像分类 文章目录 昇思25天学习打卡营第16天 | Vision Transformer图像分类Vision Transform&#xff08;ViT&#xff09;模型TransformerAttention模块Encoder模块 ViT模型输入 模型构建Multi-Head Attention模块Encoder模块Pa…

Java对象转换为JSON字符串

0 写在前面 业务中有很多场景需要 把一个带有数据的 Java对象/Java集合转换为JSON 存入数据库中。 在需要的时候还需要吧和这个JSON字符串拿出来再次转换成Java对象/集合 1 Java对象与JSON字符串互转 引入依赖: <dependency><groupId>com.alibaba</groupId&…

解决VMware虚拟机在桥接模式下无法上网的问题

解决VMware虚拟机在桥接模式下无法上网的问题 windows11系统自动启动了热点功能&#xff0c;开启热点可能会干扰虚拟机的桥接设置。 方法一&#xff1a;windows11可以提供网络热点服务 方法二&#xff1a;手动指定桥接的物理网卡 方法一&#xff1a;关闭热点功能 优点&#xff…

【Java项目笔记】01项目介绍

一、技术框架 1.后端服务 Spring Boot为主体框架 Spring MVC为Web框架 MyBatis、MyBatis Plus为持久层框架&#xff0c;负责数据库的读写 阿里云短信服务 2.存储服务 MySql redis缓存数据 MinIO为对象存储&#xff0c;存储非结构化数据&#xff08;图片、视频、音频&a…

【开发指南】HTML和JS编写多用户VR应用程序的框架

1.概述 Networked-Aframe 的工作原理是将实体及其组件同步到连接的用户。要连接到房间&#xff0c;您需要将networked-scene组件添加到a-scene元素。对于要同步的实体&#xff0c;请向其添加networked组件。默认情况下&#xff0c;position和rotation组件是同步的&#xff0c;…

【Spring Cloud】掌握Gateway核心技术,实现高效路由与转发

目录 前言示例创建一个服务提供者创建网关 创建common子项目 前言 Spring Cloud Gateway 是一个基于 Spring Boot 的非阻塞 API 网关服务&#xff0c;它提供了动态路由、请求断言、过滤器等功能。 以下是关于 Spring Cloud Gateway 的示例&#xff1a; 示例 创建一个服务提…

什么是 std::ios::sync_with_stdio(false)

介绍 std::ios::sync_with_stdio(false) 是 C 中的一个配置设置&#xff0c;用于控制标准 I/O 流&#xff08;如 std::cin, std::cout&#xff09;的行为。这个设置主要用于优化输入输出操作的性能&#xff0c;尤其是在处理大量数据时。 在 C 中&#xff0c;标准流库&#xf…

PHP连接MySQL数据库

PHP本身不具备操作MySQL数据库的能力&#xff0c;需要借助MySQL扩展来实现。 1、PHP加载MySQL扩展&#xff1a;php.ini文件中。&#xff08;不要用记事本打开&#xff09; 2、PHP中所有扩展都是在ext的文件夹中&#xff0c;需要指定扩展所在路径&#xff1a;extension_dir。 3、…

3D问界—MAYA制作铁丝栅栏(透明贴图法)

当然&#xff0c;如果想通过建立模型法来实现铁丝栅栏的效果&#xff0c;也不是不行&#xff0c;可以找一下栅栏建模教程。本篇文章主要是记录一下如何使用透明贴图来实现创建铁丝栅栏&#xff0c;主要应用于场景建模&#xff0c;比如游戏场景、建筑场景等大环境&#xff0c;不…

Spring3(代理模式 Spring1案例补充 Aop 面试题)

目录 一、代理模式 介绍 意图 主要解决的问题 使用场景 实现方式 关键代码 应用实例 优点 缺点 使用建议 注意事项 结构 什么是代理模式&#xff1f; 为什么要用代理模式&#xff1f; 有哪几种代理模式&#xff1f; 1. 静态代理 实现 2. 基于接口的动态代理…

基于python旅游景点满意度分析设计与实现

1.1研究背景与意义 1.1.1研究背景 随着旅游业的快速发展&#xff0c;满意度分析成为评估旅游景点质量和提升游客体验的重要手段。海口市作为中国的旅游城市之一&#xff0c;其旅游景点吸引了大量游客。然而&#xff0c;如何科学评估和提升海口市旅游景点的满意度&#xff0c;…

Qt创建列表,通过外部按钮控制列表的选中下移、上移以及左侧图标的显现

引言 项目中需要使用列表QListWidget,但是不能直接拿来使用。需要创建一个列表,通过向上和向下的按钮来向上或者向下移动选中列表项,当当前项背选中再去点击确认按钮,会在列表项的前面出现一个图标。 实现效果 本实例实现的效果如下: 实现思路 思路一 直接采用QLis…

Spring Security之安全异常处理

前言 在我们的安全框架中&#xff0c;不管是什么框架&#xff08;包括通过过滤器自定义&#xff09;都需要处理涉及安全相关的异常&#xff0c;例如&#xff1a;登录失败要跳转到登录页&#xff0c;访问权限不足要返回页面亦或是json。接下来&#xff0c;我们就看看Spring Sec…

海外营销推广:快速创建维基百科(wiki)词条-大舍传媒

一、维基百科的永久留存问题 许多企业和个人关心维基百科是否能永久留存。实际上&#xff0c;只要企业和个人的行为没有引起维基百科管理方的反感&#xff0c;词条就可以长期保存。如果有恶意行为或被投诉&#xff0c;维基百科可能会对词条进行删除或修改。 二、创建维基百科…

为fooocus v2.5.0安装groundingdino

在win10下折就fooocus&#xff0c;使用git pull命令更新本地&#xff0c;然后…\python_embeded\python.exe -m pip install -r .\requirements_versions.txt更新依赖关系包。 卡在groundingdino的安装上&#xff0c;先在requirements_versions.txt中删除它&#xff0c;安装其他…

第十课:telnet(远程登入)

如何远程管理网络设备&#xff1f; 只要保证PC和路由器的ip是互通的&#xff0c;那么PC就可以远程管理路由器&#xff08;用telnet技术管理&#xff09;。 我们搭建一个下面这样的简单的拓扑图进行介绍 首先我们点击云&#xff0c;把云打开&#xff0c;点击增加 我们绑定vmn…

线程的中断和同步问题

1、自动终断【完成】&#xff1a;一个线程完成执行后&#xff08;即run方法执行完毕&#xff09;&#xff0c;不能再次运行 。 2、手动中断&#xff1a; stop( ) —— 已过时&#xff0c;基本不用。&#xff08;不安全&#xff0c;就像是突然停电&#xff09; interrupt( ) …

VTK----3D picking的原理、类型及实现

目录 3D picking概述 3D射线投射原理 VTK picking框架 vtkPicker(选Actor) vtkPointPicker(选点) vtkCellPicker(选单元) vtkAreaPicker(框选) 3D picking概述 3D picking 是一种在三维场景中确定用户点击或指向的对象的技术。这在3D应用程序和游戏中非常常见,…

CentOS 7 初始化环境配置详细

推荐使用xshell远程连接&#xff0c;如链接不上 请查看 CentOS 7 网络配置 修改主机名 hostname hostnamectl set-hostname xxx bash 关闭 SElinux 重启之后生效 配置yum源&#xff08;阿里&#xff09; 先备份CentOS-Base.repo&#xff0c;然后再下载 mv /etc/yum.repos…

MySQL学习记录 —— 이십이 MySQL服务器日志

文章目录 1、日志介绍2、一般、慢查询日志1、一般查询日志2、慢查询日志FILE格式TABLE格式 3、错误日志4、二进制日志5、日志维护 1、日志介绍 中继服务器的数据来源于集群中的主服务。每次做一些操作时&#xff0c;把操作保存到重做日志&#xff0c;这样崩溃时就可以从重做日志…