文章目录
一、DAC介绍
1.DAC作用
- DAC(Digital-to-Analog Converter),即为
数字/模拟转换模块
,又称D/A转换器; - 作用就是把
输入的数字编码,转换成对应的模拟电压输出
,它的功能与ADC 相反。即为输出波形和输出固定电压
2.DAC介绍
- 在常见的数字信号系统中,大部分传感器信号被化成电压信号,而ADC 把电压模拟信号转换成易于计算机存储、处理的数字编码,由计算机处理完成后,再由DAC 输出电压模拟信号,该电压模拟信号常常用来驱动某些执行器件,使人类易于感知。
- STM32 具有片上DAC 外设,它的分辨率可配置为8 位或12 位的数字输入信号,具有两个DAC输出通道,这两个通道互不影响,每个通道都可以使用DMA 功能,都具有出错检测能力,可外部触发。
- 在双DAC模式下,2个通道可以独立地进行转换,也可以同时进行转换并同步地更新2个通道的输出。DAC可以通过引脚输入参考电压
VREF+
以获得更精确的转换结果。 - DAC工作在12位模式时,数据可以设置成
左对齐或右对齐
。
3.DAC功能框图
(1)整个DAC 模块围绕框图下方的“数字至模拟转换器x”展开,它的左边分别是参考电源的引脚:VDDA、VSSA 及Vref+,其中STM32 的DAC 规定了它的参考电压Vref+ 输入范围为2.4——3.3V。
(2)“数字至模拟转换器x”的输入为DAC 的数据寄存器“DORx”的数字编码,经过它转换得的模拟信号由图中右侧的“DAC_OUTx”输出。而数据寄存器“DORx”又受“控制逻辑”支配,它可以控制数据寄存器加入一些伪噪声信号或配置产生三角波信号。
(3)图中的左上角为DAC 的触发源,DAC根据触发源的信号来进行DAC 转换,其作用就相当于DAC 转换器的开关,它可以配置的触发源为外部中断源触发、定时器触发或软件控制触发
。
4.参考电压
与ADC 外设类似,DAC 也使用VREF+ 引脚作为参考电压,在设计原理图的时候一般把VSSA 接地,把VREF+ 和VDDA 接3.3V,可得到DAC 的输出电压范围为:0~3.3V。
如果想让输出的电压范围变宽,可以在外部加一个电压调理电路,把0~3.3V 的DAC 输出抬升到特定的范围即可。
5.数模转换及输出通道
框图中的“数字至模拟转换器x”是核心部件,整个DAC 外设都围绕它而展开。它以左边的VREF+作为参考电源,以DAC 的数据寄存器“DORx”的数字编码作为输入,经过它转换得的模拟信号由右侧的“DAC_OUTx”通道输出。
其中各个部件中的“x”是指设备的标号,在STM32 中具有2 个这样的DAC 部件,每个DAC 有1 个对应的输出通道连接到特定的引脚,即:PA4-通道1,PA5-通道2
,为避免干扰,使用DAC 功能时,DAC 通道引脚需要被配置成模拟输入功能(AIN)。
6.触发源及DHRx 寄存器
在使用DAC 时,不能直接对上述DORx 寄存器写入数据,任何输出到DAC 通道x 的数据都必须写入到DHRx 寄存器中(其中包含DHR8Rx、DHR12Lx 等,根据数据对齐方向和分辨率的情况写入到对应的寄存器中)。
数据被写入到DHRx 寄存器后,DAC 会根据触发配置进行处理,若使用硬件触发,则DHRx 中的数据会在3 个APB1 时钟周期后传输至DORx,DORx 随之输出相应的模拟电压到输出通道;
若DAC 设置为外部事件触发,可以使用定时器(TIMx_TRGO)、EXTI_9 信号或软件触发(SWTRIGx)这几种方式控制数据DAC 转换的时机,例如使用定时器触发,配合不同时刻的DHRx 数据,可实现DAC 输出正弦波的功能。
二、HAL库配置初始化
1.配置时钟
2.开启DAC
- Output Buffer:关闭后可输出更低的电压值
- Trigger:触发方式,这里选择TIM2定时器触发
- Wave generation mode:关闭后可输出自定义波形
3.开启DMA
- Mode:选择循环模式
- Data Width:发送数据长度,半字即可
4.配置定时器
- 初始化定时器触发频率为1:84MHZ /(84 * 20) =50000HZ
- 触发事件选择“更新触发事件”
5.配置中断优先级
三、RTT中初始化
1.board.h
在board.h中添加宏定义,使用DAC的驱动函数
#define BSP_USING_DAC1
2.board.c
在board.c中添加如下初始化代码,进行DAC引脚初始化。
/**
* @brief DAC初始化
* @param dacHandle
*/
void HAL_DAC_MspInit(DAC_HandleTypeDef* dacHandle)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if (dacHandle->Instance == DAC)
{
/* DAC clock enable */
__HAL_RCC_DAC_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
/**DAC GPIO Configuration
PA5 ------> DAC_OUT2
*/
GPIO_InitStruct.Pin = GPIO_PIN_5;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* DAC DMA Init */
/* DAC2 Init */
hdma_dac2.Instance = DMA1_Stream6;
hdma_dac2.Init.Channel = DMA_CHANNEL_7;
hdma_dac2.Init.Direction = DMA_MEMORY_TO_PERIPH;
hdma_dac2.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_dac2.Init.MemInc = DMA_MINC_ENABLE;
hdma_dac2.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
hdma_dac2.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
hdma_dac2.Init.Mode = DMA_CIRCULAR;
hdma_dac2.Init.Priority = DMA_PRIORITY_LOW;
hdma_dac2.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
if (HAL_DMA_Init(&hdma_dac2) != HAL_OK)
{
Error_Handler();
}
__HAL_LINKDMA(dacHandle, DMA_Handle2, hdma_dac2);
}
}
3.dac.c
#include "dac.h"
// 方波缓冲波形
uint16_t signalSquareBuffer[DAC_SAMPLE_POINT] = {0};
/*======================================================##### 外部调用 #####==================================================*/
/**
* @brief DAC输出DMA初始化
*/
void DAC_TIME_DMA_Init(void)
{
DAC_ChannelConfTypeDef sConfig = {0};
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
/* DMA controller clock enable */
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_DMA1_CLK_ENABLE();
/** DAC Initialization
*/
hdac.Instance = DAC;
if (HAL_DAC_Init(&hdac) != HAL_OK)
{
Error_Handler();
}
/** DAC channel OUT2 config
*/
sConfig.DAC_Trigger = DAC_TRIGGER_T2_TRGO;
sConfig.DAC_OutputBuffer = DAC_OUTPUTBUFFER_DISABLE;
if (HAL_DAC_ConfigChannel(&hdac, &sConfig, DAC_CHANNEL_2) != HAL_OK)
{
Error_Handler();
}
htim2.Instance = TIM2;
htim2.Init.Prescaler = 20 - 1;
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
htim2.Init.Period = 84 - 1;
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim2) != HAL_OK)
{
Error_Handler();
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
/* DMA interrupt init */
/* DMA1_Stream6_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA1_Stream6_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA1_Stream6_IRQn);
}
/**
* @brief 启动DAC的定时器和DMA输出
*/
void Start_Dac_Time_DMA(void)
{
HAL_TIM_Base_Start(&htim2);
HAL_DAC_Start_DMA(&hdac, DAC_CHANNEL_2, (uint32_t *)signalSquareBuffer, DAC_SAMPLE_POINT, DAC_ALIGN_12B_R);
}
/**
* @brief 关闭DAC的定时器和DMA输出
*/
void Stop_Dac_Time_DMA(void)
{
HAL_TIM_Base_Stop(&htim2);
HAL_DAC_Stop_DMA(&hdac, DAC_CHANNEL_2);
}
/**
* @brief 方波信号缓存填充
* @param duty: 占空比设置0 ~ 100
* @param vol : 电压设置 0-2.5V
*/
void signalSquare (uint16_t duty, float vol)
{
if (duty < 0)
duty = 0;
else if (duty > DAC_SAMPLE_POINT)
duty = DAC_SAMPLE_POINT;
if (vol > DAC_OUTPUT_MAX)
vol = DAC_OUTPUT_MAX;
else if (vol < 0)
vol = 0;
for (int i = 0; i < duty; i++)
{
signalSquareBuffer[i] = vol * DAC_REFERENCE_VALUE / DAC_OUTPUT_MAX;
}
for (int i = duty; i < DAC_SAMPLE_POINT; i++)
{
signalSquareBuffer[i] = 0;
}
}
/**
* @brief 输出方波参数设置
* @param duty:占空比设置
* @param vol :输出电压
* @param fre :输出频率
*/
void I_DAC_Output(uint16_t duty, float vol, uint32_t fre)
{
signalSquare(duty, vol);
//计算PWM频率,所对应的自动重装载值 ---> ARR = 主频 / (预分频+1) / 预期PWM频率(Hz) - 1
float prescaler = (TIMER_FREQ * 1.0) / (84 * DAC_SAMPLE_POINT * fre) - 1;
//配置PSC预分频值
__HAL_TIM_SET_PRESCALER(&htim2, prescaler);
}
/**
* @brief 实时输出方波数据
*/
void I_DAC_Output_Signal(void)
{
uint16_t pwr, pt, it;
// 数据改变后再输出
if (g_laser.param_flag == 1)
{
g_laser.param_flag = 0;
pwr = g_laser.laser_pwr;
pt = g_laser.laser_pt;
it = g_laser.laser_it;
I_DAC_Output(100.0 * pt / (pt + it), pwr * I_DAC_MAX / I_PWR_MAX, 1000.0 / (pt + it));
}
}
/*=====================================================####### END #######=================================================*/
4.dac.h
#ifndef APPLICATIONS_SYSTEM_INC_DAC_H_
#define APPLICATIONS_SYSTEM_INC_DAC_H_
#include <rtthread.h>
/**====================================================###### 宏定义 ######==================================================*/
#define DAC_OUTPUT_MAX (3.3f) // DAC输出最大值
#define I_DAC_MAX (2.5f) // I_DAC输出最大电压
#define I_PWR_MAX 850 // I_DAC输出功率最大值
#define DAC_REFERENCE_VALUE 4095 // DAC参考值
#define TIMER_FREQ 84000000 // 定时器主频
#define DAC_SAMPLE_POINT 100 // 采样点个数
/**====================================================####### END #######=================================================*/
/**=================================================##### 函数及变量声明 #####===============================================*/
extern void DAC_TIME_DMA_Init(void); // DAC输出DMA初始化
extern void Start_Dac_Time_DMA(void); // 启动DAC的定时器和DMA输出
extern void Stop_Dac_Time_DMA(void); // 关闭DAC的定时器和DMA输出
extern void signalSquare (uint16_t duty, float vol); // 方波信号缓存填充
extern void I_DAC_Output(uint16_t duty, float vol, uint32_t pscVal); // 输出方波参数设置
extern void I_DAC_Output_Signal(void); // 实时输出方波数据
/**====================================================####### END #######=================================================*/
#endif /* APPLICATIONS_SYSTEM_INC_DAC_H_ */
5.其他波形函数
// 正弦波信号缓存填充
void signalSin(void)
{
for (int i = 0; i < 100; i++)
{
signalSinBuffer[i] = ((sin(i * 2 * PI / 100) + 1) * (4096 / 2));
}
}
// 三角波信号缓存填充,duty: 对称性设置 0 ~ 100
void signalRamp(uint16_t duty)
{
if(duty < 0) duty = 0;
if (duty > 100) duty = 100;
for (int i = 0; i < duty; i++)
{
signalRampBuffer[i] = i * 4095 / duty;
}
for (int i = duty; i < 100; i++)
{
signalRampBuffer[i] = (100 - i) * 4095 / (100 - duty);
}
}
四、测试验证
1.main.c测试
int main(void)
{
DAC_TIME_DMA_Init(); // DAC输出DMA初始化
Start_Dac_Time_DMA(); // 启动DAC的定时器和DMA输出
while (1)
{
I_DAC_Output(50, 5, 500);
rt_thread_mdelay(1);
}
return RT_EOK;
}
总结:通过使用示波器测试输出的波形,可以正常观察到方波信号,并且可以更改方波的幅度、频率和占空比。同时上面还附带了其他波形生成的函数以供使用。
标签:duty,DMA,HAL,DAC,void,Init From: https://blog.csdn.net/Hei_se_meng_yan/article/details/140503492