STM32 DAC实战:从原理到波形生成,打通数字与模拟世界
2026/8/27 7:15:06 网站建设 项目流程

1. 从数字到模拟:为什么STM32的DAC值得你花时间

如果你玩过STM32,大概率已经用它的ADC把模拟世界的电压信号变成数字量,在代码里分析处理了。但反过来呢?当你的代码算出一个结果,如何让它去驱动一个真实的物理世界设备?比如,让一个扬声器发出特定频率的声音,让一个电机平滑地转动,或者让一个LED的亮度缓缓变化。这时候,你就需要数模转换器(DAC)出场了。

STM32内部集成的DAC,对于很多嵌入式应用来说,是一个被低估的“瑞士军刀”。它不像GPIO点灯那样直观,也不像PWM调光那样常见,但它的价值在于“精确”和“连续”。PWM输出的是方波,通过调节占空比来等效模拟电压,但其本质还是数字开关,在驱动一些对噪声敏感的高精度模拟器件(如运放、某些传感器基准源)或需要生成纯净波形(如正弦波、音频)时,就显得力不从心。而DAC输出的,是真正连续变化的电压值,直接从芯片引脚上给出一个0到3.3V(或参考电压范围内)的任意电压。

我最初接触DAC是为了做一个简易的信号发生器,需要输出一个频率可调的正弦波。用PWM加低通滤波的方案,波形毛刺多,频率和幅度调整起来非常麻烦。换上STM32的DAC后,代码里直接计算正弦表并定时更新DAC的输出寄存器,一个干净漂亮的正弦波就在示波器上显示出来了,那种“所见即所得”的精准控制感,是数字方案很难比拟的。这个实验不仅让我搞懂了DAC的配置,更让我理解了数字系统与模拟世界接口的核心思想。

所以,无论你是想制作一个简易音频播放器、控制一个线性电源的电压、构建一个自动化测试的激励源,还是仅仅为了更深入地理解MCU的模拟外设,STM32的DAC实验都是一个绝佳的切入点。它连接了你的算法逻辑与真实的物理效应,是嵌入式开发中从“虚拟”走向“实体”的关键一步。

2. 核心原理:STM32 DAC是如何工作的

在动手写代码之前,我们得先搞清楚STM32内部的DAC模块到底是怎么把一堆二进制数字变成引脚上的电压的。这有助于我们理解后续配置中的各种参数,以及如何避免一些常见的性能陷阱。

2.1 DAC的基本架构与数据通路

STM32的DAC模块核心是一个“数字到模拟转换器”,但其周围配套的“基础设施”决定了它的易用性和灵活性。以常见的STM32F1/F4系列为例,其DAC模块主要包含以下几个关键部分:

  1. DAC数据寄存器 (DAC_DHRx):这是我们的代码与DAC硬件交互的主要窗口。我们想输出什么电压值,就换算成数字量,写入这个寄存器。它有多种对齐格式(右对齐12位、左对齐12位、8位右对齐),以适应不同精度的需求。
  2. DAC数据保持寄存器:这是一个内部的缓冲器。当我们写入DAC_DHRx时,数据并不会立刻进入DAC的核心转换电路,而是先暂存在这里。这种设计可以防止在转换过程中数据被意外修改,保证输出电压的稳定。
  3. DAC转换器核心:这是实现数模转换的模拟电路部分,通常采用电阻网络(如R-2R梯形网络)或电容阵列结构。它将保持寄存器中的数字量,转换成对应的模拟电压。这个转换需要一定的时间,即建立时间(Settling Time)。
  4. 输出缓冲器:这是一个运算放大器构成的缓冲级。它的作用有两个:一是增强DAC的输出驱动能力,使其能够直接驱动一定的外部负载(如10kΩ到50kΩ的电阻);二是提供低输出阻抗,减少外部电路对DAC输出电压的影响。需要注意的是,这个缓冲器是可以被软件关闭的。关闭后,DAC的输出阻抗会变高,驱动能力变弱,但可以改善建立时间和功耗,在驱动高阻抗负载(如运放的同相输入端)时可以考虑关闭。

数据通路是这样的:CPU或DMA将数据写入DAC_DHRx-> 数据被锁存到数据保持寄存器-> 在指定的触发事件(或软件触发)到来时,保持寄存器的数据被加载到DAC转换器核心-> 转换器开始工作,经过一个建立时间后,模拟电压稳定出现在输出引脚上,并经过输出缓冲器后输出到芯片外部。

