【正点原子STM32连载】第四十一章 DAC输出正弦波实验 摘自【正点原子】APM32F407最小系统板使用指南

本文主要是介绍【正点原子STM32连载】第四十一章 DAC输出正弦波实验 摘自【正点原子】APM32F407最小系统板使用指南,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1)实验平台:正点原子stm32f103战舰开发板V4
2)平台购买地址:https://detail.tmall.com/item.htm?id=609294757420
3)全套实验源码+手册+视频下载地址: http://www.openedv.com/thread-340252-1-1.html##

第四十一章 DAC输出正弦波实验

本章将使用TMR7的更新事件用于TRGO触发DAC通道1输出DMA1数据流5通道7从使用软件算法生成的正弦波数据,以输出正弦波。通过本章的学习,读者将学习到DAC、TMR、DMA的使用。
本章分为如下几个小节:
41.1 硬件设计
41.2 程序设计
41.3 下载验证

41.1 硬件设计
41.1.1 例程功能

  1. 按下KEY_UP和KEY0按键,可分别利用DAC输出高采样率和低采样率的正弦波
  2. 可通过USMART设置DAC输出正弦波的频率
  3. LED0闪烁,指示程序正在运行
    41.1.2 硬件资源
  4. LED
    LED0 - PF9
  5. 按键
    KEY0 - PE4
    KEY_UP - PA0
  6. USART1(PA9、PA10连接至板载USB转串口芯片上)
  7. 正点原子 2.8/3.5/4.3/7/10寸TFTLCD模块(仅限MCU屏,16位8080并口驱动)
  8. ADC1
    通道1 - PA1
  9. DAC
    通道1 - PA4
  10. TMR7
  11. DMA1
    数据流5 通道7
    41.1.3 原理图
    本章实验使用的DAC为APM32F407的片上资源,因此没有对应的连接原理图。
    41.2 程序设计
    41.2.1 Geehy标准库的TMR驱动
    本章实验中将使用TMR7的更新事件产生TRGO事件,该TRGO事件将用于触发DAC,其具体的步骤如下:
    ①:配置TMR7的自动重装载值和预分频器数值
    ②:配置TMR7的TRGO事件的触发源为更新事件
    ③:使能TMR7
    在Geehy标准库中对应的驱动函数如下:
    ①:配置TMR
    请见第16.2.1小节中配置TMR的相关章节。
    ②:配置TMR的TRGO触发源
    该函数用于配置TMR的TRGO触发源,其函数原型如下所示:
    void TMR_SelectOutputTrigger(TMR_T* tmr, TMR_TRGO_SOURCE_T TRGOSource);
    该函数的形参描述,如下表所示:
    形参 描述
    tmr 指向TMR外设结构体的指针
    例如:TMR1、TMR2等(在apm32f4xx.h文件中有定义)
    TRGOSource 指定的TRGO触发源
    例如:TMR_TRGO_SOURCE_RESET、TMR_TRGO_SOURCE_UPDATE等(在apm32f4xx_tmr.h文件中有定义)
    表41.2.1.1 函数TMR_SelectOutputTrigger()形参描述
    该函数的返回值描述,如下表所示:
    返回值 描述
    无 无
    表41.2.1.2 函数TMR_SelectOutputTrigger()返回值描述
    该函数的使用示例,如下所示:
#include "apm32f4xx.h"
#include "apm32f4xx_tmr.h"void example_fun(void)
{/* 配置TMR1的TRGO触发源为更新事件 */TMR_SelectOutputTrigger(TMR1, TMR_TRGO_SOURCE_UPDATE);
}

③:使能TMR
请见第16.2.1小节中使能TMR的相关内容。
41.2.2 Geehy标准库的DMA驱动
请见第三十四章“DMA实验”中Geehy标准库的DMA驱动的相关内容。
41.2.3 Geehy标准库的DAC驱动
本章实验与第四十章一样,使用DAC通道1(PA4引脚)输出电压,不同之处在于,本章使用使用DMA自动将DAC通道1待输出的数据写入DAC的数据保持寄存器,以输出正弦波。其中对DAC的操作请见第四十章中Geehy标准库的DAC驱动的相关内容,本小节仅介绍DAC使用DMA的相关步骤,其具体的步骤如下:
①:使能DAC通道1DMA
在Geehy标准库中对应的驱动函数如下:
①:使能DAC通道DMA
该函数用于使能DAC指定通道的DMA,其函数原型如下所示:
void DAC_DMA_Enable(DAC_CHANNEL_T channel);
该函数的形参描述,如下表所示:
形参 描述
channel 指定DAC的通道
例如:DAC_CHANNEL_1、DAC_CHANNEL_2(在apm32f4xx_dac.h文件中有定义)
表41.2.3.1 函数DAC_DMA_Enable()形参描述
该函数的返回值描述,如下表所示:
返回值 描述
无 无
表41.2.3.2 函数DAC_DMA_Enable()返回值描述
该函数的使用示例,如下所示:

