小功率锂电池充电器方案设计
项目背景
随着便携式电动工具、智能穿戴设备、小型无人机等产品的普及,对小功率、高效率、安全可靠的锂电池充电器需求日益增长。传统的充电管理芯片方案成本较高且灵活性有限,而基于微控制器的软件定义充电方案则能以更低的成本实现更精细的控制和更丰富的功能。
本方案针对8.4V/2.5Ah锂电池组,设计了一款基于ATtiny26微控制器的智能充电器。通过软件算法将8位硬件PWM扩展至12位精度,实现了对充电电流和电压的精准控制,同时集成了完整的充电状态机、多重保护机制和温度监测功能,为小功率锂电池充电提供了完整的参考设计。
主要创新点:
文章摘要
本文详细介绍了一种基于ATtiny26微控制器的小功率锂电池充电器设计方案。系统针对8.4V/2.5Ah锂电池组,采用三阶段充电策略:预充(PC)、恒流(CC)、恒压(CV)。通过软件扩展将8位硬件PWM提升至12位精度,实现对充电电流和电压的精准控制。
核心要点:
系统具备完整的充电状态流程图、详细的C语言实现代码以及关键术语参数表,为小功率锂电池充电器设计提供完整参考方案。
锂电池组参数为8.4V,2.5Ah,内部有温度电阻NTC监测电温。使用DC-DC转换器,电压输出最大值9.2V,恒电流输出2.5A。充电模式有预充PC,恒定电流充电CC,恒定电压充电CV。控制芯片采用ATiny26-16SU,20却SOIC,工作电压4.5V-5.5V,最髙频率16MHz,工作温度-45°C~+85°C,资源,2KB Flah,128B SRAM,128BEEPROM,带有8bit PWM,10bit ADC,等等。 对OCV,CCV,充电电流I,电池包温度进行实时采样,均使用四分之一一滤波器输出这些参数。因为电池电压不会突变,而且变化非常缓慢,OCV采样周期一般设定10秒,关断PWM,延时10ms到100ms,启动ADC。 由于该芯片的PWM模块是8位的,因此要进行软件扩展到12位,这样才能精准的控制充电电流,电压。 采用OCV电压进行充电模式转换,当OCV<3V时,充电器进入预充模式,设定为十分之一电池容量的恒定电流,电压限制到OCV加内阻和线阻的压降,这设定限压为9V。 当OCV值>=3V时,转入恒流充电模式,设定电流为2.5A,限压9V。 当OCV值≥8.2V(可根据实际电池特性调整)时,转入恒压充电模式,设定限流2.5A。 当电流降到20分之C(电池容量)时,终止充电。 如果电池一直不拔出来,当电池电压降到8V,重新进CV充电模式。 电池插入检测用NTC判断,或者用CCV减OCV判断。 PWM的扩展逻辑如下: PWM每5ms调整一次,调整周期为5ms,设定一个4bit的计数器Tc,计数周期同PWM调整周期。脉宽寄存器Rp,临时脉宽寄存器TRp,记录当时脉宽寄存器OTRp,扩展脉宽寄存器WRp,TRp=Rp+1,就是PWM宽加1/256。 当Tc<WRp,Rp=TRP 当Tc≥WRp,Rq=OTRp 当WRp≥16,Rp+1,TRp=Rp,OTRp=Rp,Tc清零。 重复以上逻辑,就实现了扩展。
预充模式的软件逻辑如下: 当CCV>=9V时,WRp-1,返回。当CCV<9V时,如果充电电流I<预充电流时,WRp+1,返回; 如果充电电流>预充电流,WRp-1,然后返回;如电流=预充电流,直接返回。 CC模式的软件逻辑如下: 当CCV≥9V时,WRp-1,返回。当CCV<9V时,如充电电流<恒充电流时,WRp+1,返回;如电流>恒定电流时,WRP-1,返回;如电流=恒定电流时,直接返回,不调PWM。 CV模式的充电逻辑如下: 当充电电流≥2.5A时,WRp-1,返回。当<2.5A时,如果CCV<8.4V,WRp+1,返回;如CCV>8.4V,WRp-1,返回;如CCV=8.4V,直接返回。
充电状态机流程图
以下是充电过程的完整状态机,清晰展示了预充(PC)、恒流(CC)、恒压(CV)三种模式的转换条件和终止条件:
#mermaid-svg-6Upl2i9nsHXqJwou{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-6Upl2i9nsHXqJwou .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-6Upl2i9nsHXqJwou .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-6Upl2i9nsHXqJwou .error-icon{fill:#552222;}#mermaid-svg-6Upl2i9nsHXqJwou .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-6Upl2i9nsHXqJwou .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-6Upl2i9nsHXqJwou .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-6Upl2i9nsHXqJwou .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-6Upl2i9nsHXqJwou .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-6Upl2i9nsHXqJwou .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-6Upl2i9nsHXqJwou .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-6Upl2i9nsHXqJwou .marker{fill:#333333;stroke:#333333;}#mermaid-svg-6Upl2i9nsHXqJwou .marker.cross{stroke:#333333;}#mermaid-svg-6Upl2i9nsHXqJwou svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-6Upl2i9nsHXqJwou p{margin:0;}#mermaid-svg-6Upl2i9nsHXqJwou defs #statediagram-barbEnd{fill:#333333;stroke:#333333;}#mermaid-svg-6Upl2i9nsHXqJwou g.stateGroup text{fill:#9370DB;stroke:none;font-size:10px;}#mermaid-svg-6Upl2i9nsHXqJwou g.stateGroup text{fill:#333;stroke:none;font-size:10px;}#mermaid-svg-6Upl2i9nsHXqJwou g.stateGroup .state-title{font-weight:bolder;fill:#131300;}#mermaid-svg-6Upl2i9nsHXqJwou g.stateGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-6Upl2i9nsHXqJwou g.stateGroup line{stroke:#333333;stroke-width:1;}#mermaid-svg-6Upl2i9nsHXqJwou .transition{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-6Upl2i9nsHXqJwou .stateGroup .composit{fill:white;border-bottom:1px;}#mermaid-svg-6Upl2i9nsHXqJwou .stateGroup .alt-composit{fill:#e0e0e0;border-bottom:1px;}#mermaid-svg-6Upl2i9nsHXqJwou .state-note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-6Upl2i9nsHXqJwou .state-note text{fill:black;stroke:none;font-size:10px;}#mermaid-svg-6Upl2i9nsHXqJwou .stateLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-6Upl2i9nsHXqJwou .edgeLabel .label rect{fill:#ECECFF;opacity:0.5;}#mermaid-svg-6Upl2i9nsHXqJwou .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-6Upl2i9nsHXqJwou .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-6Upl2i9nsHXqJwou .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-6Upl2i9nsHXqJwou .edgeLabel .label text{fill:#333;}#mermaid-svg-6Upl2i9nsHXqJwou .label div .edgeLabel{color:#333;}#mermaid-svg-6Upl2i9nsHXqJwou .stateLabel text{fill:#131300;font-size:10px;font-weight:bold;}#mermaid-svg-6Upl2i9nsHXqJwou .node circle.state-start{fill:#333333;stroke:#333333;}#mermaid-svg-6Upl2i9nsHXqJwou .node .fork-join{fill:#333333;stroke:#333333;}#mermaid-svg-6Upl2i9nsHXqJwou .node circle.state-end{fill:#9370DB;stroke:white;stroke-width:1.5;}#mermaid-svg-6Upl2i9nsHXqJwou .end-state-inner{fill:white;stroke-width:1.5;}#mermaid-svg-6Upl2i9nsHXqJwou .node rect{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-6Upl2i9nsHXqJwou .node polygon{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-6Upl2i9nsHXqJwou #statediagram-barbEnd{fill:#333333;}#mermaid-svg-6Upl2i9nsHXqJwou .statediagram-cluster rect{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-6Upl2i9nsHXqJwou .cluster-label,#mermaid-svg-6Upl2i9nsHXqJwou .nodeLabel{color:#131300;}#mermaid-svg-6Upl2i9nsHXqJwou .statediagram-cluster rect.outer{rx:5px;ry:5px;}#mermaid-svg-6Upl2i9nsHXqJwou .statediagram-state .divider{stroke:#9370DB;}#mermaid-svg-6Upl2i9nsHXqJwou .statediagram-state .title-state{rx:5px;ry:5px;}#mermaid-svg-6Upl2i9nsHXqJwou .statediagram-cluster.statediagram-cluster .inner{fill:white;}#mermaid-svg-6Upl2i9nsHXqJwou .statediagram-cluster.statediagram-cluster-alt .inner{fill:#f0f0f0;}#mermaid-svg-6Upl2i9nsHXqJwou .statediagram-cluster .inner{rx:0;ry:0;}#mermaid-svg-6Upl2i9nsHXqJwou .statediagram-state rect.basic{rx:5px;ry:5px;}#mermaid-svg-6Upl2i9nsHXqJwou .statediagram-state rect.divider{stroke-dasharray:10,10;fill:#f0f0f0;}#mermaid-svg-6Upl2i9nsHXqJwou .note-edge{stroke-dasharray:5;}#mermaid-svg-6Upl2i9nsHXqJwou .statediagram-note rect{fill:#fff5ad;stroke:#aaaa33;stroke-width:1px;rx:0;ry:0;}#mermaid-svg-6Upl2i9nsHXqJwou .statediagram-note rect{fill:#fff5ad;stroke:#aaaa33;stroke-width:1px;rx:0;ry:0;}#mermaid-svg-6Upl2i9nsHXqJwou .statediagram-note text{fill:black;}#mermaid-svg-6Upl2i9nsHXqJwou .statediagram-note .nodeLabel{color:black;}#mermaid-svg-6Upl2i9nsHXqJwou .statediagram .edgeLabel{color:red;}#mermaid-svg-6Upl2i9nsHXqJwou #dependencyStart,#mermaid-svg-6Upl2i9nsHXqJwou #dependencyEnd{fill:#333333;stroke:#333333;stroke-width:1;}#mermaid-svg-6Upl2i9nsHXqJwou .statediagramTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-6Upl2i9nsHXqJwou :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
电池拔出
电池电压降到8V时重新进入
电池插入检测通过
OCV >= 3V
OCV ≥ 8.2V
I ≤ 0.125A (C/20)
空闲状态
预充模式(PC)
每5ms调整PWM
CCV >= 9V
CCV < 9V
I < 0.25A
I > 0.25A
I = 0.25A
WRp-1
WRp+1
WRp-1
不调PWM
PC_充电中
PC_电压检查
PC_限压处理
PC_电流检查
PC_加脉宽
PC_减脉宽
PC_保持
恒流模式(CC)
每5ms调整PWM
CCV >= 9V
CCV < 9V
I < 2.5A
I > 2.5A
I = 2.5A
WRp-1
WRp+1
WRp-1
不调PWM
CC_充电中
CC_电压检查
CC_限压处理
CC_电流检查
CC_加脉宽
CC_减脉宽
CC_保持
恒压模式(CV)
每5ms调整PWM
I ≥ 2.5A
I < 2.5A
CCV < 8.4V
CCV > 8.4V
CCV = 8.4V
WRp-1
WRp+1
WRp-1
不调PWM
CV_充电中
CV_电流检查
CV_限流处理
CV_电压检查
CV_加脉宽
CV_减脉宽
CV_保持
充电完成
状态转换条件说明
| 预充模式(PC) | OCV < 3V | OCV >= 3V | 电流:0.25A (C/10)限压:9V |
| 恒流模式(CC) | OCV >= 3V | OCV ≥ 8.2V | 电流:2.5A限压:9V |
| 恒压模式(CV) | OCV ≥ 8.2V | I ≤ 0.125A (C/20) | 电压:8.4V限流:2.5A |
终止条件:当充电电流降至电池容量的1/20(即0.125A)时,充电完成。
重新激活条件:如果电池未拔出且电压降至8V,系统重新进入恒压模式(CV)进行补充充电。
/**
* @brief ATtiny26 12位PWM扩展核心函数
*
* 该函数每5ms被调用一次,实现8位硬件PWM到12位软件扩展PWM的功能
* 通过4位计数器Tc和扩展脉宽寄存器WRp实现12位精度控制
*
* @note 硬件PWM为8位(0-255),通过本函数扩展为12位(0-4095)
*/
#include <avr/io.h>
#include <avr/interrupt.h>
// 全局变量定义
volatile uint8_t Tc = 0; // 4位计数器,计数范围0-15
volatile uint8_t Rp = 0; // 当前8位PWM脉宽寄存器(0-255)
volatile uint8_t TRp = 0; // 临时脉宽寄存器 = Rp + 1
volatile uint8_t OTRp = 0; // 记录上一周期脉宽寄存器
volatile uint8_t WRp = 0; // 扩展脉宽寄存器(高4位,0-15)
/**
* @brief 12位PWM扩展调整函数
*
* 每5ms定时器中断调用此函数,实现PWM精度从8位扩展到12位
* 扩展原理:通过16个PWM周期(4位计数器)的占空比调整实现12位精度
*
* 12位PWM值 = (WRp << 8) | Rp
* 其中:WRp为高4位(0-15),Rp为低8位(0-255)
*/
void pwm_12bit_expansion(void) {
// 计算临时脉宽寄存器 TRp = Rp + 1(处理溢出)
TRp = (Rp == 255) ? 0 : (Rp + 1);
// 根据Tc和WRp的关系调整Rp值
if (Tc < WRp) {
// Tc < WRp时,使用TRp(增加脉宽)
Rp = TRp;
} else {
// Tc ≥ WRp时,使用OTRp(保持或减少脉宽)
Rp = OTRp;
}
// 更新硬件PWM占空比(ATtiny26的PWM输出)
// 假设使用OCR1A作为PWM输出寄存器
OCR1A = Rp;
// 检查WRp是否达到16(进位条件)
if (WRp >= 16) {
// WRp≥16时,Rp加1,更新相关寄存器,Tc清零
Rp = (Rp == 255) ? 0 : (Rp + 1); // Rp加1,处理溢出
TRp = Rp; // 更新临时寄存器
OTRp = Rp; // 更新记录寄存器
Tc = 0; // 4位计数器清零
WRp = WRp – 16; // WRp减去16(实际处理进位)
}
// Tc计数器递增(4位计数器,0-15循环)
Tc = (Tc + 1) & 0x0F; // 保持4位范围
// 更新记录寄存器为当前Rp值(用于下一周期)
OTRp = Rp;
}
/**
* @brief 设置12位PWM占空比
*
* @param duty_12bit 12位PWM值(0-4095)
*
* 将12位PWM值分解为:
* – WRp: 高4位(duty_12bit >> 8)
* – Rp: 低8位(duty_12bit & 0xFF)
*/
void set_12bit_pwm_duty(uint16_t duty_12bit) {
// 确保输入值




