[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功能。
#define DAC_PIN GPIO_PIN_4#define DAC_GPIO GPIOA#define DAC_CHANNEL DAC_CHANNEL_1#define DAC_TIM TMR6#define DAC_TRIG DAC_TRIGGER_TMR6_TRGO
//gpio configRCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_GPIOA);GPIO_Config_T GPIO_ConfigStruct;GPIO_ConfigStruct.pin = DAC_PIN;GPIO_ConfigStruct.mode = GPIO_MODE_ANALOG;GPIO_Config(DAC_GPIO, &GPIO_ConfigStruct);//dac configRCM_EnableAPB1PeriphClock(RCM_APB1_PERIPH_DAC);DAC_Config_T DAC_ConfigStruct;DAC_ConfigStruct.trigger = DAC_TRIG;DAC_ConfigStruct.waveGeneration = DAC_WAVE_GENERATION_NONE;DAC_ConfigStruct.maskAmplitudeSelect = DAC_TRIANGLE_AMPLITUDE_1;DAC_ConfigStruct.outputBuffer = DAC_OUTPUT_BUFFER_ENBALE;DAC_Config((uint32_t)DAC_CHANNEL, &DAC_ConfigStruct);DAC_DMA_Enable(DAC_CHANNEL);DAC_Enable(DAC_CHANNEL);
2.2、TMR的配置
通过用户手册可知,DAC支持多种触发源,这里我们用基础定时器TMR6去触发DAC转换。
定时器配置成最简单的向上计数即可,不需要开中断,更新事件用于触发输出。
定时器的溢出频率也就决定了DAC输出电压的变化快慢,也就是division 和 period,
这两个值越小,生成的正弦波频率越高;或者减小正弦波的采用点数,正弦波频率也能提高。
//timer configRCM_EnableAPB1PeriphClock(RCM_APB1_PERIPH_TMR6);TMR_BaseConfig_T TMR_BaseConfigStruct;TMR_BaseConfigStruct.clockDivision = TMR_CLOCK_DIV_1;TMR_BaseConfigStruct.countMode = TMR_COUNTER_MODE_UP;TMR_BaseConfigStruct.division =60-1;TMR_BaseConfigStruct.period =4;TMR_ConfigTimeBase(DAC_TIM, &TMR_BaseConfigStruct);TMR_EnableAutoReload(DAC_TIM);TMR_SelectOutputTrigger(DAC_TIM, TMR_TRGO_SOURCE_UPDATE);TMR_Enable(DAC_TIM);
2.3、DMA的配置
DMA配成从内存到DAC,内存地址要自增,而DAC数据寄存器地址不变。
因为这里用的DAC是12位,所以内存和外设的数据宽度都设为半字,也就是16位。
如果想开启一次后一直产生波形,则使用循环模式;如果想每个波形周期都要手动启动,则使用正常模式。
通过手册上的请求映射表可看出对应的DMA通道是DMA2-channel3。
特别要注意DAC的数据寄存器地址,这里需要仔细看手册。
//DMA configRCM_EnableAHBPeriphClock(RCM_AHB_PERIPH_DMA2);DMA_Config_T dmaConfig;DMA_Reset(DAC_DMA_CHANNEL);dmaConfig.peripheralBaseAddr = (uint32_t)DAC_DATA_ADDRESS;dmaConfig.dir = DMA_DIR_PERIPHERAL_DST;dmaConfig.memoryBaseAddr = (uint32_t)NULL;dmaConfig.bufferSize =0;dmaConfig.peripheralInc = DMA_PERIPHERAL_INC_DISABLE;dmaConfig.memoryInc = DMA_MEMORY_INC_ENABLE;dmaConfig.peripheralDataSize = DMA_PERIPHERAL_DATA_SIZE_HALFWORD;dmaConfig.memoryDataSize = DMA_MEMORY_DATA_SIZE_HALFWORD;dmaConfig.loopMode = DMA_MODE_CIRCULAR;dmaConfig.priority = DMA_PRIORITY_HIGH;dmaConfig.M2M = DMA_M2MEN_DISABLE;DMA_Config(DAC_DMA_CHANNEL, &dmaConfig);
2.4 启动DMA的传输
上面的初始化完成后,需要启动使能相关外设才会产生波形。
先设置定时器的自动加载值,这个值决定了波形的频率,
然后波形采样点数组的地址和长度赋值给DMA的地址、长度寄存器,
最后使能DAM传输。
//启动传输voiddac_dma_transmit(unsignedshort*addr,unsignedintlen,unsignedshortperiod){if(len >65535) len =65535;DMA_Disable(DAC_DMA_CHANNEL);TMR_ConfigAutoreload(DAC_TIM,period-1);DAC_DMA_CHANNEL->CHNDATA = len;DAC_DMA_CHANNEL->CHMADDR = (uint32_t)addr;DMA_Enable(DAC_DMA_CHANNEL);}
三、使用与测试
为了展示最终效果,还需要制作一个正弦波的采样点数组。
方法很多,这里就通过正弦函数sin来计算一个360点的波形数据。
正弦函数计算结果有正有负,为了好看,统统平移到0V以上。
#define WaveLength 360unsignedshortWaveBuffer[WaveLength +1];#include<math.h>#define PI 3.14159intgenerate_sine_wave(unsignedshortbuf[]){doubletemp;unsignedshorti;for(i =0; i < WaveLength; i++){temp =sin(i * PI /180.0) *2048.0;temp = temp +2047.0;buf[i] = (unsignedshort)temp;}}
最后一步,调用刚才写好的的函数即可。
intmain(void){usart_printf_init();printf("This a dac and dma demo(sine wave)\r\n");generate_sine_wave(WaveBuffer);dac_dma_init();dac_dma_transmit(WaveBuffer, WaveLength,4);while(1){}}
用示波器观察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所有,任何人未经允许禁止转载。
