跳转至

Air201-LuatOS-如何使用电源管理功能

一、电源管理介绍

电源管理是电子设备中非常重要的一部分,尤其是在移动设备和嵌入式系统中。它直接关系到设备的能效、续航时间和整体性能。以下是关于电源管理的一些基本知识和常见概念的介绍。

1.1 电源管理的定义

电源管理指的是在电子设备内部合理利用和调节电源,以保证各个组件在最佳运行条件下工作,同时延长设备的使用时间和确保能量的有效利用。

1.2 电源管理的目标

  • 延长电池寿命:通过有效的充放电管理,使电池在使用过程中更长久地保持能量。
  • 优化能耗:监测各个部件的能耗,减少不必要的能量消耗。
  • 提供稳定的电力供给:确保设备在不同的负载和环境条件下都能稳定工作。

1.3 常见术语

  • Vbat(电池电压):指设备中电池当前的电压值,通常用于评估电池的状态。
  • 电池电量百分比:代表电池当前剩余能量的百分比,通常用于用户界面显示。
  • 充电状态:指设备是否正在充电,影响电池电量的监测和管理策略。
  • 电池放电曲线:描述不同电压下电池剩余电量的关系,帮助计算电量百分比。
  • 电池调节电压:电池在充放电过程中,可以通过一定控制策略或电路设计,维持电池电压在一定范围内的稳定。Air201 的电池电压上限值默认为 4.2V,可在代码中自行更改电池电压阈值。
  • 快速充电:快速充电是一种技术,它允许电池在较短时间内充入更多电量。快速充电技术包括恒流充电与恒压充电。在快速充电的恒流阶段,充电器会以一个固定的电流给电池充电,直到电池电压达到一个预设的电压值(Air201 默认为 4.2V),随后切换至恒压模式,此时电流逐渐减小,直到电池充满后停止充电。

1.4 常见功能

  • 电量监测:通过 ADC(模数转换)读取电池电压,定期更新电量状态。
  • 充电检测:利用 GPIO 中断检测充电状态,判断电池是否处于充电状态。
  • 自动关机:根据电量百分比自动关闭设备,以防止电池过度放电。
  • 过压保护:当充电到达电池电压阈值时会自动停止充电,待剩余电量与满电量相差超过 120mA 时恢复充电。
  • 用户接口:通过长按电源键执行关机命令,提升用户体验。
  • 路径管理:当插入 USB 既可以给锂电池充电,同时也可以给主板供电

1.5 实践中的应用

在实际应用中,电源管理代码通常涉及多个功能模块,包括初始化电源,实时监测电量,响应用户操作和维护设备状态。一个良好的电源管理系统能够显著提高设备的性能和用户满意度。

通过合理的电源管理策略,用户不仅可以延长设备的使用时间,还可以确保设备在关键时刻的稳定性和可靠性。在编写电源管理程序时,应充分考虑设备的使用场景和用户的需求。

二、演示功能概述

在本演示中,我们将展示电源管理系统的核心功能模块,以及如何通过不同的技术手段实现有效的电源管理。这些功能旨在提升设备的性能和用户体验,以下是主要演示功能的概述:

1. 电量监测:本系统通过 ADC(模数转换器)实时读取电池电压,并定期更新电量状态。通过精确监测电池电压,可以让用户清晰掌握当前电池剩余电量,避免意外关机带来的困扰。

2. 充电检测:系统利用 GPIO 中断技术,能够快速检测电池的充电状态。当设备连接到电源时,将自动判断电池是否在充电状态,并相应地调整电源管理策略,以优化充电效率和电池寿命。

3. 自动关机:根据监测到的电量百分比,该系统能够在电量过低时自动关机,以防止电池过度放电。这种智能管理能够保护电池,延长其使用寿命,确保设备在低电量时不会意外关机。

4. 用户接口:系统提供了便捷的用户交互接口,用户可以通过长按电源键实现关机操作。提升用户体验的同时,确保用户可以方便地控制设备的电源状态。

通过这些功能的实现,用户不仅可以延长设备的使用时间,还能够保证设备在关键时刻的稳定性和可靠性。在实际应用中,这些功能能够有效地提升设备性能,满足用户对电源管理的需求。

三、演示硬件环境

3.1 开发板

本文以 Air201 开发板 为例,Air201 开发套件如下图所示:

淘宝购买链接:Air201 开发套件淘宝购买链接

此开发套件的详细使用说明参考:Air201 产品手册 中的 Air201 硬件手册Air201 的 LuatOS 快速入门

3.2 SIM 卡