理解这个通路非常重要。比如,当你发现输出电压更新不及时,就要考虑是数据写入慢了,还是触发事件频率不对,或者是输出缓冲器驱动能力不足导致外部负载影响了响应速度。

2.2 触发机制:谁来决定DAC何时更新

DAC不会自动、连续地转换数据寄存器里的值。它需要一个“开始转换”的信号,这就是触发。STM32的DAC支持多种触发源:

  • 软件触发:通过设置控制寄存器中的TENx位为0,然后向SWTRIG位写1来手动触发一次转换。这种方式最简单,但时序完全由软件控制,难以保证精确的周期性。
  • 定时器触发:这是最常用、最强大的方式。你可以将一个定时器(如TIM2, TIM4, TIM6, TIM7等)的更新事件(UEV)连接到DAC的触发输入。这样,DAC的更新频率就由定时器的溢出频率精确控制。例如,定时器每1ms溢出一次,DAC就会每1ms更新一次输出电压。这对于生成任意波形(正弦波、三角波)至关重要。
  • 外部引脚触发:某些型号支持通过外部EXTI线(如EXTI9)触发。适用于需要与外部事件同步的场景。

关键点在于:触发事件发生时,DAC会将DAC_DHRx寄存器中的数据加载到其内部的保持寄存器中,并启动一次新的数模转换。因此,你必须在下次触发到来之前,将新的数据写入DAC_DHRx。如果使用定时器触发,通常我们会配合DMA,在定时器中断或DMA传输完成中断中准备下一批数据,或者直接让DMA自动从内存搬运数据到DAC_DHRx,从而实现不间断的波形输出。

2.3 分辨率与参考电压:你的输出精度天花板

DAC的性能有两个硬性指标:分辨率参考电压

  • 分辨率:STM32的DAC通常是12位的。这意味着DAC_DHRx寄存器可以存放0到4095(2^12 - 1)之间的数字。这个数字我们称为DigitalValue
  • 参考电压 (Vref+):这是DAC转换的基准。DAC输出的模拟电压Vout由公式决定:Vout = (DigitalValue / 4095) * Vref+对于大多数STM32,Vref+引脚通常与VDDA(模拟电源)相连,典型值为3.3V。因此,当DigitalValue为4095时,Vout约为3.3V;为0时,Vout为0V。有些型号有独立的Vref+引脚,可以接入更精准、更稳定的基准电压源(如2.5V的REF系列芯片),从而获得更高精度的输出电压。

一个常见的误解:认为12位DAC的最小电压步进(LSB)是3.3V / 4095 ≈ 0.806mV。理论上是的,但实际上由于DAC的积分非线性(INL)、微分非线性(DNL)误差以及参考电压的噪声,实际精度会打折扣。在要求不高的场合,我们可以按此计算;在高精度应用中,必须查阅芯片数据手册的电气特性章节,了解其实际精度指标,并做好PCB的模拟部分布局布线(如将VDDA、VSSA与数字电源分开,增加滤波电容)。

3. 硬件连接与工程准备

理论清楚了,我们开始动手。一个完整的DAC实验,硬件连接是基础,它直接决定了信号的质量。

3.1 最小系统与引脚确认

首先,你需要一块STM32开发板(如STM32F103C8T6核心板、STM32F407 Discovery等)。找到芯片数据手册或开发板原理图,确认DAC输出引脚。以STM32F103C8T6为例:

  • DAC通道1输出:PA4
  • DAC通道2输出:PA5

这两个引脚是复用功能,除了作为DAC输出,还可以作为普通GPIO或ADC输入。在我们的实验中,需要将其配置为DAC模拟输出模式。

硬件连接清单

  1. STM32开发板及USB数据线(用于供电和程序下载)。
  2. 示波器:这是观察DAC输出波形最直观、最必要的工具。如果没有示波器,也可以用万用表测量直流电压,但无法观察动态波形。
  3. 杜邦线若干。
  4. (可选)一个运算放大器(如LM358)、电阻、电容,用于搭建一个简单的放大或滤波电路,验证DAC驱动外部电路的能力。

3.2 开发环境与工程创建

