打印
[应用相关]

STM32单片机DAC基础及软件启动DAC触发并通过串口观察数据的方法

[复制链接]
25|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
磨砂|  楼主 | 2025-1-12 18:51 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
   DAC是数字量到模拟量的转换器。DAC可以由软件触发,也可以由定时器或外部中断信号触发。

一、DAC基础知识
1、 DAC的结构和特性
        STM32F474有4个DAC模块。每个DAC模块有1个或2个DAC转换器,每个转换器对应一个输出通道。其中只有DAC1、DAC2共计有3个对外输出的通道,另有DAC3、DAC4共计有4个内部通道。(对照地,STM32F407只有一个DAC模块。这个DAC模块有两路DAC通道,每个通道有独立的12位DAC转换器。两个通道可以独立输出,也可以同步输出,以产生噪声波和三角波)。



(1)STM32G4**DAC特性
        Seven 12 bit DAC channels (3 external buffered and 4 internal unbuffered) can be used to

convert digital signals into analog voltage signal outputs. The chosen design structure is

composed of integrated resistor strings and an amplifier in inverting configuration.

        This digital interface supports the following features:

Up to two DAC output channels;
8-bit or 12-bit output mode;
Buffer offset calibration (factory and user trimming);
Left or right data alignment in 12-bit mode;
Synchronized update capability;
Noise-wave generation;
Triangular-wave generation;
Saw tooth wave generation;
Dual DAC channel independent or simultaneous conversions;
DMA capability for each channel;
External triggers for conversion;
Sample and hold low-power mode, with internal or external capacitor;
Up to 1 Msps for external output and 15 Msps for internal output;
        The DAC channels are triggered through the timer update outputs that are also connected  

to different DMA channels.

        资料来源于数据手册:STM32G474RET6 -PDF数据手册-参考资料-立创商城。

(2) 数模转换器
        DAC的核心是12位的数模转换器,它将数据输出寄存器DORx(字母x是1、2、...,表示通道1或通道2。等等,总共7个通道,下同)的12位数字量转换为模拟电压输出到复用引脚DAC _OUTx。DAC还有个输出缓冲器,如果使用输出缓冲器,可以降低输出阻抗并提高输出的负载能力。

        数据输出寄存器DORx的内容不能直接设置,而是由控制逻辑部分生成。DORx的数据可以来自于数据保持寄存器DHRx,也可以来自于控制逻辑生成的三角波数据或噪声波数据,抑或DMA缓冲区的数据。

        DAC的转换可以由软件指令触发,也可以由定时器、高分辨率定时器的TRGO信号触发,或外部中断线EXTI_9或EXTI_10触发。DAC在总线AHB2上,DAC的工作时钟信号就是PCLK1。DAC输出的模拟电压由寄存器DORx的数值和参考电压VREF+决定,输出电压的计算公式为:



        数模转换器的原理图:



2、DAC数据格式
(1) 使用单通道独立输出
        使用单通道独立输出时,向DAC写入数据有3种格式:8位右对齐、12位左对齐和12位右对齐,这3种格式的数据写入相应的对齐数据保持寄存器DAC_DHR8Rx、DAC_DHR12Lx或DAC_DHR12Rx,然后被移位保存到数据保持寄存器DHRx,DHRx的内容再被加载到通道数据输出寄存器DORx。




12位右对齐

(2)使用DAC双通道同步输出
        使用DAC双通道同步输出时,有3个专用的双通道寄存器用于向两个DAC通道同时写入数据,写入数据的格式有3种,其中高位是DAC2,低位是DAC1。用户写入的数据会被移位保存到数据保持寄存器DHR2和DHR1,然后再被加载到通道数据输出寄存器DOR2和DOR1。



3、DAC转换时间
        不能直接将数据写入DOR,需要将数据写入DHR后,再转移到DOR。使用软件触发时,经过一个APB1时钟周期后,DHR的内容移入DOR;使用外部硬件触发(定时器触发或EXTI9、EXTI10线触发)时,触发信号到来后,需要经过3个APB1时钟周期才将DHR的内容移入DOR。下图是软件触发时的DAC转换时序,当DOR的内容更新后,引脚上的模拟电压需要经过一段时间tSETTING之后才稳定,具体时间长度取决于电源电压和模拟输出负载。