#include "apm32f4xx.h"
#include "apm32f4xx_dac.h"void example_fun(void)
{/* 使能DAC通道1DMA */DAC_DMA_Enable(DAC_CHANNEL_1);
}

41.2.4 DAC驱动
本章实验的DAC驱动主要负责向应用层提供DAC的初始化和使能DAC输出指定幅值、频率的正弦波。本章实验中,DAC的驱动代码包括dac.c和dac.h两个文件。
DAC驱动中,DAC的初始化函数,如下所示:

/*** @brief	初始化DAC输出波形* @param	outx: 待初始化的DAC通道* @arg1: 	DAC通道1* @arg2: 	DAC通道2* @retval	无*/
void dac_dma_wave_init(uint8_t outx)
{GPIO_Config_T gpio_init_struct;DAC_Config_T dac_init_struct;DMA_Config_T dma_init_struct;DAC_CHANNEL_T dac_channel = (outx == 1) ? DAC_CHANNEL_1 : DAC_CHANNEL_2;DMA_Stream_T *dma_stream = (outx == 1) ? DMA1_Stream5 : DMA1_Stream6;/* 使能时钟 */RCM_EnableAPB1PeriphClock(RCM_APB1_PERIPH_DAC);		/* 使能DAC时钟 */RCM_EnableAHB1PeriphClock(RCM_AHB1_PERIPH_GPIOA);	/* 使能DAC输出引脚端口时钟 */RCM_EnableAPB1PeriphClock(RCM_APB1_PERIPH_TMR7);	/* 使能TMR7时钟 */RCM_EnableAHB1PeriphClock(RCM_AHB1_PERIPH_DMA1);	/* 使能DMA1时钟 *//* 配置DAC输出引脚 */gpio_init_struct.pin	= (outx == 1) ? GPIO_PIN_4 : GPIO_PIN_5;gpio_init_struct.mode	= GPIO_MODE_AN;gpio_init_struct.pupd	= GPIO_PUPD_NOPULL;GPIO_Config(GPIOA, &gpio_init_struct);/* 配置DAC */dac_init_struct.trigger				= DAC_TRIGGER_TMR7_TRGO;dac_init_struct.outputBuffer		= DAC_OUTPUT_BUFFER_ENBALE;dac_init_struct.waveGeneration		= DAC_WAVE_GENERATION_NONE;dac_init_struct.maskAmplitudeSelect	= DAC_LFSR_MASK_BIT11_1;DAC_Config(dac_channel, &dac_init_struct);/* 复位DMA */DMA_Reset(dma_stream);while (DMA_ReadCmdStatus(dma_stream) != DISABLE);/* 配置DMA */dma_init_struct.channel				= DMA_CHANNEL_7;dma_init_struct.peripheralBaseAddr	= (uint32_t)0;dma_init_struct.memoryBaseAddr		= (uint32_t)g_dac_sin_buf;dma_init_struct.dir					= DMA_DIR_MEMORYTOPERIPHERAL;dma_init_struct.bufferSize			= 0;dma_init_struct.peripheralInc		= DMA_PERIPHERAL_INC_DISABLE;dma_init_struct.memoryInc			= DMA_MEMORY_INC_ENABLE;dma_init_struct.peripheralDataSize	= DMA_PERIPHERAL_DATA_SIZE_HALFWORD;dma_init_struct.memoryDataSize		= DMA_MEMORY_DATA_SIZE_HALFWORD;dma_init_struct.loopMode			= DMA_MODE_CIRCULAR;dma_init_struct.priority			= DMA_PRIORITY_MEDIUM;dma_init_struct.fifoMode			= DMA_FIFOMODE_DISABLE;dma_init_struct.fifoThreshold		= DMA_FIFOTHRESHOLD_FULL;dma_init_struct.memoryBurst			= DMA_MEMORYBURST_SINGLE;dma_init_struct.peripheralBurst		= DMA_PERIPHERALBURST_SINGLE;DMA_Config(dma_stream, &dma_init_struct);
}