这里以使用最广泛的STM32CubeIDE(基于HAL库)为例进行说明。如果你习惯标准库或LL库,原理相通,只是函数名称和配置方式略有差异。

  1. 新建工程:打开STM32CubeIDE,选择对应的芯片型号(如STM32F103C8Tx)。

  2. 图形化配置 (CubeMX)

    • 引脚配置:在Pinout & Configuration标签页,找到PA4引脚。点击它,在弹出的功能选择菜单中选择ADC1_IN4?等等,这里有个坑!对于DAC输出,正确的选择是DAC_OUT1。同样,将PA5配置为DAC_OUT2。配置成功后,引脚会变成绿色。
    • DAC模块配置:在左侧分类中找到Analog->DAC1
      • 勾选Channel 1和/或Channel 2
      • Output Buffer:选择Enable。初次实验建议开启缓冲,驱动能力强。后续可以尝试关闭,观察波形变化。
      • Trigger:选择None。我们先从最简单的“无触发”模式开始,即DAC输出由软件直接控制,写入数据后立即更新(实际上仍有内部延迟)。后续生成波形时再改为Timer 6 Trigger Out event之类的定时器触发。
      • Wave generation mode:选择None。波形生成模式是DAC的一个高级功能,可以自动生成噪声波或三角波,我们先不用。
    • (为后续实验准备)定时器配置:如果我们计划用定时器触发DAC生成波形,可以提前配置一个定时器。例如,配置TIM6(一个基本定时器,常专用于DAC触发)。
      • Timers->TIM6中,使能它。
      • 设置Prescaler(预分频器)和Counter Period(自动重装载值ARR),以得到所需的触发频率。例如,如果系统时钟是72MHz,预分频器设为71,ARR设为999,则定时器更新频率为:72MHz / (71+1) / (999+1) = 1000Hz,即每秒触发DAC 1000次。
      • 最关键的一步:在Trigger Output (TRGO) Parameters部分,将Master Mode Selection设置为Update Event。这样,TIM6的每次更新事件就会在其TRGO引脚上产生一个触发信号,这个信号可以被DAC捕获。
    • 回到DAC配置:将DAC的Trigger改为Timer 6 Trigger Out event。这样,DAC的更新就与TIM6的更新同步了。
    • (可选)DMA配置:如果要输出一段连续的波形数据(如一个正弦波表),为了不占用CPU,可以配置DMA。在DAC1DMA Settings标签页,点击Add,选择DAC1Stream通常为固定的(如DMA1_Stream5 for DAC1 on F4),方向为Memory-To-Peripheral,模式为Circular(循环模式)。这样,DMA会自动循环地将内存中的波形数据搬运到DAC数据寄存器。
  3. 生成代码:配置完成后,点击Project -> Generate Code。CubeIDE会自动生成初始化代码,包括MX_DAC_Init()MX_TIM6_Init()等函数。

4. 基础实验:输出固定电压与三角波

我们从最简单的开始,验证DAC的基本功能。

4.1 实验一:软件控制输出固定电压

这个实验的目标是:通过软件写入一个数字值,让PA4引脚输出一个固定的直流电压,并用万用表测量验证。

核心代码(main.c中的while循环之前)

// DAC已经由CubeMX初始化好了,缓冲开启,无触发模式。 // 我们直接使用HAL库函数设置输出电压。 uint16_t dac_value = 2048; // 12位数据,中间值 2048 / 4095 * 3.3V ≈ 1.65V if (HAL_DAC_SetValue(&hdac1, DAC_CHANNEL_1, DAC_ALIGN_12B_R, dac_value) != HAL_OK) { Error_Handler(); } // 对于无触发模式,设置值后需要启动DAC转换(对于有些HAL版本,SetValue已包含启动) if (HAL_DAC_Start(&hdac1, DAC_CHANNEL_1) != HAL_OK) { Error_Handler(); }

将程序下载到开发板,用万用表测量PA4(或连接了PA4的排针)与GND之间的电压。你应该能测量到大约1.65V的电压。尝试修改dac_value为0、1024、4095,分别测量电压,应该接近0V、0.825V、3.3V。

注意:实测电压可能与计算值有微小偏差,这主要是参考电压(VDDA)并非精确的3.300V以及DAC自身误差导致的。这是正常现象。

4.2 实验二:生成一个周期性的三角波

现在我们来点动态的。不使用定时器触发,而是在主循环里不断修改DAC值,模拟一个三角波。这种方法简单,但波形频率不稳定,受循环内其他代码影响。

核心思路

  1. 定义一个变量dac_value和一个方向标志direction(递增或递减)。
  2. 在主循环while(1)中,根据方向增加或减少dac_value
  3. 每次变化后,调用HAL_DAC_SetValue更新输出。
  4. 加入一个简单的HAL_Delay来控制更新速度(但这很不精确)。

