【GD32】从零开始学GD32单片机 | DAC数模转换器 + 三角波输出例程

news/2024/12/22 20:48:11 标签: 单片机, 嵌入式硬件, c语言, mcu

目录

  • 简介
  • 输出缓冲
  • 外部触发
  • 数据转换
  • 噪声波
    • LSFR噪声模式
    • 三角噪声模式
  • 例程

简介

上一篇讲解了ADC的使用,所以这一篇讲DAC的使用,两者其实就是互补的关系,ADC将模拟信号转为数字信号,而DAC将数字信号转为模拟信号。具体的使用上DAC就要比ADC要简单地多。

下面是DAC的结构框图。

在这里插入图片描述

输出缓冲

为了降低输出阻抗,并在没有外部运算放大器的情况下驱动外部负载,DAC内集成了一个输出缓冲区。在默认情况下,输出缓冲区是开启的,可以通过设置状态寄存器寄存器的DBOFFx来开启或者关闭缓冲区。

外部触发

和ADC一样,DAC也支持外部触发,并输出结果。用户可以选择不同的触发源,但大部分都是定时器TRGO的外部触发。

下面是DAC外部触发的对照表。

在这里插入图片描述

数据转换

如果使能了外部触发,当触发事件发生,DAC保持数据寄存器的内容才会被转移到DAC数据输出寄存器。而在外部触发没有使能的情况下,DAC保持数据寄存器内的值会被自动转移到DAC数据输出寄存器。
当DAC保持数据寄存器内的值加载到数据输出寄存器时,经过时间t之后,模拟输出变得有效,t的值与电源电压和模拟输出负载有关。

噪声波

DAC中可以将噪声波叠加到输出数据中,通过这种功能可以方便用户输出一些特殊的波形。GD32的噪声波有两种模式——LFSR噪声模式和三角噪声模式

LSFR噪声模式

在DAC控制逻辑中有一个线性反馈移位寄存器(LFSR)。在LFSR噪声模式下,LFSR 的值与 数据保持寄存器的值相加后,被写入到数据输出寄存器。
当配置的DAC噪声波位宽小于12时,LFSR的值等于LFSR寄存器最低的DWBWx位,DWBWx 位决定了不屏蔽LFSR的哪些位。

在这里插入图片描述

三角噪声模式

该模式顾名思义就是帮助用户产生三角波的。DAC会将一个三角波信号与数据保持寄存器的值相加后,写入到数据输出寄存器。三角波信号的最小值为0,最大值为(2 << DWBWx) - 1

在这里插入图片描述

例程

这个例程配置DAC输出一个三角波,将DAC的输出连接到其中一个ADC输入,读取原始值并通过串口输出到上位机中显示。
使用的软件示波器上位机下载链接:VOFA+

#include "gd32f4xx.h"
#include "systick.h"
#include "debug.h"

#define TAG "main"

int main(void)
{
    systick_config();
	debug_init();
	LOG(TAG, "DAC demo");
    
    /* 初始化ADC */
    rcu_periph_clock_enable(RCU_GPIOA);
    rcu_periph_clock_enable(RCU_ADC0);
    
    gpio_mode_set(GPIOA, GPIO_MODE_ANALOG, GPIO_PUPD_NONE, GPIO_PIN_1);  // PA1
    
    adc_deinit();
    adc_clock_config(ADC_ADCCK_PCLK2_DIV6);  // ADC时钟,120MHz / 6 = 20MHz
    adc_sync_mode_config(ADC_SYNC_MODE_INDEPENDENT);  // 独立模式
    adc_data_alignment_config(ADC0, ADC_DATAALIGN_RIGHT);  // 数据右对齐
    adc_resolution_config(ADC0, ADC_RESOLUTION_12B);  // 12位分辨率
    adc_external_trigger_config(ADC0, ADC_ROUTINE_CHANNEL, EXTERNAL_TRIGGER_DISABLE);  // 禁用外部触发
    adc_channel_length_config(ADC0, ADC_ROUTINE_CHANNEL, 1);  // 1个规则通道
    adc_routine_channel_config(ADC0, 0, ADC_CHANNEL_1, ADC_SAMPLETIME_56);  // 转换时间 = (1 / 20MHz) * (12 + 56) = 3.4us
    adc_enable(ADC0);  // 使能ADC
    delay_1ms(1);
    adc_calibration_enable(ADC0);  // 校准ADC
    
    /* 初始化定时器 */
    rcu_periph_clock_enable(RCU_TIMER5);

    timer_parameter_struct timer_conf = {0};

    timer_deinit(TIMER5);
    timer_struct_para_init(&timer_conf);
    timer_conf.prescaler = 59;  // CK_TIMER = 60MHz / (59 + 1) = 1MHz
    timer_conf.alignedmode = TIMER_COUNTER_EDGE;
    timer_conf.counterdirection = TIMER_COUNTER_UP;  // 向上计数
    timer_conf.period = 999;  // 周期 = (1 / 1MHz) * (999 + 1) = 1ms
    timer_init(TIMER5, &timer_conf);
    timer_master_output_trigger_source_select(TIMER5, TIMER_TRI_OUT_SRC_UPDATE);  // 更新事件触发输出
    timer_enable(TIMER5);  // 使能定时器
    
    /* 初始化DAC */
    rcu_periph_clock_enable(RCU_GPIOA);
    rcu_periph_clock_enable(RCU_DAC);
    
    gpio_mode_set(GPIOA, GPIO_MODE_ANALOG, GPIO_PUPD_NONE, GPIO_PIN_4);  // PA4
    
    dac_deinit(DAC0);  // 复位DAC
    dac_trigger_source_config(DAC0, DAC_OUT0, DAC_TRIGGER_T5_TRGO);  // 定时器外部触发
    dac_trigger_enable(DAC0, DAC_OUT0);  // 使能外部触发
    dac_wave_mode_config(DAC0, DAC_OUT0, DAC_WAVE_MODE_TRIANGLE);  // 三角噪声波模式
    dac_triangle_noise_config(DAC0, DAC_OUT0, DAC_TRIANGLE_AMPLITUDE_2047);  // 增益
    dac_enable(DAC0, DAC_OUT0);  // 使能DAC
    dac_data_set(DAC0, DAC_OUT0, DAC_ALIGN_12B_R, 0x7F0);

	while (1) {
        adc_software_trigger_enable(ADC0, ADC_ROUTINE_CHANNEL);
        while (RESET == adc_flag_get(ADC0, ADC_FLAG_EOC));
        printf("adc: %d\r\n", adc_routine_data_read(ADC0));
        delay_1ms(10);
	}
}
  1. 初始化ADC