在中国大陆环境下,使用移动,电信,联通的物联网卡或者手机卡都可以。

3.3 PC 电脑

PC 电脑推荐使用 Windows10 及以上版本。

3.4 数据通信线

1. USB 转 Type-C 数据线

它的一端是 USB 接口,另一端是 Type-C 接口。

四、演示软件环境

4.1 Luatools 下载调试工具

Luatools 工具由合宙推出,支持最新固件获取、固件打包、trace 打印及单机烧录等功能。

工具使用说明参考:Luatools 下载和详细使用

4.2 获取固件及源码

1. 底层 core 下载地址:SDK& Demo - luatos@air201 - 合宙文档中心

本文使用的固件版本为 LuatOS-SoC_V2002_Air201.soc

2. demo 位置

本 demo 的主要功能是对电池管理系统的控制和监测

demo 位置参考:https://gitee.com/openLuat/LuatOS-Air201/tree/master/demo/pm

五、软硬件资料

5.1 电池充电器说明

注意注意,锂电池与锂离子电池不是同一种电池。锂电池是一个统称,而锂离子电池是锂电池的其中一种类型。

Air201 选用了一款高度集成的单电池锂离子电池充电器,该充电器具有系统电源路径管理,可供空间有限的便携式应用使用。功能具有涓流充电、恒流充电、恒压充电、充电终止和自动充电。而且可以自动调节充电电流与充电电压,或者进入其他模式。

5.1.1 锂电池充电过程

锂离子电池的充电过程可概括为四个阶段:涓流充电、恒流充电、恒压充电以及充电终止。以下为这四个阶段的详细介绍。

1. 涓流充电

涓流充电用于先对完全放电的电池单元进行预充,也叫恢复性充电。在电池电压低于 3V 左右时,先采用最大 0.1C 的恒定电流对电池进行充电。(C 是充放电倍率,表示电池在单位时间内充放电电流与电池额定容量的比值。例如,一个电池的额定容量是 1000mAh,1C 就表示以 1000mA 的电流在单位时间内进行充放电,也表示电池在一小时内完全充放电。)

2. 恒流充电

在涓流充电之后,当电池电压上升到涓流充电阈值以上时,此时提高充电电流从而进行恒流充电。这个阶段的电流通常在 0.2C~1.0C 之间,电池电压随着恒流充电过程在逐步升高。恒流充电时的电流并不要求十分精确,准恒定电流也可以。

当在线性充电器设计中,电流经常会随着电池电压的上升而上升,应尽量减轻传输晶体管上的散热问题。选择大于 1.0C 的恒流充电并不会缩短整个充电充电周期时间,这种做法是不可取的。

当以更高电流进行充电时,由于电极反应的过压以及电池内部阻抗上的电压上升,电池电压会更快速地上升。恒流充电阶段就会变短,同时下面恒压充电阶段的时间会相应增加,因此总的充电周期时间并不会缩短。

3. 恒压充电

当电池电压上升到 4.2V(默认值)时,恒流充电结束,开始恒压充电阶段。在此阶段,电流会根据电芯的饱和程度逐渐减少,直到达到一个很小的值,可称为判满电流值时,充电终止。

4. 充电终止

与镍电池不同,此时不建议对锂离子电池进行连续涓流充电。连续涓流充电会导致金属锂出现极板电镀效应。从而使电池不稳定,并且极大可能导致突然的自动快速解体。

这个阶段有两种典型的充电终止方法:一种是采用最小充电电流判断,当恒压充电阶段的充电电流减小到判满电流值时终止充电;另一种是从恒压充电阶段开始计时,持续充电两个小时后终止充电过程。Air201 选择的电池充电器充电终止方法为第一种。

5. 充电恢复说明

充电终止后,当电池放电后剩余电量与满电量相差超过 120mV 时会恢复充电,转为恒压充电阶段。

5.1.2 锂电池放电说明

由于锂电池内部结构所致,在放电时锂电池不能全部移向正极,必须要保留一部分锂离子在负极,从而保证在下次充电时锂离子能够畅通地嵌入通道。否则,电池寿命将会大大缩短。

1. 放电电压平台

锂电池在放电过程中,电压会保持在一个相对稳定的范围内,这个范围被称为放电电压平台。对于大多数锂电池来说,这个平台大约在 3.7V 到 3.0V 之间。

2. 放电电流

放电电流是指电池在放电过程中的电流强度。放电电流的大小会直接影响到电池的放电时间和寿命。大电流放电时会导致电池温度升高,也可能会缩短电池寿命。(锂电池放电电流不应超过电池容量的 3 倍,例如 1000mAH 电池,放电电流应严格控制在 3A 以内,否则会导致电池损坏。)

