简介一套10A太阳能充电控制器的完整开源资料面向嵌入式、电力电子及新能源方向的工程师和爱好者适用于中小型离网光伏系统的设计参考与二次开发。内容覆盖MPPT最大功率点跟踪、涓流/恒流/恒压三段式充电、短路/反接/过温保护等核心环节并给出主控选型、电压电流采样、MOSFET驱动和显示模块的电路说明原理图与代码完全开放。整包共390个文件以C源文件、头文件、原理图与PCB工程、hex固件及工程配置文件为主压缩后仅1.99MB目录清晰便于按模块查阅。目前已有2174人学习/下载。通过该资源可获取可编译的完整工程、硬件电路设计、调试日志与烧录固件既能深入理解从光伏阵列到电池管理的控制链路也可直接修改参数实现定制化充电策略是学习太阳能控制器内部机制和进行产品化改造的实用素材。 做太阳能充电控制器这件事其实最初是被网上各种“原理图”坑怕了。大部分帖子只放一张清晰度堪忧的原理图代码要么没有要么跟原理图对不上引脚照着抄到最后只能靠猜。所以我做这版10A太阳能充电控制器时给自己定了一条规矩原理图和代码必须完全开放并且拿到就能用、能看懂、能改。项目用STM32F103C8T6做核心控制走非同步Buck降压拓扑实现MPPT充电板子实测充电电流能做到10A给12V铅酸电池或磷酸铁锂电池充电都行。下面把这版的设计思路、关键原理图模块、代码框架、调试踩坑和校准方法一次讲清楚。1. 10A这个数字怎么来的——先把系统边界划清楚很多朋友一上来就找原理图其实不对。画板子之前第一件事是把需求边界定死。10A充电控制器听起来就是一个电流值但背后牵涉到太阳能板功率、电池电压、拓扑结构、散热方式一整套连锁决策。1.1 应用场景和功率预算我这版的目标场景是12V电池系统面板用18V或36V的常规光伏板。12V电池充满电压一般在14.4V左右10A充电意味着要给电池送大约144W的功率。考虑Buck电路效率在85%到90%之间输入端至少要准备160W到170W。对应市面上常见的180W或200W太阳能板Vmp大约18VImp接近9到10A跟控制器刚好匹配。这里需要先说透一个概念太阳能板不是直接接电池就能充满的。板子的最佳工作点最大功率点随光照和温度漂移整天在变。如果用传统PWM方式把板子直接斩波到电池电压板子会被强行拉离最佳工作点尤其是在中午强光时18V的板子被拉到14.4V甚至更低浪费掉的功率相当可观。MPPT充电控制器的核心就是通过Buck电路主动调节输入侧等效阻抗让太阳能板始终工作在最大功率点附近。10A这个档位刚好是MPPT控制器里性价比最合适的门槛——再往上走功率管的损耗和散热成本会明显增加。1.2 为什么选STM32F103C8T6而不是专用充电IC做太阳能控制器市面上有一堆专用充电芯片比如CN3767、LT3652这类外围简单画完就能用。但它们有个共同问题充电策略是芯片写死的没法做MPPT也没法根据电池类型自由调恒压值和浮充值。对做产品或者想深入学电源控制的人来说这不是省事是把路堵死了。用STM32F103C8T6的好处首先自然是便宜几块钱一片。其次是这个芯片的ADC和定时器资源完全够用三四个ADC通道做电压电流采样高级定时器TIM1输出带死区互补PWM可以给半桥驱动提供信号以后想升级同步整流也不用换主控。第三是资料生态好不管你用标准库、HAL库还是直接寄存器操作网上都能找到参考。说实话做10A这个量级的电源控制用C8T6属于杀鸡用牛刀但留出的裕量能让后续迭代轻松很多。这类项目选型还有个容易被忽略的点开发调试成本。C8T6的SWD调试只需要一根几块钱的ST-Link单步、看变量、改参数都很方便。用专用充电IC出了问题只能靠万用表猜效率完全不在一个量级。2. 原理图核心模块拆解——电源路径和控制部分是两套逻辑原理图我分成两部分讲功率路径负责把太阳能板的能量搬进电池控制部分负责采样和决策。两者之间用PWM信号和模拟采样通道连接。很多人画这类板子习惯把功率和控制混在一起布局开起来没问题一到EMI和纹波就头疼后面调试部分会专门讲。2.1 输入侧设计TVS、防反接和板级供电太阳能板输入端我第一件事不是接Buck而是加TVS管和输入电容。光伏板在室外走线很长雷击感应或者热插拔都会产生高压尖峰。输入端我放了SMBJ30A双向TVS把瞬态尖峰钳在30V左右后面接了两个并联的470uF电解电容吸收低频纹波再加一个1uF陶瓷电容滤高频。这样能保证后级MOSFET不会因为输入浪涌击穿。防反接这块很多人图省事直接串二极管。但10A电流流过二极管就算是肖特基也要白白损失接近4W功率。我的做法是用一个P沟道MOSFET做极性保护放在输入负极或者正极回路里利用MOSFET的体二极管先导通再让管子完全打开压降只有毫伏级。这个电路结构不复杂就是一颗PMOS加两颗电阻但能省下好几瓦的损耗对整机效率贡献很明显。板级供电直接从电池取电因为控制器要给电池充电电池本身就是最稳定的电源。用一个LDO把电池电压降到3.3V给STM32供电型号用的AMS1117-3.3虽然效率不高但控制电路整体功耗只有几十毫安完全够用而且便宜、到处都能买到。注意输入侧要加一颗反接二极管防止电池接反时把LDO烧掉。2.2 Buck功率级MOSFET、驱动、电感与续流二极管这是整张原理图的核心也是10A电流真正流过的地方。拓扑选的是非同步Buck降压高边用N沟道MOSFET做开关低边用肖特基二极管续流。选N沟道而不是P沟道是因为N沟道MOSFET的导通电阻普遍更低、价格更便宜大电流场景优势明显。代价是需要一个自举驱动电路把栅极电压抬到源极以上这里用了一颗IR2104半桥驱动芯片成本不高电路也简单还能顺便提供死区保护避免上下管直通。开关频率取50kHz。频率越高电感和电容越小但MOSFET开关损耗越大频率太低则电感体积大到不实用。50kHz在这个功率档位下属于比较均衡的选择。电感值按连续导通模式CCM来算$$D \frac{V_{out}}{V_{in}} \frac{14.4}{18} \approx 0.8$$$$\Delta I 0.3 \times 10A 3A$$$$L \frac{(V_{in} - V_{out}) \times D}{f_s \times \Delta I} \frac{(18 - 14.4) \times 0.8}{50000 \times 3} \approx 19.2\mu H$$实际取标称值22uH磁芯用铁硅铝磁环饱和电流一定要留够。10A满载下电感峰值电流接近11.5A所以我选了饱和电流15A以上的磁环避免电感在峰值电流时饱和导致电流失控。MOSFET选型是另一个关键点。10A电流流过MOSFET导通损耗等于I²乘以Rds(on)平方关系让导通电阻的影响被放大。如果用一颗Rds(on)25mΩ的管子10A下损耗就有2.5W散热片都要做大一号。我最终选的是Rds(on)在8mΩ左右的逻辑电平MOSFET10A时导通损耗约0.8W配合PCB铜箔散热还能接受。如果你手头的管子导通电阻偏大一个务实方案是两颗并联均流但最好还是直接选低内阻的管子一劳永逸。续流二极管用MBR164516A/45V的肖特基。Buck电路里二极管导通占空比约(1-D)也就是20%左右平均电流约2A但峰值电流和MOSFET一样是11A所以耐流等级不能只看平均电流。肖特基的好处是正向压降低、反向恢复快对效率帮助明显。2.3 ADC采样通道电压分压、电流采样与滤波STM32要干活必须精确知道三件事太阳能板电压、电池电压、充电电流。太阳能板电压和电池电压都用电阻分压后送ADC。分压电阻取100k/10k的比例把最高40V的输入电压降到3.3V以内ADC读数乘11就是真实电压。这里有个容易踩的坑分压电阻的精度直接影响测量精度普通5%贴片电阻实际误差可能到10%我全部用的1%精度电阻。再一个细节是ADC引脚对地并联100nF电容用来滤除采样线上的高频干扰。STM32的ADC输入阻抗不低但开关电容采样瞬间会抽取电流电容起到了电荷池的作用能让采样更稳定。电流采样我用了方案和大多数人不太一样。低边采样在原理上最简单在电池负极和地之间串一个0.01Ω的采样电阻10A电流产生100mV压降再用运算放大器放大10倍到1V量程送给ADC。这个方案的坑在于低边采样会把地平面抬高影响控制器自己的地参考而且采样信号很容易被功率开关的噪声污染。更稳的方案是隔离放大器或者霍尔传感器但成本高。综合考虑后我还是用低边采样但在PCB布局上做了文章采样电阻单独走线用开尔文连接的方式引出采样线采样信号经过一级RC低通滤波100Ω电阻100nF电容截止频率约16kHz再进运放。运放用LM358就够供电3.3V注意需要选择能工作在3.3V单电源下的版本。经过这样的处理电流采样在10A时读数稳定到±0.1A以内对充电控制来说已经非常够用了。3. 代码实现——充电状态机和MPPT才是控制器的灵魂原理图把硬件通道搭好了剩下的核心工作全在代码里。充电控制器绝对不能写成上来就给最大占空比的傻PWM必须有完整的充电策略和状态机。我把整个控制程序拆成几个层次ADC数据采集层、物理量换算层、MPPT控制层、充电状态机层、保护逻辑层。3.1 充电状态机从黑夜到充满的完整流程状态机是整个程序的骨架。我的控制器有五个状态NIGHT夜间待机、BULK恒流充电、CV恒压充电、FLOAT浮充、FAULT故障保护。NIGHT状态下太阳能板电压低于电池电压加上回差电压说明天黑了。此时MOSFET完全关闭MCU进入低功耗模式只保留极低功耗的电压检测每几秒唤醒一次看看天亮了没有。这个状态太重要了——很多控制器晚上电池被反向漏电拖垮就是没处理好。天亮后太阳能板电压上来了控制器进入BULK状态。这时电池电压较低充电策略是恒定电流充目标电流10A。在BULK状态下控制器的主要任务不是稳压而是全力从太阳能板榨取最大功率。所以BULK状态下MPPT是激活的不断调整占空比让面板输出最大功率同时把充电电流限制在10A以内。如果光照充足实际充电电流可能被限制在10A光照不足时MPPT找到的就是当前光照下的最大功率点充电电流自然降低。当电池电压升到恒压阈值12V铅酸电池对应14.4V状态切换到CV。这时仍然跑MPPT但优先级变了电压环开始介入通过PID调节器降低充电电流让电池电压稳定在14.4V防止过充。CV状态维持一段时间电流逐渐减小。当电流降到设定值比如0.5A以下说明电池基本满了进入FLOAT状态。FLOAT状态把充电电压降到13.6V左右只提供涓流补充电池自放电。这个状态不需要MPPT了占空比直接由电压环决定。太阳能功率富余的部分就白白放掉这是正常的因为电池已经不需要那么多能量了。3.2 ADC采样和物理量换算ADC采样看似简单实际有很多细节。我每路ADC连续采样16次去掉最大最小值再取平均这个简单的滤波算法能滤掉大部分随机噪声。采样通道顺序也要注意功率MOSFET开关瞬间产生的高频噪声是最大的干扰源所以采样要避开PWM开关瞬间在PWM周期的固定时刻触发ADC转换。物理量换算时需要用万用表实测校准分压比和采样电阻的实际值不能直接用理论值。具体校准方法在第5章讲。uint16_t read_adc_filtered(uint8_t ch) { uint32_t sum 0; uint16_t min_val 0xFFFF, max_val 0; for (int i 0; i 16; i) { ADC1-SQR3 ch; ADC1-CR2 | ADC_CR2_SWSTART; while (!(ADC1-SR ADC_SR_EOC)); uint16_t val ADC1-DR; sum val; if (val min_val) min_val val; if (val max_val) max_val val; } sum - min_val max_val; return (uint16_t)(sum 4); }换算公式很简单float v_pv adc_vpv * 11.0f / 4096.0f * 3.3f; float v_bat adc_vbat * 11.0f / 4096.0f * 3.3f; float i_charge adc_ibat * 10.0f / 4096.0f * 3.3f / 10.0f; // 运放放大10倍3.3 MPPT扰动观察法的工程实现MPPT算法网上资料一大把但真正能稳定跑起来的实现不多。扰动观察法PO的思路是周期性地给占空比加一个小扰动然后观察输出功率的变化方向。功率变大就保持扰动方向功率变小就反过来。逻辑非常简单但参数调不好就会出现剧烈震荡或者追踪速度慢。void mppt_poll(void) { float p_now v_pv * i_pv; float dp p_now - p_prev; float dv v_pv - v_prev; // 根据功率变化方向调整占空比步进 if (dp 0) { if (dv 0) duty MPPT_STEP; else duty - MPPT_STEP; } else { if (dv 0) duty - MPPT_STEP; else duty MPPT_STEP; } // 限幅 if (duty 0.05f) duty 0.05f; if (duty 0.95f) duty 0.95f; p_prev p_now; v_prev v_pv; }这里有个细节就是补偿dv方向。因为光伏板的P-V曲线是单峰曲线最大功率点处dP/dV0但单纯看功率变化方向来确定占空比在光照快速变化时容易误判。我加入电压变化方向的判断是为了区分“占空比扰动引起的功率变化”和“光照变化引起的功率变化”虽然不能完全消除误判但在阴晴交替的天气下能明显减少错误追踪。MPPT的扰动周期和步长是一对冤家。周期太短MPPT和电压环互相打架周期太长追踪速度慢。我最终取的是每200ms扰动一次占空比步长0.5%。你如果发现输出功率波动厉害可以减小步长如果发现追踪速度太慢可以增加步长或者改成变步长。3.4 保护逻辑过压、过温、反接、夜间软件层面的保护比硬件保护更细腻。第一个是电池过压保护。虽然状态机已经在CV阶段限压了但万一采样异常或者状态机跑飞电池电压超过15V必须立即关断PWM并进入FAULT状态且需要人工复位或者等到电压降到安全值以下才能复位。第二个是过温保护。MOSFET和电感长时间大电流工作温度上升很快我在MOSFET旁边贴了一个NTC热敏电阻10k分压后送ADC。温度超过80度时通过调整最大允许电流来降额超过95度时直接停机。这块在夏天户外暴晒场景下特别重要没有热保护控制器连续满载跑几小时烧管子是必然的。第三个是电池反接保护。除了硬件上的PMOS极性保护软件里也要检测上电后先读一遍电池电压如果电压极性异常或者电压低于2V默认电池没接好或者接反不启动PWM。4. 调板和踩坑记录——原理图看着对代码跑起来全是细节这一章是血泪经验。原理图是静止的代码是活蹦乱跳的两者第一次相遇时各种问题全冒出来了。我把我实际调试中遇到的最有代表性的几个坑列出来按排查链路一步步还原希望能帮你少走弯路。4.1 上电就炸栅极驱动根因是复位期间GPIO浮空第一版样板焊好准备上电测试。我把程序下载进去刚把太阳能板接上只听“啪”一声IR2104冒烟了。第一反应是驱动芯片接错线反复对照原理图没错。又换一片还是炸。后来用示波器抓MCU复位瞬间的PWM引脚波形发现问题了STM32在复位期间所有GPIO是浮空输入状态引脚电平不确定。如果此时PWM引脚被外部环境拉到高电平IR2104的输入就等于被强制拉高高边MOSFET立即导通。电池和太阳能板之间相当于没有开关直接连起来了瞬间电流直接击穿驱动芯片。解决办法有两个我都做了一是软件上在main函数最开始就把PWM引脚配置为推挽输出且输出低电平同时把PWM定时器设置为关闭状态再初始化时钟和外设二是硬件上在每个PWM输入引脚对地加10k下拉电阻保证MCU没起来时驱动输入保持低电平。改动完之后随便上下电都没再炸过。这个坑说明一个问题单片机程序跑起来之前的状态和跑起来之后同等重要。4.2 电流采样毛刺大到MPPT乱跳布局和滤波都要修第二版样板能正常充放电了但发现一个奇怪现象光照稳定时MPPT输出功率在82W到96W之间来回跳完全不稳定。用示波器看电流采样信号噪声尖峰高达300mV而有效信号在1V左右信噪比完全不行。排查链路是这样的先怀疑软件滤波不够把滑动窗口从16次加到64次改善有限。再用示波器探针逐一排除断开PWM噪声消失恢复PWM噪声回来。说明噪声来源于功率开关。问题出在PCB布局。我最初把采样电阻放在了电感附近功率路径的地电流和采样回路的地电流共用了一段走线开关瞬间的地弹全叠加到采样信号上了。后续按开尔文接法整改采样电阻独立走线离功率地单点汇聚采样信号线双线平行走到运放中间不穿功率路径。另外在运放输入前加了一级100Ω100nF的RC低通滤波截止频率约16kHz能把高频开关噪声压下去。改完之后采样纹波从300mV降到20mV以内MPPT输出也稳住了。4.3 电池反接瞬间烧掉采样电阻教训很深刻有一次测试完我顺手把电池拆下来结果电池线接回去的时候正负极拿反了。因为前面加了PMOS极性保护理论上不该出事。但电流采样电阻在电池负极回路上电池反接时采样电阻变成直接从电池正极到负极的唯一低阻路径瞬间几十安培电流直接把0.01Ω电阻烧成断路。这里的问题在于PMOS极性保护挡住了反向电流但采样电阻接在保护之前的低边反而成了反接时的“优先受害者”。解决办法是把极性保护MOSFET放到采样电阻之前让反接时的电流路径先经过PMOS关断采样电阻就不会暴露在反压下。同时采样电阻选更大功率封装的2512封装短时间能扛过载。4.4 MPPT在阴天反复横跳光照突变检测要单独做还有一次阴天测试云层快速移动导致光照忽强忽弱。这时候MPPT的表现很糟糕功率一会认为要增大占空比一会认为要减小在20%到80%占空比范围内剧烈摆动。电流一会2A一会6A回到家里看日志才发现充电量少得可怜。根因是扰动观察法把光照变化误判成了占空比扰动引起的功率变化。云层遮住阳光时功率下降MPPT认为是占空比方向错了就反向调整结果光照恢复后功率又变化算法陷入无效震荡。修法是在MPPT里增加一个判定条件如果检测到太阳能板电压变化超过一个阈值比如5%且变化方向和占空比扰动方向不一致就判断是外部环境变化暂停MPPT调整等光伏板稳定后再恢复。增加这个逻辑之后阴天情况下的充电量提升明显因为不再做无用功了。5. 校准、效率测试和移植建议——提交之前把活做完程序调通能充电不代表能交付。ADC采集用的分压电阻和采样电阻都有误差必须做整机校准否则显示的电压电流和实际差很多。另外这套系统的参数能不能复用也需要清楚边界。5.1 电压电流校准流程我用的方法是“两基准点校准法”。电压校准用可调直流稳压电源供电分别在10V和20V两点测ADC原始值和万用表实测值算比例系数和零点偏移。电流校准用电子负载给电池放电回路加已知电流在2A和8A两点校准。实际操作时可以把校准系数直接写死在程序里。需要注意每个板子的电阻误差都不同如果你复刻原理图建议自己重新跑一遍校准流程直接用我这套系数会有误差。STM32F103内部有一个3.3V基准电压实际可能偏到3.45V所以校准计算时最好用实际测量的VREF值而不是理论3.3V。更精确的办法是用ADC的内部参考电压通道VREFINT反推VREF我偷懒没做直接用万用表测了3.3V输出脚的电压效果也够好。5.2 整机效率实测数据校准完以后我做了完整的效率测试。用直流电源模拟太阳能板的IV特性分别设置面板电压18V、电流8.3A对应约150W输入输出接可调电子负载模拟12V电池。实测在50kHz、满载10A充电时整机效率在88%到91%之间浮动。其中功率管导通损耗约0.8W续流二极管损耗约1.1W采样电阻损耗0.1W控制电路功耗0.15W合计损耗约2.15W跟输入输出功率差基本吻合。如果后面升级同步整流把续流二极管换成第二个MOSFET效率能再往上提2到3个百分点但控制复杂度明显增加。5.3 改成24V系统或者磷酸铁锂电池需要注意什么这套系统移植到24V电池系统也很方便。24V电池充满电压是28.8V太阳能板可以用36V或48V的。需要改的地方包括分压电阻比例要重新算不能让ADC超压TVS管选型要提高到60VMOSFET耐压最好选60V以上LDO换成支持宽压输入的型号。软件层面只需要改充电电压目标值和浮充电压其他状态机逻辑完全不用动。如果换磷酸铁锂电池12V铁锂充满电压是14.6V比铅酸略高浮充电压14.2V左右而且不需要恒压时间过长。我在代码里用宏定义把所有充电电压和回差电压集中统一管理切换电池类型时只需要改一个配置头文件这个习惯强烈建议你养成。5.4 开源复刻时的建议最后说说复刻这套项目时的建议。原理图和代码我都放在开源仓库里了但我不建议你直接把Gerber发出去打样然后焊完就上电而是建议按这个顺序来先把原理图每个模块梳理清楚确认自己能讲明白每个电阻电容的作用然后只焊电源和MCU最小系统部分下载跑一个LED闪烁程序验证基本功能再焊采样通道用稳压源验证电压电流读数准确最后才焊功率部分接电池假负载调PWM波形。一步一步验收每步都确认无误再进入下一步。我在实际调这版控制器时最大的体会是电源类项目和纯数字逻辑项目完全不同它牵涉到磁学、热学、模拟采样、嵌入式软件多个维度。哪个维度没做好系统都会用各种诡异现象告诉你。能把一块板子从原理图到代码都吃透再回头去看网上的各种原理图你就能一眼分辨出哪些是能跑的设计哪些只是画着好看。这套控制器不是终点我后面还打算把同步整流和蓝牙模块加进去到时候再继续分享。本文还有配套的精品资源点击获取