核心代码片段

uint16_t dac_value = 0; int8_t direction = 1; // 1表示增加,-1表示减少 HAL_DAC_Start(&hdac1, DAC_CHANNEL_1); while (1) { // 更新DAC值 if (HAL_DAC_SetValue(&hdac1, DAC_CHANNEL_1, DAC_ALIGN_12B_R, dac_value) != HAL_OK) { Error_Handler(); } // 更新下一个值 dac_value += direction; if (dac_value >= 4095) { dac_value = 4095; direction = -1; } else if (dac_value <= 0) { dac_value = 0; direction = 1; } // 延迟控制频率(粗略) HAL_Delay(1); // 每1ms更新一次,理论三角波周期约为 4096*2ms ≈ 8.2秒 }

用示波器探头连接到PA4,你应该能看到一个阶梯状的三角波。因为每次更新电压后,电压会保持1ms,所以波形是阶梯状的,而非光滑直线。同时,由于HAL_Delay本身精度和循环内其他潜在开销,波形的周期和占空比可能不精确。

5. 进阶实战:定时器触发与DMA输出精确波形

要产生稳定、高频、光滑的波形,必须将DAC的更新从不可控的主循环中解放出来,交给硬件定时器和DMA。

5.1 配置定时器触发

按照第3.2节的说明,在CubeMX中配置TIM6,并让DAC1通道1使用TIM6的更新事件作为触发源。生成代码后,在main函数中启动定时器。

关键代码

// 启动定时器,它将周期性地产生触发信号 if (HAL_TIM_Base_Start(&htim6) != HAL_OK) { Error_Handler(); } // 启动DAC,并指定其触发源 if (HAL_DAC_Start(&hdac1, DAC_CHANNEL_1) != HAL_OK) { Error_Handler(); } // 注意:此时DAC不会立即输出,它等待第一次定时器触发

此时,如果你还没有给DAC数据寄存器写入数据,DAC输出可能为0或不确定。DAC会在每次定时器触发时,将当前DAC_DHRx寄存器中的值转换输出。所以,我们需要在触发之前就把数据准备好。

5.2 配合DMA输出正弦波

这是DAC应用的经典场景。我们预计算一个正弦波表(数组)存放在内存中,然后让DMA在定时器的每次触发时,自动将数组中的下一个值搬运到DAC的数据寄存器。

步骤详解

  1. 生成正弦波表

    #define SINE_WAVE_TABLE_SIZE 100 // 一个周期的点数 uint16_t sineWaveTable[SINE_WAVE_TABLE_SIZE]; void generate_sine_wave_table(void) { for (int i = 0; i < SINE_WAVE_TABLE_SIZE; i++) { // 计算正弦值,范围[-1, 1] float sine_value = sinf(2 * M_PI * i / SINE_WAVE_TABLE_SIZE); // 将[-1,1]映射到[0, 4095],并偏移到中间值附近,避免负电压(单电源供电) // 例如,输出以1.65V为中心,幅值1V的正弦波: Vout = 1.65 + 1.0 * sin(...) // DigitalValue = (Vout / Vref) * 4095 // 假设Vref=3.3V,则中心值对应 1.65/3.3*4095 ≈ 2048 // 幅值1V对应 1.0/3.3*4095 ≈ 1241 float voltage = 1.65f + 1.0f * sine_value; // 电压值 if (voltage < 0) voltage = 0; if (voltage > 3.3) voltage = 3.3; sineWaveTable[i] = (uint16_t)((voltage / 3.3f) * 4095); } }

    main函数初始化部分调用这个函数。

  2. 配置并启动DMA: 在CubeMX中已为DAC1添加了DMA流。生成代码后,我们需要手动启动DMA传输。

    // 启动DAC的DMA传输 if (HAL_DAC_Start_DMA(&hdac1, DAC_CHANNEL_1, (uint32_t*)sineWaveTable, SINE_WAVE_TABLE_SIZE, DAC_ALIGN_12B_R) != HAL_OK) { Error_Handler(); } // 启动定时器 if (HAL_TIM_Base_Start(&htim6) != HAL_OK) { Error_Handler(); }

    HAL_DAC_Start_DMA函数会做几件事:配置DMA,将sineWaveTable的地址作为源地址,DAC的数据寄存器地址作为目标地址,设置数据长度和对齐方式,并启动DMA循环传输。

  3. 计算与验证波形频率

    • 定时器触发频率 (Ft):由TIM6的配置决定。例如,系统时钟72MHz,预分频器71,ARR为999,则Ft = 72MHz / (71+1) / (999+1) = 1000 Hz
    • DAC更新频率:等于定时器触发频率,即每秒更新1000次。
    • 输出正弦波频率 (Fout)Fout = Ft / SINE_WAVE_TABLE_SIZE = 1000 Hz / 100 = 10 Hz。 所以,我们将在示波器上看到一个10Hz的正弦波。要改变输出频率,可以调整定时器的分频和重载值(改变Ft),或者调整正弦波表的点数(改变一个周期的采样数)。