3. 放电截止电压

为了保证锂电池放电后石墨层中仍留有部分锂离子,就需要严格限制最低放电截止电压,也就是要避免过放电。当电池电压降到这个值时,电池应停止放电。对于大多数锂电池,这个截止电压大约是 2.5V 到 3.0V。

Air201 在代码中已经设置当电池电量低于 30% 且两分钟内没有充电时自动关机。

4. 放电效率

放电效率是指电池放电过程中电能转换的效率。锂电池放电效率可达到 90% 以上。

5. 放电深度

放电深度是指电池放电时放电量与电池总容量的比率。即放电深度=(放电量/电池总容量)*100% 。(例如,如果一个电池的总容量是 1000mAh,放电到 500mAh,那么放电深度就是 50%。)

6. 放电速率

放电速率是指电池单位时间内放电时放电量与电池额定容量的比值,用充放电倍率 C 表示。(例如,一个电池的额定容量为 1000mAh,如果用 200mA 的电流进行放电,其放电倍率为 0.2C,因为 200mA/1000mAh=0.2。)

7. 温度影响

锂电池的放电性能受温度影响较大。在低温下,电池的放电性能会下降,而在高温下,电池的寿命可能会缩短。

8. 循环寿命

锂电池的循环寿命是指电池可以经历的充放电次数。一般来说,锂电池的循环寿命可以达到几百到几千次,具体还要取决于电池的类型、使用条件和放电深度。

9. 自放电

锂电池即使在不使用的情况下也会进行自动放电,这种现象称为自放电。锂电池的自放电率相对较低,通常每月 1% 到 2%。

5.2 sensor 库介绍

sensor 库是一个传感器操作库,用于在 LuatOS 环境中处理各种传感器的数据交互。它支持多个传感器的操作,包括 DS18B20 温度传感器、DHT11/DHT12 温湿度传感器、HX711 称重传感器、CS1237 传感器、WS2812B RGB LED 灯带以及 SC12A 触摸传感器等。

此为 sensor 库 API 接口介绍 ,大家可自行参考测试。

5.3 Air201 开发板烧录说明

5.3.1 选择固件和脚本

1. 打开 Luatools_v3 工具

2. 点击 项目管理测试

3. 根据图示操作

注意,大家只需要跟着做到第四步即可,第五步跟着后面的操作再做。

5.3.2 烧录

1. 将 Air201 开发板通过 USB 数据线连接至电脑,如下图所示:

由于 Air201 单板子没有复位键和 BOOT 键,所以数据线与 Air201 开发板之间还需要接一个 USB_BOOT 板,不过大家放心,Air201 套件里已经包括了 USB_BOOT 板,不需要单独购买。 而且特别注意,USB_BOOT 板在接 Air201 开发板时不用区分正反。

2. 根据下方操作进行烧录

此时就需要大家先点击 Luatools 工具上的 下载脚本下载底层和脚本,再执行下方操作了。

开发板处于未开机状态:此时先按住下载模式按键(BOOT 键)不放,再长按开机键(POW 键)开机,若不出意外开发板将会进入下载模式,Luatools 工具下载进度条会开始跑,这时便可以松开 BOOT 键和 POW 键,等到工具提示下载完成即可。

开发板已经处于开机状态:此时可以先按住 BOOT 键不放,再短按复位键(RST 键)后开发板会重启并进入下载模式。

5.3.3 不同模式下的端口显示

1. 正常开机模式

2. 下载模式

六、功能验证

6.1 ChargeICLogic.lua 中代码介绍

该文件中的代码无需修改,大家直接调用这个文件就行。

6.1.1 代码说明

该文件中的代码是一个用 Lua 编写的脚本,主要用于与充电 IC(集成电路)进行交互,进行状态监测和控制。

6.1.2 代码逐步解析

1. 注释部分

这段注释用于指出代码的主要功能,即处理充电 IC 相关的逻辑。

--[[
充电IC的相关逻辑
]]

2. 电池上限电压表

此部分定义了电池上限电压的不同设置值。Vreg 表包含多个十六进制数,每个数对应不同的电池上限电压。在 V_CTRL 寄存器中,前四位用来设置电池上限电压,其余位可以用作其他设置。默认情况下,其他位为 0。

local Vreg = {
    '0x00', -- 4.2V
    ...
    '0xF8', -- 4.1V
}

3. 快速充电电流表