4、输出噪声波和三角波
        DAC内部使用线性反馈移位寄存器(Linear Feedback Shift Register,LFSR)生成变振幅的伪噪声,每次发生触发时,经过3个AHB2时钟周期后,LFSR生成一个随机数并移入DOR。注意,要生成噪声波或三角波,必须使用外部触发。

        可以在直流信号或慢变信号上叠加一个小幅三角波。在DAC控制寄存器DAC_CR的MAMPx[3:0]位设置一个参数用于表示三角波最大振幅,振幅为1~4095(非连续)。每次发生触发时,内部的三角波计数器就会递增或递减,在保障不溢出的情况下,会和数据保持寄存器DHRx的值叠加后,移送到数据输出寄存器DORx,如下图



5、双通道同步转换
        为两个通道选择相同的外部触发信号源,就可以实现两个DAC通道同步触发。如果为两个DAC通道设置输出数据,需要将两个通道的数据合并设置到一个32位双DAC数据寄存器DAC_DHR8RD、DAC_DHR12LD或DAC_DHR12RD里,然后DAC再自动将数据移送到寄存器DOR1和DOR2里。

6、DMA请求
        每个DAC通道有一个独立的DMA请求,DMA传输方向是从存储器到外设。单个DAC通道受外部触发工作时,可以使用DMA进行数据传输,DMA缓冲区的数据在外部触发作用下,依次转移到DAC通道的输出寄存器。

        在双通道模式下,用户可以为每个通道的DMA请求配置DMA流,并为每个DAC通道准备DMA缓冲区的数据;也可以只为一个通道的DMA请求配置DMA流,并为两个通道准备数据,在发生DMA请求时可以将DMA缓冲区的一个32位数据分解送到两个DAC通道。

7、DAC的中断
        DAC模块的两个通道只有一个中断号,且只有一个中断事件,即DMA下溢(underrun)事件。DAC的DMA请求没有缓冲队列,如果第二个外部触发到达时,尚未收到第一个外部触发的确认,就不会发出新的DMA请求,这就是DMA下溢事件。一般是因为DAC外部触发频率太高,导致DMA下溢,应适当降低DAC外部触发频率以消除DMA下溢。

二、DAC的HAL驱动程序
1、DAC驱动宏函数
        DAC驱动程序的头文件是stm32g4xx_hal_dac.h和stm32g4xx_hal_dac_ex.h。直接操作DAC相关寄存器的宏函数如下表。宏函数中的参数__HANDLE__是DAC对象指针,__DAC_Channel__是DAC通道,__INTERRUPT__是DAC的中断事件类型,__FLAG__是事件中断标志。

宏函数

功能描述

__HAL_DAC_DISABLE(__HANDLE__,__DAC_Channel__)

关闭DAC的某个通道

__HAL_DAC_ENABLE(__HANDLE__, __DAC_Channel__)

开启DAC 的某个通道

__HAL_DAC_DISABLE_IT(  HANDLE__,__INTERRUPT__ )

禁止DAC 模块的某个中断事件源

__HAL_DAC_ENABLE_IT(  HANDLE__,__INTERRUPT__ )

