
简介面向电子爱好者与嵌入式开发者的10A太阳能充电控制器开源方案包含完整原理图与固件源码适用于中小型光伏系统的学习、复刻与二次开发。压缩包共390个文件、约1.99MB以c/h源码与工程配置为主辅以sch原理图、pcb布线文件、hex烧录固件以及xls参数表、doc说明文档和lis/obj等编译中间文件硬件设计到软件实现形成完整闭环。内容覆盖MPPT最大功率点跟踪、涓流/恒流/恒压充电策略、过充过放与短路、反接、过温保护以及电压电流采样、MOSFET功率开关、显示与通信接口等模块代码中可对照PO等算法与控制逻辑。已有2185人学习适合希望动手理解控制器工作原理、验证电路或裁剪功能定制新应用的读者。1. 10A 太阳能充电控制器开源图纸为什么这块板值得照着做一遍上个月某开发者抱来一块烧黑的板子问我能不能照着手里的开源图纸重画一版。这类 10A 太阳能充电控制器看着简单一块太阳能板、一节铅酸电池、一个开关管好像接上就能用。真动手才发现10A 这个档位卡在比较尴尬的位置——大到 MOS 会烫、采样会漂、电池反接会炸管小到还不值得上 MPPT。这份把原理图和代码一起放开的资源价值就在这里功率回路、三路采样、三段式充电状态机、保护逻辑全摊在同一张图上能看清每个电阻为什么取这个值、每行代码为什么卡这个阈值做离网小系统、太阳能充电器、12V 储能盒的或者想拿一块真实功率板练嵌入式的都适合照着走一遍。2. 拓扑与器件选型PWM 直充在 10A 档位的那笔成本账2.1 为什么这个功率段优先 PWM 直充而不是 MPPT太阳能板 18V 峰值电压、12V 铅酸电池PWM 直充的动作只有一个高边开关导通把板子直接并在电池上关断断开。板子的工作点被电池电压钳住跑不到 Vmp 上去这是它的先天缺陷也是它便宜的原因。算笔账。100W 板 Vmp 18V、Imp 5.5A峰值 99W。钳到 13.2V 之后电流基本落在 Isc 附近约 5.8A进电池约 76W换成 MPPT 能拿到 92W 上下。差距 15%~25%冷天、电池深度亏电时更明显——这时候电池电压低钳位点更远离 Vmp。但 10A 档的产品逻辑不一样。整机成本被压到几十块钱加电感、加同步整流、加一路 MPPT 扰动观察环路BOM 涨的钱比多发的那点电还贵还得处理电感啸叫、环路震荡这些新问题。小系统阴影遮挡又严重扰动观察法追到局部极值是常事追上去了也未必比直充多。维度PWM 直充MPPT功率器件1 只高边开关 防反 MOS开关 电感 同步管 更多采样相对 PV 峰值取电75%~85%92%~96%固件规模状态机 300~500 行需要环路 扰动观察1000 行起适合场景100W 以内、板压与电池压差 1.5 倍板压远高于电池压、200W 以上判断口径我一般这么定PV 的 Vmp 除以电池标称电压小于 1.6 就老老实实 PWM超过 2 再考虑升压或 MPPT。18V 板配 12V 电池是 1.5站在分界线上这也是为什么市面上一半的 10A 太阳能控制器都长这样。2.2 功率回路器件与余量导通损耗要按热态算选型最容易犯的错是拿 25°C 的 Rds(on) 直接乘电流。MOSFET 的导通电阻随结温上涨100°C 时按 1.5~1.6 倍估才靠谱。耐压取 PV 开路电压的 2 倍以上。18V 板标称 Voc 22V冷天能摸到 23V所以开关管选 60V 档留足雪崩余量。电流按 10A 连续器件至少 30A 级别Rds(on) 在 Vgs10V 下压到 10mΩ 以内。下面是按这组参数速算损耗和结温的脚本改几个数字就能对比不同管子。# 10A PWM 直充回路开关管与采样电阻损耗/温升速算 I 10.0 # 充电电流 A Rds25 8e-3 # MOS 导通电阻 25°C, Vgs10V k_hot 1.55 # 100°C 时导通电阻上浮系数 D 0.96 # 直充时开关几乎常通占空比接近 1 Qg 60e-9 # 栅极总电荷 C fsw 200 # 开关频率 Hz直充拓扑通常很低 Rshunt 5e-3 # 低边采样电阻 Rth_ja 62 # TO-220 无散热片结到环境热阻 °C/W Tamb 40 # 机壳内环境温度 P_cond I**2 * Rds25 * k_hot * D P_gate Qg * 12 * fsw P_shunt I**2 * Rshunt Tj Tamb (P_cond P_gate) * Rth_ja print(f导通损耗 {P_cond:.2f} W 驱动损耗 {P_gate*1000:.1f} mW) print(f采样电阻损耗 {P_shunt:.2f} W) print(f估算结温 {Tj:.0f} °C)跑出来导通损耗约 1.19W、结温 114°C已经贴着器件上限。这就是血泪经验8mΩ 的管子看着够用热起来就不够。把 Rds25 换成 3mΩ 再跑损耗掉到 0.45W结温 68°C才算留出余量。脚本里的 k_hot 和 Rth_ja 别照抄按实际封装和散热铜箔面积改Rshunt 那 0.5W 是实打实的发热采样电阻一定选 2512 以上的封装。2.3 采样与驱动单片机引脚能直接碰的边界三路采样决定整块板的精度上限。电压通道用分压。PV 侧 Voc 最高 23V取 120k/20k 得到 1/7满量程 23.1V电池侧最高 16V取 100k/20k 得到 1/6满量程 19.8V。12 位 ADC、3.3V 参考单 LSB 是 0.806mV折算到电池端约 4.8mV对 12V 铅酸够用——恒压点 14.4V 允许 ±0.1V分辨率差了两个数量级。电流通道用 5mΩ 低边采样加 20 倍同相放大10A 时 50mV 进运放输出 1.0V折算下来单 LSB 约 8mA。放大倍数别贪大20 倍时 15A 峰值对应 1.5V还留在运放的线性区里。驱动这块有个硬边界N 沟道 MOS 做高边栅极要抬到 Vbat10V 以上。PWM 直充的占空比长期接近 100%自举电容没有刷新窗口会掉电。常见做法是要么用 P 沟道 MOS 配 NPN 拉低栅极、电阻上拉关断要么上电荷泵给 N 沟道供栅压。前者便宜、慢一点后者驱动干净但多几块钱。图纸里用哪种看栅极那一路有没有二极管加电容的倍压结构就知道。3. 把原理图读成信号链从 PV 入口到电池端的四段路3.1 入口这一段保险丝、TVS、防反 MOS 的先后顺序从 PV 往里走顺序是自恢复保险丝、TVS、防反 MOS、输入电容、开关管。保险丝按 1.5 倍电流取10A 系统选 15A 自恢复件动作时间要够慢避免开机浪涌误跳。TVS 选钳位电压略高于 PV 最高 Voc 的型号12V 系统一般在 26V 上下它的作用不是稳压是吸收线缆电感和雷击耦合过来的尖峰。防反不能用肖特基二极管。0.7V 压降乘 10A 就是 7W散热片压不住换成 Rds(on) 10mΩ 的 P 沟道 MOS损耗 1W同一个封装就能带。注意体二极管方向要顺着正常电流装反了等于在入口串了个正向二极管。输入电容做高频旁路不是储能。PWM 直充在开关沿上会有 di/dt 尖峰1000μF 低 ESR 电解并 0.1μF 瓷片基本够电容离开关管漏极越近越好超过 2cm 就用铜箔补。3.2 三路采样网络分压比、RC 滤波与同步采样通道前端结构满量程折算精度滤波PV 电压120k/20k 分压1/723.1V5.6mV10k 1μFfc≈16Hz电池电压100k/20k 分压1/619.8V4.8mV10k 1μF充电电流5mΩ 采样 20 倍同相放大15A8mA1k 1μF两级板温10k NTC 上拉分压-20~100°C约 0.5°C软件中值RC 的截止频率算一遍10k 配 1μF 是 15.9Hz对 200Hz 的开关纹波衰减约 12 倍看着够实际直充时电池端纹波幅度能到几百毫伏光靠硬件滤不干净。真正管用的是同步采样——在开关导通的中点触发 ADC避开开关沿。代码里把 ADC 触发源挂到 PWM 定时器的中点比较事件上比后面堆十级数字滤波都省事。分压电阻别一味求大。100k/20k 这一路在 16V 时漏 0.27mA一年不到 2.4Ah可以接受再往上加到兆欧级ADC 采样保持电容充不满读数会飘反而踩坑。3.3 栅极驱动回路与功率环布局栅极串 22Ω 是通吃值太小振铃、太慢发热。关断路径反向并一只二极管加 10Ω让关断更快减少开关管在米勒平台停留的时间。电荷泵或 P-MOS 上拉这两种结构看驱动波形差别很大电荷泵的 Vgs 是平的P-MOS 上拉在开通瞬间有个 RC 上升占空比接近 100% 时反而没影响。用示波器加弹簧地线量栅源Vgs 低于 8V 就要查泵电容是不是漏电或者容值不够。布局上两条规矩。功率环路——输入电容正极、开关管漏源、电池正极——围出来的面积越小越好超过 3cm² 就开始往外辐射。采样地单独走从电池负端子单独拉一根到 ADC 地别和功率地共用一段铜皮否则 10A 电流在几毫欧走线上产生的压降会直接叠进电流读数里。3.4 保护逻辑在图纸上的落点谁管硬件、谁管软件保护项采样点阈值归属动作电池过压BAT ADC15.5V / 100ms软件二级关断60s 后重试电池欠压BAT ADC11.0V软件断开负载输出过流/短路采样电阻 比较器15A硬件一级直接拉驱动使能脚过温NTC75°C软件降额 50%95°C 关断PV 反接防反 MOS—硬件体二极管自动阻断夜间倒灌PV/BAT 比较PV BAT1V软件强制占空比归零分界线很清楚微秒级的事交给硬件比较器毫秒级以上的交给固件。比较器输出直接接到驱动的使能脚不经过单片机这一条在调板子的时候能救命——固件跑飞了硬件还在。4. 固件怎么跑三段式充电状态机与三处保护阈值4.1 时基与主循环三级任务的划分依据直充拓扑的电流变化慢状态机 200ms 跑一次完全够采样要快10ms 一次配合同步触发保护必须在采样之后立刻判不能等状态机。/* main.c —— 1ms 时基驱动的三级任务骨架 */ volatile uint32_t g_ms 0; void SysTick_Handler(void) { g_ms; } int main(void) { board_init(); /* 时钟、GPIO、ADC、PWM 定时器 */ adc_calibrate(); /* 上电零点校准见 4.4 */ pwm_set_duty(0); /* 上电先关断避免插入电池时的浪涌 */ uint32_t t_fast 0, t_slow 0; while (1) { uint32_t now g_ms; if (now - t_fast 10) { /* 10ms采样 滤波 保护 */ t_fast now; sample_all(); /* PV / BAT / 电流 / NTC */ filter_update(); /* 中值 一阶低通 */ protect_check(); /* 见 4.3优先级最高 */ } if (now - t_slow 200) { /* 200ms充电状态机 */ t_slow now; charge_fsm(); /* 见 4.2 */ } } }三个数字都有来由1ms 时基是 SysTick 的常规配置够细也不占 CPU10ms 采样周期对应 100Hz 的采样率对 200Hz 开关纹波正好做整周期平均200ms 状态机是为了让占空比调节不跟着纹波抖调节步长乘以周期等效积分时间落在秒级。4.2 三段式充电恒流、恒压、浮充的切换条件铅酸电池的充电曲线是三段先恒流把电量灌进去电压爬到 14.4V 转恒压电流自然回落到 0.1C 以下转浮充 13.5V。typedef enum { ST_IDLE, ST_CC, ST_CV, ST_FLOAT, ST_FAULT } chg_state_t; static chg_state_t s ST_IDLE; static int16_t duty 0; /* 0~1000 对应 0~100% 占空比 */ void charge_fsm(void) { float vbat bat_volt(), ibat bat_curr(), vpv pv_volt(); switch (s) { case ST_IDLE: /* 等光照PV 要高出电池 3V 才启动 */ if (vpv vbat 3.0f vbat 12.6f) s ST_CC; break; case ST_CC: /* 恒流电流环占空比逐拍累加 */ duty (int16_t)(KP_I * (CC_TARGET - ibat)); duty clamp(duty, 0, DUTY_MAX); if (vbat CV_TARGET - 0.05f) s ST_CV; /* 提前 50mV 换段防超调 */ break; case ST_CV: /* 恒压电压环接管电流自己往下掉 */ duty (int16_t)(KP_V * (CV_TARGET - vbat)); duty clamp(duty, 0, DUTY_MAX); if (ibat CC_TARGET * 0.1f) s ST_FLOAT; break; case ST_FLOAT: /* 浮充只补自放电 */ duty (int16_t)(KP_V * (FLOAT_TARGET - vbat)); duty clamp(duty, 0, DUTY_MAX); if (vbat FLOAT_TARGET - 0.4f) s ST_CC; break; case ST_FAULT: duty 0; /* 故障锁存靠 protect_check 恢复 */ break; } if (s ! ST_FAULT) pwm_set_duty(duty); }关键在三个地方。KP_I 和 KP_V 是增量式积分增益量纲是「占空比计数 / A」和「占空比计数 / V」实际取值靠试先给 2看电流超调超调超过 15% 就减半。换段点提前 50mV 是为了压超调电池有内阻恒压段一开端电压会先冲一下。浮充回差 0.4V 是防抖没有回差的话负载一波动就在恒压和浮充之间来回跳。CC_TARGET 默认按 0.1C 设100Ah 电池就是 10A正好吃满这块板小容量电池一定要在参数表里改小10A 灌进 20Ah 电池两个小时就鼓包。4.3 保护与故障恢复四级判断的先后顺序static void protect_check(void) { float vbat bat_volt(), vpv pv_volt(), ibat bat_curr(); float temp ntc_temp(); /* 1) 过压硬件比较器已兜底这里做二级关断 */ if (vbat OVP) { fault_set(FAULT_OVP); return; } /* 2) 夜间/阴影PV 低于电池就强制关断防倒灌 */ if (vpv vbat 1.0f) { pwm_set_duty(0); s ST_IDLE; return; } /* 3) 过温先降额再关断别一步切死 */ if (temp 95.0f) { fault_set(FAULT_OTP); return; } else if (temp 75.0f) { duty duty / 2; } /* 4) 过流占空比很小却有大电流判短路锁 30s */ if (ibat 15.0f duty 100) { fault_set(FAULT_OCP); } } static void fault_set(uint8_t code) { pwm_set_duty(0); s ST_FAULT; g_fault_ms g_ms; /* 记录时刻30s 后自动重试 */ }顺序不能乱。过压要第一个判电池已经 15.5V 了还在充电就是析氢夜间关断排在过温前面是因为 PV 掉下来的时候开关还开着倒灌电流会立刻把采样电阻那一路拉负。过温走降额不走直接关断太阳底下中午限一半功率比整机停掉再重启的体验好得多。过流那条花了我最久才想明白直充拓扑里占空比接近 1 的时候电流本来就大不能按电流绝对值判短路判据是「占空比很小却电流很大」那才是输出端短路此时占空比还没来得及升上去。4.4 参数表与两点标定参数符号取值依据恒流目标CC_TARGET10.0A0.1C100Ah 电池恒压点CV_TARGET14.4V 25°C2.40V/单体浮充点FLOAT13.5V2.25V/单体温度补偿—-18mV/°C-3mV/°C/单体 × 6欠压保护LVD11.0V1.83V/单体过压保护OVP15.5V2.58V/单体恢复回差—0.3V防抖PWM 频率fsw200Hz兼顾纹波与开关损耗标定用两点法代码里就三行/* 两点标定raw_low/raw_high 是实测 ADC 码值ref_* 是万用表读数 */ typedef struct { float k, b; } cal_t; static inline float cal_apply(const cal_t *c, uint16_t raw) { return c-k * raw c-b; } /* k (ref_high - ref_low) / (raw_high - raw_low); b ref_low - k * raw_low; 系数写进 Flash掉电不丢 */电压通道给 12.00V 和 15.00V 两个点电流通道先用 0A 校零点再给 5A 校增益。零点不校的话运放失调乘 20 倍之后能差出 0.2A浮充阶段判「电流小于 1A」会一直判错。5. 调试排查实录五个把 10A 板跑挂的坑与对应解法5.1 采样与读数类数字跳状态机跟着乱跳坑一太阳一好充电电流读数上下跳 1A状态机在恒流和恒压之间反复切。现象是串口打印的电流值每秒抖三四次占空比跟着抖能听到板子有轻微啸叫。 原因是 ADC 采样点落在开关沿上直充时电池端纹波几百毫伏采样时刻差几十微秒读数就差很多。 解决是三点ADC 触发源改到 PWM 定时器中点比较事件做同步采样软件加五点的中值滤波再串一阶低通系数取 1/8占空比每次调节限幅 ±20 个计数不让它一步跳到位。改完之后电流读数波动压到 50mA 以内。坑二电池电压读数总比万用表高 0.3V恒压点提前到电池充不满。现象是万用表量 13.9V固件显示 14.2V恒压段早早开始充电电流一直上不去。 原因是分压电阻的地接在功率地上10A 电流在几毫欧的共用铜皮上产生压降被一起采进去了。 解决是把采样地单独走 Kelvin 到电池负端子同时在固件里做线阻补偿——按电流大小反推压降1mΩ 线阻在 10A 下就是 10mV补偿量写死也行。5.2 功率与驱动类发热、炸管、倒灌坑三MOS 摸上去烫手效率比算的低一截。现象是 8A 充电半小时管子外壳 80°C导通损耗明显超过按 25°C 参数估的值。 先查栅极示波器加弹簧地线量 Vgs只有 6.8V。Rds(on) 在 Vgs6.8V 时比 10V 高出将近一倍。 原因是用的 P-MOS 上拉驱动上拉电阻 10k 太大栅极电荷放不干净。把上拉降到 2.2k、关断路径串 10Ω 加反向二极管Vgs 拉到 10.2V同样电流下外壳温度掉到 55°C。坑四电池反接一次采样电阻和单片机一起报废。现象是用户把电池正负接反上电瞬间冒烟拆开看 5mΩ 采样电阻开路MCU 的 ADC 引脚对地短路。 原因是分压网络和采样放大直接挂在电池上没有限流也没有钳位反接时电流从 ADC 引脚灌进内部 ESD 二极管。 解决是每一路采样串 10k 限流电阻ADC 引脚加 BAT54S 双肖特基钳到 3.3V 和地电池入口并一只 5W 的 TVS 做最后一道。反接 MOS 只保护功率回路保护不了采样网络这两件事得分开做。坑五阴天傍晚电池电量反而往下掉。现象是光照弱的时候充电电流显示为零但电池电压一小时掉 0.1V。 原因是 PV 电压跌到接近电池电压时高边开关还开着电流从电池倒灌回板子体二极管那条路也能走。 解决是固件里把「PV BAT 1.0V 立刻占空比归零」放在保护判断的第二位1V 的回差是留给云层飘过的回差太小会在多云天反复开关。硬件上再并一只反向肖特基做兜底。5.3 状态机与参数类充不满、鼓包、重启坑六顺带记一笔电池两个月就鼓了。现象是 20Ah 小电池配这块 10A 板充了两个月外壳变形。 原因是 CC_TARGET 用的默认 10A对 20Ah 电池是 0.5C远超 0.1C~0.2C 的合理区间加上恒压点没做温度补偿夏天机壳内 50°C 时等效恒压点偏高。 解决是把恒流目标做成按容量配置的参数上电读一次配置区恒压点接 NTC 做 -18mV/°C 的补偿。这条不是代码 bug是参数没跟着电池走但后果最贵。6. 进阶用法把 10A 板当标定平台用可调电源校到 ±2%板子焊完能充电只是及格能不能批量交付看的是标定。我一般不用真太阳板做验证——光照不可控同一块板上午下午能差 20%。做法是用一台可调直流电源模拟 PV一台电子负载或大功率电阻模拟电池把工作点一个个顶到边界上。测试点按这张表走每个点稳定 30 秒再记录测试点PV 模拟电压电池端电压期望电流允差启动阈值从 12V 缓升12.2V15.5V 起充±0.3V恒流段18.0V12.5V10.0A±2%恒压转段18.0V14.35V开始回落±0.1V浮充转段18.0V14.4V 1.0A±0.1A夜间关断从 18V 降到 12V12.8V0A无倒灌过流保护18.0V输出短接15A 内切断 10ms同步采样的效果在这个环节最能看出来把示波器探头挂在采样电阻两端看 ADC 触发那一刻的波形如果触发点正好落在纹波谷底或峰顶读数就会系统性偏一头。调触发相位的时候用可调电源把电流固定在 5.00A转动定时器的比较值看串口读数什么时候和万用表最贴——这个最贴的点就是同步点比看 datasheet 算半天快。还有一个我常做的动作把恒压点故意设成 13.8V跑一整晚第二天早上量电池端电压如果比设定值低 0.2V 以上说明线阻补偿没做对或者采样地在漏电流。这个测试不花设备专治「板上看着都对、装上系统就不对」。从那以后我每次改完采样或换 MOS都会强制走一遍这张测试表尤其是同步采样相位和线阻补偿这两项——它们不出错的时候你感觉不到出错的时候表现为「电池总是差一点充满」查起来能耗掉一整天。希望帮到你。本文还有配套的精品资源点击获取