可以看到本章实验DAC驱动中的DAC初始化函数与第三十九章“DAC输出实验”中对DAC的初始化基本一致,不过本章配置DAC通道1的触发源为TMR7的TRGO事件,同时还配置了DMA,DMA存储器的地址配置为了数组g_dac_sin_buf的地址,因此该数组将用于存放正弦波的数据。
DAC驱动中使能DAC输出指定幅值、频率的正弦波的函数,如下所示:

/*** @brief	使能DAC输出波形* @param	outx: 待初始化的DAC通道* @arg1:	 DAC通道1* @arg2: 	DAC通道2* @param	ndtr: DMA数据流传输一次数据项的数目* @param	arr : DAC触发定时器的自动重装载值* @param	psc : DAC触发定时器的预分频器数值* @retval	无*/
void dac_dma_wave_enable(uint8_t outx, uint16_t ndtr, uint16_t arr, uint16_t psc)
{TMR_BaseConfig_T tmr_init_struct;DAC_CHANNEL_T dac_channel = (outx == 1) ? DAC_CHANNEL_1 : DAC_CHANNEL_2;DMA_Stream_T *dma_stream = (outx == 1) ? DMA1_Stream5 : DMA1_Stream6;uint32_t pdata = (uint32_t)((outx == 1) ? &(DAC->DH12R1) : &(DAC->DH12R2));/* 配置TMR7 */tmr_init_struct.period		= arr;						/* 自动重装载值 */tmr_init_struct.division	= psc;						/* 预分频器数值 */TMR_ConfigTimeBase(TMR7, &tmr_init_struct);				/* 配置TMR7 *//* 配置更新事件用于TRGO */TMR_SelectOutputTrigger(TMR7, TMR_TRGO_SOURCE_UPDATE);TMR_Enable(TMR7);										/* 使能TMR7 *//* 关闭DAC和DMA */DAC_Disable(dac_channel);								/* 关闭DAC */DMA_Disable(dma_stream);								/* 禁止DMA数据流 */while (DMA_ReadCmdStatus(dma_stream) != DISABLE)/* 配置DAC和DMA */DAC_DMA_Enable(dac_channel);							/* 使能DAC DMA */DMA_ConfigDataNumber(dma_stream, ndtr);					/* 配置传输的数据项数目 */dma_stream->PADDR = pdata;								/* 配置外设地址 */DMA_Enable(dma_stream);									/* 使能DMA数据流 */DAC_Enable(dac_channel);								/* 使能DAC */
}

该函数配置了TMR7并配置TMR7的更新事件用于TRGO,因此TMR7的溢出频率就决定了DAC输出正弦波的频率,同时也配置和使能了DAC和DMA。
41.2.5 实验应用代码
本章实验的应用代码,如下所示:
/* 正弦波数据缓冲区 */

