当前位置: 首页 > news >正文

[APM32E1] 玩转APM32的DMA-用DAC和DMA生成正弦波

一、前言
DAC可以产生0到3.3V的模拟电压,如果动态改变DAC输出的电压,那么就可以生成各种各样的模拟波形。
这里我们就用DAC+DMA+TMR配合来生成正弦波。

首先看看APM32E103上的相关外设资源:
2个12位的DAC;

2个16位基本定时器TMR6/7;

两个DMA,DMA1有7个通道,DMA2有5个通道。



DAC有两个,一个是DAC_OUT1(PA4) ,还有一个是DAC_OUT2(PA5),两个可以同时输出,这里以DAC_OUT1为例。




DAC支持定时器触发,也就是说定时器溢出时更新一次DAC的数据寄存器,这里用基础定时器就够了。









每触发一次DAC转换需要更新DAC数据寄存器的值,用DMA把数组中的数传输到DAC数据寄存器中


二、DAC、TMR、DMA的初始化配置
2.1、DAC的配置
这里以DAC1为例,DAC的配置和单独使用DAC类似,先初始化GPIO为模拟复用。
触发配成TMR6触发,不用内部的波形发生功能,使能输出缓存。
最后一定记得开启DAC的DMA功能。

复制
  1. #define DAC_PIN GPIO_PIN_4
  2. #define DAC_GPIO GPIOA
  3. #define DAC_CHANNEL DAC_CHANNEL_1
  4. #define DAC_TIM TMR6
  5. #define DAC_TRIG DAC_TRIGGER_TMR6_TRGO
复制
  1. //gpio config
  2. RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_GPIOA);
  3. GPIO_Config_T GPIO_ConfigStruct;
  4. GPIO_ConfigStruct.pin = DAC_PIN;
  5. GPIO_ConfigStruct.mode = GPIO_MODE_ANALOG;
  6. GPIO_Config(DAC_GPIO, &GPIO_ConfigStruct);
  7. //dac config
  8. RCM_EnableAPB1PeriphClock(RCM_APB1_PERIPH_DAC);
  9. DAC_Config_T DAC_ConfigStruct;
  10. DAC_ConfigStruct.trigger = DAC_TRIG;
  11. DAC_ConfigStruct.waveGeneration = DAC_WAVE_GENERATION_NONE;
  12. DAC_ConfigStruct.maskAmplitudeSelect = DAC_TRIANGLE_AMPLITUDE_1;
  13. DAC_ConfigStruct.outputBuffer = DAC_OUTPUT_BUFFER_ENBALE;
  14. DAC_Config((uint32_t)DAC_CHANNEL, &DAC_ConfigStruct);
  15. DAC_DMA_Enable(DAC_CHANNEL);
  16. DAC_Enable(DAC_CHANNEL);





2.2、TMR的配置
通过用户手册可知,DAC支持多种触发源,这里我们用基础定时器TMR6去触发DAC转换。
定时器配置成最简单的向上计数即可,不需要开中断,更新事件用于触发输出。
定时器的溢出频率也就决定了DAC输出电压的变化快慢,也就是division 和 period,
这两个值越小,生成的正弦波频率越高;或者减小正弦波的采用点数,正弦波频率也能提高。


复制
  1. //timer config
  2. RCM_EnableAPB1PeriphClock(RCM_APB1_PERIPH_TMR6);
  3. TMR_BaseConfig_T TMR_BaseConfigStruct;
  4. TMR_BaseConfigStruct.clockDivision = TMR_CLOCK_DIV_1;
  5. TMR_BaseConfigStruct.countMode = TMR_COUNTER_MODE_UP;
  6. TMR_BaseConfigStruct.division =60-1;
  7. TMR_BaseConfigStruct.period =4;
  8. TMR_ConfigTimeBase(DAC_TIM, &TMR_BaseConfigStruct);
  9. TMR_EnableAutoReload(DAC_TIM);
  10. TMR_SelectOutputTrigger(DAC_TIM, TMR_TRGO_SOURCE_UPDATE);
  11. TMR_Enable(DAC_TIM);



2.3、DMA的配置
DMA配成从内存到DAC,内存地址要自增,而DAC数据寄存器地址不变。
因为这里用的DAC是12位,所以内存和外设的数据宽度都设为半字,也就是16位。
如果想开启一次后一直产生波形,则使用循环模式;如果想每个波形周期都要手动启动,则使用正常模式。
通过手册上的请求映射表可看出对应的DMA通道是DMA2-channel3。



特别要注意DAC的数据寄存器地址,这里需要仔细看手册。





复制
  1. //DMA config
  2. RCM_EnableAHBPeriphClock(RCM_AHB_PERIPH_DMA2);
  3. DMA_Config_T dmaConfig;
  4. DMA_Reset(DAC_DMA_CHANNEL);
  5. dmaConfig.peripheralBaseAddr = (uint32_t)DAC_DATA_ADDRESS;
  6. dmaConfig.dir = DMA_DIR_PERIPHERAL_DST;
  7. dmaConfig.memoryBaseAddr = (uint32_t)NULL;
  8. dmaConfig.bufferSize =0;
  9. dmaConfig.peripheralInc = DMA_PERIPHERAL_INC_DISABLE;
  10. dmaConfig.memoryInc = DMA_MEMORY_INC_ENABLE;
  11. dmaConfig.peripheralDataSize = DMA_PERIPHERAL_DATA_SIZE_HALFWORD;
  12. dmaConfig.memoryDataSize = DMA_MEMORY_DATA_SIZE_HALFWORD;
  13. dmaConfig.loopMode = DMA_MODE_CIRCULAR;
  14. dmaConfig.priority = DMA_PRIORITY_HIGH;
  15. dmaConfig.M2M = DMA_M2MEN_DISABLE;
  16. DMA_Config(DAC_DMA_CHANNEL, &dmaConfig);



