ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

10A太阳能充电控制器开源图纸:PWM直充与三段式充电调试

10A太阳能充电控制器开源图纸:PWM直充与三段式充电调试

简介:面向电子爱好者与嵌入式开发者的10A太阳能充电控制器开源方案,包含完整原理图与固件源码,适用于中小型光伏系统的学习、复刻与二次开发。压缩包共390个文件、约1.99MB,以c/h源码与工程配置为主,辅以sch原理图、pcb布线文件、hex烧录固件,以及xls参数表、doc说明文档和lis/obj等编译中间文件,硬件设计到软件实现形成完整闭环。内容覆盖MPPT最大功率点跟踪、涓流/恒流/恒压充电策略、过充过放与短路、反接、过温保护,以及电压电流采样、MOSFET功率开关、显示与通信接口等模块,代码中可对照P&O等算法与控制逻辑。已有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) 在 Vgs=10V 下压到 10mΩ 以内。下面是按这组参数速算损耗和结温的脚本,改几个数字就能对比不同管子。

# 10A PWM 直充回路:开关管与采样电阻损耗/温升速算 I = 10.0 # 充电电流 A Rds25 = 8e-3 # MOS 导通电阻 @25°C, Vgs=10V 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 做高边,栅极要抬到 Vbat+10V 以上。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/7)23.1V5.6mV10k + 1μF,fc≈16Hz
电池电压100k/20k 分压(1/6)19.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 < BAT+1V软件强制占空比归零

分界线很清楚:微秒级的事交给硬件比较器,毫秒级以上的交给固件。比较器输出直接接到驱动的使能脚,不经过单片机,这一条在调板子的时候能救命——固件跑飞了,硬件还在。

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 的常规配置,够细也不占 CPU;10ms 采样周期对应 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.1C(100Ah 电池)
恒压点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) 在 Vgs=6.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,都会强制走一遍这张测试表,尤其是同步采样相位和线阻补偿这两项——它们不出错的时候你感觉不到,出错的时候表现为「电池总是差一点充满」,查起来能耗掉一整天。希望帮到你。

本文还有配套的精品资源,点击获取

返回列表