做工业产品选型这些年我最大的感受是芯片的纸面资源越来越不重要功耗和外围成本反而成了卡脖子的点。CH32L103这颗国产RISC-V工业MCU主频96MHz封装小到QFN28/TSSOP20却把USB PD PHY、运放、比较器、触摸按键检测全塞了进去低功耗表现也到了uA级别。对我来说它解决的是工业现场常见的“小封装、省外围、低功耗、还得够用”这类问题。这篇内容适合正在评估低功耗MCU选型的嵌入式工程师也适合做电池供电设备、Type-C配件、便携仪表的硬件开发者参考。1. 芯片定位与核心亮点它不是又一颗Cortex-M01.1 RISC-V内核和96MHz主频实时性到底够不够CH32L103用的是沁恒自研的青稞V4C内核属于RISC-V 32位指令集支持IMAC组合也就是整数乘法除法、原子操作都有。看到“RISC-V”很多人会担心生态不成熟实际上这两年用下来MounRiver Studio里点个编译、烧录、断点调试流程和传统IDE没什么区别。96MHz主频在这个级别里算比较能打的。很多工业现场任务比如Modbus轮询、传感器采集、PWM输出其实对绝对算力要求不高对“中断来得够不够快、能不能在几微秒内完成上下文切换”反而敏感。青稞内核的硬件压栈机制在这里起了作用中断现场由硬件自动压栈响应速度比软件压栈快不少。我在一个编码器计数应用里实测外部中断频率到几十kHz时中断服务函数里只做标志位操作CPU占用率依然很低。另一个容易忽略的维度是代码迁移。CH32L103的外设库函数风格和ST早期标准外设库相似GPIO_Init、ADC_Init这套写起来很顺。如果你是从STM32F103这类平台转过来的看EVT例程基本没有陌生感。但注意寄存器名字、位定义和ST不是一一对应别指望直接把工程搬过来重写一遍外设初始化是正常工作量。1.2 内置运放、比较器和PD PHY省掉了哪些外围这颗芯片最让我心动的地方不是主频而是模拟资源和协议资源的集成度。工业采集场景里最常见的是电流采样、温度采样、微弱信号放大。以前要么外挂一颗运放要么用分立电阻搭差分放大占板面积大不说失调和温漂还难控制。CH32L103内部集成了运放可以直接做信号跟随或者放大输出送给内部ADC。板子上少一颗运放BOM成本降一点故障点也少一个。比较器也是工业场景里的高频外设。过流保护、电压阈值检测、掉电预警这些都是“只要一个电平跳变事件”的需求。比较器的输出可以直接触发中断也可以接到定时器的刹车输入实现硬件级别的紧急保护。我在一个电机驱动板上就是这么用的采样电阻上的压降进比较器一旦超过设定阈值定时器PWM立即刹车比软件检测快一个数量级。更特别的是USB PD PHY。这颗芯片内置USB2.0全速控制器同时集成了PD物理层可以做Type-C接口的DFP/DRP检测也能跑PD协议协商。这意味着做Type-C受电设备时不需要再外挂一颗协议芯片一颗CH32L103既完成协议通信又完成整机逻辑控制。这个特性在同价位MCU里是稀缺卖点接了Type-C项目的朋友可以重点考虑。1.3 算账式选型便宜芯片不等于便宜方案很多工程师选型时只看芯片单价这其实是最大的误区。工业产品的成本要算整板BOM、PCB面积、生产测试、返修率。一颗MCU便宜两块钱但外面多挂一颗运放、一颗触摸芯片、一颗PD协议IC总价反而高了。举个例子一个带触摸按键的便携仪表如果MCU不自带触摸检测要么用专用触摸芯片要么自己搭RC充放电电路再用ADC采样两种方案都要额外元件和校准时间。CH32L103自带TouchKey模块直接铺几个焊盘就能做出触摸按键生产时不用额外贴片软件里跑一下自校准就行。这种省下来的隐性成本比MCU芯片本身的价差更有价值。小封装也一样。TSSOP20可以塞进很多原来用8位单片机才放得下的空间但性能和外设完全是32位级别。如果产品尺寸是硬约束选一颗高集成度的芯片有时比刻意追求低价更有意义。2. 低功耗实践模式选择、漏电流排查与续航估算2.1 睡眠/停止/待机三种模式怎么选一张表看清差异CH32L103和大多数低功耗MCU一样提供了睡眠、停止、待机三档低功耗模式。三者的功耗、唤醒源和恢复方式差别很大选错了轻则功耗不达标重则唤醒后程序跑飞。模式CPU外设时钟典型电流量级唤醒源唤醒后行为睡眠停止可配置继续运行mA级往下任意中断从原指令继续执行停止停止大部分关闭uA级EXTI、RTC等从中断处继续但时钟需重新初始化待机停止几乎全关最低RTC、唤醒IO、复位类似复位重启程序从头运行睡眠模式适合“任务间隙短睡”的场景比如主循环里等待串口指令等一条1ms的定时器中断唤醒做一次状态刷新。停止模式适合需要保持RAM数据、且唤醒后还想接着原来逻辑走的情况很多电池设备的主休眠态都用这一档。待机模式适合唤醒后直接重新初始化的场景比如RTC定时唤醒采集一轮数据采完继续睡完全不需要保留唤醒前的运行现场。我自己的习惯是优先用停止模式做常态休眠因为既能保住运行现场功耗又能压到uA级。只有当静态功耗实在压不下去、或者唤醒本身就是一次“重新开机”才会用待机模式。2.2 唤醒之后时钟“变了”停止模式恢复代码框架停止模式下大部分时钟停止唤醒后系统时钟并不一定自动恢复到原来的96MHz配置。如果不重新初始化时钟轻则串口波特率不对重则外设时序全部错乱。这是低功耗开发里最容易踩的坑。可以参考下面的框架代码来理解这个流程// 进入停止模式WFI等待唤醒 // 以官方EVT中CH32L103例程为准 PWR_EnterSTOPMode(PWR_STOPEntry_WFI, PWR_STOPMode_LP); // 唤醒后的第一条工作恢复系统时钟 SystemInit(); // 如果是内部时钟方案重新配置HSI/PLL // 接着重新初始化用到的外设时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1, ENABLE);为什么唤醒后要调用SystemInit因为在停止模式下PLL和HSE/HSI可能都被关闭唤醒后系统默认跑到低速时钟或者未锁定状态。直接操作外设会得到错误时序。我把这个步骤称为“唤醒三板斧”恢复时钟、重配时钟树、重新初始化用到的外设。顺序不能反先有时钟才能操作寄存器。另外要注意睡眠模式唤醒后不需要恢复时钟因为睡眠模式只停了CPU核心系统时钟和外设时钟仍然是开着的。这也是为什么短睡场景用睡眠模式更合适的原因之一。2.3 电流从哪漏掉GPIO浮空、外部电路和测量陷阱芯片手册上的uA级待机电流是在理想条件下测出来的但实际整板电流经常高出几十倍。我排查过很多次低功耗异常最经典的漏电路径有三个。第一个是GPIO浮空。CH32L103复位后未使用的引脚默认可能是浮空输入状态CMOS输入级在输入电平不确定时会出现穿透电流单个引脚多出来的电流不大但几十个引脚加起来非常可观。解决方法是把所有未使用引脚配置成模拟输入或者推挽输出低电平在进入低功耗之前统一处理。for (uint8_t i 0; i TOTAL_PINS; i) { GPIO_InitStructure.GPIO_Mode GPIO_Mode_AIN; // 或推挽输出低 GPIO_Init(GPIOx, GPIO_InitStructure); }第二个是外部电路。MCU睡了但板上的电阻分压网络、传感器、LED限流电阻还在耗电。比如电池电压检测用的两个100k分压电阻在低功耗模式下依然从电池抽电电流大约5V/200k25uA直接毁掉整个低功耗设计。对策是给这些电路串联MOS管开关或者用MCU GPIO控制传感器电源只在采集瞬间开启。第三个是测量陷阱。调试器挂在SWIO上时目标板电流测不准因为调试器本身在供电和维持通信。我习惯用精密电源或uA表直接给目标板供电断开调试器再读数。用万用表电流档时也要注意量程内阻建议先短路开机等系统稳定后再断开短路线串电流表否则启动瞬间的大电流会在电流档内阻上产生压降导致MCU复位。2.4 用状态机管理休眠流程从采集到唤醒的代码结构低功耗设备的代码最忌讳在中断服务函数里就地休眠。中断里跑休眠逻辑不仅可读性差还容易因为事件标志没有正确处理导致醒来后不知道下一步干什么。我习惯用一个4状态的小状态机来管理NORMAL、PREPARE_SLEEP、SLEEP、RESUME。typedef enum { ST_NORMAL, ST_PREPARE_SLEEP, ST_SLEEP, ST_RESUME } SysState; SysState currentState ST_NORMAL; volatile uint8_t event 0; // 主循环 while(1) { switch(currentState) { case ST_NORMAL: // 执行一次采集任务 if (采集完成) { event EVT_MEASURE_DONE; currentState ST_PREPARE_SLEEP; } break; case ST_PREPARE_SLEEP: // 关闭传感器电源、未用GPIO配置为AIN DisablePeripherals(); currentState ST_SLEEP; PWR_EnterSTOPMode(PWR_STOPEntry_WFI, PWR_STOPMode_LP); // 唤醒后回到这里 SystemInit(); currentState ST_RESUME; break; case ST_RESUME: InitPeripherals(); currentState ST_NORMAL; break; } }这样做的优势是每个状态里只做一件事出问题可以加日志追踪。实际项目中RTC中断只负责置一个”该醒了”的标志真正的恢复动作都放在状态机里做。中断里处理的事情越少系统的确定性越好。2.5 电池能用多久平均功耗与容量估算续航估算不需要多高深的公式关键是把“平均电流”算准。平均电流不是某一时刻的电流而是各模式电流按时间占比加权平均电流 (运行时间×运行电流 睡眠时间×睡眠电流) / 总时间 续航小时 电池容量(mAh) / 平均电流(mA)举个例子一个传感器每隔10秒采集一次单次采集耗时50ms、电流10mA其余时间停止模式20uA。那么平均电流大约是平均电流 (0.05×10 9.95×0.02) / 10 ≈ 0.0699 mA如果用一节600mAh的锂电池理论续航大约 600/0.0699 ≈ 8584小时约357天。但实际还要考虑电池自放电、DC-DC转换效率、温度下降导致的容量衰减工程上通常按理论值的一半到三分之一来预估更稳妥。我用这个粗算方式做过很多个低功耗方案给需求方报续航时留足余量后期少很多麻烦。3. 外设配置指南GPIO、ADC、定时器与DMA的落地要点3.1 先画引脚分配表再做GPIO复用与重映射CH32L103的封装不大QFN28和TSSOP20都是“脚少事多”的类型引脚规划是硬件工程师要仔细做的功课。我的习惯是在开写代码前先在表格里把每个引脚的用途、功能模式、是否可重映射列出来避免最后发现某个外设引脚跟另外一个功能打架。引脚默认功能复用功能备用功能项目分配PA0GPIOADC通道OPA输入温度采集PA1GPIOUART2_TX比较器输入串口调试PB4GPIOSPI1_NSSTIM3_CH1BLDC霍尔采样GPIO初始化本身不复杂关键是复用功能的配置要和实际需求匹配。下面是一段典型的复用推挽输出配置GPIO_InitTypeDef GPIO_InitStructure {0}; GPIO_InitStructure.GPIO_Pin GPIO_Pin_5; GPIO_InitStructure.GPIO_Mode GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOA, GPIO_InitStructure); // 若引脚需要重映射到其他位置打开重映射功能 GPIO_PinRemapConfig(GPIO_Remap_SPI1, ENABLE);需要提醒的是不同封装的可用引脚数量不同TSSOP20上很多复用功能物理上就不存在。一定先翻数据手册里的引脚定义表确认这个封装下外设引脚是否真的引出。我见过一个同事从QFN28原理图直接复制到TSSOP20结果USART1_TX映射的引脚在封装里根本没有板子打回来才发现。3.2 ADC采集链路采样时间、信号源阻抗与内部OPA配合CH32L103的ADC是12位逐次逼近型支持规则组和注入组采集精度完全够工业传感使用。但ADC用不好往往不是分辨率问题而是采样保持电路和信号源阻抗的匹配问题。如果信号源阻抗过高采样电容充电时间不够ADC采样结果会偏低且跳动。解决办法有两个方向一是增大ADC采样周期给它更多充电时间二是在信号源和ADC之间加一个运放做缓冲利用运放的低输出阻抗驱动ADC采样电容。CH32L103内部运放正好可以用在这里省掉外部跟随器。ADC_InitTypeDef ADC_InitStructure {0}; ADC_InitStructure.ADC_Mode ADC_Mode_Independent; ADC_InitStructure.ADC_ScanConvMode DISABLE; ADC_InitStructure.ADC_ContinuousConvMode DISABLE; ADC_InitStructure.ADC_ExternalTrigConv ADC_ExternalTrigConv_None; ADC_InitStructure.ADC_DataAlign ADC_DataAlign_Right; ADC_InitStructure.ADC_NbrOfChannel 1; ADC_Init(ADC1, ADC_InitStructure); ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_239_5); ADC_Cmd(ADC1, ENABLE); ADC_SoftwareStartConvCmd(ADC1, ENABLE); while(ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) RESET); uint16_t value ADC_GetConversionValue(ADC1);实操中我会把采样率要求不高的通道采样时间尽可能调大比如工业传感器输出阻抗一般都在10k以上采样时间取最长档位测出来的数据稳定性会好很多。用OPA做跟随之后采样时间就可以适当缩小换取更高吞吐率。比较器在采集链路里的角色更偏“守护”。比如用比较器监测电源轨一旦电压低于阈值就触发紧急中断在掉电完成前把关键参数写入Flash。这个功能在很多工业设备里都是刚需比软件轮询可靠得多。3.3 高级定时器与PWM电机控制场景的可玩性CH32L103虽然定位低功耗但它的高级定时器能力一点不像低功耗芯片。互补PWM、死区插入、刹车输入这些传统电机控制必备的功能都有。对于小功率BLDC或者PMSM的方波控制、几瓦到几十瓦量级的驱动板用它可以做得很紧凑。高级定时器的配置项比普通定时器多不少核心的几个参数是预分频器决定PWM频率基准自动重载值决定周期比较值决定占空比死区时间寄存器决定上下桥臂换相时的间隔。还有刹车输入可以配置成低电平有效或高电平有效一旦触发PWM输出立即进入安全状态。我把比较器输出接到定时器刹车输入的做法在这颗芯片上再次验证是可行的硬件过流比较器翻转定时器PWM立刻关闭完全不需要软件参与。这个设计在电机驱动、开关电源这类对保护速度有要求的场景里非常实用。普通定时器还可以做编码器接口模式配合霍尔传感器或者增量编码器读位置和速度。工业传感设备里这种“低速高可靠”的控制需求这颗芯片完全覆盖得住。3.4 DMA与UART/SPI不占用CPU的数据搬运方案低功耗MCU的省电原则里有一条让CPU尽快回到睡眠把数据搬运这种事情交给DMA。CH32L103有DMA控制器常见的恒温采集、串口收发都可以交给它。串口接收是我最常用的DMA场景。配置DMA循环模式接收数据接收缓冲是环形缓冲区DMA每收一个字节就往缓冲区写一次当串口空闲中断来临时CPU只要一次性把缓冲区的完整帧解析掉。这样做的好处是主循环大部分时间不用管串口也不用逐字节读数据寄存器CPU利用率大幅下降。// DMA接收串口数据到缓冲区的框架示意 DMA_InitTypeDef DMA_InitStructure {0}; DMA_InitStructure.DMA_DIR DMA_DIR_PeripheralSRC; DMA_InitStructure.DMA_PeripheralInc DMA_PeripheralInc_Disable; DMA_InitStructure.DMA_MemoryInc DMA_MemoryInc_Enable; DMA_InitStructure.DMA_PeripheralDataSize DMA_PeripheralDataSize_Byte; DMA_InitStructure.DMA_MemoryDataSize DMA_MemoryDataSize_Byte; DMA_InitStructure.DMA_Mode DMA_Mode_Circular; DMA_InitStructure.DMA_Priority DMA_Priority_High; DMA_InitStructure.DMA_M2M DMA_M2M_Disable; DMA_Init(DMA1_Channel5, DMA_InitStructure); DMA_Cmd(DMA1_Channel5, ENABLE);ADC连续采集也可以开DMA让ADC转换结果自动存数组主循环定期取平均值即可。这种“采集走DMA、CPU按时醒来处理一轮”的模式是低功耗工业传感设备的典型写法。4. 工业选型判断适合做什么别硬做什么4.1 主场场景工业变送器、便携仪表与触摸面板CH32L103最适合的其实就是中小规模工业控制设备。两线制4-20mA变送器是典型场景整机功耗需要控制在4mA以内传感器、MCU、输出电路全部要精打细算。它的uA级睡眠电流和快速唤醒能力让这类设备可以“睡一会儿、采一会儿、回传一会儿”。便携检测仪也是很好的匹配。手持设备用锂电池对外设的需求通常就是ADC采传感器、串口或者蓝牙模块通信、按键操作。内置运放把传感器信号调理好触摸按键替代机械按键解决防水防尘问题QFN28封装让整机可以做得很小。工业现场面板类似触摸按键、小屏、Modbus通信这颗芯片做起来很顺手。如果项目需要在掉电前保存参数内置比较器可以直接监测电源跌落触发中断后进入Flash写入流程。再加上独立看门狗防止程序跑飞工业可靠性设计需要的几个要素都齐了。4.2 Type-C PD设备受电端与协议协商内置PD PHY让它成为Type-C外设里的好用选项。像电动工具、蓝牙音箱、小家电这类用Type-C接口充电的产品以前需要一个协议芯片负责PD诱骗一个MCU负责整机逻辑现在一颗芯片两个角色一起干。作为受电端CH32L103可以识别适配器的PD能力并发出请求获取合适的电压档位作为DRP或者Source端它也可以做角色检测和协商。缺点是它本身不是电源管理IC大功率的开关电源控制还是要靠专门方案。但协议协商、状态显示、充电曲线管理这些逻辑层工作它做起来绰绰有余。一颗带PD功能的Type-C设备整机从硬件到软件都可以围绕CH32L103来组织。4.3 别硬上的场景Linux、大存储和工业以太网选型不只看“它能做什么”更要看“它不该做什么”。CH32L103只有几十KB Flash和20KB级别的RAM跑Linux想都不要想。如果产品需要一个完整的GUI框架、需要动态加载模块那该上MPU还是上MPU。本地大量日志存储也不适合。如果每分钟都记录一条带时间戳的日志Flash很快会被写满而且频繁擦写会缩短Flash寿命。这种需求最好外挂Flash芯片或者选大容量型号。需要工业以太网的项目也建议慎重。CH32L103没有内置以太网MAC外接Ethernet PHY没有意义需要走SPI接口的W5500这类网络芯片来实现。如果你对网络吞吐有比较高要求选一颗带MAC的MCU更合适。还有一点如果产品需要蓝牙低功耗这颗芯片本身不带BLE。虽然可以通过串口外挂BLE模块但如果你追求“一体解决”建议去看带蓝牙的MCU型号。选型时把通信方式先定下来再决定用不用CH32L103比拿到芯片后才发现少功能要省事得多。4.4 横向评估清单拿什么指标和同类MCU比做MCU选型对比时我一般列一个清单逐项打分不只看单一维度评估维度重点观察项功耗指标停止/待机电流、唤醒时间、RTC功耗模拟资源ADC位数和采样率、运放数量、比较器响应协议集成USB/PD/CAN/以太网是否有硬件支持封装选择引脚数、封装尺寸、设计匹配度工具链IDE是否免费、例程是否完整、调试器是否易用供货与文档手册质量、勘误表、原厂支持力度和同价位Cortex-M系列比CH32L103的特点是“小封装里的高集成度”。如果你需要的正好是低功耗模拟采集Type-C PD它的竞争力很强。如果需求是跑复杂协议栈、大内存应用那它会很难受。选型不是找“最强芯片”而是找“刚好匹配”的芯片。5. 开发环境、硬件设计与调试避坑实录5.1 开发环境快速上手MounRiver Studio与EVT例程开发CH32L103最省力的方式是直接用MounRiver Studio它是沁恒基于Eclipse做的IDE内置了RISC-V GCC编译器、调试插件和下载工具装完就能编译烧录。新建工程时直接选CH32L103芯片型号会自动生成启动文件和链接脚本不需要自己折腾。官方会提供一个叫CH32L103EVT的示例工程压缩包解压后每个外设都有独立Demo目录GPIO、ADC、OPA、COMP、TIM、SPI、I2C、USART、USB、PD、TouchKey这些全都有。我最推荐的起步顺序是先跑GPIO翻转确认开发环境通然后再跑ADC和OPA例程熟悉模拟外设配置最后再碰低功耗和PD相关例程因为这两个涉及的状态机关系相对复杂。EVT里的代码质量比较高但毕竟是通用示例上项目前建议把底层外设初始化重新梳理一遍删掉用不到的部分降低Flash占用。5.2 烧录调试中的隐蔽坑待机模式连不上怎么办调试低功耗代码时最典型的尴尬场景是程序执行到PWR_EnterSTANDBYMode之后调试器就再也连不上了。这是因为待机模式下调试访问基本关闭此时点击下载OpenOCD会报连接失败。解决方法其实很简单按住板子上的复位键让芯片保持在复位状态然后在IDE里点下载点击后瞬间松开复位键烧录器会在芯片重新上电运行前抓住设备。或者用官方调试器配合“下载前自动复位”的选项也能绕开这个问题。如果芯片进入的是停止模式而不是待机模式一般还能通过调试器唤醒。但保险起见调试低功耗代码时我通常先屏蔽掉真正进入低功耗的语句用一个延时替代确认其他功能正常后再打开。这样能避免“程序跑哪去了都不知道”的情况。另一个坑是SWIO调试引脚被复用。如果量产的固件里把SWIO引脚配成了其他功能后续升级固件时下载器连不上会非常麻烦。我建议在产品上预留一个隔离点SWIO通过0欧电阻或者焊盘跳线接到调试器接口量产固件正常工作时该引脚尽量保持可调试状态。5.3 硬件设计细节电源滤波、复位、触摸走线与调试口规划硬件侧的功夫同样不能省。电源上每个VDD引脚旁边放一个0.1uF陶瓷电容位置尽量贴近引脚整体再加一个10uF钽电容或陶瓷电容做低频去耦。VDDA模拟电源建议串一个磁珠或者小电阻再接入避免数字电路噪声通过电源耦合进ADC。这个细节对12位ADC的采样精度影响很大。复位电路不需要太复杂10k上拉电阻加0.1uF对地电容就够用。如果需要更高的可靠性可以加外部复位芯片但大部分工业场景下简易RC复位搭配内置上电复位就够了。触摸按键的走线有讲究。TouchKey检测依赖电极对地的寄生电容走线越长、途经区域越靠近高频信号误触发的概率越高。我习惯把触摸电极焊盘放在板边走线尽量粗短周围用地铜皮包裹不要和PWM信号线平行。如果是金属外壳设备更要预留绝缘层或者远离金属结构件。时钟方面CH32L103支持内部时钟启动很多应用可以省掉外部晶振。但如果你要跑USB全速或者对串口波特率精度有较高要求建议仔细评估内部时钟的精度是否满足必要时还是放一颗外部晶振稳妥。设计上预留晶振焊盘前期用内部时钟调试后期如果精度不够再贴晶振兼顾了成本、灵活性和风险。最后分享一点个人心得CH32L103这颗芯片我用下来的整体印象是“麻雀虽小五脏俱全”但它并不是万能钥匙。选型时最好先画出整机功耗预算表算清楚每个模块的电量分配再决定主控休眠策略同时对照外设清单确认运放、比较器、PD、触摸这些集成功能是否真的能把外围电路省下来。如果这两个问题的答案都是“是”把它放进设计里基本不会错。拿到评估板之后先别急着跑Demo把一颗LED接在未使用引脚上写一个“睡眠后LED是否意外点亮”的小测试一个小时就能暴露整板的漏电路径。低功耗MCU的选型芯片本身只是起点整板功耗才是最终结果。
阅读完成 · 觉得有帮助?