uint16_t g_dac_sin_buf[4096];/*** @brief	生成正弦数据* @param	maxval : 正弦的振幅* @param	samples: 正弦一个周期的采样点个数* @retval	无*/
static void dac_creat_sin_buf(uint16_t maxval, uint16_t samples)
{uint16_t i;double w;uint16_t outdata;w = (2 * 3.1415926) / samples;						/* ω=2π/T */for (i=0; i<samples; i++){outdata = maxval * sin(w * i + 0) + maxval;	/* y=Asin(ωx+φ)+b */if (outdata > 4095)								/* 限制上限 */{outdata = 4095;}g_dac_sin_buf[i] = outdata;}
}int main(void)
{uint8_t key;uint8_t t = 0;uint16_t dacdata;uint16_t dac_voltage;uint16_t adcdata;uint16_t adc_voltage;NVIC_ConfigPriorityGroup(NVIC_PRIORITY_GROUP_3);	/* 设置中断优先级分组为组3 */sys_apm32_clock_init(336, 8, 2, 7);					/* 配置系统时钟 */delay_init(168);										/* 初始化延时功能 */usart_init(115200);									/* 初始化串口 */usmart_dev.init(84);								/* 初始化USMART */led_init();											/* 初始化LED */lcd_init();											/* 初始化LCD */adc_init();											/* 初始化ADC */dac_dma_wave_init(1);								/* 初始化DAC输出波形 *//* 生成正弦数据,振幅约3.3(V),100个数据 */dac_creat_sin_buf(2048 - 1, 100);/* 定时器触发速率100KHz,100个数据,输出约1KHz的正弦波 */dac_dma_wave_enable(1, 100, 10 - 1, 84 - 1);lcd_show_string(30, 50, 200, 16, 16, "APM32", RED);lcd_show_string(30, 70, 200, 16, 16, "DAC DMA Sine WAVE TEST", RED);lcd_show_string(30, 90, 200, 16, 16, "ATOM@ALIENTEK", RED);lcd_show_string(30, 130, 200, 16, 16, "DAC VAL:", BLUE);lcd_show_string(30, 150, 200, 16, 16, "DAC VOL:0.000V", BLUE);lcd_show_string(30, 170, 200, 16, 16, "ADC VOL:0.000V", BLUE);while (1){t++;key = key_scan(0);if (key == WKUP_PRES)		/* ADC通道1输出高采样率正弦波 */{/* 生成正弦数据,振幅约3.3(V),100个数据 */dac_creat_sin_buf(2048 - 1, 100);/* 定时器触发速率300KHz,100个数据,输出约3KHz的正弦波 */dac_dma_wave_enable(1, 100, 10 - 1, 28 - 1);}else if (key == KEY0_PRES)	/* ADC通道1输出低采样率正弦波 */{/* 生成正弦数据,振幅约3.3(V),100个数据 */dac_creat_sin_buf(2048 - 1, 10);/* 定时器触发速率300KHz,10个数据,输出约30KHz的正弦波 */dac_dma_wave_enable(1, 10, 10 - 1, 28 - 1);}/* 获取并显示DAC输出电压的数字量 */dacdata = DAC_ReadDataOutputValue(DAC_CHANNEL_1);lcd_show_xnum(94, 130, dacdata, 5, 16, 0, BLUE);/* 计算并显示DAC输出电压的模拟量 */dac_voltage = (dacdata * 3300) / 4095;lcd_show_xnum(94, 150, dac_voltage / 1000, 1, 16, 0, BLUE);lcd_show_xnum(110, 150, dac_voltage % 1000, 3, 16, 0x80, BLUE);/* 获取、计算并显示ADC采集到电压的模拟量 */adcdata = adc_get_result_average(ADC_ADCX_CHY, 20);adc_voltage = (adcdata * 3300) / 4095;lcd_show_xnum(94, 170, adc_voltage / 1000, 1, 16, 0, BLUE);lcd_show_xnum(110, 170, adc_voltage % 1000, 3, 16, 0x80, BLUE);if (t == 10){LED0_TOGGLE();t = 0;}delay_ms(5);}
}

可以看到,在实验应用代码中,定义了函数dac_creat_sin_buf(),该函数用于生成正弦波数据,并保存至数组g_dac_sin_buf中。在完成DAC初始化后,便生成了一组正弦波数据,并调用函数dac_dma_wave_enable()使能DAC通道1输出指定的正弦波,随后便通过扫描到的不同按键值,输出不同频率的正弦波。同时也还使能了ADC便于观察DAC输出的电压,DAC输出电压的模拟量和数字量以及ADC采集到电压的模拟量都将被实时地在LCD上进行显示。
41.3 下载验证
在完成编译和烧录操作后,可以看到LCD上实时地显示了DAC通道1输出电压的数字量和模拟量以及ADC1通道1采集到电压的模拟量,此时可以将PA1引脚(ADC1采集引脚)和PA4引脚(DAC通道1)短接,便可以看到LCD上显示的ADC采集到电压的模拟量随DAC通道1输出电压的模拟量变化。为了更方便地观察DAC通道1输出的正弦波,可以使用示波器观察PA4引脚的输出,通过按下KEY0按键可以观察到频率约为30KHz的正弦波,而按下KEY_UP按键则可以观察到频率约为3KHz的正弦波。

这篇关于【正点原子STM32连载】第四十一章 DAC输出正弦波实验 摘自【正点原子】APM32F407最小系统板使用指南的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/202870

相关文章

不懂推荐算法也能设计推荐系统

本文以商业化应用推荐为例,告诉我们不懂推荐算法的产品,也能从产品侧出发, 设计出一款不错的推荐系统。 相信很多新手产品,看到算法二字,多是懵圈的。 什么排序算法、最短路径等都是相对传统的算法(注:传统是指科班出身的产品都会接触过)。但对于推荐算法,多数产品对着网上搜到的资源,都会无从下手。特别当某些推荐算法 和 “AI”扯上关系后,更是加大了理解的难度。 但,不了解推荐算法,就无法做推荐系

基于人工智能的图像分类系统

