一. 背景及问题:
由于项目需要,板子硬件接的PMU【rk808】是不带电池功能的,不支持库仑计计算电量,而项目又需要接电池使用,所以硬件把电池端接到一个ADC口,做了个简单的电池电路,通过ADC读取数值来确定电池电压,然后换算电池电量。

下面是原理图方面:可以看到,原理图通过把电池电压分压之后,接到了主控端的ADC0口,现在就需要写个驱动通过读取ADC的数值来粗略计算电池的电量,然后上报上层显示状态信息。

二.思路和方法:

电池驱动部分大概流程是这样的:
Android内核中的电池驱动采取的是linux 内核驱动中的 power_supply子系统框架进行上报电池状态。power_supply主要通过sys文件系统向用户层提供读取电池状态的接口,路径为 /sys/class/power_supply/ , 该目录下通常会有 ac , battery, usb 三个目录,代表给Android系统供电的三种能源类型,其中电池的状态就在battery的目录下,当电池状态变化的时候会通过uevent机制通知上层,然后上层通过读取该目录下相应的值来动态的显示电池状态。

电池驱动的源码目录:kernel/drivers/power

三. 技术总结

我这里参考rk818电池驱动的实现方式,把电池电量和状态上报到上层,实现动态的显示电池状态信息。

1、对电源(ac , battery, usb)进行初始化(这里说的是rk818):