这部分定义了快速充电电流的设置值。Ichg 表中列出的十六进制数对应于不同充电电流的设置。在 I_CTRL 寄存器中,前三位是设置快速充电电流的,其余位是其他设置,默认为 0; 该电流值是通过电阻 Rsns 计算得出的,Air201 的 Rsns 值为 1KΩ,经过公式计算后得出 Ireg=500mA

local Ichg = {
    '0x00', -- 0.5×Ireg
    '0x20', -- 0.2×Ireg
    ...
}

4. GPIO 引脚初始化

获取一个 GPIO 引脚并对其进行设置。pcb.chargeCmdPin() 用于获取充电命令引脚,gpio.setup 将引脚 GPIO1 设置为输出模式,且初始化电平为高,同时启用内部上拉。

local gpio_pin = pcb.chargeCmdPin()
gpio.setup(gpio_pin, 1, gpio.PULLUP)

5. 任务初始化

这部分是主任务的初始化。首先等待 1000 毫秒,然后通过 sensor.yhm27xx 函数与传感器进行通信,以获取设备信息。后面再进行传感器设备中各寄存器参数配置。

sys.taskInit(function()
    sys.wait(1000)
    ...
end)

6. 传感器数据读取

通过 sensor.yhm27xx 函数调用来读取传感器的数据。 参数说明:gpi``o_pin 是传感器对应引脚,0x04 是传感器设备 ID,0x08 是设备信息寄存器的地址。等待 200 毫秒等待数据返回。

local result, data = sensor.yhm27xx(gpio_pin, 0x04, 0x08)
sys.wait(200)

7. 设备存在检查

当检测到传感器存在时,会进行进一步的设置。此时根据不同寄存器的地址设置不同功能。

if result == true and data ~= nil then
    log.info("yhm27xxx", "yhm27xx存在--")
    ...
end

8. 电压跟随与快速充电电流设置

读取 I_CTRL 寄存器并设置电压跟随与快速充电电流。如果操作成功,将会再次读取配置寄存器并确认设置结果,记录相关的日志信息。 参数说明:gpio_pin 是传感器对应引脚,0x04 是芯片设备 ID 号,0x01 是 I_CTRL 寄存器地址,0x02 是设置电压跟随与快速充电电流为 0.5×Ireg。 特别说明:若需要在同一寄存器中设置多个参数,则需要将参数值进行或运算。(例如,要设置电压跟随和快速充电电流为 0.5×Ireg,则参数值为 0x02+0x00=0x02 。快速充电电流表可参考下方表格)

I_CTRL 寄存器地址前三位是设置快速充电电流,其余位是其他设置,默认为 0; Ireg 是电阻 Rsns 配置的电流值,Air201 的电阻 Rsns 值为 1KΩ,通过公式 Ireg=500/Rsns,得出 Ireg=500mA。

配置编号
快速充电电流值
配置编号
快速充电电流值
0x00(默认)
0.5×Ireg = 250mA
0x80
1.0×Ireg = 500mA
0x20
0.2×Ireg = 100mA
0xA0
1.5×Ireg = 750mA
0x40
0.7×Ireg = 350mA
0xC0
2.0×Ireg = 1000mA
0x60
0.9×Ireg = 450mA
0xE0
3.0×Ireg = 1500mA
result = sensor.yhm27xx(gpio_pin, 0x04, 0x01, 0x02)

9. 电池上限电压设置

读取 V_CTRL 寄存器并设置电池上限电压。如果操作成功,将会再次读取配置寄存器并确认设置结果,记录相关的日志信息。 参数说明:gpio_pin 是传感器对应引脚,0x04 是芯片设备 ID 号,0x00 是 V_CTRL 寄存器地址,0x00 是设置电池上限电压为 4.2V,可参考下方表格。

配置编号
上限电压
配置编号
上限电压
配置编号
上限电压
配置编号
上限电压
0x00
4.2V
0x40
4.3V
0x80
4.4V
0xC0
4.5V
0x10
4.225V
0x50
4.325V
0x90
4.425V
0xD0
4.525V
0x20
4.25V
0x60(默认)
4.35V
0xA0
4.45V
0xE0
4V
0x30
4.275V
0x70
4.375V
0xB0
4.475V
0xF8
4.1V
result = sensor.yhm27xx(gpio_pin, 0x04, 0x00, 0x00)

10. 异常处理

如果在读取传感器信息时返回结果为假,则说明传感器不存在,记录警告日志。

else
    log.warn("yhm27xxx", "yhm27xx不存在")

6.1.3 总结说明

这段代码的主要功能是与充电 IC 进行交互以监控其状态,配置其控制参数,并在不同情况下记录相关的日志信息。通过这种方式,系统能够检测充电 IC 的存在与状态,并进行相应的初始化与配置,以确保充电过程的正常进行。全程使用异步任务管理,提升系统响应能力。