因为ADC的相关讲解和例程已经在前面的文章中包含,所以这里不再赘述,跳转栏目目录可以找到。这里的ADC配置为通道1,即PA1管脚

  1. 初始化定时器

这个定时器用来触发DAC输出,同样前面的文章中讲过定时器相关的内容,我这里配置1ms触发一次,使用更新事件的外部触发

  1. 初始化DAC

前面我使用的是定时器5,所以先使能DAC的外部触发,触发源为定时器5;使能DAC内部的三角波噪声生成器;增益选择2047,即三角波峰值;设置DAC数据寄存器值为2032,所以三角波最终的输出范围就是2032-4079;输出到通道0,对应PA4管脚。

  1. 主循环

主循环每隔10ms使能ADC采集一次数据,并通过串口发送到上位机。

在这里插入图片描述


http://www.niftyadmin.cn/n/5795848.html

相关文章

day11|150,239,347

150 其实不难&#xff0c;理解规律&#xff0c;遇到符号就需要提出来做运算。 class Solution {public int evalRPN(String[] tokens) {//向零截断&#xff0c;正数向下取整&#xff0c;负数向上取整//Queue<Integer> num new Queue<>()&#xff1b;是错的注意区…

数据结构:链表(经典算法例题)详解

目录 1.移除链表元素 2.反转链表 3.链表的中间结点 4.合并两个有序链表 5.环形链表的约瑟夫问题 6.分割链表 我以过客之名&#xff0c;祝你前程似锦 1.移除链表元素 &#xff08;1&#xff09;题目&#xff1a; https://leetcode.cn/problems/remove-linked-list-element…

Vue3 基础记录

Vue3 创建 基于vue-cli ## 查看vue/cli版本&#xff0c;确保vue/cli版本在4.5.0以上 vue --version## 安装或者升级你的vue/cli npm install -g vue/cli## 执行创建命令 vue create vue_test## 随后选择3.x ## Choose a version of Vue.js that you want to start the pr…

Golang学习历程【第三篇 基本数据类型类型转换】

Golang学习历程【第三篇 基本数据类型】 1. 总览2. 基本数据类型2.1 整型2.2 浮点型2.2 布尔型2.3 字符2.4 字符串2.4.1 常用定义方式2.4.2 转移字符2.4.3 常用方法2.4.3 字符串中字符替换 3. 类型转换3.1 整型与整型转化3.2 浮点数与整型转换3.3 其他类型与string类型转换3.4 …

亚马逊API接口深度解析:如何高效获取商品详情与评论数据

在当今数字化时代&#xff0c;电商平台的数据对于商家和开发者来说至关重要。亚马逊作为全球领先的电商平台&#xff0c;其API接口为开发者提供了丰富的商品信息和评论数据。本文将深入探讨如何使用亚马逊API接口获取商品详情和商品评论&#xff0c;同时提供简洁明了的使用方法…

代码随想录算法训练营第十一天-239.滑动窗口最大值

解题思想与代码实现&#xff0c;令人叹为观止队列的最佳应用从总体上讲&#xff0c;完成代码的思路是非常清晰的 根据窗口大小&#xff0c;从源数据第一个开始&#xff0c;把数据依次压入队列中从压入队列的数据中找出最大值&#xff0c;放入结果集合中再将队列中第一个元素弹出…

保姆级教程Docker部署RabbitMQ镜像

目录 1、创建挂载目录 2、运行RabbitMQ容器 3、Compose运行RabbitMQ容器 4、开启界面插件 5、查看RabbitMQ运行状态 6、常见问题处理 1、创建挂载目录 # 创建宿主机rabbitMQ挂载目录 sudo mkdir -p /data/docker/rabbitmq/log# 修改log目录权限 sudo chmod 777 /data/do…

HTML 新手易犯的标签属性设置错误

滥用target"_blank"属性&#xff1a;将所有链接的目标设为_blank会在新标签页中打开链接&#xff0c;这可能会导致用户在不知情的情况下打开大量新标签页&#xff0c;影响用户体验。正确的做法是只在需要新标签页打开的链接上使用该属性&#xff0c;并在标签中添加适…