2.4 启动DMA的传输
上面的初始化完成后,需要启动使能相关外设才会产生波形。
先设置定时器的自动加载值,这个值决定了波形的频率,
然后波形采样点数组的地址和长度赋值给DMA的地址、长度寄存器,
最后使能DAM传输。

复制
  1. //启动传输
  2. voiddac_dma_transmit(unsignedshort*addr,unsignedintlen,unsignedshortperiod)
  3. {
  4. if(len >65535) len =65535;
  5. DMA_Disable(DAC_DMA_CHANNEL);
  6. TMR_ConfigAutoreload(DAC_TIM,period-1);
  7. DAC_DMA_CHANNEL->CHNDATA = len;
  8. DAC_DMA_CHANNEL->CHMADDR = (uint32_t)addr;
  9. DMA_Enable(DAC_DMA_CHANNEL);
  10. }



三、使用与测试
为了展示最终效果,还需要制作一个正弦波的采样点数组。
方法很多,这里就通过正弦函数sin来计算一个360点的波形数据。
正弦函数计算结果有正有负,为了好看,统统平移到0V以上。

复制
  1. #define WaveLength 360
  2. unsignedshortWaveBuffer[WaveLength +1];
  3. #include<math.h>
  4. #define PI 3.14159
  5. intgenerate_sine_wave(unsignedshortbuf[])
  6. {
  7. doubletemp;
  8. unsignedshorti;
  9. for(i =0; i < WaveLength; i++)
  10. {
  11. temp =sin(i * PI /180.0) *2048.0;
  12. temp = temp +2047.0;
  13. buf[i] = (unsignedshort)temp;
  14. }
  15. }



最后一步,调用刚才写好的的函数即可。

复制
  1. intmain(void)
  2. {
  3. usart_printf_init();
  4. printf("This a dac and dma demo(sine wave)\r\n");
  5. generate_sine_wave(WaveBuffer);
  6. dac_dma_init();
  7. dac_dma_transmit(WaveBuffer, WaveLength,4);
  8. while(1)
  9. {
  10. }
  11. }



用示波器观察PA4脚上的波形。




顺便又做了个锯齿波。



以上方法除了用来产生各种自定义的波形,也可以用来播放wav音频。

APM32E10x_SDK_V1.3.1_dac_dma.rar(777.77 KB, 下载次数: 9)


---------------------
作者:shanyuxiang
链接:https://bbs.21ic.com/icview-3482339-1-1.html?_dsign=d86d9ab9
来源:21ic.com
此文章已获得原创/原创奖标签,著作权归21ic所有,任何人未经允许禁止转载。

http://www.cnnetsun.cn/news/1925327.html

相关文章:

  • 如何快速提升惠普游戏本性能:OmenSuperHub终极优化指南
  • 保姆级教程:STM32CubeMX 6.2.1从下载到固件库安装的完整避坑指南
  • 避坑指南:在华为云上安装Kali Linux,镜像格式转换和SSH配置最容易出错的几个地方
  • RePKG:解锁Wallpaper Engine资源宝库的终极密钥
  • 数字电子技术进阶:超前进位加法器的Verilog实现与性能分析
  • 如何构建企业级AI网关:终极部署与优化指南
  • 【2026年-12期】Using microservice architecture for conditional decision-making
  • 联想M920x黑苹果终极指南:3步打造完美macOS工作站
  • IPXWrapper:让经典游戏在Windows 11上重获联机新生的技术桥梁
  • 解锁智能内容获取:Jina AI Reader深度解析与实战指南
  • 拓朋A36plus 10W对讲机,自驾游车队通联的可靠伙伴
  • 告别枯燥指令!用Chestcommands插件为你的Minecraft服务器打造可视化菜单(附完整配置模板)
  • Unity与佳能单反深度集成:实时拍照与视频流获取实战
  • 如何用5个步骤创建属于你的智能桌面伙伴?
  • LS-DYNA新手避坑指南:用LS-PrePost给复合材料壳单元铺层的四种方法(附BETA参数设置对比表)
  • MemFactory:Agent 记忆终于有了自己的 LLaMA-Factory
  • 龙迅LT9611芯片深度评测:MIPI转HDMI1.4的4K输出性能实测
  • 华为OD机试真题 新系统 - 失灵的键盘(C/C++/Py/Java/Js/Go)
  • HCL模拟器实战:从零搭建华三虚拟网络实验室
  • 终极指南:用Chinese-ERJ LaTeX模板快速搞定《经济研究》投稿格式
  • 2026 年 AI 智能体领域的残酷竞争:从 OpenClaw、MoltBook 到 Hermes
  • 3D打印老手也容易忽略的细节:Dreamer Nx WIFI打印中的模型预处理与切片参数优化
  • 神界原罪2模组管理终极指南:告别模组混乱,开启流畅游戏体验
  • STM32WLE5CCU6实战:从官方WL55JC例程到国产CN470频段,手把手移植LoRaWAN节点程序
  • 告别抖动!STM32控制28BYJ-48步进电机的三种励磁方式详解与选型
  • 告别玄学调参:手把手教你用SCCB协议配置OV5640寄存器(附完整代码)
  • 【生成式AI可观测性白皮书】:从Prompt注入到推理退化,构建具备因果推断能力的智能告警闭环
  • 5个颠覆性策略:NocoDB分布式架构优化实战突破
  • LinuxCNC终极实战指南:开源数控系统深度解析与高效部署
  • 从图论到网络:Dijkstra与Bellman-Ford算法如何塑造互联网的路径选择