6.2 pcb.lua 中代码介绍

该文件中的代码无需修改,大家直接调用这个文件就行。

6.2.1 代码说明

这段 Lua 代码定义了一个名为 pcb 的模块,其主要功能是管理和操作硬件的某些特性,特别是与 PCB(印刷电路板)相关的设置和状态。

6.2.2 代码逐步解析

1. 模块初始化和变量定义:

这里定义了一个表 pcb,用于存放后续的函数。定义了存储系统的 OTP 区域和硬件的默认版本号以及出厂测试结果的初始值。

local pcb = {}
local otpZone = 2  -- OTP 存储区的编号
local hversion = "1.0.3"  -- 默认版本号
local test = false  -- 初始出厂测试结果

2. 读取 OTP 存储区和解析硬件版本号的函数:

loadParam 函数首先检查 OTP 是否存在,然后读取 OTP 存储区中的数据,解码 JSON 格式的数据以获取硬件版本号和出厂测试结果。若解码失败或缺少数据,则根据设备型号进行版本号的设置。

local function loadParam()
    if not otp then
        return
    end
    local len = otp.read(otpZone, 0, 1)
    local otpdata = otp.read(otpZone, 1, string.byte(len))
    local obj, result, errMsg = json.decode(otpdata)
    ...
end
loadParam()

3. GNSS 电源控制功能:

此函数根据硬件版本控制 GNSS 模块的电源状态。通过 GPIO 接口进行电源的开启或关闭。

function pcb.gnssPower(onoff)
    if hversion == "1.0.2" then
        gpio.setup(2, onoff and 1 or 0)
        gpio.setup(26, onoff and 1 or 0)
        gpio.setup(27, onoff and 1 or 0)
    elseif hversion == "1.0.3" then
        gpio.setup(25, onoff and 1 or 0)
        gpio.setup(26, onoff and 1 or 0)
    end
end

4. ES8311 电源引脚的获取:

根据硬件版本返回 ES8311 控制器对应的电源引脚。

function pcb.es8311PowerPin()
    if hversion == "1.0.2" then
        return 25
    elseif hversion == "1.0.3" then
        return 2
    end
end

5. 充电 IC CMD 引脚的获取:

此函数根据硬件版本返回充电 IC 的命令引脚。

function pcb.chargeCmdPin()
    if hversion == "1.0.2" then
        return 20
    elseif hversion == "1.0.3" then
        return 27
    end
end

6. 出厂测试结果获取及版本号管理:

提供了获取当前硬件版本号和出厂测试结果的方法,以及设置硬件版本号的功能。

function pcb.test()
    return test
end
function pcb.hver()
    return hversion
end
function pcb.sethver(ver)
    hversion = ver
end

6.2.3 总结说明

整段代码的主要功能是提供对印刷电路板(PCB)相关硬件配置和状态的管理。通过读取 OTP 存储区,程序能够获取设备的特定参数(如版本号和出厂测试结果)。此外,它还实现对 GNSS 电源、ES8311 音频编解码器以及充电 IC 的控制接口,根据不同的硬件版本提供不同的引脚控制方案。

6.3 batteryManage.lua 中代码介绍

6.3.1 代码说明

这段代码是一个电池管理模块的实现,主要用于监控和管理电池状态,包括电压检测、充电状态判断、电量计算和自动关机功能。

6.3.2 代码逐步解析

1. 模块定义

创建一个名为 M 的模块,定义模块名称为“batteryManage”。

local M = {}
M.moduleName = "batteryManage"

2. 引入库

引入了 syssysplus 库,这些库提供了系统和附加功能的支持。

sys = require "sys"
sysplus = require "sysplus"

3. 初始化状态变量

  • M.vbat: 当前电池电压。
  • M.batteryPercent: 电池电量百分比,初始为 50。
  • M.isCharge: 标识是否正在充电。
  • M.BATTERY_UPDATE_INTERVAL: 电池信息更新的时间间隔(秒)。
  • M.noChargeDuration: 记录未插入电源的时间。
  • M.CHARGE_THRESHOLD_TIME: 未充电持续时间的阈值(2 分钟)。
  • M.blueLedM.redLed: 用于设置蓝灯和红灯的状态,分别用 GPIO 接口进行控制。
M.vbat = 0
M.batteryPercent = 50
M.isCharge = false
M.BATTERY_UPDATE_INTERVAL = 10
M.noChargeDuration = 0
M.CHARGE_THRESHOLD_TIME = 120
M.blueLed = gpio.setup(1, 0)
M.redLed = gpio.setup(16, 0)

4. 电池放电曲线

定义一个电池放电曲线数组,该数组存储对应电量的电压值,用于通过电压推算电量百分比。

M.battery = {4175, 4128, ... , 3269}

5. 重置未充电时长

重置未充电计时器的函数。

function M.resetNoChargeDuration()
    M.noChargeDuration = 0
end

6. 关机函数

该函数用于发布关机信号,以请求系统关机。

function M.powerOff()
    log.info("powerOff", "关机")
    sys.publish("SYS_SHUTDOWN")
end

7. 充电状态检测

使用 GPIO 中断回调来检测充电状态,如果通过 GPIO 引脚检测到充电状态发生变化,将更新充电状态并检查电池状态。

function M.chargeCheck()
    ...
end

8. 电量百分比计算

根据当前电压 M.vbat 计算电池百分比。如果电压在放电曲线数组的范围内,则返回相应的电量百分比。

function M.mathBatteryPercent()
    ...
end

9. 定期检查电池状态

定期更新电池电量,读取电压并计算电量。如果电量低于 30%,且没有插入电源的时间超过 2 分钟,则调用关机函数。

function M.checkBattery()
    ...
end

10. 长按电源键关机

实现了长按电源键的关机逻辑,按下电源键 3 秒后进行关机,松开时取消关机。

function M.pwrkeyCb()
    ...
end

6.3.3 总结说明

整体而言,该模块负责监控电池的电压和充电状态。它能够定期更新电量百分比,并在必要时自动关机。此外,该模块还允许用户通过长按电源键来手动关机。其主要功能是确保设备在电量过低时保护系统不受损坏,同时提供充电状态的实时反馈。

6.3 main.lua 中代码介绍

6.3.1 代码说明

该文件中的代码使用 Lua 语言编写,主要用于管理和监控设备的电源和电池状态。

6.3.2 代码逐步解析

1. 项目和版本信息

这部分定义了项目名称和版本,并记录日志信息,便于后续的调试和维护。

PROJECT = "demo"
VERSION = "1.0.0"
log.info("main", PROJECT, VERSION)

2. 库的引入

在这里,引入了多个必要的库文件,包括系统管理库 sys、电路板库 pcb、充电逻辑库和电池管理库。通过这些库,程序可以实现对系统的控制和管理。 特别说明:主要功能代码已经模块化到 batteryManage 库文件中。

sys = require "sys"
sysplus = require "sysplus"
pcb = require "pcb"
require "ChargeICLogic"
batteryManage = require "batteryManage"

3. 开机防抖

这部分代码用于防止开机时的抖动,并点亮红色指示灯,以表明设备正在启动。

pm.power(pm.PWK_MODE, true)
batteryManage.redLed(1) -- 默认开机时红灯亮

4. GPIO 设置

通过 gpio.setup 配置了两个引脚:一个用于监测充电状态,另一个用于电源键的回调。

gpio.setup(40, batteryManage.chargeCheck, gpio.PULLDOWN, gpio.BOTH)  -- 充电状态引脚
gpio.setup(46, batteryManage.pwrkeyCb, gpio.PULLUP, gpio.BOTH)       -- 电源键引脚

5. 关机处理

这段代码订阅了系统关机事件,当设备接收到关机信号时,会调用 pm.shutdown() 函数进行安全关机。

sys.subscribe("SYS_SHUTDOWN", function()
    pm.shutdown()
end)

6. 启动初始检查

在系统启动时,首先进行一次电池充电状态检查,并设置一个定时器,每 60 秒更新一次电池电量信息。

batteryManage.chargeCheck()
sys.timerLoopStart(batteryManage.checkBattery, batteryManage.BATTERY_UPDATE_INTERVAL * 1000) --每60s更新一次电池电量

7. 日志输出

此段代码设置了一个定时器,每隔 5 秒输出一次当前电池状态的日志信息,包括电压、电池电量及充电状态,便于监控和调试。

sys.timerLoopStart(function()
    log.info(batteryManage.moduleName, "当前电压:", batteryManage.vbat, "电池电量:", batteryManage.batteryPercent, "%", "充电状态:", batteryManage.isCharge and "正在充电" or "未充电")
end, 5000) -- 每5s打印一次状态信息

8. 程序运行

最后,sys.run() 函数用于启动系统的主循环,让程序保持运行状态。

sys.run()

6.3.3 总结说明

该代码的主要功能是管理和监控嵌入式设备的电源与电池状态,具备开机防抖、充电状态检测、定时更新电池电量和记录日志等功能。

