尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

STM32 DAC到比较器内部连接配置实战:DAC1_OUT1直连COMP输入

STM32 DAC到比较器内部连接配置实战:DAC1_OUT1直连COMP输入 不知道你有没有过这种经历原理图设计得挺顺DAC输出本来是想引到板子上的比较器输入脚结果一查参考手册发现这块MCU的DAC1_OUT1和DAC1_OUT2竟然能通过内部连接直接驱动COMP1和COMP2的负输入也就是标题里说的那个INP-。当时觉得“这多省事啊内部直接连了连走线都省了”。可真上手配置的时候问题一个接一个CubeMX里找不到对应的开关、代码生成完毕之后比较器输出纹丝不动、DAC内部输出到底要不要使能也没人告诉我。这篇文章就把我折腾这个内部连接配置的完整过程写出来包含原理、寄存器/代码配置、实测验证和一堆翻车点。内容主要面向用STM32G4/G0/L4系列做模拟信号处理的开发者如果你也在配DAC到COMP的内部通路这篇应该能帮你少走不少弯路。1. 为什么DAC输出能不走引脚直接进比较器内部互连的背景1.1 一个看起来简单却折腾了两天的需求我的应用场景很简单用一个DAC输出动态阈值给比较器做门限判断。如果你也做过类似电路肯定会想DAC输出走一根外部线接到比较器输入不就行了确实可以但有三个问题第一PCB上多一根模拟走线就多一个被干扰的机会第二很多封装下DAC_OUT引脚和COMP的输入引脚未必在相邻位置绕线麻烦第三引脚资源紧张时你甚至可能希望DAC_OUT这个引脚干脆别引出来省下来做别的功能。这时候芯片内部互连的价值就体现出来了。在STM32的不少系列里DAC的输出不仅仅能接到芯片引脚还能直接接到片内其他模拟外设的输入。标题里的场景就是典型DAC1_OUT1和DAC1_OUT2分别用来驱动COMP1和COMP2的负输入不需要任何外部元器件也不需要占用外部引脚。听起来非常理想但配过之后你会发现这不是“勾个选项”就能搞定的里面藏着不少细节。1.2 芯片内部到底发生了什么外设互连的路径要理解这个配置问题先得搞清楚内部信号是怎么走的。拿DAC1_OUT1来说它其实是DAC模块内部经过缓冲器之后的一个节点。这个节点有两个去处一个是通过引脚PAD输出到外部另一个是直接送到片内的信号互连网络供比较器、运算放大器、ADC等外设使用。比较器COMP1的负输入也就是你会在STM32文档里看到的INM或者有的系列叫INP-的输入端在输入选择逻辑上有多个候选源包括外部引脚、VREF、DAC1_OUT1等。最终选择哪个源由比较器控制寄存器里对应的输入选择位决定。所以从信号路径上看这个内部连接需要满足两个前提DAC通道要真正被使能并启动。芯片不会因为你初始化了DAC外设就把模拟输出节点激活必须调用启动接口让DAC持续工作内部输出节点才有有效电平。比较器的输入源选择要指到正确的DAC通道上。如果你想让COMP1的负输入用DAC1_OUT1却在配置时写成了DAC2_OUT1那肯定是不对的。这两步缺一不可。很多人配完之后发现比较器输出没反应八成都是第一步没做好或者第二步选错了源。1.3 不同系列MCU的内部连接差异先说一个重要结论STM32不同系列对DAC到COMP内部连接的支持差异很大不能指望同一套代码到处都能用。我自己主要用G4系列G4里的COMP数量多DAC也有两个连接矩阵非常灵活。DAC1的两个输出通道可以分别连接到多个比较器的输入不只是COMP1/COMP2。G0系列同样有DAC到COMP的内部连接但比较器数量少输入源选项也少一些。L4系列也是可以支持的但寄存器布局和HAL库枚举名跟G4不一样。F3系列更老互连逻辑类似可寄存器命名和配置流程也有不少出入。这给我们的第一个经验就是看代码之前先确认好自己具体用的是哪个型号、哪个封装、哪个HAL版本。很多时候网上找的例子用的是G474或者G431你拿G070去编译HAL枚举压根对不上。不同系列的差异不光是寄存器位名称的问题连CubeMX界面上的配置入口都不一样。2. 卡住我的第一个坑CubeMX里根本找不到“内部输出”开关2.1 图形界面配置的盲区动手的时候我第一反应是打开CubeMX把DAC1和COMP1都配上。DAC这边很简单开启两个通道对齐方式选12位右对齐触发方式选None输出缓冲开启。COMP那边就有点懵了输入源配置的下拉框里我看到的选项大部分是“COMP_Input_From_IOx”这种引脚输入DAC相关的选项在特定条件下才出现。后来我发现CubeMX的界面显示是动态的。你只在DAC里启用了通道但没有把DAC配置成“内部连接到比较器”的模式时COMP输入源下拉框里可能根本不会出现DAC1_OUT1的选项。这个“内部连接模式”在旧版CubeMX里没有显式开关它完全靠你勾选DAC的具体引脚映射来决定。具体表现就是如果你在DAC配置页的“Output Buffer”区域让两个通道都输出到外部引脚DAC1_OUT1和DAC1_OUT2出现在Pinout视图里那COMP的输入源里就可能只给你引脚选项或者即使给了DAC选项生成的代码里也会出现引脚复用冲突编译时报错。反过来如果你的DAC通道没有映射到任何引脚也就是你觉得“反正内部连接嘛引脚就不引出来了吧”CubeMX又可能会直接把DAC通道当成未使用等代码生成完之后连HAL_DAC_Start都省了这又是一层坑。2.2 回到寄存器层面看问题被CubeMX卡了一段时间之后我索性打开参考手册直接从寄存器层面捋这件事。内部连接的本质是把DAC输出缓冲器后的模拟信号送到一个内部节点上关键控制位在DAC模块的某个寄存器里它决定通道输出是只走片内还是也走片外。COMP那边由比较器输入选择位决定对应HAL库的Init结构体里那个Input成员。我看到参考手册里对DAC内部连接的描述是DAC通道输出可以被连接到芯片内部的模拟外设具体配置方式取决于DAC通道是否被使能。也就是说在寄存器层面DAC内部输出的激活条件就是DAC通道使能并且处于运行状态。没有额外的“内部使能位”这是很多开发者容易误解的地方。不过有些系列确实多了一个互连相关的控制位比如需要在SYSCFG里额外配置。G4好像没有这个操作但F3系列我记得有SYSCFG_CFGR1里的某些位和模拟互连相关。所以在排查问题的时候最好先把对应系列参考手册里的“interconnection”相关章节翻出来看一遍不要只盯着COMP的寄存器。2.3 先点亮DAC还是先配置比较器初始化顺序另一个容易被忽视的点是初始化顺序。从逻辑上讲DAC和COMP的初始化谁先谁后不影响它们最终的工作状态只要在系统运行起来之后两个都启用了就行。但为了防止启动瞬间外部引脚出现不确定状态我的经验是先初始化并启动DAC再配置并启动COMP。你可能觉得这不算什么大事但我在调试时确实遇到过这样的情况先启动COMPCOMP的负输入暂时处于浮空状态DAC还没输出稳定电压正输入接的是某个传感器信号结果比较器输出直接乱跳甚至把中断触发了N次。后来改成先启动DAC等DAC输出稳定后再启动比较器问题就消失了。所以在代码里main函数顺序一般是MX_DAC1_Init(); MX_COMP1_Init(); ... HAL_DAC_Start(hdac1, DAC_CHANNEL_1); HAL_DAC_Start(hdac1, DAC_CHANNEL_2); HAL_Delay(10); // 给DAC缓冲器一点稳定时间 HAL_COMP_Start(hcomp1); HAL_COMP_Start(hcomp2);这里插一句HAL_DAC_Start本身是非常快的但模拟电路建立稳定需要一点时间尤其是DAC输出端有大电容负载哪怕是内部节点的寄生电容加个几毫秒延时没什么坏处。3. 彻底搞懂COMP输入选择位INP-的真正来源3.1 比较器输入端的复用能力现在我们专门说说比较器的输入端。在STM32的文档里比较器有两个输入端一个叫正输入一个叫负输入。不同系列对这两个输入端的命名略有差别有的叫INP和INP-有的叫INP和INM。标题里说的“COMP1 and COMP2 INP-”我理解就是指比较器的负相输入端也就是内部负输入。比较器的负输入选择通常由COMP_CSR寄存器里的某个位段控制。以G4为例COMPx_CSR里有INMSEL位段这个位段的值决定了负输入源是外部引脚还是内部信号比如DAC1_CH1、DAC1_CH2、DAC2_CH1、VREF等。同理正输入也有一个位段选择可以选引脚或DAC输出。从配置角度看难点就在于这个位段足足有3到4位可选来源非常多而且不同型号的映射表不一样。你一不小心选错调试时很难察觉因为表面上看代码没问题寄存器也写了可信号根本不是你想要的。3.2 DAC1_OUT1/OUT2与COMP1/COMP2的对应关系先说结论在G4系列中DAC1_OUT1和DAC1_OUT2具体能接到哪个比较器看的是连接矩阵里的实际存在路径。我来列一个我个人整理的简化对应关系这里以G474为例具体还要以自己型号的参考手册为准内部信号可以连接的目标DAC1_OUT1COMP1的INM、COMP2的INM、COMP3的INM、COMP4的INMDAC1_OUT2COMP1的INM、COMP2的INM、COMP3的INM、COMP4的INMDAC2_OUT1COMP5的INM、COMP6的INMDAC2_OUT2COMP5的INM、COMP6的INM也就是说DAC1_OUT1并不是只能配给COMP1它也可以配给COMP2甚至COMP3/COMP4。标题里的“DAC1_OUT1和DAC1_OUT2分别驱动COMP1和COMP2”只是其中一种很自然的用法。你要做的是在CubeMX里面明确选择“让DAC1_OUT1作为COMP1的负输入源”并且在代码里确认COMP1的INMSEL位段配置正确。如果你用HAL库COMP1初始化结构体里的Input参数应该设置成对应的枚举值比如hcomp1.Init.Input COMP_INPUT_MINUS_DAC1_CH1; hcomp2.Init.Input COMP_INPUT_MINUS_DAC1_CH2;注意这里的枚举名在不同HAL库版本里可能不太一样有的版本叫COMP_INPUT_MINUS_DAC1_CH1有的可能叫COMP_INPUT_MINUS_DAC1_OUT1。编译时如果你的头文件里找不到对应枚举优先去看看这个芯片系列的HAL头文件里COMP枚举是怎么定义的。3.3 手写寄存器/HAL配置的完整流程如果你不想完全依赖CubeMX也可以自己裸配一遍这样更能理解问题。以G4系列的HAL库为例关键步骤大概是这样的先配置DAC。DAC通道的配置核心是输出缓冲、触发、采样保持等参数。对于内部连接这种场景输出缓冲建议开启因为比较器输入端的寄生电容和漏电流虽然小但DAC输出缓冲能提供更稳定的驱动能力。DAC_ChannelConfTypeDef sConfig {0}; sConfig.DAC_SampleAndHold DAC_SAMPLEANDHOLD_DISABLE; sConfig.DAC_Trigger DAC_TRIGGER_NONE; sConfig.DAC_OutputBuffer DAC_OUTPUTBUFFER_ENABLE; HAL_DAC_ConfigChannel(hdac1, sConfig, DAC_CHANNEL_1); HAL_DAC_ConfigChannel(hdac1, sConfig, DAC_CHANNEL_2);然后是COMP。COMP配置除了输入源还有输出极性、迟滞、blanking等参数。做阈值比较时迟滞可以根据需要开能防止输出抖动但也会让翻转阈值出现一点点偏差。hcomp1.Instance COMP1; hcomp1.Init.Input COMP_INPUT_MINUS_DAC1_CH1; hcomp1.Init.Output COMP_OUTPUT_NONE; hcomp1.Init.OutputPol COMP_OUTPUTPOL_NONINVERTED; hcomp1.Init.Hysteresis COMP_HYSTERESIS_LOW; HAL_COMP_Init(hcomp1);初始化完成之后就是启动顺序的问题。DAC先启动再启动比较器。我给了一份示例就是前面那个顺序。一切正常的话比较器正输入如果是外部信号比如传感器输出它就会在那个信号穿过DAC设定阈值电平时发生翻转逻辑完全正确。4. 实测验证与常见翻车点出现异常时按这个顺序排查4.1 用内部连接驱动比较器波形为什么不对配置全部完成之后我在一个引脚上观察比较器输出发现波形完全不对。当时我的功能是做一个过零检测的变体DAC输出一个缓慢扫描的斜坡比较器正输入接一个正弦波希望比较器输出一个占空比随DAC电压变化的方波。实际看到的现象是比较器输出恒为高不管DAC值怎么变输出就是不翻。那时候我第一个反应是检查初始化代码有没有正确执行HAL_COMP_Start返回正常DAC_SetValue返回正常一切API层面的状态都正常。后来我怀疑是不是COMP的输入源没被正确指向DAC于是回去翻寄存器。通过调试器读COMP_CSR的值和参考手册对比发现INMSEL写的确实是指向DAC1_CH1的编码理论上没问题。那问题在哪问题出在DAC通道虽然初始化了但HAL_DAC_Start之后我立马就往DAC的数据寄存器里写了个0。写到0也没问题但比较器负输入变成0V正输入还是正弦波正常情况下正弦波正半周大于0V输出应该翻转才对。可我的正输入其实没接好外部信号这个时候它处于浮空状态电压不确定比较器输出当然乱跑或者卡死在一个电平上。这个“翻车”给我一个很重要的教训内部连接验证的第一步不是看最终波形而是先确认输入源本身有没有信号。我的意思是先用最简单的方式测试比较器本身是否工作比如给DAC一个固定高值比如0x0FFF再看比较器输出是否为一个明确状态然后把DAC设成0再看输出是否翻到另一个状态。如果这两步都正确说明DAC到COMP的内部连接路径是通的如果不动那才需要往互连配置方向排查。4.2 初始化顺序、DAC缓冲器、功耗与阻抗那些细节在做这个测试的时候我还特意对比了一下DAC输出缓冲开启和关闭的区别。理论上关闭DAC输出缓冲能省一点功耗适合低功耗设计但比较器输入端的等效阻抗不是无穷大内部走线也有寄生电容。当DAC带不动这些负载时实际到达比较器输入端的电压会和设定值有偏差比较器翻转阈值因此不准。实测下来开启DAC输出缓冲之后比较器翻转阈值和理论值之间的误差从最开始的30多毫伏缩小到了十毫伏以内。所以我的建议是能用缓冲就开着除非你对功耗要求极其苛刻并且能接受阈值误差。功耗方面也得提一句。开启DAC和COMP之后即使是内部连接、没有外部负载功耗也会增加几百微安级别。如果做的是电池设备建议加一个低功耗模式管理不用DAC时把DAC通道停止进入睡眠前把COMP也关掉。HAL库里有HAL_DAC_Stop和HAL_COMP_Stop别嫌麻烦。4.3 跨型号移植注意事项如果你前期用G4调通了后期想换到G0或者L4有一个非常容易踩的坑输入选择位的枚举定义和CubeMX支持情况都可能变。比如G0系列里COMP1/COMP2的负输入确实可以选DAC1_OUT1但HAL枚举名可能是COMP_INPUT_MINUS_DAC1_CH1也可能是COMP_INPUT_MINUS_DAC1_OUT1取决于HAL版本。而且G0的DAC通道数量更少只有DAC1没有DAC2直接把G4代码搬过去编译会报错。L4系列则要小心COMP的数量比较少有些型号只有COMP1和COMP2输入源矩阵中DAC1_OUT1可以进COMP1但COMP2能不能进需要看具体型号的手册不能想当然。我整理的跨系列排查问题清单如下编译报错时优先检查HAL库头文件里的COMP_INPUT枚举而不是到处改自己的代码。上电后通过调试器读COMP_CSR和DAC_CSR确认寄存器位真的写进去了有时候初始化流程被某个断言提前返回后续寄存器根本不会被写入。检查DAC通道是否真的处于使能状态HAL_DAC_Start没被调用或者被调用了又被某段代码误Stop都会导致内部输出节点无效。如果CubeMX配置方式不一致比如G4里“连接到COMP”的设置在DAC页面L4里却在COMP页面一定要在生成代码之后打开对应外设的.c文件核对初始化代码。5. 我的最终配置方案与一份可直接套用的调试流程5.1 CubeMXHAL的组合正确姿势这套配置我用CubeMX配合HAL库最终跑通了。这里说一下我实际可行的操作路径供你参考在CubeMX中DAC1启用通道1和通道2Output Buffer设置为Enable。触发设置保持None。如果你在Pinout视图里看到DAC1_OUT1和DAC1_OUT2没有出现在引脚上没关系这正是内部连接时常见的样子——它们不会映射到外部引脚。COMP1和COMP2分别启用COMP1的配置界面里Input Source选择DAC1_CH1COMP2选择DAC1_CH2。然后在Project Manager里生成代码。生成后的代码里DAC的初始化函数会生成DAC_ChannelConfTypeDef配置COMP的初始化函数会生成COMP_HandleTypeDef结构体以及对应的Input枚举。最后在main函数里按前面讲的顺序启动DAC和COMP即可。如果你用的是比我的版本更老的CubeMX可能会遇到COMP输入源下拉列表里根本没有DAC选项的情况。这时候有两个办法一个是在线升级CubeMX到较新版本另一个是生成代码后手动在COMP初始化结构体里填入正确的枚举名然后重新编译。第二个办法可行但每次重新生成代码都会被覆盖属于临时方案能升级工具链就升级。5.2 排查工具与验证方法写代码只是一半调试验证才是重头戏。我会把DAC内部连接是否正常的验证拆成三个层次。第一层静态电平验证。给DAC写入0x000记录COMP输出状态再写入0xFFF记录COMP输出状态。两次状态不同说明DAC到COMP的路径基本通了。如果两次都一样先怀疑DAC没启动再看COMP输入源选对没有。第二层动态阈值验证。DAC输出一个缓慢三角波或阶梯波COMP正输入接一个固定直流电压。这时候COMP输出在DAC电压跨越固定电压时应该翻转。记录翻转时DAC的数字编码换算成电压和直流电压源的实际值对比能算出整个链路的误差。第三层带信号验证。把正输入接上实际目标信号比如传感器输出或者正弦波DAC设为固定阈值观察COMP输出频率和信号是否一致。这一层过了说明你可以放心使用这个内部连接通路做产品功能了。调试时我用到的工具很简单一个能读寄存器/变量数值的调试器一个示波器。如果你手头没有示波器用逻辑分析仪观察比较器输出引脚的电平翻转时间也能凑合。5.3 一个偷懒但很管用的确认方法最后分享一个小技巧在怀疑内部连接没打通的时候先在DAC配置里临时把同一个通道同时映射到外部引脚然后在程序里用万用表量一下DAC引脚的实际电压确认DAC本身输出正常。确认之后再注释掉引脚映射的配置切回内部连接比较器输入端的信号应该和刚才外部引脚上量到的一样。这个方法虽然多花几分钟接线但它能帮你快速区分“DAC输出毛病”和“COMP配置毛病”省下不少猜来猜去的时间。不过要注意不是所有系列都允许DAC通道同时走内部和外部有些型号下两者是互斥的。这时你可以写两个版本的配置固件一个验证用带引脚输出一个正式用纯内部连接对比着测试。我个人的实际体验是这种“临时拉一根线出来”的做法在开发阶段很值得因为内部模拟信号节点是看不到的能用外部引脚复现出来的电压值才是最直观的调试依据。还有一个容易被忽略的细节比较器的输出引脚在CubeMX里可能会被复用成别的功能比如TIMER输入捕获、PWM刹车如果你配置了COMP输出到引脚同时又把该引脚复用给了外设比较器输出信号可能根本不会出现在你预期的引脚上。检查Pinout视图里的复用冲突是调这个问题时最容易被忽略的一步。我在实际项目中把这个配置用在了电池电压检测上DAC输出阈值COMP输出直接触发中断做欠压保护整个路径完全在片内省了一颗比较器芯片和一路PCA——这种“用内部资源换外部BOM”的思路在成本敏感的产品里是真香的。
返回列表