示波器实测:将探头连接到PA4,你应该能看到一个非常光滑、稳定的10Hz正弦波。尝试改变SINE_WAVE_TABLE_SIZE或定时器参数,观察波形频率的变化。你会发现,通过这种方法,可以轻松生成数百Hz甚至更高频率的波形(受限于DAC的建立时间和DMA速度)。

5.3 动态更新波形:双缓冲技巧

上面的例子是静态波形表。如果我们想实时改变波形(比如改变频率、幅度,甚至切换成方波、三角波),怎么办?直接在DMA传输过程中修改sineWaveTable数组是危险的,可能导致输出波形出现毛刺或断裂。

一个实用的技巧是使用双缓冲(Ping-Pong Buffer)。原理是准备两个一样大的缓冲区(BufferA和BufferB)。当DMA正在从BufferA中读取数据并发送给DAC时,CPU可以安全地准备下一帧数据到BufferB。当DMA完成BufferA的传输后,会产生一个传输完成中断(或半传输完成中断),在中断服务程序里,我们迅速将DMA的目标地址切换到BufferB,同时CPU开始填充BufferA。如此循环。

HAL库的DMA循环模式本身并不直接支持双缓冲,但我们可以利用DMA的“半传输完成中断”(HT)和“传输完成中断”(TC)来模拟。或者,更直接的方法是使用更底层的寄存器操作或LL库来配置双缓冲模式。对于STM32F4等系列,DMA控制器本身支持双缓冲(DMA_CR寄存器的DBM位)。