6.4 完整代码展示

此处只展示 batteryManage.luamain.lua。其余完整代码请下载源码查看。

batteryManage.lua

local M = {}

M.moduleName = "batteryManage"

-- 引入必要的库文件(lua编写), 内部库不需要require
sys = require "sys"
sysplus = require "sysplus"

-- 当前vbat电压
M.vbat = 0
-- 电池电量百分比
M.batteryPercent = 50
-- 是否正在充电
M.isCharge = false
-- 电池电量更新间隔,单位为秒
M.BATTERY_UPDATE_INTERVAL = 10
-- 记录未插入电源的持续时间
M.noChargeDuration = 0
-- 充电阈值时间,单位为秒
M.CHARGE_THRESHOLD_TIME = 120
-- 蓝灯状态
M.blueLed = gpio.setup(1, 0)
-- 红灯状态
M.redLed = gpio.setup(16, 0)

-- 电池放电曲线(根据您的需求调整)
M.battery = {4175, 4128, 4108, 4093, 4079, 4067, 4055, 4043, 4031, 4019, 4007, 3995, 3984, 3971, 3960, 3947, 3936, 3924, 3913, 3901, 3890, 3878, 3866, 3856, 3845, 3834, 3823, 3812, 3801, 3790, 3781, 3769, 3758, 3749, 3738, 3728, 3718, 3708, 3699, 3688, 3679, 3671, 3661, 3652, 3644, 3636, 3628,
                 3620, 3613, 3605, 3599, 3592, 3585, 3579, 3573, 3568, 3562, 3557, 3552, 3547, 3542, 3537, 3532, 3527, 3523, 3519, 3515, 3510, 3505, 3501, 3497, 3492, 3488, 3484, 3479, 3475, 3470, 3466, 3461, 3457, 3452, 3447, 3442, 3436, 3431, 3425, 3420, 3414, 3408, 3401, 3394, 3387, 3379, 3371,
                 3362, 3352, 3341, 3327, 3312, 3294, 3269}

function M.resetNoChargeDuration()
    M.noChargeDuration = 0  -- 重置未充电计时器
end

-- 发布关机消息,进行关机
function M.powerOff()
    log.info("powerOff", "关机")
    sys.publish("SYS_SHUTDOWN")
end

-- gpio中断回调(用于充电状态检测)
function M.chargeCheck()
    -- 检测充电状态
    if gpio.get(40) == 0 then
        M.blueLed(0)
        if M.isCharge then
            M.isCharge = false
            M.checkBattery()  -- 检查电池状态
        end
    else
        M.blueLed(1)
        if not M.isCharge then
            M.isCharge = true
            M.checkBattery()  -- 检查电池状态
        end
    end
end

-- 通过当前电压推算电量百分比
function M.mathBatteryPercent()
    if M.vbat >= M.battery[1] then
        return 100
    end
    if M.vbat <= M.battery[#M.battery] then
        return 0
    end
    for i = 1, #M.battery do
        if M.vbat <= M.battery[i] and M.vbat > M.battery[i + 1] then
            return (100 - i)
        end
    end
end

-- 电池电量周期性更新,60s更新一次,且如果电量低于30%,并且2分钟没有插入电源,则关机
function M.checkBattery()
    adc.open(adc.CH_VBAT)                 -- 打开ADC通道
    local tmp = adc.get(adc.CH_VBAT) -- 读取VBAT电压
    adc.close(adc.CH_VBAT)                -- 关闭ADC通道

    if M.vbat == 0 then -- 初始值
        M.vbat = tmp
    else
        if tmp - M.vbat > 100 then
            log.info("vbat", "电压跳变!!!", M.vbat, "-->", tmp)
            M.vbat = M.vbat + 5
        else
            M.vbat = tmp
        end
    end

    M.batteryPercent = M.mathBatteryPercent()  -- 计算电池百分比
    if M.batteryPercent <= 30 then
        if M.isCharge then
            M.resetNoChargeDuration()  -- 如果正在充电,重置计时器
        else
            M.noChargeDuration = M.noChargeDuration + M.BATTERY_UPDATE_INTERVAL  -- 未充电计时器累加
            if M.noChargeDuration >= M.CHARGE_THRESHOLD_TIME then
                M.powerOff()  -- 如果未插入电源超过2分钟,则关机
            end
        end
    else
        M.resetNoChargeDuration()  -- 电量正常,重置计时器
    end
end

-- 长按电源键关机
function M.pwrkeyCb()
    if gpio.get(46) == 1 then
        log.info("按键丢手")
        if sys.timerIsActive(M.powerOff) then
            sys.timerStop(M.powerOff)
            sys.publish("EXIT_FLYMODE")  -- 取消关机
        end
    else
        log.info("按键按下")
        sys.timerStart(M.powerOff, 3000)   -- 长按3秒关机
    end
end

return M

main.lua

-- LuaTools需要PROJECT和VERSION这两个信息
PROJECT = "demo"
VERSION = "1.0.0"

log.info("main", PROJECT, VERSION)

-- 引入必要的库文件(lua编写), 内部库不需要require
sys = require "sys"
sysplus = require "sysplus"

pcb = require "pcb"
require "ChargeICLogic"
batteryManage = require "batteryManage"

-- 开机防抖
pm.power(pm.PWK_MODE, true)

batteryManage.redLed(1) -- 默认开机时红灯亮

-- GPIO 设置
gpio.setup(40, batteryManage.chargeCheck, gpio.PULLDOWN, gpio.BOTH)  -- 充电状态引脚
gpio.setup(46, batteryManage.pwrkeyCb, gpio.PULLUP, gpio.BOTH)       -- 电源键引脚

-- 关机
sys.subscribe("SYS_SHUTDOWN", function()
    pm.shutdown()
end)

-- 启动初始检查
batteryManage.chargeCheck()
sys.timerLoopStart(batteryManage.checkBattery, batteryManage.BATTERY_UPDATE_INTERVAL * 1000) --每60s更新一次电池电量

-- 日志信息输出(可选)
sys.timerLoopStart(function()
    log.info(batteryManage.moduleName, "当前电压:", batteryManage.vbat, "电池电量:", batteryManage.batteryPercent, "%", "充电状态:", batteryManage.isCharge and "正在充电" or "未充电")
end, 5000) -- 每5s打印一次状态信息

-- 用户代码已结束---------------------------------------------
-- 结尾总是这一句
sys.run()

6.5 运行结果展示

通过 Luatoos 工具查看

七、总结

本文介绍了电源管理系统的基本概念、目标以及在实际应用中的实现方式。通过深入分析电源管理的各个功能模块,我们了解了电量监测、充电检测、自动关机和用户接口等关键功能。这些功能共同构成了一个高效且智能的电源管理系统,能够显著提升设备的性能和用户满意度。

在电源管理实践中,合理的策略和有效的代码实施是成功的关键。通过实时监测电池状态和智能控制电源操作,不仅能够延长设备的使用时间,还能确保设备在关键时刻的稳定性与可靠性。这对于以移动设备和嵌入式系统为主的应用场景尤其重要。

未来,我们可以继续探索更为先进的电源管理技术,比如基于人工智能的预测分析和优化算法,以便进一步提高电源管理的智能化水平。这将为设备的使用者带来更佳的体验,使电源管理成为每一款电子设备中不可或缺的一部分。

八、扩展

九、常见问题

1. 设备无法开启或频繁自动关机

检查电池电量是否低于安全工作水平,确保设备充电。如果电量很低,请连接电源并充电一段时间再尝试开机。

2. 电池电量显示不准确

确保电池电压监测模块正常工作,检查 ADC 通道是否正确配置。如有必要,重新校准电池电量百分比计算的映射表。

3. 充电状态无法正确识别

检查充电检测的 GPIO 引脚连接是否正常,确认是否存在接触不良或线路损坏的问题。此外,确保所用充电器和数据线与设备兼容。

4. 系统在充电时无法正常运行

检查是否有其他程序或任务占用过多资源,导致系统不稳定。可以考虑优化系统任务的优先级或关闭不必要的功能模块。

5. 长按电源键无反应

确认电源键的 GPIO 引脚连接正常,确保其可被正常识别。如果仍然无效,请检查相关代码对电源键的事件处理是否正确。

6. 设备发热严重

设备发热可能是由于充电速率过高或设备运行负载过重引起的。建议降低负载、优化代码,或调整充电策略。

7. 无法更新固件或脚本

检查 USB 连接是否正常,确保 Luatools 工具正确识别设备。注意遵循烧录步骤,并确保所用的固件版本与硬件兼容。

给读者的话

本篇文章由 马梦阳 开发; 本篇文章描述的内容,如果有错误、细节缺失、细节不清晰或者其他任何问题,总之就是无法解决您遇到的问题; 请登录合宙技术交流论坛,点击 此处放文档链接 用截图标注 + 文字描述的方式跟帖回复,记录清楚您发现的问题; 我们会迅速核实并且修改文档; 同时也会为您累计找错积分,您还可能赢取月度找错奖金!