对应驱动文件:drivers/power/rk818_charger.c

     469 static const struct power_supply_desc rk818_ac_desc = {470         .name           = "ac",//设备名称471         .type           = POWER_SUPPLY_TYPE_MAINS,//类型472         .properties     = rk818_ac_props,//属性473         .num_properties = ARRAY_SIZE(rk818_ac_props),//属性数目474         .get_property   = rk818_cg_ac_get_property,//得到属性的函数475 };476477 static const struct power_supply_desc rk818_usb_desc = {478         .name           = "usb",479         .type           = POWER_SUPPLY_TYPE_USB,480         .properties     = rk818_usb_props,481         .num_properties = ARRAY_SIZE(rk818_usb_props),482         .get_property   = rk818_cg_usb_get_property,483 };

对应驱动文件:drivers/power/rk818_battery.c

    1017 static const struct power_supply_desc rk818_bat_desc = {1018         .name           = "battery",1019         .type           = POWER_SUPPLY_TYPE_BATTERY,1020         .properties     = rk818_bat_props,1021         .num_properties = ARRAY_SIZE(rk818_bat_props),1022         .get_property   = rk818_battery_get_property,1023 };

这里主要是实现给电源名字类型等赋初值,最主要是将get_property函数指向我们写好的可以得到电源的属性的函数的起始地址,以便当内核需要用到驱动的信息的时候进行回调。

2、通过power_supply_register(devm_power_supply_register最终也是调用power_supply_register)将所提供的电源进行注册,即把他们的属性写到sys文件系统里,使用户空间可以得到有关电源的信息。

    489   cg->usb_psy = devm_power_supply_register(cg->dev, &rk818_usb_desc,490                                                                   &psy_cfg);

power_supply_register调用内核提供的函数device_create()和power_supply_create_attrs来实现电源的注册,这里电源类型是usb。

3、Power Supply驱动程序头文件是kernel/include/linux/power_supply.h,注册和注销驱动程序的函数如下:

int power_supply_register(struct device *parent,struct power_supply *psy);void power_supply_unregister(struct power_supply *psy);struct power_supply {const char *name; /*设备名称*/enum power_supply_type type; /* 类型 */enum power_supply_property *properties; /* 属性指针 */size_t num_properties; /*属性的数目*/char* *supplied_to;size_t num_supplicants;int (*get_property)(struct power_supply *psy, /*获得属性*/enum power_supply_property psp,union power_supply_propval *val);void (*external_power_changed)(struct power_supply *psy);/* ...... 省略部分内容 */

内核主要通过get_property这个函数指针来获得驱动中的有关电池的信息,而这个函数在内核中只给出了声明,我们在写驱动的时候要自己实现这个函数,即将自己写的函数赋值给这个函数指针,当内核需要驱动中电源信息的时候就回调这个get_property函数。另外,我们写驱动程序的时候又要给用户提供接口,内核中提供给用户的接口就是sysfs,通过读取sysfs文件系统中文件内容,就可以得到电源的信息。内核主要通过两个文件power_supply_class.c 和power_supply_core.c,我们调用其中的函数就可以把电源(BATTERY,USB或AC)的信息展现给用户,有关电源的属性写在/sys/class/powersupply文件夹下(此文件夹为程序运行后所生成的)。

ac和usb只创建了一个online属性,上层通过判断ac和usb的online状态(1表示设备接入,0表示设备拔出)便可知道当前系统是由什么设备在充电了;而battery则创建了如status、health、present、capacity、batt_vol等等和电池相关的诸多属性,上层通过这些电池属性uevent便可监控电池的当前工作状态了。下面举例是battery,ac和usb同理。

     955     static int rk818_battery_get_property(struct power_supply *psy,956                                       enum power_supply_property psp,957                                       union power_supply_propval *val)958   {959         struct rk818_battery *di = power_supply_get_drvdata(psy);960961         switch (psp) {962         case POWER_SUPPLY_PROP_CURRENT_NOW:963                 val->intval = di->current_avg * 1000;/*uA*/ //获取电池电流964                 if (di->pdata->bat_mode == MODE_VIRTUAL)965                         val->intval = VIRTUAL_CURRENT * 1000;966                 break;967         case POWER_SUPPLY_PROP_VOLTAGE_NOW:968                 val->intval = di->voltage_avg * 1000;/*uV*/ //获取电池电压969                 if (di->pdata->bat_mode == MODE_VIRTUAL)970                         val->intval = VIRTUAL_VOLTAGE * 1000;971                 break;972         case POWER_SUPPLY_PROP_PRESENT:973                 val->intval = is_rk818_bat_exist(di);974                 if (di->pdata->bat_mode == MODE_VIRTUAL)975                         val->intval = VIRTUAL_PRESET;976                 break;977         case POWER_SUPPLY_PROP_CAPACITY:978                 val->intval = di->dsoc; //获取电池电量979                 if (di->pdata->bat_mode == MODE_VIRTUAL)980                         val->intval = VIRTUAL_SOC;981                 DBG("<%s>. report dsoc: %d\n", __func__, val->intval);982                 break;983         case POWER_SUPPLY_PROP_HEALTH:984                 val->intval = POWER_SUPPLY_HEALTH_GOOD;985                 break;986         case POWER_SUPPLY_PROP_TEMP:987                 val->intval = di->temperature;988                 if (di->pdata->bat_mode == MODE_VIRTUAL)989                         val->intval = VIRTUAL_TEMPERATURE;990                 break;

各能源设备属性概况如下(adb工具或者接串口cat可以查看):

 /sys/class/power_supply/ac/online AC 电源连接状态/sys/class/power_supply/usb/online USB电源连接状态/sys/class/power_supply/battery/status 充电状态/sys/class/power_supply/battery/health 电池状态/sys/class/power_supply/battery/present 使用状态/sys/class/power_supply/battery/capacity 电池 level/sys/class/power_supply/battery/batt_vol 电池电压/sys/class/power_supply/battery/batt_temp 电池温度/sys/class/power_supply/battery/technology 电池技术

当供电设备的状态或者电量发生变化后,会调用power_supply_changed(&battery_data->battery)更新这些文件;

 //Send uevent.power_supply_changed(&battery_data->battery);

备注:
1、Uevent机制
Uevent是内核通知android有状态变化的一种方法,比如USB线插入、拔出,电池电量变化等等。其本质是内核发送(可以通过socket)一个字符串,应用层(android)接收并解释该字符串,获取相应信息。

使用示例:

下面说说我之前在项目上实现的电池驱动,Android需要显示电池电量和充放电状态,所以按照电池驱动框架做了一个伪电池驱动, 主要是使用它的AC充电状态和电池电量这两个property。

1、充放电状态检测和电池电量更新:主要是通过检测一个GPIO的电平来实现,高电平则认为AC充电器拔掉,电池处于放电状态;低电平则认为充电器插入,电池进入充电状态。

     87 static int gt_ac_set_property(struct power_supply *psy,88                         enum power_supply_property psp,89                         const union power_supply_propval *val)90 {9192         int ret = 0;9394         switch (psp) {95         case POWER_SUPPLY_PROP_ONLINE:96                 data->bat_status = data->ac_online = val->intval;97                 power_supply_changed(data->ac);98                 break;99         default:100                 ret = -EINVAL;101                 break;102         }103         return ret;104 }105 static int gt_battery_get_property(struct power_supply *psy,106                                  enum power_supply_property psp,107                                  union power_supply_propval *val)108 {109110         int ret = 0;111         switch (psp) {112         case POWER_SUPPLY_PROP_STATUS:113                 val->intval = data->bat_status;114                 break;115         case POWER_SUPPLY_PROP_HEALTH:116                 val->intval = POWER_SUPPLY_HEALTH_GOOD;117                 break;118         case POWER_SUPPLY_PROP_PRESENT:119                 val->intval = data->bat_present;120                 break;121         case POWER_SUPPLY_PROP_TECHNOLOGY:122                 val->intval = POWER_SUPPLY_TECHNOLOGY_LION;123                 break;124         case POWER_SUPPLY_PROP_CAPACITY:125                 val->intval = data->bat_capacity;126                 break;127         default:128                 ret = -EINVAL;129                 break;130         }131132            return ret;133     }

2、通过ADC来读取对应电池电压(这里由于adc的量程最大只能到1.8v,所以硬件做了分压处理,大概分了1/3,即读到的电压值应该乘以3就是对应电池电压值)对应的ADC值,然后通过ADC值换算出对应的电池电压,rk3288的adc采样的位数是10,所以adc值对应2的10次方,也就是1024。
具体换算:ADC值 = 1024 x ADC电压值/1800, //电压单位是mV
然后算出对应电压值,在根据电压值大概计算当前电量,3.5V左右对应0%,4.2V左右对应100%电量。

3、对应的节点:

/sys/class/power_supply/gt-ac
/sys/class/power_supply/gt-battery

手动改变电池上报的电量:echo 90 > /sys/class/power_supply/gt-battery //上报90%电量值给上层
手动改变电池充放电状态:echo 1 > /sys/class/power_supply/gt-ac //设置为充电状态

4.调试手段:

获取电池信息

adb命令:adb shell dumpsys battery
得到信息如下:
AC powered: false
USB powered: true
Wireless powered: false
status: 1 #电池状态:2:充电状态 ,其他数字为非充电状态
health: 2
present: true
level: 55 #电量: 百分比
scale: 100
voltage: 3977
current now: -335232
temperature: 335 #电池状态
technology: Li-poly

5、驱动调试过程问题点【后续需注意的问题】
5.1驱动申请ADC通道时,会出现申请报错的情况?
驱动需要获取ADC通道来使用时,需要对驱动的加载时间进行控制,必须要在saradc初始化之后。saradc是使用module_platform_driver()进行平台设备驱动注册,最终调用的是module_init()。所以用户的驱动加载函数只需使用比module_init()优先级低的,例如:late_initcall(),就能保证驱动的加载的时间比saradc初始化时间晚,可避免出错。

5.2驱动的ADC通道获取的电压值采用了分压,大概分到三分之一,所以在计算电池电压的时候,需要乘以3后才得到实际电池电压。

RK3288_Android7.1通过ADC实现电池电量粗略计算上报相关推荐

  1. ADC 采集电池电量

    单片机内部的多路ADC采集之间可能会相互影响,使用的时候需要参考datasheet,在NUC100中,ADC7 比较与ADC6同时使用时,ADC6的采样就会出问题,采集的电量值一直保持不变. 其次,A ...

  2. stm32l151 ADC通过DMA通道定时采样电池电量

      最近在使用stm32l151开发一个项目,我的项目需求是ADC采集电池电量,通过DMA通道传送出来.然而我并不是得到了电池电量数据后就立马连续输出,而是通过tim4定时器每1s访问一次采样得到的电 ...

  3. 基于STM32HAL库ADC+DMA模式,高精度采集电池电量与芯片内部温度方法 (48脚 使用内部参考电压方案)

    目录 概述 1.原理图 2.在这先普及一下概念 3.通过查看STM32L0中文数据手册中301页,第14.10 小节 ,DataSheet 4.ADC通道转换模式的理解 5.STM32CubeMx工具 ...

  4. STM32F103C8T6电池电量ADC检测代码,适用于基于库函数的STM32微控制器

    以下是基于库函数的STM32微控制器,如STM32F103C8T6,用于电池电量ADC检测的代码: #include "stm32f10x.h"void ADC1_Init(voi ...

  5. WINCE6.0+S3C2443下ADC在电池驱动应用

    ********************************LoongEmbedded************************ 作者:LoongEmbedded(kandi) 时间:201 ...

  6. Arduino检测外部电池电量方法

    文章目录 测量电池电量原理 测电池电压可能方案 代码 atmega寄存器说明 测量电池电量原理 对于锂电池而言,可以用电池电压近似代替指示电池电量,一般来说单节锂电池电压范围是3~4.2v,测得电池电 ...

  7. android 电量管理机制,电池电量分析---android篇

    原标题:电池电量分析---android篇 本文将从底层到上层介绍Android系统中电量显示这一块,电池检测采用的是ADC采样,不是使用市场上封装好的电量计芯片: 1.驱动层 Linux内核中提供p ...

  8. mt6737电池电量计算

    这里采用的是库伦积分计算电量的方式,硬件连接如下 通过cs_p,cs_n接到一个10豪欧的电阻来测量电流,进行电流积分来计算消耗的电量(DOD指放电深度). 公式 DOD1 = DOD0 + (-Ca ...

  9. 一种基于电池电压计算电池电量的算法的分析总结

    这种算法应用在低成本的无界面的移动产品上,不依于赖库伦计,纯电压计算电压的方法. 首先,设置一些基本的变量: struct capacity {int capacity;int min;int max ...

最新文章

  1. 在应用程序中替换Linux中Glibc的malloc的四种方法
  2. KVM — Overview
  3. java ajax 点赞功能_Ajax+jQuery+bootstrap+Java实现异步点赞功能,并限制点击次数
  4. java webrtc ns降噪_单独编译和使用webrtc音频降噪模块(附完整源码+测试音频文件)...
  5. 【摄影测量原理】第四章:解析空中三角测量
  6. 转载|网络编程中阻塞式函数的底层逻辑
  7. Java番外篇1——正则表达式
  8. 文件批量传输组件作为架包使用说明
  9. c语言100块钱买100只鸡算法,JS计算输出100元钱买100只鸡问题的解决方法
  10. 简述中断处理的6个步骤_计算机组成原理期末考试简答题重点分解
  11. 《OpenStack实战指南》—— 1.4 OpenStack与CloudStack的比较
  12. cad计算机快捷键设置,2014年CAD计算机快捷键
  13. FreeCAD源码分析:Assembly3模块
  14. 控制台程序实现暂停功能
  15. [MFC] 绘制图像ROI区域(OpenCv库)
  16. 小不点浏览器 v1.00 官方
  17. 案例:理想主义的猪与结果导向的猪
  18. Gradient Normalization在多任务学习中的优化实践
  19. 谈谈Linux发行版的入门选择
  20. Android随机点名器,Excel基础知识-详解随机点名器

热门文章

  1. 服务器系统内存坏了会怎样,存储服务器故障的六大原因你知道几点?
  2. 8月11日 网工学习 APR协议 传输层协议 TCP UDP 数据封装转发全过程
  3. mac使用u盘安装系统
  4. Java猜数字大小游戏
  5. 解决echarts中地图重叠问题
  6. 机器人编程趣味实践14-机器人三维仿真(Gazebo+TurtleBot3)
  7. 植物三维模型快速重建
  8. EverBox开发笔记-3-iCloud Document Storage
  9. 记一次连接数据库报错The server time zone value '�й���׼ʱ��' is unrecognized or represents more than one time zo
  10. java消息队列mq_我爱java系列---【消息队列(rabbitmq)】