STM32G474使用TIM2触发DAC输出,数据从内存到外设就要使用DMA来协助。DAC1每隔1秒输出一个正弦波数据,就会模拟近似得到模拟的正弦波形。用来测试CPU内部的运算放大器,或者用作其它模拟输入信号。
测试程序如下:
#include "DAC.h"
#include "stdio.h" //getchar(),putchar(),scanf(),printf(),puts(),gets(),sprintf()
#include "string.h" //使能strcpy(),strlen(),memset()
#include "delay.h"
/*
STM32G474之DAC输出,使用DMA从内存搬运数据到DAC1外设,使用TIM2触发DAC1输出
*/
void DAC1_CHANNEL_1_Init(void);
const uint16_t Sine12bit[32] = { 511, 611, 707, 796, 873, 937, 984, 1013,
1023, 1013, 984, 937, 873, 796, 707, 611,
511, 412, 315, 227, 149, 86, 38, 9,
0, 9, 38, 86, 149, 227, 315, 412};
//函数功能:DAC1通道1初始化
void DAC1_CHANNEL_1_Init(void)
{
DAC_HandleTypeDef hdac1;
//DAC1句柄,若直接对寄存器DAC1->DHR12R1和DAC1->DHR12R2操作,就可以将其设置为局部变量
//DAC1->DHR12R1为DAC通道1(12位右对齐数据保持寄存器)
//DAC1->DHR12R2为DAC通道2(12位右对齐数据保持寄存器)
GPIO_InitTypeDef GPIO_InitStruct = {0};
DAC_ChannelConfTypeDef sConfig; //DAC通道参数相关结构体
DMA_HandleTypeDef hdma_dac1_ch1;
TIM_HandleTypeDef htim2;
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
__HAL_RCC_DAC1_CLK_ENABLE();
//使能DAC1时钟
//RCC_AHB2ENR寄存器bit18(DAC1EN),令DAC1EN=1使能DAC1时钟
__HAL_RCC_GPIOA_CLK_ENABLE();//GPIOA时钟使能
GPIO_InitStruct.Pin = GPIO_PIN_4; //选择引脚号码为4
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG; //模拟模式
GPIO_InitStruct.Pull = GPIO_NOPULL; //引脚上拉和下拉都没有被激活
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
//根据GPIO_InitStruct结构变量指定的参数初始化GPIOA的外设寄存器,将PA4引脚连接到DAC1通道1
hdac1.Instance = DAC1; //DAC1
HAL_DAC_Init(&hdac1); //初始化DAC
sConfig.DAC_HighFrequency = DAC_HIGH_FREQUENCY_INTERFACE_MODE_ABOVE_160MHZ;
//DAC时钟选择
sConfig.DAC_DMADoubleDataMode = DISABLE; //双重数据模式(高带宽模式)关闭
sConfig.DAC_SignedFormat = DISABLE; //有符号模式关闭
sConfig.DAC_SampleAndHold = DAC_SAMPLEANDHOLD_DISABLE; //关闭采样保持
// sConfig.DAC_Trigger = DAC_TRIGGER_NONE; //不需要外部触发
sConfig.DAC_Trigger = DAC_TRIGGER_T2_TRGO;//使用TIM2触发DAC输出
sConfig.DAC_Trigger2 = DAC_TRIGGER_NONE;
sConfig.DAC_OutputBuffer = DAC_OUTPUTBUFFER_ENABLE; //DAC输出缓冲器打开
sConfig.DAC_ConnectOnChipPeripheral = DAC_CHIPCONNECT_ENABLE;//允许内部连接DAC_Channel
// sConfig.DAC_ConnectOnChipPeripheral = DAC_CHIPCONNECT_DISABLE; //不允许内部连接DAC_Channel
sConfig.DAC_UserTrimming = DAC_TRIMMING_FACTORY; //工厂矫正模式
HAL_DAC_ConfigChannel(&hdac1, &sConfig, DAC_CHANNEL_1); //初始化
__HAL_RCC_DMA1_CLK_ENABLE(); //DMA1时钟使能
__HAL_RCC_DMAMUX1_CLK_ENABLE();//设置RCC->AHB1ENR寄存器bit2(DMAMUX1EN位),令DMAMUX1EN=1,DMAMUX1时钟使能
hdma_dac1_ch1.Instance = DMA1_Channel1;//使用DMA1的通道1
hdma_dac1_ch1.Init.Request = DMA_REQUEST_DAC1_CHANNEL1;//DMA需求来源DAC1
hdma_dac1_ch1.Init.Direction = DMA_MEMORY_TO_PERIPH;
//设置DMA_CCRx寄存器bit4(DIR位),令DIR=1,数据传输方向:内存到外设
hdma_dac1_ch1.Init.PeriphInc = DMA_PINC_DISABLE;
//设置DMA_CCRx寄存器bit6(PINC位),令PINC=0,外设地址不增加
hdma_dac1_ch1.Init.MemInc = DMA_MINC_ENABLE;
//设置DMA_CCRx寄存器bit7(MINC位),令MINC=1,内存地址增加
hdma_dac1_ch1.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
//设置DMA_CCRx寄存器bit9:8(PSIZE[1:0]位),令PSIZE[1:0]=10,外设数据宽度32bits
hdma_dac1_ch1.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
//设置DMA_CCRx寄存器bit11:10(MSIZE[1:0]]位),令MSIZE[1:0]=01,内存数据宽度16bits
hdma_dac1_ch1.Init.Mode = DMA_CIRCULAR;
//DMA_CIRCULAR:"DAM循环搬运数据"
//DMA_NORMAL;若启动一次DMA,则DMA搬运一次数据
//设置DMA_CCRx寄存器bit5(CIRC位),令CIRC=1,这里设置"DAM循环搬运数据"
hdma_dac1_ch1.Init.Priority = DMA_PRIORITY_VERY_HIGH;
//设置DMA_CCRx寄存器bit13:12(PL[1:0]),令PL[1:0]=11b,DMA抢占优先级最高
HAL_DMA_Init(&hdma_dac1_ch1);
__HAL_LINKDMA(&hdac1,DMA_Handle1,hdma_dac1_ch1);
//将DAC1与DMA联系起来
//把hdac1结构体的参数赋给DMA_Handle1
__HAL_RCC_TIM2_CLK_ENABLE();//使能"定时器2"的时钟,Enable TIM2 clock
htim2.Instance = TIM2;
htim2.Init.Period = 9999;
//定时器周期(9999+1)
htim2.Init.Prescaler = 16;
//设置TIM1预分频器为(16+1)
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
//设置时钟分频系数,TIM1_CR1中的CKD[9:8]=00b,tDTS=ttim_ker_ck;
//溢出时间为(9999+1)*1*(16+1)/170000000/1=0.01秒
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
htim2.Init.RepetitionCounter = 0;//重复计数(1-0),产生一次中断
htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;//TIM_AUTORELOAD_PRELOAD_DISABLE;
//这里设置"使能重装载"
HAL_TIM_Base_Init(&htim2);
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig);
sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;
//TIM_TRGO_UPDATE是"更新事件用来触发器输出"Update event is used as trigger output (TRGO2)
// sMasterConfig.MasterOutputTrigger2 = TIM_TRGO2_RESET;
//TIM1_TRGO2是adc_ext_trg10,用来触发ADC1/2/3/4/5
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig);
//Configures the TIM in master mode.
/*
HAL_NVIC_SetPriority(DMA1_Channel1_IRQn, 0, 0);
//设置NVIC中断分组4:4位抢占优先级,0位响应优先级
//选择中断优先级组4,即抢占优先级为4位,取值为0~15,响应优先级组为0位,取值为0
//DMA1_Channel1_IRQn抢占优先级为5,响应优先级为0
HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);//使能DMA1_Channel1_IRQn中断
*/
HAL_DAC_Start_DMA(&hdac1, DAC_CHANNEL_1,(uint32_t*)Sine12bit, 32, DAC_ALIGN_12B_R);
//启动DMA传输
HAL_TIM_Base_Start(&htim2);//启动TIM2
}
测试PA4引脚的波形如下: