如果你正在 STM32G4 或者 L4 系列上做数字电源、电机控制或者信号阈值检测大概率会遇到这样一个场景想用 DAC 输出一个电压作为比较器的反相输入阈值信号从 GPIO 进入比较器正相输入比较器输出直接去触发保护逻辑。这本来是个很常规的用法难点在于 DAC 和 COMP 都在同一颗芯片里外围电路上不需要飞线但你翻开参考手册会发现内部互连的配置并不是“把两个外设都打开”那么简单。这个问题的核心就是标题里说的DAC1_OUT1 和 DAC1_OUT2 通过芯片内部连接去驱动 COMP1 和 COMP2 的 INP-。很多人第一次配这个的时候都会踩坑包括我自己。明明 DAC 引脚上电压是对的比较器输入选择也选了内部通道但输出就是不动或者根本没反应。这篇文章我把整个调试过程和原理拆开讲一遍包括寄存器怎么配、HAL 库怎么配、踩过哪些坑尽量一次说透。1. 问题背景DAC 到比较器的内部互连到底要解决什么事1.1 这个功能你用在哪先说你什么时候会用到这个内部互连。比较器的本质是一个模拟电压比较器两个输入一个正相一个反相输出就是比较结果。比如你做数字电源的过压保护采集输出电压分压后送到 COMPx 的正相输入反相输入接一个参考电压当输出电压超过参考值比较器输出翻转去触发定时器刹车或者直接拉断 PWM。这个参考电压怎么给最简单的办法是电阻分压但分压值固定想调阈值就得换电阻或者用数字电位器。更灵活的办法是用 DAC 输出一个可编程电压。DAC 是芯片自带的精度和稳定性都够软件改一个寄存器就能调整阈值这是很多人选择这条路的原因。但这里有个问题DAC 的输出引脚和 COMP 的输入引脚是两个独立的引脚。你要么在 PCB 上拉一根线把 DAC 引脚连到 COMP 引脚要么就用芯片内部已经做好的互连通道。内部互连的好处很明显省掉一个引脚和一段走线减少 PCB 面积和干扰。更重要的是内部连接路径短noise 更小而且不占用引脚资源这对于多通道的电源管理来说非常关键。1.2 这个场景适合谁来参考如果你在调 STM32G4 系列、L4 系列并且打算用 DAC 给比较器提供阈值电压这篇文章就是为你准备的。无论你是用寄存器操作还是 HAL 库无论你用的是 CubeMX 自动生成还是纯手写核心原理和坑点都是一样的。需要先说明一点不同型号的芯片DAC 和 COMP 的互连排列并不完全一样。我以 STM32G4 系列的常见配置为例DAC1 的两个输出通道DAC1_OUT1 和 DAC1_OUT2可以分别连接到 COMP1 和 COMP2 的输入。COMP1 的反相输入 INM 可以选 DAC1_OUT1COMP2 的反相输入 INM 可以选 DAC1_OUT2。正是这个功能让你可以只用一颗芯片就实现两路独立可调阈值的比较器。如果手上是其他系列思路完全一致只是具体的寄存器位、可选的输入源可能有差别。关键是掌握配置思路和排查方法遇到问题能顺着路径找。2. 核心原理从 DAC 输出到 COMP 反相输入的路径拆解2.1 DAC 输出端的内部细节很多人第一次配 DAC 就默认只要使能 DAC 通道把值写进去输出电压就自动出现在引脚上也自动进入内部互连节点。实际情况是DAC 内部的模拟输出和外部引脚之间是需要单独控制的。以 G4 系列的 DAC1 为例控制寄存器 DAC_CR 里面除了通道使能位 EN1、EN2还有输出使能位 OUT1EN、OUT2EN。ENx 是控制 DAC 内部核心电路是否工作OUTxEN 是控制 DAC 输出缓冲器是否连接到外部引脚 PA4、PA5。这两个位是独立的。所以你可以有四种组合EN1OUT0DAC 内部工作但不输出到引脚。这是纯内部互连的场景DAC 输出节点只服务于内部连接。EN1OUT1DAC 输出到引脚同时内部节点也有这个电压。这是“既给外部用也做内部比较器参考”的场景。EN0DAC 完全关闭无论 OUT 怎么设都没有输出。这个区别是很多人第一步就踩坑的地方。如果你只配了 EN11没有配 OUT1EN引脚上量不到电压这是正常的不代表配置失败。如果你的设计里根本不需要 DAC 外部引脚输出那就不用管 OUT1EN让信号直接走内部路径就好。2.2 COMP 输入端的多路选择比较器这边COMPx 的输入选择是通过 COMPx_CSR 寄存器里的 COMPx_INPSEL 和 COMPx_INMSEL 位段来控制的。INPSEL 控制正相输入INMSEL 控制反相输入。这里要提醒一个容易混淆的地方标题里写的 INP-在参考手册里多写为 INM 或者 INN代表负输入/反相输入而 INP 才是正相输入。中文里一个“负”一个“正”英文里一个是“Minus”一个是“Plus”千万别搞反。我见过不少人把 DAC 输出接到比较器反相输入的代码写成了正相输入结果比较器逻辑全反输出状态怎么也对不上。在 G4 系列里COMP1 的 INMSEL 位段可以选 GPIO 输入也可以选 DAC1_OUT1、DAC1_OUT2、内部参考电压 VREFINT 等。如果你要 DAC1_OUT1 接到 COMP1 的反相端就把 COMP1_CSR 的 COMP1_INMSEL 设为 DAC1_OUT1 对应的编码。同理COMP2 的 INMSEL 设为 DAC1_OUT2 对应的编码。这样 DAC1 的两个通道就独立驱动了两个比较器的反相输入端。注意这个“独立”DAC1_OUT1 管 COMP1 的阈值DAC1_OUT2 管 COMP2 的阈值互不干扰可以分别赋不同的电压。2.3 寄存器联动的顺序问题配置的顺序其实很有讲究。我建议的顺序是先开时钟再使能 DAC 通道给 DAC 写入目标电压值然后再配置比较器的输入选择最后使能比较器。为什么是这个顺序因为比较器在使能瞬间两个输入端的电压差会立刻决定输出状态。如果你先把比较器打开再去初始化 DAC那么在比较器刚使能的那段时间里DAC 输出可能还是 0V 或者处于未稳定状态比较器输出可能会产生一个错误跳变虽然时间极短但在保护电路里可能造成误触发。另外DAC 的输出电压不是“写寄存器瞬间就稳定”的它要经过 DAC 内核的建立时间输出缓冲器也有压摆率的限制。虽然这个时间很短微秒级但如果你在 DAC 稳定之前就使能了比较器比较器输出可能先出一个错误电平直到 DAC 稳定后才翻转回来。这种毛刺在示波器上很容易被忽略但对于硬件保护链路来说是致命的。所以规范的做法是DAC 先开、先写、先延时稳定然后配 COMP 输入选择最后打开 COMP。内核里多几行代码的事却能帮你少排查很多奇怪的问题。3. 实操配置从时钟到验证的完整步骤3.1 时钟与 GPIO 的取舍配置的第一步是开时钟。G4 系列中DAC1 挂在 APB1 总线上COMP 挂在 APB2 总线上需要分别使能对应的时钟位。这里有一个很多人忽略的好处使用内部互连时COMP 的输入不需要占用 GPIO。也就是说COMP1 的反相输入如果选了 DAC1_OUT1那么原本可以作为 COMP1 反相输入的引脚 PB0、PB1 等可以完全释放出来做其他功能比如当作普通 GPIO 或者复用为其他外设。同理DAC 输出引脚 PA4、PA5 也可以不输出或者干脆做别的功能。这一点在做多通道电源、引脚资源紧张的项目里特别有用。因为比较器的输入选择多路复用器是内部的你选了 DAC 作为输入源这些引脚就从 COMP 相关配置中脱离出来了。所以在 CubeMX 里你会看到当你把 COMP 的输入源选为内部 DAC 连接时对应的引脚不会出现在 COMP 的 PIN 配置里哪怕这个引脚本来支持 COMP 输入。在 CubeMX 中步骤是开启 DAC1分别使能通道 1 和通道 2不启用“Output Buffer”对应的引脚输出如果需要输出到引脚再勾。然后开启 COMP1 和 COMP2正相输入选择你实际要检测的信号引脚比如某个 GPIO Analog 引脚反相输入选择“Connected to DAC1_OUT1”或“Connected to DAC1_OUT2”。CubeMX 会自动把引脚配置和内部互连处理到位生成的初始化代码顺序也是正确的。3.2 DAC 通道配置HAL 库方式用 STM32CubeMX 生成工程后DAC 和 COMP 的初始化代码会自动生成。但如果你更习惯直接写代码或者需要在现有工程里手动加我贴一段 HAL 库配置要点。先看 DAC 部分DAC_ChannelConfTypeDef dacConfig {0}; // 使能 DAC 外设时钟 __HAL_RCC_DAC1_CLK_ENABLE(); // 配置 DAC 句柄 hdac1.Instance DAC1; HAL_DAC_Init(hdac1); // 通道 1 配置 dacConfig.DAC_SampleAndHold DAC_SAMPLEANDHOLD_DISABLE; dacConfig.DAC_Trigger DAC_TRIGGER_NONE; dacConfig.DAC_OutputBuffer DAC_OUTPUTBUFFER_ENABLE; HAL_DAC_ConfigChannel(hdac1, dacConfig, DAC_CHANNEL_1); // 启动通道 1写入 12 位数据电压 Vref * 2048 / 4096 HAL_DAC_Start(hdac1, DAC_CHANNEL_1); HAL_DAC_SetValue(hdac1, DAC_CHANNEL_1, DAC_ALIGN_12B_R, 2048);有两个参数要特别解释。第一个是DAC_SampleAndHold。G4 系列 DAC 支持采样保持模式可以降低功耗代价是输出纹波略大。驱动比较器阈值时我建议关掉这个模式让 DAC 以正常连续模式输出。原因很简单采样保持模式在不采样的时候是保持电容上的电压不可避免有衰减和 Drop极端情况下会影响比较器翻转点的准确性。做电源保护电路阈值的稳定性最重要省那一点功耗不值得。第二个是DAC_OutputBuffer。这个位决定 DAC 输出是否经过片内输出缓冲器。对于内部连接来说这个位一般建议保持使能。因为比较器的输入阻抗虽然高但也存在输入偏置电流如果 DAC 输出阻抗太高会在比较器输入端形成分压导致实际阈值偏离预期值。输出缓冲器的作用就是降低输出阻抗让 DAC 输出的电压更“硬”带负载能力更强。通道 2 的配置和通道 1 完全一样只是把DAC_CHANNEL_1换成DAC_CHANNEL_2句柄还是同一个hdac1。3.3 COMP 输入选择配置接下来是比较器配置。下面这段是 COMP1 反相输入接 DAC1_OUT1 的核心代码COMP_HandleTypeDef hcomp1; hcomp1.Instance COMP1; hcomp1.Init.InputPlus COMP_INPUT_PLUS_IO1; // 正相输入选择外部信号引脚 hcomp1.Init.InputMinus COMP_INPUT_MINUS_DAC1_OUT1; // 反相输入选择 DAC1_OUT1 hcomp1.Init.OutputPol COMP_OUTPUTPOL_NONINVERTED; // 输出不反相 hcomp1.Init.Hysteresis COMP_HYSTERESIS_NONE; // 先关迟滞便于调试 hcomp1.Init.BlankingSrce COMP_BLANKINGSRC_NONE; // 关闭消隐便于调试 HAL_COMP_Init(hcomp1); HAL_COMP_Start(hcomp1);关键就是InputMinus COMP_INPUT_MINUS_DAC1_OUT1。这一行在内部状态上等价于把 COMP1 的 INMSEL 寄存器位段设置为对应 DAC1_OUT1 的编码。如果你从寄存器层面直接看COMP1_CSR 里的 COMP1_INMSEL 位段已经被设成了正确的值。COMP2 的配置类似只需要改成hcomp2.Instance COMP2然后InputMinus COMP_INPUT_MINUS_DAC1_OUT2InputPlus选对应的外部信号引脚。这里有个细节值得注意HAL_COMP_Start和HAL_COMP_Init的调用顺序。HAL 库生成的代码通常是 Init 在前Start 在后。Init 里写入的是配置所以它不使能比较器Start 里面才会设置 COMPx_EN 位真正让比较器开始工作。调试的时候你可以先把 Init 做了确认寄存器配置无误再 Start 使能这样便于在比较器工作前检查 DAC 值是否已经稳定。3.4 验证与实测配置完成后怎么确认内部连接真的生效我常用的验证方法是这样的第一步先把 DAC 输出电压设为某个明确的中间值比如 12 位模式下写 2048也就是参考电压的一半如果 Vref3.3V则输出约 1.65V。第二步在正相输入端加一个可调的电压。你可以用信号发生器也可以用一个电位器分压。注意正相输入是 GPIO 模拟输入引脚先用万用表量一下输入引脚的电压。第三步用示波器或逻辑分析仪观察 COMPx 输出引脚或者是映射到某个 GPIO 的 COMPx_OUT。当你把正相输入电压从 0V 往上慢慢调经过 1.65V 附近时COMP 输出应该发生一次翻转。如果正相电压低于 DAC 电压输出是一个电平高于 DAC 电压输出变成另一个电平。翻转点对应的正相电压应该和 DAC 设定值非常接近。这个验证方法能同时验证两件事DAC 输出的电压值对不对以及比较器的输入选择、极性配置是否正确。如果一切正常说明内部连接已经生效DAC1_OUT1 确实“进入”了 COMP1 的反相输入端。对于 COMP2 完全同理把 DAC 通道 2 的值改成另一个目标电压再调整另一个正相输入引脚的电压观察 COMP2 的输出。4. 常见问题与排查技巧实录4.1 问题速查表我在调试这个功能的时候前后踩了不少坑。整理了一个速查表建议先把这张表收藏一下遇到问题直接对号入座。故障现象可能原因解决方法DAC 引脚量不到电压ENx 未置位或 OUTxEN 未置位检查 DAC_CR需要引脚输出时置位 OUTxENCOMP 输出始终不翻转COMP 输入选择配错或 DAC 值未写入检查 COMPx_INMSEL 选择源检查 DAC 值COMP 输出逻辑反了正相/反相输入选反或输出极性不对检查 INPSEL/INMSEL检查 OutputPolDAC 写入值后电压不对对齐方式或参考电压选错检查 DAC_ALIGN_12B_R 设置确认 Vref只有一路比较器工作另一路 DAC 通道未使能检查 DAC1 的 EN2 位通道 2 启动比较器输出有毛刺未关采样保持或迟滞设置不当关闭 SampleAndHold按需配置迟滞4.2 实战排查DAC 输出正常COMP 却不翻转这是我被折磨最久的一个问题。一开局我就做了个错误假设只要 DAC 在输出COMP 就一定能感知到。结果是我把 DA 读出的电压值、采样保持、输出缓冲器全部折腾一遍之后才发现问题出在比较器的 INMSEL 位段上。具体情况是这样的我用 CubeMX 把 COMP1 的 InputMinus 配成了“Connected to DAC1_OUT1”没有生成报错INMSEL 寄存器位看起来也对。但比较器输出就是不翻转。后来用示波器去量 COMP1 的正相输入引脚发现引脚上根本没有电压。原因是 CubeMX 里配置正相输入为某个模拟引脚时那个引脚对应的 GPIO 模式必须是 Analog。而我在 CubeMX 中把那个引脚同时配置成了别的外设复用功能导致引脚输入被外设占用信号根本没有进比较器。排查这类问题最有效的方法是“断链式排查”信号路径上每一个环节都验证。先看 DAC 节点电压再看 COMP 输入选择位再看正相引脚配置最后看 COMP 输出。哪个环节断了就是哪个问题。另外一个常见的问题是对寄存器位段的“想当然”。我见过有人直接在 COMP_CSR 寄存器上手动把 INMSEL 置为 3然后说“我对了”但实际查手册发现那个值 3 对应的不是 DAC1_OUT1 而是某个 GPIO 输入。不同系列的寄存器编码真的不一样L4、G4、H7 之间就有差别务必以自己芯片的参考手册为准。4.3 关于内部互连与引脚复用的坑内部互连有一个“隐形”作用一旦你把 COMP 的输入源选成 DAC 内部连接原本作为 COMP 输入的 GPIO 就可以另作他用。这是优势但同时也埋了坑。如果你的项目原本用了某个 GPIO 作为比较器输入后来改成内部互连在代码里把这个引脚的 COMP 功能关了但忘了把引脚配置成新的用途那这个引脚会处于“悬空”状态。悬空的模拟引脚很容易受外部噪声干扰如果你在同一个 PCB 上还有其他走线经过附近会有意想不到的串扰。建议凡是释放出来的引脚要么闲置时配置为 Analog 模式要么配置为明确的数字输入/输出模式绝不要让它悬空。还有一点G4 系列的 DAC1_OUT1 和 DAC1_OUT2 在芯片内部连接到了多个外设不只是比较器还有运算放大器 OPA 等。你要确认自己用到的互连路径没有与其他外设冲突。比如你把 DAC1_OUT1 同时连给了 COMP1 的 INM 和 OPA 的输入且 OPA 的输出驱动能力较强可能会在 DAC 输出节点上引起轻微的反灌影响 DAC 建立精度。虽然实际影响往往很小但做高精度模拟链路时值得留个心眼。4.4 独家排查技巧逐步扫描 DAC 输出定位翻转点除了常规的电压表法另一个很实用的调试技巧是软件控制 DAC 输出从 0 扫描到满量程同时用 GPIO 读取 COMP 输出状态。比如你把 COMP 正相输入固定在一个已知电压上比如 1.2V然后写一个 for 循环让 DAC 输出从 0 开始每个步进增加 10mV每步读取一次 COMP 输出引脚的电平。当 DAC 输出从低于 1.2V 增长到高于 1.2V 时COMP 输出会翻转。你在串口打印出翻转时对应的 DAC 值就能非常精确地确认内部连接是否工作。这个方法的优势是不需要外部电压源不需要示波器只要串口能打印就能测。尤其在调试板子还没贴片完全、示波器探头没法夹的时候很有用。如果你发现翻转点与预期 DAC 电压相差超过 30mV先别急着认为是内部连接配置问题先检查 Vref 的电压精度和 DAC 校准。DAC 本身有 offset 误差温度变化也会有影响。但如果在 10mV 以内就算正常范围。5. 扩展从单路到多路、从比较到 PWM 保护5.1 用 DAC_OUT1 和 DAC_OUT2 分别驱动 COMP1 和 COMP2把一路调通之后第二路就是水到渠成的事。DAC1_OUT1 驱动 COMP1 的 INMDAC1_OUT2 驱动 COMP2 的 INM两路阈值互不影响。这里有个设计上的好处值得说因为 DAC 通道是独立的你可以在运行过程中随时用HAL_DAC_SetValue修改任意一路的输出电压也就是动态改变比较器阈值。这在数字电源里非常实用。比如开机阶段用较低的过压阈值做保护启动完成后软件把阈值调整到正常运行范围。整个过程是连续的不会产生抖动因为 DAC 内部是真正的模拟电压输出不是 PWM 加滤波那种有纹波的方案。需要注意如果两路 DAC 通道共用一个参考电压 Vref则它们的满量程电压一致。如果你需要两路阈值有不同的满量程范围需要检查芯片是否支持 DAC 独立参考电压如果不支持可能需要外部电压参考芯片或者用分压网络转接。5.2 比较器输出触发定时器刹车形成硬件保护链内部互连的价值如果只停留在“省一根线”那就太小看它了。它真正强悍的地方在于把 DAC、COMP、定时器刹车这三者串联起来构建一条完全没有软件参与的高速保护链路。比如某路的输出电流经过采样电阻转换成电压后进入 COMP 的正相输入DAC 提供过流阈值到反相输入。COMP 输出接到定时器的刹车输入引脚。当电流超过阈值COMP 输出翻转定时器刹车输入检测到有效电平PWM 输出立即被硬件封锁。整个过程只需要几百纳秒远快于 ADC 采样加软件判断的几十微秒。配合本文讲的内部互连这条链路不需要外部 DAC 参考电压源不需要飞线完全片内完成。这在开关电源的短路保护、电机驱动的堵转保护上价值非常大。调试这条链路时有一个关键点定时器刹车输入的极性和 COMP 的输出极性要匹配。建议用示波器单次触发模式人为把 COMP 正相输入电压快速越过阈值观察 PWM 是否被迅速切断。如果发现切断有延迟优先检查 GPIO 的复用功能配置是否正确以及定时器刹车极性是否设反。从实际项目角度看我这几年用这个功能调过几个电源原型经验就一条先把 DAC 和 COMP 的模拟链路调稳定再去接定时器和 PWM。模拟链路没通之前后面所有数字逻辑的状态都不可靠。你如果按这个顺序去做整个调试过程会顺畅得多。