目录 引言项目背景环境准备 硬件要求软件安装与配置系统设计 系统架构关键技术代码示例 数据预处理模型训练模型预测应用场景结论 1. 引言 图像分类是计算机视觉中的一个重要任务,目标是自动识别图像中的对象类别。通过卷积神经网络(CNN)等深度学习技术,我们可以构建高效的图像分类系统,广泛应用于自动驾驶、医疗影像诊断、监控分析等领域。本文将介绍如何构建一个基于人工智能的图像分类系统,包括环境

水位雨量在线监测系统概述及应用介绍

在当今社会,随着科技的飞速发展,各种智能监测系统已成为保障公共安全、促进资源管理和环境保护的重要工具。其中,水位雨量在线监测系统作为自然灾害预警、水资源管理及水利工程运行的关键技术,其重要性不言而喻。 一、水位雨量在线监测系统的基本原理 水位雨量在线监测系统主要由数据采集单元、数据传输网络、数据处理中心及用户终端四大部分构成,形成了一个完整的闭环系统。 数据采集单元:这是系统的“眼睛”,

嵌入式QT开发:构建高效智能的嵌入式系统

摘要: 本文深入探讨了嵌入式 QT 相关的各个方面。从 QT 框架的基础架构和核心概念出发,详细阐述了其在嵌入式环境中的优势与特点。文中分析了嵌入式 QT 的开发环境搭建过程,包括交叉编译工具链的配置等关键步骤。进一步探讨了嵌入式 QT 的界面设计与开发,涵盖了从基本控件的使用到复杂界面布局的构建。同时也深入研究了信号与槽机制在嵌入式系统中的应用,以及嵌入式 QT 与硬件设备的交互,包括输入输出设

JAVA智听未来一站式有声阅读平台听书系统小程序源码

智听未来,一站式有声阅读平台听书系统 🌟&nbsp;开篇:遇见未来,从“智听”开始 在这个快节奏的时代,你是否渴望在忙碌的间隙,找到一片属于自己的宁静角落?是否梦想着能随时随地,沉浸在知识的海洋,或是故事的奇幻世界里?今天,就让我带你一起探索“智听未来”——这一站式有声阅读平台听书系统,它正悄悄改变着我们的阅读方式,让未来触手可及! 📚&nbsp;第一站:海量资源,应有尽有 走进“智听

poj 1258 Agri-Net(最小生成树模板代码)

感觉用这题来当模板更适合。 题意就是给你邻接矩阵求最小生成树啦。~ prim代码:效率很高。172k...0ms。 #include<stdio.h>#include<algorithm>using namespace std;const int MaxN = 101;const int INF = 0x3f3f3f3f;int g[MaxN][MaxN];int n

poj 1287 Networking(prim or kruscal最小生成树)

题意给你点与点间距离,求最小生成树。 注意点是,两点之间可能有不同的路,输入的时候选择最小的,和之前有道最短路WA的题目类似。 prim代码: #include<stdio.h>const int MaxN = 51;const int INF = 0x3f3f3f3f;int g[MaxN][MaxN];int P;int prim(){bool vis[MaxN];

poj 2349 Arctic Network uva 10369(prim or kruscal最小生成树)

题目很麻烦,因为不熟悉最小生成树的算法调试了好久。 感觉网上的题目解释都没说得很清楚,不适合新手。自己写一个。 题意:给你点的坐标,然后两点间可以有两种方式来通信:第一种是卫星通信,第二种是无线电通信。 卫星通信:任何两个有卫星频道的点间都可以直接建立连接,与点间的距离无关; 无线电通信:两个点之间的距离不能超过D,无线电收发器的功率越大,D越大,越昂贵。 计算无线电收发器D

【区块链 + 人才服务】可信教育区块链治理系统 | FISCO BCOS应用案例

伴随着区块链技术的不断完善,其在教育信息化中的应用也在持续发展。利用区块链数据共识、不可篡改的特性, 将与教育相关的数据要素在区块链上进行存证确权,在确保数据可信的前提下,促进教育的公平、透明、开放,为教育教学质量提升赋能,实现教育数据的安全共享、高等教育体系的智慧治理。 可信教育区块链治理系统的顶层治理架构由教育部、高校、企业、学生等多方角色共同参与建设、维护,支撑教育资源共享、教学质量评估、

poj 1734 (floyd求最小环并打印路径)

题意: 求图中的一个最小环,并打印路径。 解析: ans 保存最小环长度。 一直wa,最后终于找到原因,inf开太大爆掉了。。。 虽然0x3f3f3f3f用memset好用,但是还是有局限性。 代码: #include <iostream>#include <cstdio>#include <cstdlib>#include <algorithm>#incl