尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

STM32C542 DAC固定电压输出实战:原理、代码与硬件设计

STM32C542 DAC固定电压输出实战:原理、代码与硬件设计 嵌入式开发里真正天天不离手的外设排第一是GPIO第二我投DAC一票。手里这块STM32C542的板子前阵要往外部模拟前端送一路固定电压做偏置最初图省事用LDO分压结果负载稍微挂重点电压就掉后来把片内DAC跑起来做固定电压输出电压稳了、纹波也小实测效果比预期利索得多。这篇就围绕STM32C542的DAC固定电压输出把原理、代码、实测和板级设计经验一次讲透。不管你是刚摸到STM32C系列的新手还是想快速把DAC用得稳的老手这篇都值得收藏。1. 项目概述为什么要折腾DAC固定电压输出1.1 STM32C542的DAC外设能干什么先明确一点STM32C542的DAC是片内集成的数模转换器分辨率12位输出范围基本是从0V到参考电压VREF。也就是说你给它一个数字码值它就能输出一个对应的模拟电压值。相比外挂DAC芯片片内DAC最大的优势就是省事不用管SPI/I2C时序不占额外引脚代码写两行就能出一个模拟信号。这个DAC模块通常会有单独的输出缓冲器开关还支持多种触发方式比如软件触发、定时器触发、外部事件触发。对于固定电压输出这个场景我们只需要最基础的功能软件触发一次然后持续输出电压即可。如果你用的是同一系列里带更多通道的型号配置逻辑也大同小异无非是多加一路Channel_2的设置。1.2 为什么需要单片机输出一路固定电压很多人第一反应是要固定电压直接上LDO或者稳压管不就完了这话对也不对。LDO分压适合纯供电场景比如给传感器供电但当你需要的是给模拟前端提供偏置电压、给比较器提供阈值电压、给增益放大器调零点或者做校准用的基准源时电阻分压方案的缺点就暴露了——负载一旦变化分压点电压跟着飘温度漂移也不可控调试一次就要动一次螺丝刀。片内DAC做固定电压输出的优点正好补上这些短板一方面输出电压完全由数字码决定改输出只需改一个变量代码可控、可在运行中调整另一方面DAC输出不依赖负载电流的分压关系在合理负载范围内电压几乎不受影响。这就是我最终选择DAC方案的根本原因不是因为它最便宜而是因为它可调、可复现、不飘。1.3 项目硬件准备清单这次演示用到的硬件不复杂基本也算嵌入式调试的标配一块STM32C542最小系统板或者自研板子板上引出了DAC输出引脚。ST-Link或者J-Link调试器用于下载和在线调试。一块数字万用表要求也不高三位半就够用来量固定电压。示波器最好备一台没有的话也可以先靠万用表顶着但示波器能帮你看纹波和噪声排查问题时差距很大。杜邦线或者探针若干注意测量点尽量靠近DAC引脚根部避免线束干扰。硬件准备到这里就差不多了下面先把DAC工作机理理顺后面配置才不会一头雾水。2. DAC工作原理与关键参数计算2.1 12位DAC输出电压的机理DAC内部本质是一个由电阻网络或者电容网络构成的数模转换阵列12位分辨率意味着它可以把参考电压VREF切成 (2^{12}4096) 个档位。输出寄存器的码值每增加1输出电压就增加一个最小步进也就是1个LSB。这个LSB电压计算公式非常关键[ 1,\text{LSB} \frac{V_{\text{REF}}}{4096} ]如果你把VREF接在3.3V上那么一个LSB大约就是 (3.3 / 4096 \approx 0.8057\text{mV})。换句话说DAC输出并不是连续的而是一格一格往上跳的微小台阶只是4096个台阶足够密大多数场景下视觉上和测量上都接近连续电压。这里有个容易踩的小误区计算码值时分母到底用4096还是4095。从满量程映射看数据寄存器最大码值是4095对应输出接近VREF而从电压分度看一个LSB是VREF/4096。实际公式可以用[ \text{DAC_Code} \frac{V_{\text{out}}}{V_{\text{REF}}} \times 4095 ]这样保证输出VREF时码值正好是4095不会溢出。两种算法差得不多但写代码时我建议统一用4095省得满量程时偶尔出现寄存器封顶导致的非线性。2.2 固定电压值的计算实例比如我希望DAC输出1.65V正好是3.3V的一半假设VREF引脚接的就是3.3V那么计算过程是这样的[ \text{DAC_Code} \frac{1.65}{3.3} \times 4095 \approx 2047.5 ]取整后我一般直接填2048。理论输出就是 (2048 / 4095 \times 3.3 \approx 1.6503\text{V})实际误差不到0.3mV完全在可接受范围内。同理如果要做成0到3.3V任意电压代我下面这个表格就能快速拿到码值调试时对照查表很方便。目标电压/VREF3.3V理论码值取整实际输出约值0V00V0.825V10240.8251V1.65V20481.6503V2.475V30722.4752V3.3V40953.3V还要提醒一句如果VREF不是直接接3.3V而是用外部基准源、或者接在VDDA上那所有计算都必须用VREF的实际电压不能想当然用3.3V。之前有朋友把VREF接了个2.5V基准代码里却按3.3V算码值输出直接偏了30%左右查了半天才找到原因。2.3 输出缓冲器开还是关STM32C542的DAC模块内部有一个输出缓冲器用途是降低DAC输出的等效阻抗。打个比方不开启缓冲时DAC输出端就像一根细水管水流有限负载重一点压力就掉开启缓冲后相当于接了一个内置功放输出阻抗降低不少能推动更大一点的负载。但缓冲器不是白开的缺点也有输出摆幅受限通常无法满摆幅到VREF高电压端会有一定饱和压降缓冲器自身有一定的offset和噪声对高精度场景反而可能不如直通。因此我的建议是做固定电压输出、后端接的输入阻抗是几十千欧级别时开启缓冲器更稳如果是做精密基准源并且后级是高阻抗负载可以考虑关掉缓冲配合运放跟随实际效果需要实测对比。大多数场景默认开启Buffer就对了。3. CubeMX配置与HAL驱动代码实现3.1 CubeMX里DAC的初始化配置打开CubeMX选中STM32C542芯片后在左侧外设列表里找到DAC1启用Output Channel 1。启用之后右侧芯片图会多出DAC_OUT1引脚确认它连到硬件上预留的输出口。然后切到Parameter Settings重点看这几项推荐配置也一并列出来参数项推荐值说明Output BufferEnable降低输出阻抗固定电压推荐开启TriggerNone软件触发固定输出用软件触发最简单Sample And HoldDisable固定电压不需要采样保持功能User TrimmingFactory使用出厂校准值Trigger选None后后续用HAL_DAC_Start启动等于是软件触发方式启动输出。如果以后要做动态波形再改成定时器触发也不迟。这里要特别提醒一个细节CubeMX生成的初始化代码MX_DAC1_Init会自动在main函数的外设初始化区域被调用但如果你的工程是在老版本HAL库上手动改的要检查一下是否有调用这个Init函数。漏掉Init直接Start大概率看不到输出。3.2 生成工程后的关键代码启动与赋值CubeMX生成工程后DAC的初始化代码基本不用大改。下面这段是它在dac.c里初始化DAC通道的关键内容static void MX_DAC1_Init(void) { DAC_ChannelConfTypeDef sConfig {0}; hdac1.Instance DAC1; if (HAL_DAC_Init(hdac1) ! HAL_OK) { Error_Handler(); } sConfig.DAC_SampleAndHold DAC_SAMPLEANDHOLD_DISABLE; sConfig.DAC_Trigger DAC_TRIGGER_SOFTWARE; sConfig.DAC_OutputBuffer DAC_OUTPUTBUFFER_ENABLE; sConfig.DAC_ConnectOnChipPeripheral DAC_CHIPCONNECT_DISABLE; sConfig.DAC_UserTrimming DAC_TRIMMING_FACTORY; if (HAL_DAC_ConfigChannel(hdac1, sConfig, DAC_CHANNEL_1) ! HAL_OK) { Error_Handler(); } }这段代码大部分是CubeMX自动生成的一般不用改。真正要用的是下面三个HAL接口我这里组装了一个最简的固定电压输出函数void dac_output_fixed_voltage(uint32_t code_value) { HAL_DAC_Start(hdac1, DAC_CHANNEL_1); HAL_DAC_SetValue(hdac1, DAC_CHANNEL_1, DAC_ALIGN_12B_R, code_value); }调用方式也直白想在DAC输出1.65V就用前面算好的2048dac_output_fixed_voltage(2048);先Start再SetValue顺序不要搞反。如果先SetValue后Start某些库版本和芯片状态下输出可能不会生效容易踩坑。3.3 避免堵在路上的几个代码细节上手时最容易碰到的几个代码层面问题我在这先提前打预防针HAL_DAC_SetValue的第三个参数DAC_ALIGN_12B_R表示12位右对齐这个别动除非你明确知道自己在做左对齐。如果后续要做多通道那么DAC_CHANNEL_1和DAC_CHANNEL_2都要分别Start。若在中断或RTOS任务里频繁更新DAC值要留意临界保护否则偶发出现输出的码值跳变。有些全系列HAL库在SetValue前还要求你先HAL_DAC_EnableDMA之类的操作但固定电压输出不需要开DMA开了反而多一种可能的配置冲突。这几点都是实际调试中肉眼见过的坑尤其是顺序问题别看它小足以让你白查一天。4. 硬件设计电源噪声与时钟抖动对DAC输出的影响4.1 固定电压也需要关注噪声吗DAC固定输出看起来是静态信号实际上芯片内部的参考电压、供电网络、数字开关噪声都可能在输出端留下痕迹。很多朋友觉得静态电压反正不变化噪声无所谓这是个误解。你用示波器交流档看DAC输出引脚往往能看到几十毫伏的纹波这就是电源和参考电压部分耦合过来的。我这次实测时就发现板上某一路DCDC开关管靠近DAC输出引脚输出端纹波明显偏大后来把去耦电容补齐、调整了走线纹波一下就下来了。固定电压不是不关注噪声而是很多时候你根本没拿示波器去量过它。模拟电路这块量过才有发言权。4.2 规避时钟抖动与电源噪声的3个PCB布局要点这一节完全是实践经验的浓缩网上零零散散的内容很多我提炼成3个必须守住的要点。要点一参考电压和模拟电源单独走星形布线DAC的输出精度和VREF直接相关VREF电压抖一下输出就跟着抖。布局上VREF的去耦电容要紧贴引脚放置容量选择0.1uF和1uF并联组合并且走线单独从电源源头拉过来不要先绕去数字电路再拐回来。这就像给钢琴调音师单独留了一间安静的房间旁边的人再闹音准也不会被带偏。要点二模拟地与数字地明确分区单点汇合如果PCB面积允许把DAC所在区域划成模拟地和数字地之间通过磁珠或者0欧电阻单点连接。特别注意DAC输出引脚、VREF去耦电容的地一定要回到模拟地不能混进数字回流路径。地平面上的噪声是模拟电路最大的暗坑之一DAC输出采样点要对齐信号地避免地弹直接抬高输出电压。要点三时钟和触发信号远离DAC输出走线STM32C542的DAC如果后续要接定时器触发做波形输出触发信号的时钟抖动会直接影响输出刷新节点的稳定性。PCB布局上时钟线、PWM线不要和DAC输出走线平行长距离并走最好用地线包一圈做隔离同时DAC输出走线尽量短粗减少寄生电容。对固定电压输出来说这一点的直接收益是输出纹波和周遭数字开关噪声明显降低。这三个要点本质上都是在回答同一个问题让干净的模拟信号不被暴躁的数字世界干扰。DAC输出是芯片为数不多直接面对外部模拟世界的接口布局上多花十分钟省下调试三天。4.3 输出端滤波电容的取舍既然输出端是固定电压很多人第一反应是加一个RC低通滤波。这个思路没错但要注意电阻不能取太大否则DAC缓冲器驱动不了后面的等效负载输出会塌。我的经验是如果DAC后面直接接MCU的ADC或者高阻抗运放串联电阻取100欧到1千欧之间并联一个0.1uF电容截止频率控制在几十千赫兹以上既滤掉高频毛刺又不影响固定电压本身。如果后面接的是低阻抗负载滤波电阻就要更小甚至只能靠去耦电容撑场面否则压降是必现的。另外输出端预留一个0.1uF对地电容的位置几乎是必须的哪怕这次不用下次做动态输出时你也会想加。PCB设计时把位置留出来就是给自己留余地。5. 实测记录与常见问题排查5.1 实测从代码烧录到引脚读数配置完成后我把程序下载到板子先用万用表直接量DAC输出引脚对地电压。理论预期是1.6503V左右实测读数是1.647V差3.3mV左右。这个误差算正常吗拆开看主要有三个来源一是万用表本身精度和表笔接触电阻引入的误差二是DAC自身的INL和DNL一般单片机内DAC的线性度在正负几个LSB水平3.3mV大约是4个LSB属于正常范围三是VREF实际电压未必就是精确的3.300V可能稍低于3.3V。接着我又用示波器看了输出纹波固定电压输出时纹波大概在5mV到8mV之间这个水平对偏置电压来说完全够用。整个流程非常顺利基本是烧录即输出没有出现必须调寄存器才能跑的情况HAL库的封装已经处理了大部分底层细节。5.2 典型问题排查表实际开发里不可能一帆风顺我整理了下面这张排查表基本覆盖了DAC固定电压输出最常见的异常现象。现象可能原因解决办法输出始终为0V未调用HAL_DAC_Start先Start再SetValue输出始终接近VREF通道配置错误或初始化未生效检查MX_DAC1_Init有没有被调用输出值偏低VREF实际电压低于预期用万用表实测VREF重新计算码值输出纹波大电源去耦不足或输出走线受干扰补0.1uF去耦电容输出引脚就近滤波带负载后电压下降输出缓冲未开或负载太重开启Buffer必要时加运放跟随上电瞬间出现毛刺DAC在初始化前默认状态不受控软件启动前把输出引脚配置为高阻或硬件加RC这表格里前三条都是软件配置问题后三条偏硬件设计。排查时我的习惯是先软件后硬件、先静态后动态先用万用表确认有没有输出再上示波器看波形细节不要一上来就怀疑芯片坏了。5.3 一次印象深刻的排查实录这里分享一个真实经历正好能把排查思路串起来。有次我帮同事看DAC输出偏低的问题他用的也是类似配置目标输出1.65V实际量出来只有1.31V。一开始我怀疑他Buffer没开检查之后没问题又怀疑是代码码值算错了重算之后也没问题。最后拿万用表直接量VREF引脚发现实际只有2.62V——板子上VREF没有直接接3.3V而是通过一个分压电阻网络供电分压值把电压拉低了。找到问题后直接从电源3.3V飞线到VREF引脚输出立刻变成1.648V。这个案例说明了一个重要原则一切软件计算都要以VREF的实际电压为基准而不是以原理图上的标称电压为基准。原理图写的3.3V实际到芯片脚上可能就不是3.3V中间那段路径上的电阻、磁珠、走线压降都是实实在在的。6. 从固定输出到动态波形正弦波与选型扩展6.1 用DAC生成正弦波的思路固定电压输出是DAC最朴素的用法一旦跑通再往上走一步就是动态波形输出最典型的是正弦波。思路说起来不复杂把正弦波的一个周期离散成128或者256个点计算好每个点的码值存入数组再让定时器以固定频率触发DAC用DMA循环搬运数组里的数据到DAC寄存器输出端就能看到一个连续正弦波。触发源这里要从None改成定时器触发比如TIM6的更新事件。频率关系大概是 (f_{\text{sin}} f_{\text{trigger}} / N)其中N是采样点数。比如定时器触发频率是128kHz采样128个点输出正弦波就是1kHz。固定电压是写死寄存器动态波形是用节奏不断改写寄存器本质还是DAC_SetValue只是数据来源变成了DMA。6.2 ADC/DAC芯片选型的几条经验做完板载DAC项目难免会被问到什么时候该用片内DAC什么时候该外挂DAC芯片。我给一点选型经验。首先看分辨率12位是否够用。如果只是出偏置电压12位完全够如果是要做高精度波形源每格电压要优于0.1mV可能得上16位外部DAC。其次看参考电压片内DAC的参考源一般就是VREF如果对温漂和绝对精度要求很高要用外部精密基准芯片。再看输出能力和建立时间片内DAC带Buffer后能驱动小负载但如果后端是50欧负载或者需要快摆率还是得外挂专用DAC加运放输出级。说到底片内DAC图的是方便快外部DAC图的是指标高两者不是替代关系是互补关系。项目里先判断需求再做选择比一上来就追高端芯片靠谱得多。6.3 后续还可以扩展什么固定电压输出能做的扩展其实不少。比如用DAC输出一路可动态调节的比较器阈值配合ADC做自动校准环路又比如用DAC给可调增益放大器提供增益控制电压代码里改一个变量就能改变系统增益再比如用DAC配合DMA输出缓慢变化的斜波做扫描类测量。这些方向都是同一套DAC基础配置上长出来的学会了固定电压输出就等于把DAC的控制通道打通了。最后分享一个实操小习惯我每次在板子上用DAC做固定电压输出都会顺手在DAC输出脚对地放一个0.1uF的预留电容位。哪怕这次用不上以后真要滤波或者做动态波形适配负载时直接焊上去就能用。DAC输出看似简单但软件计算、参考电压、PCB布局这三件事只要有一件不给力输出电压就不会让你省心。按上面的流程走一遍基本能一次点亮。
返回列表