1、DAC简介
Digital-to-Analog Converter的缩写。数模转换器。又称D/A转换器,简称DAC,是指将离散的数字信号转换为连续变量的模拟信号的器件。典型的数字模拟转换器将表示一定比例电压值的数字信号转换为模拟信号。
STM32F1中有两个DAC,可以同时使用STM32的DAC模块是12位数字输入,电压输出型的DAC。
DAC 有两个用途:输出波形和输出固定电压
DAC工作原理
DAC工作框图
这里我们把它分为三部分讲解:
"触发方式 " “控制逻辑” "数模转换器"
1触发方式
第一部分,触发方式,是指DAC转换可以由某外部事件触发(定时器计数器、外部中断线)。配置控制位TSELx[2:0]可以选择8个触发事件之一触发DAC转换,任意一种触发源都可以触发DAC转换。
具体的外部触发可看下图:
六个是定时器触发:TIM2,TIM4,TIM5,TIM6,TIM7和TIM8。剩下两个分别是:EXTI线路9(PC9)和软件触发
每次DAC接口侦测到来自选中的定时器TRGO输出,或者外部中断线9的上升沿,近存放在寄 存器DAC_DHRx中的数据会被传送到寄存器DAC_DORx中。在3个APB1时钟周期后,寄存器 DAC_DORx更新为新值。
如果选择软件触发,一旦SWTRIG位置’1’,转换即开始。在数据从DAC_DHRx寄存器传送到 DAC_DORx寄存器后,SWTRIG位由硬件自动清’0’
2控制逻辑
此部分决定了DAC的波形控制,输出方式,DMA传输,等等,
我们来具体讲解下,
从框图可以看出,DAC受DORx寄存器直接控制的,但是
数据并不是直接传入DORx的,需要先传入DHRx 之后在间接地传给DORx寄存器 不能直接往DORx寄存器写入数据
- 如果没有选择硬件触发(TENx=0),在一个APB1周期后传入DORx,
- 如果选择硬件触发(TENx=1),则在3个APB1周期后传入DORx
一旦数据从DAC_DHRx寄存器装入DAC_DORx寄存器,在经过时间Tsetting(大约3us) 之后,输出即有效,这段时间的长短依电源电压和模拟输出负载的不同会有所变化
上图的DMAENx TENx MAMOx[3:0],WAVENx[1:0]位 都是由DAC_CR寄存器控制的
DMAENx 控制DAC通道1/2 的DMA使能
MAMP2[3:0]:DAC通道2屏蔽/幅值选择器 位 27:24 由软件设置这些位,
用来在噪声生成模式下选择屏蔽位,在三角波生成模式下选择波形的幅值。
WAVE2[1:0]:DAC通道2噪声/三角波生成使能位23:22
决定是否产生波形,和产生什么波形。
00:关闭波形发生器;
10:使能噪声波形发生器;
1x:使能三角波发生器。
TENx:DAC通道x触发使能,用来使能/关闭DAC通道x的触发。
0:关闭DAC通道x触发,写入DAC_DHRx寄存器的数据在1个APB1时钟周期后传入 DAC_DORx寄存器;
1:使能DAC通道x触发,写入DAC_DHRx寄存器的数据在3个APB1时钟周期后传入 DAC_DORx寄存器。
注意:如果选择软件触发,写入寄存器DAC_DHRx的数据只需要1个APB1时钟周期就可以传入 寄存器DAC_DORx。
3数模转换器
VDDA和VSSA为DAC模块模拟部分的供电。
Vref+则是DAC模块的参考电压。
DAC_OUTx就是DAC的输出通道了(对应PA4或者PA5引脚)
从左边的参考电压Vref+ ---->数模转换器 ---->模拟信号输出引脚
注意:DAC的引脚应该设置成模拟输入(AIN)模式
DAC输出电压:
数字输入经过DAC被线性地转换为模拟电压输出
其范围为 0~VREF+
DAC输出 = VREF x (DOR/4095)
特殊功能:
噪声波形生成,三角波形生成,外部触发转换,双DAC同时或者分别转换;每个通道都有DMA功能;
参考电压:2.4V~ 3.3V
DAC的主要特征
- 2个DAC转换器:
- 每个转换器对应1个输出通道;
- 8位或者12位单调输出;
- 12位模式下数据左对齐或者右对齐;
- 同步更新功能;
- 噪声波形生成;
- 三角波形生成;
- 双DAC通道同时或者分别转换;
- 外部触发转换;
- 输入参考电压VREF+。
DAC原理总括
数字/模拟转换模块(DAC)是12位数字输入,电压输出的数字/模拟转换器。DAC可以配置为8位或12位模式,也可以与DMA控制器配合使用。DAC工作在12位模式时,数据可以设置成左对齐或右对齐。DAC模块有2个输出通道,每个通道都有单独的转换器。在双DAC模式下,2个通道可以独立地进行转换,也可以同时进行转换并同步地更新2个通道的输出。DAC可以通过引脚输入参考电压VREF+ 以获得更精确的转换结果。
以上转载 于他处:
原文链接:https://blog.csdn.net/as480133937/article/details/102309242
2、 硬件设计
本实验通过LED2指示灯来提示系统运行状态,S1用来增加DAC输入值,S2用来减少DAC输入值,输入值的改变将控制DAC_OUT1的电压输出,再通过USART1将输出的电压值打印出来
- LED2指示灯
- DAC_OUT1(PA4)
- USART1串口
- S1和KS2按键
3、 STM32CubeMX设置
- RCC设置外接HSE,时钟设置为72M
- PE5设置为GPIO推挽输出模式、上拉、高速、默认输出电平为高电平
- USART1选择为异步通讯方式,波特率设置为115200Bits/s,传输数据长度为8Bit,无奇偶校验,1位停止位
- PE3,PE3设置为GPIO输入模式、上拉模式
- 激活DAC_OUT1,使能输出缓冲,不使用触发功能
DAC集成了2个输出缓存,可以用来减少输出阻抗,无需外部运放即可直接驱动外部负载。每个 DAC通道输出缓存可以通过设置DAC_CR寄存器的BOFFx位来使能或者关闭
输入工程名,选择工程路径(不要有中文),选择MDK-ARM V5;勾选Generated periphera initialization as a pair of ‘.c/.h’ files per IP ;点击GENERATE CODE,生成工程代码
4、程序编程
在dac.c文件中可以看到DAC初始化相关函数
void MX_DAC_Init(void){
DAC_ChannelConfTypeDef sConfig = {0};
/** DAC Initialization */
hdac.Instance = DAC;
if (HAL_DAC_Init(&hdac) != HAL_OK){
Error_Handler();
}
/** DAC channel OUT1 config */
sConfig.DAC_Trigger = DAC_TRIGGER_NONE; //不使用触发功能
sConfig.DAC_OutputBuffer = DAC_OUTPUTBUFFER_DISABLE; //输出缓冲关闭
if (HAL_DAC_ConfigChannel(&hdac, &sConfig, DAC_CHANNEL_1) != HAL_OK){
Error_Handler();
}
}
void HAL_DAC_MspInit(DAC_HandleTypeDef* dacHandle){
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(dacHandle->Instance==DAC) {
/* DAC clock enable */
__HAL_RCC_DAC_CLK_ENABLE(); //使能DAC时钟
__HAL_RCC_GPIOA_CLK_ENABLE();
GPIO_InitStruct.Pin = GPIO_PIN_4;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG; //模拟
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}
}
DAC函数:
/* IO operation functions *****************************************************/
HAL_StatusTypeDef HAL_DAC_Start(DAC_HandleTypeDef* hdac, uint32_t Channel); //开启DAC输出
HAL_StatusTypeDef HAL_DAC_Stop(DAC_HandleTypeDef* hdac, uint32_t Channel); //关闭DAC输出
HAL_StatusTypeDef HAL_DAC_Start_DMA(DAC_HandleTypeDef* hdac, uint32_t Channel, uint32_t* pData, uint32_t Length, uint32_t Alignment); //需要函数中不断开启 //开启DAC的DMA输出
HAL_StatusTypeDef HAL_DAC_Stop_DMA(DAC_HandleTypeDef* hdac, uint32_t Channel); //关闭DAC的DMA输出
HAL_StatusTypeDef HAL_DAC_SetValue(DAC_HandleTypeDef* hdac, uint32_t Channel, uint32_t Alignment, uint32_t Data); //设置DAC输出值
uint32_t HAL_DAC_GetValue(DAC_HandleTypeDef* hdac, uint32_t Channel); //获取DAC输出值
在main函数中开启DAC转换并输出。
int main(void)
{
///****省略***/
uint16_t dac_val; //读取的DAC值
float dac_vol; //转换后的电压值
uint8_t t = 0;
uint16_t dac_setval = 0; //DAC设置值
HAL_DAC_Start(&hdac,DAC_CHANNEL_1); //开启DAC通道1
HAL_DAC_SetValue(&hdac,DAC_CHANNEL_1,DAC_ALIGN_12B_R,0); //设置初始值为0
while (1)
{
t++;
if(HAL_GPIO_ReadPin(GPIOE,GPIO_PIN_4)==0) //S1 ++
{
HAL_Delay(10);
if(HAL_GPIO_ReadPin(GPIOE,GPIO_PIN_4)==0)
{
if(dac_setval < 4000)
dac_setval += 200;
HAL_DAC_SetValue(&hdac,DAC_CHANNEL_1,DAC_ALIGN_12B_R,dac_setval); //设置
}
}
if(HAL_GPIO_ReadPin(GPIOE,GPIO_PIN_3)==0) //S2 --
{
HAL_Delay(10);
if(HAL_GPIO_ReadPin(GPIOE,GPIO_PIN_3)==0)
{
if(dac_setval > 200)
dac_setval -= 200;
else
dac_setval = 0;
HAL_DAC_SetValue(&hdac,DAC_CHANNEL_1,DAC_ALIGN_12B_R,dac_setval); //设置
}
}}
if(t == 10 ){ //定时时间到
printf1("DAC_OUT1 DAC setval: %d\r\n",dac_setval);
dac_val = HAL_DAC_GetValue(&hdac,DAC_CHANNEL_1);
printf1("DAC_OUT1 DAC value: %d\r\n",dac_val);
dac_vol = dac_val * (3.3/4096);
printf1("DAC_OUT1 VOL value: %.2fV\r\n",dac_vol);
printf1("\r\n");
t = 0;
}
HAL_GPIO_TogglePin(GPIOE,GPIO_PIN_5);
HAL_Delay(100);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
}
5、下载验证
程序编译无误后下载到板子上,按下按键即可更改输出的电压值。
6、扩展(DAC输出三角波形)
在上文可以学到DAC的触发模式可以选择定时器触发,因此可以利用定时器来让DAC输出三角波形,再利用ADC读取电压并通过串口输出。
6.1、STM32cubeMX添加设置
使能DAC输出通道1,DAC外部触发(Trigger) 选择定时器2触发,波形生成模式(Wave generation mode) 选择三角波发生器(Triangle wave generation),最大三角波幅(Maximum Triangle Amplitude)设置为4095,即3.3V。
打开Timers,使能定时器2,设置定时器为50ms更新一次,触发器事件选择选择为更新事件。
激活ADC1通道1,设置右对齐,关闭扫描、连续及间断模式,使能regular conversion,设置软件触发、设置采样时间1.5个周期
输入工程名,选择工程路径(不要有中文),选择MDK-ARM V5;勾选Generated periphera initialization as a pair of ‘.c/.h’ files per IP ;点击GENERATE CODE,生成工程代码
6.2、程序编程
在main函数中分别开启定时器TIM2 和开启DAC,while循环中添加如下测试程序,ADC1是12位转换精度,因此电压分辨率为:3.3/ = 3.3/4096。
int main()
{
///****省略***///
uint16_t AD_Value; //读取的DAC值
float Vol_Value; //转换后的电压值
//如果只需要生成三角波,只需要开启定时器和DAC通道即可
HAL_TIM_Base_Start(&htim2); //开启定时器
HAL_DAC_Start(&hdac, DAC_CHANNEL_1);//开启DAC通道1
while (1)
{
//while循环中只读取ADC,不做DAC处理
HAL_ADC_Start(&hadc1); //启动ADC转换
HAL_ADC_PollForConversion(&hadc1,10); //等待转换完成,10ms表示超时时间
AD_Value = HAL_ADC_GetValue(&hadc1); //读取ADC转换数据(12位数据)
printf1("ADC1_IN1 ADC value: %d\r\n",AD_Value);
Vol_Value = AD_Value*(3.3/4096); //AD值乘以分辨率即为电压值
printf1("ADC1_IN1 VOL value: %.2fV\r\n",Vol_Value);
HAL_GPIO_TogglePin(GPIOE,GPIO_PIN_5);
HAL_Delay(100);
}
}
程序说明:
这里讲下三角波的频率
简单的说,首先设置一个DAC最大幅值, 之后设置定时器溢出时间,在每次定时器发生溢出等事件之后(50ms),定时器会发送触发信号TRGO到DAC,这时内部的三角波计数器就会累加1 然后于DAC_DHRx寄存器的值相加,写到DAC_DORx计数器中,如果该值小于设定的最大幅值,就会正常输出,当大于最大幅值时就会递减,减到0之后又开始累加,周而复始,就形成了三角波。
三角波频率:
设三角波幅值为3.3V,即4095,所以一个周期计数器计数4096*2=8192次,则三角波频率为“定时器频率/8192”
6.3、下载验证
程序编译无误后下载到板子上,(注意:需要用一根杜邦线将PA1和PA4两个引脚连接起来),即可看到DAC输出电压由低到高,再由高到低输出。
7、参考文献
【STM32】HAL库 STM32CubeMX教程十---DAC_stm32 cubemx 生成dac例程-CSDN博客