1. 项目概述:深入解读CH32F20x系列MCU的模拟外设
最近在做一个需要高精度温度采集和模拟信号调理的项目,选型时把目光投向了国产的CH32F20x系列MCU。这个系列,尤其是CH32F205/207/203这几款,以其高性价比和丰富的外设资源在圈内口碑不错。但说实话,刚开始看数据手册时,关于其内置温度传感器、DAC(数模转换器)和OPA(运算放大器)这部分,总觉得有些细节没说透,比如温度传感器的精度到底受什么影响、DAC输出如何稳定、OPA怎么配置才能发挥最佳性能。网上的资料要么太零散,要么就是直接照着手册念,对于实际开发中可能遇到的坑,提得很少。
我花了不少时间,结合数据手册、勘误文档以及自己的实测,把这几个模拟外设的特性、工作原理、配置方法和避坑要点都梳理了一遍。这篇文章就是这些经验的总结,目标很明确:让你在用到CH32F20x的模拟功能时,能快速上手,少走弯路。无论你是正在评估这颗芯片,还是已经用上了但在调试中遇到了问题,相信下面的内容都能给你提供直接的帮助。
2. 核心外设深度解析与设计考量
CH32F20x系列作为一款主打高性能和集成度的MCU,其模拟外设的设计意图是让用户在单芯片上完成信号采集、处理和输出的闭环,减少外部元件,降低系统成本和PCB面积。理解每个外设的设计逻辑,是正确使用它们的前提。
2.1 温度传感器:不仅仅是“有”这个功能
很多MCU都宣称内置温度传感器,但CH32F20x的这颗需要你特别关注其“测量方法”而非“绝对精度”。数据手册会给出一个典型值,比如±1.5°C,但这个精度是在特定条件下(例如,特定的VREF+、特定的采样时间、特定的环境)校准出来的。它的核心价值在于测量芯片结温的相对变化,而不是提供一个实验室级别的绝对温度值。
它的工作原理是利用半导体PN结的正向压降与温度的线性关系。在芯片内部,一个特殊的PN结被用作传感元件,其电压V_BE会随温度变化。内部电路将这个电压通过一个模拟开关连接到ADC的输入通道(通常是ADC_IN16或ADC_IN17,具体需查对应型号的数据手册)。因此,温度测量本质上是一次ADC转换。这意味着测量结果的质量,直接受到ADC参考电压稳定性、采样时间、电源噪声乃至软件滤波算法的影响。
在设计上,它最适合的应用场景包括:
- 系统过热保护:监控MCU自身温度,在结温超过安全阈值(如85°C或105°C)时,触发降频、关闭外设或报警。
- 环境温度趋势监测:在设备内部,如果MCU与环境热耦合较好,可以用它来近似反映环境温度的变化趋势,用于风扇调速等。
- 校准其他传感器:作为辅助参考,对其他温度传感器(如热敏电阻)的读数进行粗略的补偿。
注意:切勿将其用于需要高精度绝对温度测量的场合,如恒温控制、医疗测温等。它的主要误差来源包括出厂校准偏差、ADC的积分非线性(INL)、差分非线性(DNL)误差,以及电源电压波动对VREF+的影响。
2.2 DAC:双通道的灵活输出引擎
CH32F20x集成了两个12位分辨率的DAC通道,这算是中高端MCU的标配。但它的亮点在于输出模式的灵活性和与内部其他模块的联动能力。每个DAC通道都可以独立配置为多种工作模式,理解这些模式的区别是高效使用的关键。
输出模式解析:
- 正常模式:DAC输出由写入数据保持寄存器(DHRx)的值直接决定。这是最常用的模式,用于产生静态电压或由软件缓慢更新的电压信号。
- 噪声生成模式:DAC输出由一个伪随机数生成器(线性反馈移位寄存器,LFSR)控制。你可以设置LFSR的掩码来改变噪声的频谱特性。这个模式常用于测试系统的噪声抑制能力,或者生成特定的白噪声信号。
- 三角波生成模式:DAC输出会在用户定义幅值(通过设置MAMPx位)内自动递增/递减,生成三角波。这个模式完全由硬件完成,不占用CPU资源,非常适合生成周期性的扫描信号(例如,用于传感器特性曲线扫描)。
关键设计考量:
- 参考电压选择:DAC的输出范围取决于其参考电压VREF+。CH32F20x的DAC通常与ADC共享VREF+引脚。你可以选择内部参考电压(如果芯片提供,通常精度一般),或者连接一个高精度、低温漂的外部基准源(如REF3025)。这是决定DAC输出绝对精度的最关键因素。
- 输出缓冲器:DAC输出端集成了可开关的输出缓冲放大器。开启缓冲器后,DAC具有更强的带负载能力(可驱动容性负载),但会引入一定的偏移误差和非线性。关闭缓冲器则能获得更好的直流精度和线性度,但驱动能力很弱,只能接高阻抗负载(如运放的同相输入端)。我的经验是,如果后级是运放电路,通常关闭缓冲器;如果直接驱动负载,则必须开启。
- 与DMA、定时器的联动:DAC支持通过DMA自动更新输出数据。你可以将一段波形数据(如正弦波表)存放在内存中,由定时器触发DMA传输到DAC,从而高效地生成任意复杂波形。这是实现高质量信号发生器的核心。
2.3 OPA:片内信号调理的瑞士军刀
集成OPA是CH32F20x系列一个非常实用的特性。它把信号调理电路从外部搬到了芯片内部,不仅节省了空间和成本,还减少了因PCB布局和外部元件离散性引入的噪声和误差。
这个片内OPA通常被设计为通用运算放大器,支持多种经典电路配置:
- 电压跟随器:用于阻抗匹配,将高阻抗信号源转换为低阻抗输出。
- 同相/反相放大器:对模拟信号进行固定增益的放大或衰减。
- 差分放大器:配合内部多路复用器,可以用于测量差分信号,抑制共模噪声。
- 滤波器:通过外部连接电阻和电容,可以构成有源低通、高通或带通滤波器。
在实际项目中的应用优势:
- 直接连接传感器:例如,可以直接将热电偶、桥式压力传感器的微小差分输出连接到OPA的输入端,进行第一级放大,再送入ADC,极大地简化了前端电路。
- DAC输出缓冲与调理:虽然DAC自带缓冲器,但有时需要额外的增益或电平移位。可以用OPA来缓冲和调理DAC的输出信号,形成完整的可编程模拟输出通道。
- 降低系统噪声:由于OPA在芯片内部,其与ADC、DAC之间的信号路径极短,受外部电磁干扰(EMI)的影响远小于使用外部运放。
实操心得:片内OPA的性能参数(如增益带宽积、压摆率、输入失调电压)通常不如顶级的外部独立运放。数据手册会给出典型值。在设计中,务必根据你的信号频率、精度要求来评估其是否满足需求。对于直流或低频高精度应用,需要注意其输入失调电压和温漂;对于高速信号,则需要关注其压摆率和带宽。
3. 外设配置与协同工作实战
理解了单个外设的特性后,如何让它们协同工作,完成一个具体的任务,才是工程实现的关键。下面我们以一个典型的应用场景为例:“使用内部温度传感器监测芯片温度,并通过DAC输出一个与温度成比例的电压,同时用OPA对这个电压进行缓冲放大”。
3.1 硬件连接与初始化顺序
假设我们使用CH32F207,并选择内部VREF+作为ADC和DAC的参考电压。
- 电源与参考电压:确保VREF+引脚连接了足够的去耦电容(通常是一个10uF的钽电容并联一个100nF的陶瓷电容),并且电源稳定。这是所有模拟电路精度的基础。
- ADC初始化(用于温度传感器):
- 使能ADC和GPIO时钟(温度传感器通道对应的GPIO,虽然信号在内部,但相关ADC模块时钟需开启)。
- 配置ADC为独立模式,设置分辨率(12位)、数据对齐方式(右对齐)。
- 设置采样时间。对于温度传感器这类高内阻信号源,必须延长采样时间!建议设置为ADC采样周期中允许的最大值(例如239.5个周期),让采样电容充分充电,这是提高测量稳定性的最重要一步。
- 校准ADC(先执行上电校准,如果有的话)。
- 选择温度传感器对应的ADC输入通道(如ADC_IN16)。
- DAC初始化:
- 使能DAC和对应GPIO端口时钟(DAC_OUT1/2对应的PA4/PA5)。
- 配置GPIO为模拟模式(非常重要,避免数字信号干扰)。
- 配置DAC通道,例如通道1。选择触发源(如果使用软件触发,则选择“软件触发”)。设置输出缓冲器状态(根据负载决定开或关)。使能DAC通道。
- 如果需要,使能DAC的DMA功能并配置DMA通道。
- OPA初始化:
- 使能OPA时钟。
- 配置OPA的工作模式。例如,配置为跟随器模式或同相放大器模式。这通常通过配置OPA的控制寄存器完成,包括选择同相输入源(来自内部多路选择器或外部引脚)、反相输入连接方式(内部反馈或外部引脚)等。
- 连接外部反馈网络(如果需要)。对于同相放大器,需要在OPA输出引脚和反相输入引脚之间连接电阻,同时在反相输入和地之间连接另一个电阻。这些电阻需要焊接在PCB上。
- 使能OPA。
初始化顺序建议:先初始化ADC和DAC,最后初始化OPA。因为OPA可能依赖于前级信号(如DAC输出)已经稳定。
3.2 温度读取与DAC输出联动代码示例
以下是基于HAL库或类似固件库的核心代码逻辑框架:
// 1. 读取温度传感器ADC值 uint16_t Read_TemperatureSensor(void) { ADC_ChannelConfTypeDef sConfig = {0}; uint16_t raw_adc_value = 0; // 选择温度传感器通道 sConfig.Channel = ADC_CHANNEL_16; // 根据实际型号调整 sConfig.Rank = 1; sConfig.SamplingTime = ADC_SAMPLETIME_239CYCLES_5; // 使用长采样时间 if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK) { Error_Handler(); } HAL_ADC_Start(&hadc1); if (HAL_ADC_PollForConversion(&hadc1, 10) == HAL_OK) { raw_adc_value = HAL_ADC_GetValue(&hadc1); } HAL_ADC_Stop(&hadc1); return raw_adc_value; } // 2. 将ADC值转换为温度(近似计算,需根据手册公式校准) float Convert_ADC_to_Temperature(uint16_t adc_value, float vref_mv) { // 公式参考数据手册:Vsense = (ADC_VALUE * VREF) / 4095 // 温度 T = (Vsense - V25) / Avg_Slope + 25 // 其中 V25 是25°C时的传感器电压,Avg_Slope是平均斜率(mV/°C) // 这些参数在数据手册的电气特性章节可以找到典型值,例如: // V25 = 1.43V, Avg_Slope = 4.3 mV/°C (这些值必须用你手中的芯片实测校准!) const float V25 = 1.43f; const float Avg_Slope = 0.0043f; // 单位 V/°C float vsense = (adc_value * vref_mv / 1000.0f) / 4095.0f; float temperature = (vsense - V25) / Avg_Slope + 25.0f; return temperature; } // 3. 根据温度设置DAC输出(例如,温度每升高1°C,DAC输出增加10mV) void Set_DAC_By_Temperature(float temperature) { // 设计输出关系:Vout = (Temperature - 20.0) * 0.01 + 1.0 (V) // 假设VREF+ = 3.3V, DAC为12位 float target_voltage = (temperature - 20.0f) * 0.01f + 1.0f; // 限制电压在0-VREF之间 if (target_voltage < 0) target_voltage = 0; if (target_voltage > 3.3f) target_voltage = 3.3f; // 计算DAC数值 uint16_t dac_value = (uint16_t)((target_voltage / 3.3f) * 4095.0f); // 写入DAC数据保持寄存器 HAL_DAC_SetValue(&hdac, DAC_CHANNEL_1, DAC_ALIGN_12B_R, dac_value); // 如果需要软件触发更新(如果配置为软件触发模式) // HAL_DAC_Start(&hdac, DAC_CHANNEL_1); } // 主循环中的调用示例 float vref_internal = 3.3f; // 假设使用内部3.3V参考,实际需测量或使用更准的基准 while (1) { uint16_t adc_val = Read_TemperatureSensor(); float temp = Convert_ADC_to_Temperature(adc_val, vref_internal); Set_DAC_By_Temperature(temp); HAL_Delay(1000); // 每秒更新一次 }3.3 OPA作为DAC缓冲器的配置要点
在上面的例子中,DAC的输出可能驱动能力不足或需要隔离。我们可以配置片内OPA为电压跟随器,连接在DAC输出和最终负载之间。
- 硬件连接:在PCB设计时,将DAC的输出引脚(如PA4)连接到OPA的同相输入端引脚(OPAx_INP),将OPA的输出引脚(OPAx_OUT)连接到负载。同时,将OPA的输出引脚通过一根短线连接到其反相输入端引脚(OPAx_INN),这样就构成了电压跟随器。
- 软件配置:
// 配置OPA为跟随器模式 OPA_HandleTypeDef hopa; hopa.Instance = OPA1; // 使用OPA1实例 hopa.Init.Mode = OPA_MODE_FOLLOWER; // 跟随器模式 hopa.Init.PowerSupplyRange = OPA_POWERSUPPLY_LOW; // 根据电源选择 hopa.Init.PowerMode = OPA_POWERMODE_NORMAL; // ... 其他参数 if (HAL_OPA_Init(&hopa) != HAL_OK) { Error_Handler(); } HAL_OPA_Start(&hopa); // 启动OPA - 效果:此时,OPA的输出电压将紧紧跟随DAC的输出电压,但提供了极低的输出阻抗,可以驱动更大的容性负载或电阻负载,而不会影响DAC本身的精度。
4. 精度提升技巧与常见问题排查
即使按照手册配置,在实际电路中也可能无法达到理想的性能。下面分享一些提升精度和稳定性的实战技巧,以及常见问题的排查思路。
4.1 温度传感器读数跳变大怎么办?
这是最常见的问题。现象是连续读取的温度ADC值波动很大,换算后的温度上下跳动好几度。
- 排查步骤1:检查电源和地。用示波器探头(设置为交流耦合)观察MCU的VDD和VSS引脚,看是否有高频噪声。模拟电路的精度直接依赖于电源的纯净度。确保电源电路有足够的滤波电容,并且PCB布局上,模拟电源部分采用了星型接地或单点接地,远离数字电源的噪声源。
- 排查步骤2:优化ADC采样时间。如3.1节所述,务必增加采样时间。温度传感器内部等效电阻较大,采样电容充电慢。将采样时间设置为最大值能显著改善。
- 排查步骤3:软件滤波。硬件稳定后,在软件端实施滤波算法。最简单的就是滑动平均滤波。连续采样N次(比如16次或32次),然后取平均值。更高级的可以用一阶低通数字滤波器。
#define FILTER_LEN 16 uint16_t adc_filter_buffer[FILTER_LEN] = {0}; uint8_t filter_index = 0; uint32_t adc_sum = 0; // 在读取ADC的函数中 raw_adc_value = HAL_ADC_GetValue(&hadc1); adc_sum -= adc_filter_buffer[filter_index]; // 减去最旧的值 adc_filter_buffer[filter_index] = raw_adc_value; // 存入新值 adc_sum += raw_adc_value; filter_index = (filter_index + 1) % FILTER_LEN; uint16_t filtered_adc_value = adc_sum / FILTER_LEN; // 计算平均值 - 排查步骤4:参考电压稳定性。如果使用VDDA作为VREF+,其噪声会直接引入测量误差。考虑使用独立、安静的外部基准源(如REF3033)为VREF+引脚供电。
4.2 DAC输出有毛刺或噪声
DAC输出在代码更新数值时出现尖峰,或者本应稳定的直流输出上有高频噪声。
- 毛刺问题:这通常发生在DAC数据更新时刻,称为“更新毛刺(Update Glitch)”。解决方案是启用DAC的输出缓冲器。缓冲器内部的放大器可以抑制这种毛刺。如果缓冲器已经开启但仍有毛刺,可以在DAC输出端对地加一个小电容(如100pF)进行滤波,但注意电容过大会影响响应速度。
- 高频噪声问题:
- 检查电源:同样,用示波器看VREF+和VDDA的噪声。
- 检查PCB布局:DAC输出走线是否过长?是否靠近时钟线、高速数据线等噪声源?理想情况下,DAC输出应使用短的走线直接连接到负载或OPA输入端,并用地线包围进行屏蔽。
- 使用OPA缓冲:如3.3节所述,使用片内OPA作为跟随器,可以有效隔离后端负载变化对DAC芯片内部的影响。
- 硬件滤波:在DAC输出端添加一个RC低通滤波器。例如,一个1kΩ电阻串联在输出通路上,后接一个100nF电容到地。这能滤除高频噪声,但会降低带宽。
4.3 OPA输出异常:饱和、振荡或精度差
- 输出饱和(始终为高电平或低电平):
- 检查供电:确认OPA的供电电压是否正常且满足信号摆幅要求。输入信号电压是否在OPA的共模输入电压范围之内?输出信号是否超出了电源轨?
- 检查反馈网络:如果是放大器配置,检查连接在OPA输出和反相输入端之间的反馈电阻是否焊接良好,阻值是否正确。开路的反馈会导致运放开环,增益极大,极易饱和。
- 输出振荡(高频自激):
- 这是运放电路的典型问题。片内OPA的相位裕度可能不高,当驱动容性负载时容易振荡。
- 解决方案:在OPA输出端串联一个小的电阻(如10-100Ω),再连接到容性负载。这个电阻与负载电容构成了一个极点,有助于稳定系统。
- 检查PCB布局,反馈走线是否过长?应尽可能短而直。
- 直流精度差(输出有固定偏差):
- 输入失调电压(Vos):这是集成运放的固有缺陷。对于直流放大应用,这个误差会被放大。数据手册会给出最大失调电压。如果误差超出可接受范围,有几种办法:一是在软件中进行校准,测量零点输出并减去;二是如果芯片支持,查找是否有内部失调电压校准寄存器(部分MCU的OPA提供此功能);三是对于要求极高的应用,可能需要考虑使用外部低失调运放。
4.4 校准:让数据更可信
对于精度要求稍高的应用,软件校准是必不可少的步骤。
温度传感器校准:
- 单点校准:在已知的、稳定的环境温度T_known(例如25°C的恒温箱)下,读取此时的ADC值ADC_known。则温度计算公式可修正为:
T = (ADC_raw - ADC_known) / Slope + T_known。其中Slope可以使用手册典型值,或通过两点校准求得。 - 两点校准:在温度T1和T2下(T1和T2相差尽量大),分别读取ADC1和ADC2。计算斜率Slope = (T2 - T1) / (ADC2 - ADC1)。然后将斜率和其中一个点代入公式。两点校准能同时修正斜率和偏移误差,效果更好。
- 单点校准:在已知的、稳定的环境温度T_known(例如25°C的恒温箱)下,读取此时的ADC值ADC_known。则温度计算公式可修正为:
DAC输出校准:
- 使用一个高精度的数字万用表测量DAC在几个关键代码值(如0, 2048, 4095)下的实际输出电压。
- 记录测量值,与理论值比较。可以在软件中建立一个查找表(LUT)或拟合一个线性/多项式校正函数,在输出前对DAC代码进行补偿。
系统级校准:
- 如果DAC+OPA作为一个模拟输出通道,可以将最终输出连接回ADC的另一个输入通道,构成一个自校准环路。MCU控制DAC输出一个已知电压,然后用ADC读回来,计算出整个链路的增益和偏移误差,并在软件中补偿。这种方法可以实现闭环校准,精度最高。
5. 进阶应用与性能压测
当你掌握了基础功能后,可以尝试一些更复杂的应用,并测试这些模拟外设的性能边界。
5.1 构建一个简易的波形发生器
利用DAC的DMA+定时器触发模式,可以轻松实现波形发生器。
- 生成波形表:在PC上或用MCU计算一个周期的波形数据(如正弦波、三角波、方波),存入数组。对于12位DAC,数组元素为
uint16_t类型。#define WAVE_TABLE_SIZE 256 uint16_t sine_wave[WAVE_TABLE_SIZE]; for (int i = 0; i < WAVE_TABLE_SIZE; i++) { sine_wave[i] = (uint16_t)(2048 * (1.0 + sin(2 * PI * i / WAVE_TABLE_SIZE))); // 输出范围 0-3.3V } - 配置DMA:将DMA配置为从内存(波形数组)到外设(DAC数据寄存器)的循环传输模式。
- 配置定时器:选择一个定时器(如TIM6)作为触发源。定时器的更新频率决定了波形输出的频率。
输出频率 = 定时器触发频率 / 波形表长度。 - 配置DAC:将DAC触发源设置为该定时器。使能DAC的DMA请求。
- 启动:启动DMA,启动定时器。此时,DAC就会以设定的频率自动、循环地输出波形表中的数据,CPU完全被解放。
性能考量:输出波形的最高频率受限于DAC的建立时间、DMA传输速度和定时器频率。对于CH32F20x,在72MHz系统时钟下,输出几十kHz的正弦波是可行的。要输出更高频率,需要减少波形表点数,但这会降低波形质量。
5.2 使用OPA和ADC实现高精度差分测量
假设要测量一个桥式传感器的差分输出(mV级)。
- 配置OPA为差分放大器:
- 将传感器的正端接OPA同相输入(OPAx_INP),负端接OPA反相输入(OPAx_INN)。
- 通过外部电阻网络设置OPA的增益。例如,选择Rf=100kΩ, Rg=10kΩ,则增益G = 1 + Rf/Rg = 11。
- 在软件中配置OPA为外部反馈模式,并选择正确的输入通道。
- 连接ADC:将OPA的输出连接到ADC的一个输入通道。
- 软件处理:
- 首先,在传感器无负载(或已知负载)时,读取一个ADC值作为“零位”基准。
- 然后,在测量时,读取新的ADC值,减去零位基准,再根据ADC参考电压和OPA的增益,换算成实际的传感器电压差。
- 注意:必须考虑OPA的输入失调电压和温漂。对于高精度测量,需要进行系统校准:在差分输入端施加几个已知的精确电压(如0V, 1mV, 2mV),记录ADC输出,建立校准曲线。
5.3 极限性能测试与数据手册参数验证
你可以设计一些简单的测试来验证芯片是否达到数据手册标称的性能:
- DAC的INL/DNL测试:编写一个程序,让DAC从0到4095步进输出每一个码值。用一个高精度(至少16位)的ADC板卡(或另一台高精度万用表)测量每个码值对应的实际电压。将测量数据导入电脑,用软件计算积分非线性(INL)和差分非线性(DNL)。这能直观地看到DAC的线性度。
- OPA的增益带宽积测试:将OPA配置为固定增益(如10倍)的同相放大器。输入一个固定幅度(如100mVpp)的正弦小信号,用信号发生器和示波器配合,逐渐增加输入信号频率,观察输出信号幅度下降到-3dB(即幅度变为原来的0.707倍)时的频率。这个频率点大约就是该增益下的带宽。可以验证其是否与手册中的增益带宽积参数相符(GBW = 增益 * 带宽)。
- 温度传感器的长期稳定性测试:将芯片置于恒温环境中(如25°C恒温箱),连续读取温度传感器ADC值数小时甚至数天,观察其波动范围。这可以评估传感器和ADC的噪声水平。
通过这些深入的测试和应用,你不仅能更好地使用CH32F20x的模拟外设,还能对其真实性能有更准确的把握,从而在未来的产品设计中做出更可靠的决策。记住,数据手册给出的是典型值或最大值,实际性能因芯片个体、供电、PCB布局和温度而异,动手测试永远是工程师最可靠的工具。