开启DA( 模块的某个中断事件源

__HAL_DAC_GET_IT_SOURCE(  HANDLE__,__INTERRUPT__ )

检查DAC模块的某个中断事件源是否开启

__HAL_DAC_GET_FLAG(  HANDLE__, __FLAG__)

获取某个事件的中断标志,检查事件是否发生

__HAL_DAC_CLEAR_FLAG(  HANDLE__, __FLAG__)

清除某个事件的中断标志



        在dac.c中,有表示DAC的外设对象变量hdac,宏函数中的参数__HANDLE__是DAC外设对象指针,就可以用&hdac。

DAC_HandleTypeDef hdac;        //表示DAC的外设对象变量
        DAC模块有两个DAC通道,用宏定义表示如下,可作为宏函数中参数__DAC_Channel_的取值。

#define DAC_CHANNEL_1        0x00000000U        //DAC通道1
#define DAC_CHANNEL_2        0x00000010U        //DAC通道2
        DAC只有两个中断事件源,就是两个DAC通道的DMA下溢事件。中断事件类型的宏定义如下,可作为宏函数中参数__INTERRUPT__的取值。

#define DAC_IT_DMAUDR1        ((uint32_t)DAC_SR_DMAUDR1))        //通道1的DMA下溢中断事件
#define DAC_IT_DMAUDR2  ((uint32_t)DAC_SR_DMAUDR2))        //通道2的DMA下溢中断事件
        对应两个中断事件源,有两个事件中断标志,其宏定义如下,可作为宏函数中参数__FLAG__的取值。

#define DAC_FLAG_DMAUDR1        ((uint32_t)DAC_SR_DMAUDR1)        //通道1的DMA下溢中断标志
#define DAC_FLAG_DMAUDR2        ((uint32_t)DAC_SR_DMAUDR2)        //通道2的DMA下溢中断标志
2、DAC驱动功能函数
        DAC驱动功能函数如下表。DAC没有以中断方式启动转换的函数,只有软件外部触发启动和DMA方式启动,DMA方式必须和外部触发结合使用。




(1)DAC初始化和通道配置
        函数HAL_DAC_Init()用于DAC模块初始化设置,其原型定义如下:

HAL_StatusTypeDef HAL_DAC_Init(DAC_HandleTypeDef* hdac);
        其中,参数hdac是定义的DAC外设对象指针。函数HAL_DAC_ConfigChannel()对某个DAC通道进行配置,其原型定义如下:

HAL_StatusTypeDef HAL_DAC_ConfigChannel(DAC_HandleTypeDef* hdac,DAC_Channe1ConfTypeDef* sConfig,uint32_t Channel);
        其中,参数sConfig是表示DAC通道属性的DAC_ChannelConfTypeDef结构体类型指针;参数Channel表示DAC通道,取值为宏定义常量DAC_CHANNEL_1或DAC_CHANNEL_2。

        在进行DAC初始化时,需要先调用HAL_DAC_Init()进行DAC模块的初始化,再调用函数HAL_DAC_ConfigChannel()对需要使用的DAC通道进行配置。

(2)软件触发转换
        函数HAL_DAC_Start()和HAL_DAC_Stop()用于启动和停止某个DAC通道,以HAL_DAC_Star()启动的通道可以使用软件触发或外部触发。这两个函数的原型定义如下:

HAL_StatusTypeDef HAL_DAC_Start(DAC_HandleTypeDef*hdac,uint32_t Channel);
HAL_StatusTypeDef HAL_DAC_Stop(DAC_HandleTypeDef*hdac,uint32_t Channel);
        使用函数HAL_DAC_SetValue()或HAL_DACEx_DualSetValue()向DAC通道写入输出数据就是软件触发转换。函数HAL_DAC_SetValue()用于向一个DAC通道写入数据,实际就是将数据写入数据保持寄存器DHRx,其原型定义如下:

HAL_StatusTypeDef HAL_DAC_SetValue(DAC_HandleTypeDef*hdac,uint32_t Channel,ui32_t Alignment,uint32_t Data);
        其中,Channel是要写入的DAC通道,Alignment表示数据对齐格式,Data是要写入的数据。向单个DAC通道写入数据有图15-2所示的3种对齐格式,参数Alignment可以从如下的3个宏定义中取值。

#define DAC_ALIGN_12B_R //12位右对齐0x00000000U
#define DAC_ALIGN_12B_L //12位左对齐0x00000004U
#define DAC_ALIGN_8B_R  //8位右对齐0x00000008U
        函数HAL_DAC_GetValue()用于读取某个DAC通道的数据输出寄存器的值,数据输出寄存器DORx是低12位有效,总是右对齐的。其原型定义如下:

uint32_t HAL_DAC_GetValue(DAC_HandleTypeDef*hdac,uint32_t Channel);
        函数HAL_DACEx_DualSetValue(用于在双通道模式下向两个DAC通道同时写入数据,其函数原型定义如下:

HAL_StatusTypeDef HAL_DACEx_DualSetValue(DAC_HandleTypeDef* hdac,uint32_t Alignment,uint32_t Datal,uint32_t Data2)
        双通道模式写入数据有3种格式,参数Alignment的取值还是前面的对齐方式宏定义。参数Datal是写入DAC通道2的数据,参数Data2是写入DAC通道1的数据。函数HAL_DACEx_DualGetValue()用于读取双通道的数据输出寄存器的内容,其原型定义如下:

uint32_t HAL_DACEx_DualGetValue(DAC_HandleTypeDef* hdac)
        函数返回值的高16位是DAC2的输出值,低16位是DAC1的输出值。

(3)产生波形
        函数HAL_DACEx_TriangleWaveGenerate()可以在输出信号上叠加一个三角波信号,该函数需要在启动DAC通道前调用。其原型定义如下:

HAL_StatusTypeDef HAL_DACEx_TriangleWaveGenerate(DAC_HandleTypeDef* hdac,uint32_t Channel,uint32_t  Amplitude);
        其中,参数Amplitude是三角波最大幅度,用4位二进制数表示,范围为1~4095,有一组宏定义可作为参数值。每次发生软件触发或外部触发时,三角波内部计数值就会变化1。内部的三角波计数器会递增或递减,在保障不溢出的情况下,会和DHRx寄存器的值叠加后移送到DORx寄存器。

        函数HAL_DACEx_NoiseWaveGenerate()用于产生噪声波,需要在启动DAC通道前调用这个函数。每次发生触发时,DAC内部就会产生一个随机数并移入DORx寄存器。其原型定义如下:

HAL_StatusTypeDef HAL_DACEx_NoiseWaveGenerate(DAC_HandleTypeDef* hdac,uint32_t Channel,uint32_t Amplitude);
        其中,参数Amplitude是生成随机数的最大幅度,用4位二进制掩码表示,有一组宏定义可作为参数值。要生成噪声波,必须使用外部触发。

(4)DAC中断处理
        DAC只有两个中断事件源,就是两个DAC通道的DMA下溢事件。如果发生DMA下溢,一般就是因为外部触发信号频率太高,重新调整外部触发信号的频率,消除DMA下溢的发生才是解决办法。

        DAC没有以中断方式启动转换的函数,HAL_DAC_Start()以软件触发或外部触发方式启动DAC转换,HAL_DAC_Start_DMA()以外部触发和DMA方式启动DAC转换。DAC驱动程序定义了几个用于DMA流中断事件的回调函数,这些回调函数与DAC的中断无关。

(5)DMA方式传输
        使用外部触发信号时,可以使用DMA方式启动DAC转换。DMA方式启动DAC转换的函数是HAL_DAC_Start_DMA),其原型定义如下:

HAL_StatusTypeDef HAL_DAC_Start_DMA(DAC_HandleTypeDef* hdac,uint32_t Channel,uint32_t* pData,uint32_t Length,uint32_t Alignment);
        其中,参数Channel是DAC通道号,pData是输出到DAC外设的数据缓冲区地址,Length是缓冲区数据个数,Alignment是数据对齐方式。

       使用DMA方式传输时,每次外部信号触发时,就会将DMA缓冲区的一个数据传输到DAC通道的数据输出寄存器DORx。设置存储器地址自增时,地址指针就会移到DMA缓冲区的下一个数据点。

        函数HAL_DAC_Start_DMA()可以启动单通道的DMA传输,也可以启动双通道的DMA传输。在启动双通道的DMA传输时,缓冲区pData里存储的应该是双通道复合数据。

        停止某个通道的DMA传输,并停止DAC的函数是HAL_DAC_Stop_DMA(),定义如下:

HAL_StatusTypeDef HAL_DAC_Stop_DMA(DAC_HandleTypeDef* hdac,uint32_t Channel);
        DAC的驱动程序定义了用于DMA流事件中断的回调函数。例如,要处理DAC1通道的DMA传输完成事件中断时,就重新实现函数HAL_DAC_ConvCpltCallbackCh1()。注意,这些回调函数是DMA流的事件中断回调函数,与DAC的中断无关,所以在使用DMA时,可关闭DAC的全局中断。

三、工程配置
1、工程描述
配置DAC1(PA4);
通过开发板的底板上的按键KeyUp和KeyDown控制DAC1输出值的增减,用软件触发方式设置DAC1的输出值;
配置ADC1(PA0);
用跳线把DAC1(PA4)和ADC1(PA0)连接起来,DAC1的输出由ADC1采集;
ADC1在定时器TIM3的TRGO信号触发下采集,TIM3定时周期500ms;
配置串口USART2,并通过串口把ADC1采集到的数据显示到串口助手上。可以同步显示到示波器上。
        部分内容参考本文作者的其他文章:细说STM32单片机双ADC同步转换和DMA传输数据到Buffer并通过串口发送数据的方法-CSDN博客  https://wenchm.blog.csdn.net/article/details/144082966

2、 时钟、DEBUG、Timer3、USART2、Project Manager Code Generater
        同参考文章。

3、 NVIC
        开启ADC1的全局中断,设置其抢占优先级为1,因为需要在ADC转换完成中断里读取转换结果数据并显示;将TIM3的定时周期设置为500ms,将TRGO信号设置为UEV事件信号。

4、 ADC1
(1) ADC1 Mode
ADC1_IN1,single_ended;
(2) ADCs_Common_Settings组
Mode:Independent Mode;
(3)ADC_Settings组
Clock Prescaler:选择同步时钟4分频;
Resolution:12位;
Data Alignment:右对齐(Right alignment);
Scan Conversion Mode:Disabled;因为只有一个通道,所以参数Scan Conversion Mode(扫描转换模式)设置为Disabled。
End of Conversion Selection:end of single conversion;
Continuous Conversion Mode:Disabled;
Discontinuous Conversion Mode:Disabled;
DMA Continuous Requests:Disabled;
(4)ADC_Regular_ConversionMode
Enable Regular Conversions:Enabled;
Number of Conversion:1;
External Trigger Conversion Source:Timer 3 Trigger out envent;
External Trigger Conversion Edge:上跳沿;
Rank1:Channel1,采样时间(Sampling Time)=24.5,无偏移;
5、 DAC1
(1)Mode
        DAC1有2个通道:out1 mode、out2 mode,☐External Trigger。

External Trigger,不选择;
out2 mode,Disable;
out1 mode,选择Connected to external pin only;
        这里有4个选项:

禁用;
仅输出到外部管脚;
仅连接到片内外设;
既输出到外部管脚又连接到片内外设;



(2) DAC out1 Settings
Mode selected:Normal;
Output Buffer:Enable;设置是否使用输出缓冲器。如果使用输出缓冲器,可以降低输出阻抗并提高输出的负载能力。默认设置为Enable。
DAC High Frequency:Automatic;
DMA Double Data:Disable;
Signed Format:Disable;
Trigger:None;
Trigger2:None;
Wave Generation Mode:Disable;
User Trimming:Factory Trimming;
6、GPIO



        GPIO的操作件都在开发板的底板上,有需要资料或底板的网友,请给我留言。

        说明:LED灯,是在MCU输出低电平时亮;按键按下的时候给MCU提供低电平。

四、软件设计
1、main.c
/* USER CODE BEGIN Includes */
#include <stdio.h>
#include "keyled.h"

/* USER CODE END Includes */
        关于引用的keyled.h 和keyled.c,请参考本文作者写的其他文章,里面有源码和使用方法。

/* USER CODE BEGIN 2 */
  HAL_DAC_Start(&hdac1,DAC_CHANNEL_1);        // Start DAC_OUT1

  uint32_t        DacOutValue=1000;                   // DAC1输出设定值,范围0--4095
  HAL_DAC_SetValue(&hdac1, DAC_CHANNEL_1, DAC_ALIGN_12B_R, DacOutValue);
  printf("DAC1 initial value = %ld\r\n",DacOutValue);

  HAL_ADC_Start_IT(&hadc1);                                //启动ADC中断方式输入
  HAL_TIM_Base_Start(&htim3);                        //启动TIM3,触发ADC定时采集

  LED1_OFF();
  LED2_OFF();
/* USER CODE END 2 */
/* USER CODE BEGIN WHILE */
  while (1)
  {
          KEYS  curKey=ScanPressedKey(KEY_WAIT_ALWAYS);
                  if (curKey==KEY_UP)
                  {
                          DacOutValue += 100;
                          if(DacOutValue >= 4000)
                                  DacOutValue = 1000;

                          LED1_ON();
                          LED2_OFF();
                  }
                  else if  (curKey==KEY_LEFT)
                  {
                            DacOutValue -= 100;
                          if(DacOutValue <= 100)
                                  DacOutValue = 1000;

                            LED2_ON();
                            LED1_OFF();
                  }

                  HAL_DAC_SetValue(&hdac1, DAC_CHANNEL_1, DAC_ALIGN_12B_R, DacOutValue);
                  printf("Value sent to ADC1 = %ld\r\n",DacOutValue);        //显示设置的DAC1输出��?

                  HAL_Delay(300); //消除按键抖动影响
            }

    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */

  /* USER CODE END 3 */
}

/* USER CODE BEGIN 4 */
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
        uint32_t val=HAL_ADC_GetValue(hadc);

        uint32_t Volt=3300*val;
        Volt=Volt>>12;

        printf("Value collected from ADC1 = %ld\r\n",Volt );
}

//串口打印
int __io_putchar(int ch)
{
        HAL_UART_Transmit(&huart2, (uint8_t*)&ch, 1, 0xFFFF);
        return ch;
}
/* USER CODE END 4 */

五、下载与运行
        每按下一次K5键 ,DAC值加100,每按下一次K3键,DAC值减100。



————————————————

                            版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

原文链接:https://blog.csdn.net/wenchm/article/details/144127805

91906780df517579e.png (2.11 KB )

91906780df517579e.png

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

101

主题

4184

帖子

2

粉丝