简化实现思路(利用半传输和传输完成中断)

  1. 使能DMA流的“半传输中断”和“传输完成中断”。
  2. 在CubeMX生成的stm32f4xx_it.c中,找到对应的DMA中断服务函数(如DMA1_Stream5_IRQHandler)。
  3. 在中断函数中判断中断标志:
    void DMA1_Stream5_IRQHandler(void) { if(__HAL_DMA_GET_FLAG(&hdma_dac1, DMA_FLAG_HTIF1_5)) { // 半传输完成 // 此时前半部分(Buffer前半)已传输完,后半部分正在传输 // CPU可以安全地修改前半部分缓冲区数据 __HAL_DMA_CLEAR_FLAG(&hdma_dac1, DMA_FLAG_HTIF1_5); } if(__HAL_DMA_GET_FLAG(&hdma_dac1, DMA_FLAG_TCIF1_5)) { // 传输完成 // 此时整个缓冲区(Buffer)已传输完,DMA将回到开头重新开始 // CPU可以安全地修改整个缓冲区数据(但时间较短) __HAL_DMA_CLEAR_FLAG(&hdma_dac1, DMA_FLAG_TCIF1_5); } // ... 调用HAL库的中断处理函数 }
    这种方法对CPU中断响应时间要求较高,如果波形数据点很多、更新频率很快,CPU可能会来不及准备新数据。对于复杂的实时波形生成,需要考虑使用更大的缓冲区、降低波形更新率,或者使用更强大的芯片(如带DAC的DSP或高性能MCU)。

6. 性能优化与常见问题排查

在实际项目中,使用DAC可能会遇到各种问题。下面分享一些我踩过的坑和优化经验。

6.1 输出噪声与纹波抑制

即使代码正确,用示波器观察DAC输出,也可能看到一些你不想要的“毛刺”或高频噪声。

  • 电源噪声:这是最主要的噪声来源。DAC的参考电压Vref+(通常连着VDDA)如果不够干净,会直接反映在输出上。
    • 对策:确保模拟电源VDDA和数字电源VDD之间使用磁珠或0Ω电阻隔离。在VDDA和VSSA引脚附近,尽可能靠近芯片放置一个10uF的钽电容和一个0.1uF的陶瓷电容进行滤波。如果板子有独立的Vref+引脚,强烈建议连接一个低噪声、高精度的基准电压源芯片。
  • 数字开关噪声:当MCU内部高速数字电路(如CPU、总线、其他外设)工作时,会产生高频噪声,通过电源或地平面耦合到敏感的模拟部分。
    • 对策:良好的PCB布局布线是关键。将模拟部分(DAC输出、运放、滤波电路)集中放置,并尽量远离数字噪声源(如晶振、开关电源、高速数据线)。使用独立的模拟地平面,并通过单点与数字地连接。
  • 输出缓冲器的影响:开启输出缓冲器可以提供更强的驱动能力,但缓冲器本身会引入一定的噪声和失调电压。关闭缓冲器可以获得更纯净的输出,但驱动能力骤降,只能驱动高阻抗负载。
    • 对策:根据负载情况选择。驱动运放同相输入端(高阻抗)可以关闭缓冲器。驱动直接接负载(如耳机、低阻值电阻)必须开启。可以用示波器对比开关缓冲器时的输出噪声,选择更优方案。
  • 软件引起的毛刺:如果在DAC转换过程中(两次触发之间)错误地写入了数据寄存器,会导致输出出现一个短暂的错误电压。
    • 对策:确保数据更新时序正确。使用DMA是避免此问题的最佳方法。如果使用中断更新,确保在DAC触发事件发生前足够早的时间完成数据写入。

6.2 建立时间与输出带宽限制

DAC不是理想器件,从数字量写入到输出电压稳定到目标值(误差范围内)需要时间,这就是建立时间。STM32 DAC的建立时间在数据手册中有明确给出,通常在几微秒量级(与负载有关)。

  • 影响:这决定了DAC能够输出的最高信号频率。根据奈奎斯特采样定理,输出信号的最高频率分量必须小于采样频率(即DAC更新频率)的一半。但实际上,由于建立时间的限制,即使更新频率很高,DAC也无法完美重建高频信号,输出波形会变得“圆滑”,高频分量衰减。
  • 实测建议:当你试图输出一个很高频率的正弦波(比如更新频率1MHz,输出10kHz正弦波),发现波形幅度下降或失真时,很可能是受到了DAC建立时间和输出缓冲器带宽的限制。此时需要降低期望的输出频率,或者查阅芯片手册,确认在当前配置下的最大可达到的无杂散动态范围(SFDR)和带宽。

6.3 调试技巧与示波器使用

  1. 无输出或输出为0

    • 检查引脚配置:首先确认CubeMX中引脚是否已正确设置为DAC_OUTx。用万用表测量引脚对地电阻,如果被配置为推挽输出且输出低,电阻会很低。
    • 检查DAC和定时器是否启动:确保调用了HAL_DAC_Start()HAL_TIM_Base_Start()
    • 检查触发模式:如果使用了定时器触发,但定时器没启动,DAC会一直等待触发,没有输出。可以先切换回软件触发或无触发模式验证DAC基本功能。
    • 检查数据写入:使用调试器,在HAL_DAC_SetValue函数后设置断点,查看传入的dac_value是否正确,以及函数返回值是否为HAL_OK
  2. 输出波形频率不对

    • 计算定时器频率:仔细核对系统时钟、定时器预分频器(PSC)和自动重载值(ARR)的计算。记住,定时器频率 =系统时钟 / (PSC+1) / (ARR+1)
    • 检查DMA传输大小:确认HAL_DAC_Start_DMA中传入的数据长度Length是否正确。它应该等于波形表的点数,而不是字节数。
    • 示波器测量:直接用示波器测量DAC输出波形的周期,反推频率。同时,可以测量定时器触发引脚(如果有引出)或另一个GPIO(在定时器更新中断中翻转)的频率,来验证定时器是否按预期工作。
  3. 波形有台阶或毛刺

    • 台阶:这是正常的,因为DAC输出是离散的。提高一个周期内的采样点数(波形表大小),台阶会变细,经过输出缓冲器和外部电路的低通滤波后,波形会更光滑。
    • 周期性毛刺:可能与电源噪声同步,检查电源质量。也可能与CPU活动周期同步,尝试在DAC输出期间关闭其他高优先级中断或外设。
    • 随机毛刺:检查PCB布局,是否存在信号串扰。确保模拟地线干净。

通过以上这些步骤,你应该能够驾驭STM32的DAC,让它成为你项目中连接数字与模拟世界的可靠桥梁。从输出一个稳定的直流电压,到生成复杂的任意波形,DAC的应用场景非常广泛。关键在于理解其工作原理,合理配置触发和DMA,并注意模拟电路设计的细节。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询