1. 项目缘起与整体设计思路
1.1 为什么偏偏选中这两颗芯片
先说说这个组合是怎么来的。做嵌入式低功耗设备的朋友大概率都遇到过同一个尴尬:主控芯片选了一颗超低功耗的型号,结果电源管理部分拖了后腿,整机待机电流死活降不下来。我之前做过一个便携式数据采集终端,主控用的是PIC18LF46K42,这颗芯片本身在休眠模式下电流可以压到几十纳安级别,但外围的LDO、分压电阻、电平转换电路加在一起,静态功耗直接飙到几百微安,等于主控省下来的电全被电源部分吃回去了。
后来接触到PCA9422这颗电源管理IC,才算是找到了一个比较优雅的解法。PCA9422是NXP推出的一款高度集成的电源管理芯片,内部集成了多个降压转换器、LDO、电池充电器以及一个相当灵活的状态机。它和PIC18LF46K42搭配起来,一个负责“管电”,一个负责“干活”,分工非常清晰。
这个项目的核心目标就是:用PCA9422做完整的电源管理,让PIC18LF46K42在需要的时候全速跑,不需要的时候彻底断电或者进入深度休眠,同时保证电池充电、电量监测、多路电压输出这些功能一个都不少。
1.2 整体架构是怎么搭的
整个系统的电源架构可以分成三层来理解。
第一层是能量输入层。这里可以是单节锂离子电池,也可以是USB供电。PCA9422内部集成了线性充电器,支持对单节锂电池进行恒流恒压充电,充电电流可以通过寄存器配置。USB供电和电池供电之间的切换由芯片内部的电源路径管理逻辑自动完成,不需要外部额外的ORing电路。
第二层是电压转换层。PCA9422内部有多个降压转换器和LDO。在我的方案里,降压转换器负责给PIC18LF46K42的主供电轨供电,LDO负责给一些对噪声敏感的模拟部分供电。具体来说,我用了两路降压:一路输出3.3V给主控和数字外设,另一路输出1.8V给一些低功耗传感器。LDO则输出2.5V给模拟前端。
第三层是监控与控制层。PIC18LF46K42通过I2C总线与PCA9422通信,读取芯片内部的ADC值来获取电池电压、充电电流、芯片温度等信息,同时通过写寄存器来配置各路输出的电压、开关状态、充电参数等。PCA9422还提供了中断输出引脚,可以在电池低电量、充电完成、过温等事件发生时通知主控。
这个架构的好处在于,主控不需要自己去管理充电逻辑,也不需要额外的ADC去采样电池电压,所有这些都被PCA9422包办了。主控只需要在需要的时候通过I2C问一下“现在电池还剩多少电”,然后决定自己要不要进入低功耗模式。
1.3 方案选型的几个关键考量
有人可能会问,为什么不直接用分立方案?一颗充电IC加几颗DC-DC加一颗LDO,成本可能还更低。这个问题我认真对比过。
分立方案的问题在于:第一,器件多,布板面积大,对于小型化设备来说很致命;第二,电源路径管理需要额外的MOSFET和逻辑电路,设计不当容易出问题;第三,低功耗状态下的静态电流很难控制,每颗芯片都有自己的静态功耗,加起来就很可观了;第四,软件层面需要分别驱动多颗芯片,开发工作量反而更大。
PCA9422把这些问题一次性解决了。它的静态电流在待机模式下可以做到非常低,具体数值取决于配置,但远低于分立方案的总和。而且它通过I2C统一管理,软件上只需要操作一颗芯片的寄存器,逻辑清晰很多。
当然,PCA9422也不是没有缺点。它的封装比较小,引脚间距很密,对手工焊接不太友好。另外它的寄存器配置项非常多,初次上手需要花时间啃数据手册。但这些都是可以克服的,后面我会详细讲怎么快速上手。
2. 核心细节解析与实操要点
2.1 PCA9422的寄存器映射与关键配置项
PCA9422的寄存器空间不算特别大,但功能密度很高。我把它分成几个功能组来理解,这样记忆和查阅都方便很多。
第一组是充电控制寄存器。这里面包含了充电使能、充电电流设置、充电终止电压设置、充电安全定时器配置等。充电电流的设置是通过一个寄存器位段来完成的,比如设置为0x05可能对应100mA,0x0A对应200mA,具体对应关系需要查数据手册的表格。充电终止电压通常设置为4.2V,但如果你用的是高压电池,也可以调到4.35V。
第二组是降压转换器控制寄存器。PCA9422内部有多个降压通道,每个通道都有自己的使能位、输出电压设置位段、工作模式选择位(PFM或PWM)。输出电压的设置通常是步进式的,比如每步25mV或50mV。工作模式的选择很关键:PFM模式在轻载时效率更高,但纹波会大一些;PWM模式纹波小,但轻载效率低。对于给MCU供电的通道,我通常建议在正常运行用PWM,在休眠时切到PFM。
第三组是LDO控制寄存器。LDO的配置相对简单,主要是使能、输出电压设置和限流设置。LDO的输出电压设置精度通常比降压转换器更高,适合给模拟电路供电。
第四组是ADC与监控寄存器。PCA9422内部有一个多通道ADC,可以测量电池电压、充电电流、各路输出的电压、芯片温度等。读取这些值需要先配置ADC的输入通道,然后启动转换,等待转换完成标志,最后读取结果寄存器。结果通常是一个12位或10位的数值,需要根据参考电压和分压比换算成实际值。
第五组是中断与状态寄存器。这里面包含了各种事件的中断使能位和状态标志位。比如电池低电量、充电完成、过温、过流等。状态标志位是只读的,读取后需要写1清除(或者写0清除,取决于具体寄存器设计)。
注意:PCA9422的寄存器在写入后可能需要一个短暂的稳定时间才能生效,特别是在切换输出电压或工作模式时。如果你在写寄存器后立即读取状态,可能会读到旧值。建议在关键配置写入后加一个几毫秒的延时。
2.2 PIC18LF46K42的I2C外设配置要点
PIC18LF46K42的I2C外设(MSSP模块)用起来还算顺手,但有几个坑我踩过,这里提前说一下。
首先是时钟频率。PCA9422支持标准模式(100kHz)和快速模式(400kHz)。我建议用400kHz,这样读取ADC值的时候不会等太久。但要注意,PIC18LF46K42在低功耗模式下如果时钟源切换到了低频振荡器,I2C的波特率也会跟着变。所以如果你打算在休眠模式下也通过I2C与PCA9422通信,要么保持一个足够高的系统时钟给I2C,要么在通信前临时切换到高频时钟。
其次是引脚配置。PIC18LF46K42的I2C引脚需要配置为开漏输出,并且使能内部上拉(如果外部没有上拉电阻的话)。但内部上拉电阻的阻值比较大,在400kHz下可能会导致上升沿变缓,影响通信可靠性。我的做法是外部加4.7kΩ的上拉电阻到3.3V,内部上拉关掉。
第三是中断处理。I2C通信最好用中断方式,而不是轮询。因为轮询会占用CPU时间,而且在低功耗应用中,你希望CPU在等待I2C传输的时候能去做别的事情或者进入空闲模式。PIC18LF46K42的I2C中断标志位需要在中断服务程序里手动清除,别忘了。
第四是错误处理。I2C总线上的错误是难免的,比如从机没有应答、总线仲裁丢失、总线超时等。PIC18LF46K42的MSSP模块有错误标志位,你需要在每次通信后检查这些标志。如果发现错误,最简单的恢复方法是发送一个停止条件,然后重新初始化I2C外设。
2.3 电源路径管理与充电逻辑的配合
PCA9422的电源路径管理逻辑是这样的:当USB插入时,芯片会优先使用USB供电,同时如果电池电量不足,会启动充电。如果USB供电能力不足(比如连接到一个只能提供100mA的USB端口),芯片会自动降低系统负载或者降低充电电流,以保证USB端口不会过载。
这里有一个关键配置:输入电流限制。PCA9422允许你设置从USB端口抽取的最大电流。如果你设置得太高,而USB端口实际提供不了那么多,电压会被拉低,可能导致系统复位。如果你设置得太低,充电速度会很慢。我的经验是,对于标准的USB 2.0端口,设置为400mA到500mA比较稳妥;对于USB 3.0端口或者专用充电器,可以设置到900mA甚至更高。
另一个关键点是充电终止判断。PCA9422在恒压充电阶段会监测充电电流,当电流降到设定阈值的某个比例时,判定充电完成。这个比例通常是可以配置的,比如10%或20%。如果你发现电池总是充不满,可能是这个阈值设得太低了;如果充电时间太长,可能是阈值设得太高了。
实操心得:在电池电压接近充满的时候,PCA9422的充电电流会逐渐减小。如果你在这个时候读取充电电流ADC值,会发现它一直在变。不要用这个值来判断电池是否充满,应该用充电完成中断或者状态寄存器里的充电完成标志位。
2.4 低功耗模式下的电源管理策略
这是整个项目最核心的部分。PIC18LF46K42有几种低功耗模式:空闲模式、休眠模式、深度休眠模式。PCA9422也有几种低功耗状态:正常工作、待机、关断。
我的策略是这样的:当系统不需要工作时,PIC18LF46K42进入深度休眠模式,同时通过I2C告诉PCA9422把不需要的电压轨关掉,只保留给主控供电的那一路,并且把这一路切换到PFM模式以降低静态功耗。如果系统长时间不需要工作,甚至可以告诉PCA9422进入待机模式,把主控的供电也关掉,只保留一个极低功耗的唤醒电路。
这里有一个细节:PIC18LF46K42在深度休眠模式下,I2C外设是停止工作的。所以你不能在休眠模式下通过I2C与PCA9422通信。正确的做法是:在进入休眠之前,把需要配置的寄存器都写好,然后进入休眠。唤醒后,重新初始化I2C,再与PCA9422通信。
另一个细节是唤醒源。PCA9422的中断输出引脚可以连接到PIC18LF46K42的外部中断引脚。当电池低电量、充电完成、或者有按键按下(如果按键电路也由PCA9422管理)时,PCA9422会拉低中断引脚,唤醒主控。这样主控就可以在大部分时间里安心休眠,只在有事件发生时才醒来处理。
3. 实操过程与核心环节实现
3.1 硬件连接与上电检查
先列一下我的硬件连接清单:
- PCA9422的SCL引脚连接到PIC18LF46K42的RC3(I2C时钟)
- PCA9422的SDA引脚连接到PIC18LF46K42的RC4(I2C数据)
- PCA9422的INT引脚连接到PIC18LF46K42的RB0(外部中断0)
- PCA9422的降压输出1连接到PIC18LF46K42的VDD
- PCA9422的LDO输出连接到模拟部分的AVDD
- 电池连接到PCA9422的BAT引脚
- USB的VBUS连接到PCA9422的VBUS引脚
上电之前,先用万用表检查一下各路电源有没有短路。特别是PCA9422的封装引脚很密,焊接时容易连锡。我一般会用放大镜或者显微镜检查一遍,确认没有桥接。
上电后,先不要急着写代码。用示波器看一下各路输出的电压是否正常。PCA9422在上电后会有一个默认的输出配置,通常是3.3V和1.8V。如果电压不对,先检查反馈电阻(如果有外部反馈的话)和配置引脚。
然后,用逻辑分析仪抓一下I2C总线。如果PCA9422没有应答,检查地址是否正确。PCA9422的I2C地址通常是7位地址,具体值取决于芯片型号和配置引脚的状态。我用的这颗地址是0x4A(7位地址),写操作时变成0x94,读操作时变成0x95。
3.2 I2C驱动层的实现
PIC18LF46K42的I2C驱动我写了一个简单的状态机,支持单字节读写和多字节连续读写。核心函数有三个:I2C_Start()、I2C_WriteByte()、I2C_ReadByte()。
// I2C初始化 void I2C_Init(void) { SSP1CON1 = 0x28; // I2C主模式,时钟 = Fosc/(4*(SSP1ADD+1)) SSP1ADD = 0x27; // 400kHz @ 16MHz Fosc SSP1STAT = 0x80; // slew rate control disabled TRISC3 = 1; // SCL输入 TRISC4 = 1; // SDA输入 } // 写一个字节到PCA9422的指定寄存器 uint8_t PCA9422_WriteReg(uint8_t reg, uint8_t data) { I2C_Start(); if (I2C_WriteByte(0x94) != 0) return 1; // 写地址 if (I2C_WriteByte(reg) != 0) return 2; // 寄存器地址 if (I2C_WriteByte(data) != 0) return 3; // 数据 I2C_Stop(); return 0; } // 从PCA9422的指定寄存器读取一个字节 uint8_t PCA9422_ReadReg(uint8_t reg, uint8_t *data) { I2C_Start(); if (I2C_WriteByte(0x94) != 0) return 1; // 写地址 if (I2C_WriteByte(reg) != 0) return 2; // 寄存器地址 I2C_Start(); // 重复起始条件 if (I2C_WriteByte(0x95) != 0) return 3; // 读地址 *data = I2C_ReadByte(0); // 读取数据,发送NACK I2C_Stop(); return 0; }这段代码看起来简单,但有几个地方容易出错。第一,I2C_WriteByte函数需要等待SSP1IF标志位置位,并且要检查ACKSTAT位来判断从机是否应答。第二,在重复起始条件之前,不能发送停止条件。第三,读取最后一个字节时要发送NACK,否则从机会继续输出数据。
注意:PIC18LF46K42的I2C外设在低功耗模式下会停止工作。如果你在休眠前没有正确关闭I2C,唤醒后可能会发现I2C总线被拉低。解决方法是在进入休眠前发送一个停止条件,并把SCL和SDA引脚配置为普通IO输入。
3.3 充电参数配置与电池监测
充电参数的配置我一般分三步走。
第一步,设置输入电流限制。根据USB端口的类型,设置合适的值。我通常先设置为500mA,如果检测到USB电压被拉低到4.5V以下,就降低到300mA。
第二步,设置充电电流。对于单节锂电池,充电电流通常设置为电池容量的0.5C到1C。比如一块1000mAh的电池,充电电流可以设置为500mA到1000mA。但要注意,充电电流越大,芯片发热越严重。PCA9422的封装散热能力有限,我一般不会超过800mA。
第三步,设置充电终止电压和终止电流。终止电压通常设为4.2V,终止电流设为充电电流的10%。
配置完成后,我写了一个简单的电池监测任务,每隔10秒读取一次电池电压和充电电流。
void Battery_Monitor(void) { uint8_t adc_high, adc_low; uint16_t adc_value; float battery_voltage; // 配置ADC测量电池电压 PCA9422_WriteReg(0x30, 0x01); // 选择电池电压通道 PCA9422_WriteReg(0x31, 0x01); // 启动单次转换 __delay_ms(2); // 等待转换完成 // 读取ADC结果 PCA9422_ReadReg(0x32, &adc_high); PCA9422_ReadReg(0x33, &adc_low); adc_value = ((uint16_t)adc_high << 4) | (adc_low >> 4); // 换算成实际电压 // 假设参考电压为2.5V,ADC为12位,分压比为1/3 battery_voltage = (adc_value / 4095.0) * 2.5 * 3.0; // 根据电压判断电量 if (battery_voltage > 4.0) { battery_level = 100; } else if (battery_voltage > 3.7) { battery_level = 75; } else if (battery_voltage > 3.5) { battery_level = 50; } else if (battery_voltage > 3.3) { battery_level = 25; } else { battery_level = 0; } }这个换算过程看起来简单,但实际调试的时候我遇到过一个问题:ADC读出来的值总是偏小。后来发现是分压电阻的精度不够,用了5%精度的电阻。换成1%精度的电阻后,读数就准了。所以如果你对电量监测精度有要求,分压电阻一定要用高精度的。
3.4 低功耗状态切换的完整流程
低功耗状态切换是整个项目最复杂的部分,我把它拆成一个完整的流程。
进入低功耗的流程:
- 检查当前是否有正在进行的任务。如果有,等待任务完成或者保存任务状态。
- 通过I2C配置PCA9422:关闭不需要的电压轨,把主供电轨切换到PFM模式,使能低电量中断。
- 关闭PIC18LF46K42的外设:关闭ADC、关闭定时器、关闭UART等。
- 配置唤醒源:使能外部中断0(连接到PCA9422的INT引脚),配置为下降沿触发。
- 发送I2C停止条件,把SCL和SDA引脚配置为普通IO输入。
- 清除所有中断标志位,使能全局中断。
- 执行
SLEEP()指令,进入休眠模式。
从低功耗唤醒的流程:
- 外部中断触发,CPU从休眠中唤醒。
- 重新初始化系统时钟(如果休眠时切换了时钟源)。
- 重新初始化I2C外设。
- 读取PCA9422的中断状态寄存器,判断唤醒原因。
- 根据唤醒原因执行相应的处理:如果是低电量,进入低电量处理流程;如果是充电完成,更新充电状态;如果是按键唤醒,执行按键处理。
- 清除PCA9422的中断标志位。
- 恢复系统状态,继续正常运行。
这个流程我调试了大概一周才稳定下来。最大的坑是:在进入休眠之前,如果I2C总线没有正确释放,PCA9422可能会认为总线忙,导致唤醒后第一次通信失败。解决方法是在休眠前发送停止条件,并且把SCL和SDA引脚配置为输入模式,让外部上拉电阻把总线拉高。
实操心得:在调试低功耗模式时,不要一开始就追求最低功耗。先把功能调通,确保休眠和唤醒都正常,然后再逐步优化功耗。我一开始就把所有外设都关了,结果唤醒后系统起不来,排查了很久才发现是某个外设的时钟没有恢复。
4. 常见问题与排查技巧实录
4.1 I2C通信失败的问题排查
I2C通信失败是最常见的问题,我整理了一个排查表。
| 现象 | 可能原因 | 排查方法 | 解决方法 |
|---|---|---|---|
| 从机无应答 | 地址错误 | 用逻辑分析仪抓波形,检查地址字节 | 确认PCA9422的7位地址,写操作左移一位 |
| 从机无应答 | 上拉电阻缺失 | 测量SCL和SDA的静态电平 | 加上4.7kΩ上拉电阻到3.3V |
| 通信偶尔失败 | 总线电容过大 | 测量上升沿时间 | 减小上拉电阻或降低通信速率 |
| 通信偶尔失败 | 电源噪声 | 用示波器观察电源纹波 | 增加去耦电容,优化PCB布局 |
| 读取数据错误 | 寄存器地址错误 | 对照数据手册检查寄存器映射 | 确认寄存器地址和位段定义 |
| 读取数据错误 | 时序不满足 | 检查建立时间和保持时间 | 降低通信速率或调整时钟配置 |
我遇到过一次很诡异的情况:I2C通信在实验室里一切正常,但到了现场就偶尔失败。后来发现是现场有强电磁干扰,导致I2C总线上的数据被干扰。解决方法是在SCL和SDA上并联一个小电容(比如100pF),滤除高频干扰。但电容不能太大,否则会影响上升沿。
4.2 充电异常的问题排查
充电异常通常表现为:电池充不满、充电时间过长、充电时芯片发热严重。
电池充不满:先检查充电终止电压设置是否正确。如果设置的是4.2V,但电池实际只能充到4.0V,可能是电池老化或者充电电流太小。另外,检查充电安全定时器是否超时。PCA9422有一个充电安全定时器,如果充电时间超过设定值,会自动终止充电。这个定时器的默认值可能比较短,需要根据电池容量调整。
充电时间过长:检查充电电流设置。如果充电电流太小,充电时间自然会长。另外,检查输入电流限制是否设置得太低,导致充电电流被限制。还有一个可能的原因是电池电压过低,进入了预充电阶段,预充电电流通常只有正常充电电流的10%,所以会慢一些。
充电时芯片发热严重:这是线性充电器的通病。PCA9422的充电器是线性的,意味着多余的电压会以热量的形式耗散。如果输入电压是5V,电池电压是3.7V,那么压差是1.3V,乘以充电电流就是功耗。比如500mA的充电电流,功耗就是0.65W,对于小封装来说已经很热了。解决方法是降低充电电流,或者改善PCB的散热设计,比如增加铜箔面积、加散热过孔。
4.3 低功耗模式下的异常唤醒
异常唤醒是指系统在没有预期事件的情况下从休眠中醒来。这个问题很隐蔽,因为唤醒后系统看起来一切正常,只是功耗比预期高。
我遇到过几种异常唤醒的情况:
第一种是中断引脚悬空。PCA9422的INT引脚是开漏输出,如果没有上拉电阻,引脚会浮空,导致随机触发中断。解决方法是在INT引脚上加一个100kΩ的上拉电阻到3.3V。
第二种是电源纹波触发。如果电源纹波比较大,可能会在INT引脚上耦合出毛刺,触发中断。解决方法是在INT引脚上加一个小电容(比如10nF)到地,滤除毛刺。
第三种是看门狗复位。如果看门狗在休眠期间超时,会导致系统复位,看起来像是被唤醒了。解决方法是在进入休眠前关闭看门狗,或者配置看门狗在休眠期间暂停。
第四种是配置错误。比如不小心使能了某个不必要的中断源,导致系统被意外唤醒。解决方法是仔细检查中断使能寄存器,只使能需要的中断源。
排查技巧:如果你怀疑系统被异常唤醒,可以在唤醒后立即读取PCA9422的中断状态寄存器,看看是哪个事件触发了中断。如果中断状态寄存器显示没有事件,那可能是PIC18LF46K42自己的某个中断源触发了唤醒,需要检查PIC18LF46K42的中断标志位。
4.4 电压轨切换时的系统复位问题
这个问题我调试了很久才找到原因。现象是:当PCA9422切换电压轨的时候,PIC18LF46K42会复位。
一开始我以为是电源跌落导致的,用示波器抓了一下3.3V的波形,发现确实有一个短暂的跌落,但跌落的幅度并不大,从3.3V跌到3.0V左右,按理说不会导致复位。后来查了PIC18LF46K42的数据手册,发现它的欠压复位阈值是可以配置的。默认情况下,欠压复位阈值可能在2.8V左右,但如果你配置了更高的阈值,比如3.0V,那么3.0V的跌落就会触发复位。
解决方法有两个:一是降低欠压复位阈值,但这会降低系统的可靠性;二是在切换电压轨之前,先把系统时钟降频,减少电流需求,从而减小电压跌落。我选择了第二种方法,在切换电压轨之前把系统时钟从16MHz降到1MHz,切换完成后再恢复。这样电压跌落就小了很多,不会触发复位了。
另外,PCA9422在切换电压轨的时候,如果输出电压设置变化太大,比如从1.8V直接跳到3.3V,内部的下拉管会先放电,导致输出电压短暂跌落。正确的做法是逐步调整输出电压,每次调整的步进不要超过0.3V,中间加一个几毫秒的延时。
4.5 常见问题速查表
| 问题类别 | 具体现象 | 可能原因 | 解决方法 |
|---|---|---|---|
| I2C通信 | 从机无应答 | 地址错误、上拉缺失 | 检查地址、加上拉电阻 |
| I2C通信 | 数据错误 | 时序不满足、干扰 | 降低速率、加滤波电容 |
| 充电 | 充不满 | 终止电压设置错误、安全定时器超时 | 调整终止电压、延长定时器 |
| 充电 | 发热严重 | 压差大、电流大 | 降低充电电流、改善散热 |
| 低功耗 | 异常唤醒 | 中断引脚悬空、电源纹波 | 加上拉电阻、加滤波电容 |
| 低功耗 | 功耗偏高 | 外设未关闭、电压轨未关断 | 检查外设时钟、配置PCA9422 |
| 电压切换 | 系统复位 | 欠压复位阈值过高、电压跌落 | 降低阈值、降频切换 |
5. 项目扩展与个人经验分享
5.1 如何扩展更多电压轨
PCA9422的电压轨数量是有限的,如果你的系统需要更多的电压轨,有几种扩展方法。
第一种方法是使用PCA9422的LDO输出作为参考,外接一个MOSFET或者负载开关,扩展出一路新的电压轨。这种方法的优点是成本低,缺点是新的电压轨没有独立的电压调节能力,只能跟随LDO的输出。
第二种方法是使用PCA9422的I2C接口,外接一颗I2C控制的DC-DC控制器。这样你可以通过PCA9422的I2C总线,间接控制更多的电压轨。这种方法的优点是扩展能力强,缺点是需要额外的芯片和布板空间。
第三种方法是使用PCA9422的GPIO引脚,控制外部的负载开关。PCA9422有几个GPIO引脚,可以通过寄存器配置为输出,用来控制外部负载开关的使能。这种方法适合只需要开关控制、不需要电压调节的场景。
我个人的建议是:如果电压轨数量不超过PCA9422本身的能力,尽量用PCA9422自带的。如果确实需要更多,优先考虑第二种方法,因为I2C控制的DC-DC控制器通常也有状态监测功能,可以纳入统一的电源管理框架。
5.2 电量计的实现思路
PCA9422内部的ADC可以测量电池电压,但仅凭电压来判断电量是不够准确的,因为锂电池的电压在放电平台期变化很小,而在接近满电和接近空电时变化很大。
如果你需要更精确的电量计,可以考虑以下几种方法。
第一种方法是库仑计。在电池和系统之间串联一个采样电阻,测量电流,然后对时间积分,得到电荷量。PCA9422的ADC可以测量充电电流,但测量放电电流可能需要额外的电路。这种方法的精度最高,但实现复杂度也最高。
第二种方法是电压查表法。在实验室里对电池进行完整的充放电测试,记录电压和电量的对应关系,做成查找表。在实际运行时,根据测量的电压查表得到电量。这种方法的精度取决于电池的一致性,如果电池批次不同,可能需要重新校准。
第三种方法是电压加电流补偿。在电压查表的基础上,根据当前的放电电流对电压进行补偿。因为电池的内阻会导致在大电流放电时电压偏低,补偿后可以得到更准确的电量估计。
我目前用的是第二种方法,因为实现简单,对于大多数应用来说精度已经足够了。如果你对电量精度要求很高,建议用第一种方法。
5.3 我在这个项目里踩过的坑
第一个坑是I2C地址搞错了。PCA9422的数据手册上写的是7位地址,但我在代码里直接用了这个地址,没有左移一位。结果I2C通信一直失败,排查了半天才发现问题。记住:7位地址在写操作时要左移一位,最低位补0;读操作时左移一位,最低位补1。
第二个坑是充电安全定时器。我一开始没有配置这个定时器,用了默认值。结果电池还没充满,充电就自动终止了。后来查了数据手册,发现默认的安全定时器时间比较短,对于大容量电池来说不够用。解决方法是在初始化的时候把安全定时器设置为最大值,或者根据电池容量计算一个合适值。
第三个坑是低功耗模式下的I2C引脚状态。我在进入休眠前没有把I2C引脚配置为输入模式,结果休眠后I2C总线被拉低,PCA9422认为总线忙,导致唤醒后第一次通信失败。解决方法是在休眠前发送停止条件,并把SCL和SDA配置为输入模式。
第四个坑是电压轨切换时的复位。前面已经详细讲过了,这里不再重复。总之,切换电压轨的时候一定要小心,尽量降频操作,逐步调整。
第五个坑是ADC读数不准。我一开始用的是5%精度的分压电阻,读数偏差很大。换成1%精度的电阻后,读数就准了。另外,ADC的参考电压也需要稳定,如果参考电压有噪声,读数也会跳。解决方法是在参考电压引脚上加一个1uF的陶瓷电容。
5.4 这个方案适合哪些场景
这个方案适合以下几类场景:
第一类是便携式低功耗设备,比如数据采集器、环境监测节点、便携式医疗设备等。这些设备通常用电池供电,对功耗要求很高,同时需要电池充电和电量监测功能。
第二类是需要多路电压输出的设备,比如同时有数字电路和模拟电路的混合信号系统。PCA9422的多路输出可以分别给不同的电路供电,避免数字噪声干扰模拟电路。
第三类是需要电源路径管理的设备,比如同时支持USB供电和电池供电的设备。PCA9422可以自动切换供电路径,不需要外部额外的ORing电路。
第四类是需要远程监控电源状态的设备,比如通过I2C总线可以读取电池电压、充电电流、芯片温度等信息的设备。这些信息可以用于系统健康监测和故障预警。
如果你的项目只需要一路3.3V输出,不需要电池充电,那用PCA9422就有点大材小用了,一颗普通的LDO就够了。但如果你需要完整的电源管理功能,PCA9422加PIC18LF46K42的组合是一个很值得考虑的方案。
5.5 后续可以优化的方向
这个项目目前已经稳定运行了,但我觉得还有几个可以优化的方向。
第一个方向是动态电压调节。根据PIC18LF46K42的工作频率,动态调整核心电压。比如在16MHz时用3.3V,在1MHz时用1.8V。这样可以进一步降低功耗。PCA9422支持通过I2C动态调整输出电压,所以硬件上是可行的,只需要在软件上实现电压和频率的联动。
第二个方向是更精细的功耗管理。目前我只是简单地关闭不需要的电压轨,未来可以根据任务的实时需求,更细粒度地管理各个外设的电源。比如在ADC不工作的时候,把模拟部分的LDO也关掉。
第三个方向是电池健康状态监测。通过记录电池的充放电曲线,估计电池的健康状态。如果发现电池容量明显下降,可以提前提醒用户更换电池。
第四个方向是固件在线升级。通过I2C或者UART接口,实现固件的在线升级。这样即使设备已经安装到现场,也可以远程更新固件,修复bug或者增加新功能。
这个方案的可扩展性还是很强的,PCA9422的寄存器空间还有不少保留位,未来如果有新的功能需求,可以通过固件升级来实现,不需要改动硬件。