Android Battery 架構【轉】

来源:https://www.cnblogs.com/linhaostudy/archive/2019/11/26/11934904.html
-Advertisement-
Play Games

"Android Battery 架構" Android電源 android中和電源相關的服務有兩個他們在 一個是 ,另一個是在目錄powe下的 。 電源管理架構 Android電源管理主要通過Wakelock機制來管理系統的狀態,整個android電源管理,可以分為四個層次:應用介面層(Power ...


Android Battery 架構

Android電源

image

android中和電源相關的服務有兩個他們在/frameworks/base/services/core/java/com/android/server/

一個是BatteryService.java,另一個是在目錄powe下的PowerManagerService.java

電源管理架構

Android電源管理主要通過Wakelock機制來管理系統的狀態,整個android電源管理,可以分為四個層次:應用介面層(PowerManager.java),Framework層(PowerManagerService.java), HAL(power.c)和linux內核層(kernel/power)。

應用介面層:PowerManager中開放的介面,應用可以調用PM的介面申請wakelock,喚醒系統,使系統進入休眠等操作。

Framework層:應用程式調用PowerManager開放的介面,對系統操作在PowerManagerService中完成,PowerManagerService計算系統中和power相關的計算,是整個電源管理的決策系統。同時協調power如何與系統其它模塊的交互,如亮屏,暗屏,系統睡眠,喚醒等。

HAL層:該層只有一個power.c文件,該文件通過上層傳下來的參數,向/sys/power/wake_lock或/sys/power/wake_unlock文件節點寫入數據來與kernel進行通信,主要功能是申請/釋放鎖,維持屏幕亮滅。

kernel層:kernel/power實現電源管理框架。 drivers/power,設備特定的電源管理框架。

電池管理架構

Android系統對電池的管理驅動繼承了linux的power supply class。在用戶層在BatteryService.java中通過廣播的方式將電池相關的屬性報給app使用,並且註冊了uevent監聽電池狀態變化,以實時獲取電池狀態。

frameworks/base/services/core/java/com/android/server/BatteryService.java

當檢測到電池狀態變化時,給 {@link android.content.Intent#ACTION_BATTERY_CHANGED  BATTERY_CHANGED action}廣播給{@link android.content.BroadcastReceiver IntentReceivers}這類的服務。

電池狀態新的值存放在{@link android.content.Intent#getExtra Intent.getExtra} ,存放的內容如下:

scale:最大電池電量值,通常100
level:當前電量值,從0到scale
status;當前充電狀態
health:電池狀態
present:bool值,如果有電池則值為true
icon-small:整型,該狀態建議使用的icon。
plugged:0,設備未插入,1:AC適配器插入, 2, USB插入
voltage:當前電池電壓mv
temperature:當前電池溫度。
technology:電池類型,如:Li-ion

onStart將電池監聽註冊到底層

public void onStart() {
    IBinder b = ServiceManager.getService("batteryproperties");
    final IBatteryPropertiesRegistrar batteryPropertiesRegistrar =
            IBatteryPropertiesRegistrar.Stub.asInterface(b);
    try {
      //註冊電池監聽,當底層電池電量發生變化調用此監聽,並調用update。
          batteryPropertiesRegistrar.registerListener(new BatteryListener());
    } catch (RemoteException e) {
        // Should never happen.
    }
    //將POWER_SERVICE作為Binder的服務端,註冊到SystemService中
    publishBinderService("battery", new BinderService());
    //將BatteryManagerInternal註冊到本地服務
    publishLocalService(BatteryManagerInternal.class, new LocalService());
}

當底層有信息,會調用update更新BatteryService中相關值。

    private void update(BatteryProperties props) {
        synchronized (mLock) {
            if (!mUpdatesStopped) {
                mBatteryProps = props;
                // Process the new values.
                processValuesLocked(false);
            } else {
                mLastBatteryProps.set(props);
            }
        }
    }

processValuesLocked函數如下:

 private void processValuesLocked(boolean force) {
313         boolean logOutlier = false;
314         long dischargeDuration = 0;
315 //獲取電池電量是否低於critical界限
316         mBatteryLevelCritical = (mBatteryProps.batteryLevel <= mCriticalBatteryLevel);
//獲取充電狀態,AC,USB,無線以及什麼都沒有接
317         if (mBatteryProps.chargerAcOnline) {
318             mPlugType = BatteryManager.BATTERY_PLUGGED_AC;
319         } else if (mBatteryProps.chargerUsbOnline) {
320             mPlugType = BatteryManager.BATTERY_PLUGGED_USB;
321         } else if (mBatteryProps.chargerWirelessOnline) {
322             mPlugType = BatteryManager.BATTERY_PLUGGED_WIRELESS;
323         } else {
324             mPlugType = BATTERY_PLUGGED_NONE;
325         }
 
344         // Let the battery stats keep track of the current level.電池統計信息和當前狀態保持一致
345         try {
346             mBatteryStats.setBatteryState(mBatteryProps.batteryStatus, mBatteryProps.batteryHealth,
347                     mPlugType, mBatteryProps.batteryLevel, mBatteryProps.batteryTemperature,
348                     mBatteryProps.batteryVoltage);
349         } catch (RemoteException e) {
350             // Should never happen.
351         }
 
//低電量關機
353         shutdownIfNoPowerLocked();
//電池溫度過高關機
354         shutdownIfOverTempLocked();
 
//force是第一次調用時標誌,如果狀態有更改依然會調用下麵的代碼
356         if (force || (mBatteryProps.batteryStatus != mLastBatteryStatus ||
357                 mBatteryProps.batteryHealth != mLastBatteryHealth ||
358                 mBatteryProps.batteryPresent != mLastBatteryPresent ||
359                 mBatteryProps.batteryLevel != mLastBatteryLevel ||
360                 mPlugType != mLastPlugType ||
361                 mBatteryProps.batteryVoltage != mLastBatteryVoltage ||
362                 mBatteryProps.batteryTemperature != mLastBatteryTemperature ||
363                 mBatteryProps.maxChargingCurrent != mLastMaxChargingCurrent ||
364                 mInvalidCharger != mLastInvalidCharger))
 
//插入狀態有更改
366             if (mPlugType != mLastPlugType) {
367                 if (mLastPlugType == BATTERY_PLUGGED_NONE) {
368                     // 不充電-->充電
369 
370                     // There's no value in this data unless we've discharged at least once and the
371                     // battery level has changed; so don't log until it does.
372                     if (mDischargeStartTime != 0 && mDischargeStartLevel != mBatteryProps.batteryLevel) {
373                         dischargeDuration = SystemClock.elapsedRealtime() - mDischargeStartTime;
374                         logOutlier = true;
375                         EventLog.writeEvent(EventLogTags.BATTERY_DISCHARGE, dischargeDuration,
376                                 mDischargeStartLevel, mBatteryProps.batteryLevel);
377                         // make sure we see a discharge event before logging again
378                         mDischargeStartTime = 0;
379                     }
380                 } else if (mPlugType == BATTERY_PLUGGED_NONE) {
381                     // 充電-->不充電 或者開機上電
382                     mDischargeStartTime = SystemClock.elapsedRealtime();
383                     mDischargeStartLevel = mBatteryProps.batteryLevel;
384                 }
385             }
//電池狀態更新
386             if (mBatteryProps.batteryStatus != mLastBatteryStatus ||
387                     mBatteryProps.batteryHealth != mLastBatteryHealth ||
388                     mBatteryProps.batteryPresent != mLastBatteryPresent ||
389                     mPlugType != mLastPlugType) {
390                 EventLog.writeEvent(EventLogTags.BATTERY_STATUS,
391                         mBatteryProps.batteryStatus, mBatteryProps.batteryHealth, mBatteryProps.batteryPresent ? 1 : 0,
392                         mPlugType, mBatteryProps.batteryTechnology);
393             }
//電池電量更新
394             if (mBatteryProps.batteryLevel != mLastBatteryLevel) {
395                 // Don't do this just from voltage or temperature changes, that is
396                 // too noisy.
397                 EventLog.writeEvent(EventLogTags.BATTERY_LEVEL,
398                         mBatteryProps.batteryLevel, mBatteryProps.batteryVoltage, mBatteryProps.batteryTemperature);
399             }
...
//發送電池狀態變化廣播
427             sendIntentLocked();
 
//對電源連接/斷開進行單獨的廣播,因為標準的intent將不會喚醒任何應用程式並且一些應用程式基於這個信息可以做一些單獨的“智能”行為
432             if (mPlugType != 0 && mLastPlugType == 0) {
433                 mHandler.post(new Runnable() {
434                     @Override
435                     public void run() {
436                         Intent statusIntent = new Intent(Intent.ACTION_POWER_CONNECTED);
437                         statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
438                         mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);
439                     }
440                 });
441             }
442             else if (mPlugType == 0 && mLastPlugType != 0) {
443                 mHandler.post(new Runnable() {
444                     @Override
445                     public void run() {
446                         Intent statusIntent = new Intent(Intent.ACTION_POWER_DISCONNECTED);
447                         statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
448                         mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);
449                     }
450                 });
451             }
//低電量電池事件通知
453             if (shouldSendBatteryLowLocked()) {
454                 mSentLowBatteryBroadcast = true;
455                 mHandler.post(new Runnable() {
456                     @Override
457                     public void run() {
458                         Intent statusIntent = new Intent(Intent.ACTION_BATTERY_LOW);
459                         statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
460                         mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);
461                     }
462                 });
463             } else if (mSentLowBatteryBroadcast && mLastBatteryLevel >= mLowBatteryCloseWarningLevel) {
464                 mSentLowBatteryBroadcast = false;
465                 mHandler.post(new Runnable() {
466                     @Override
467                     public void run() {
468                         Intent statusIntent = new Intent(Intent.ACTION_BATTERY_OKAY);
469                         statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
470                         mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);
471                     }
472                 });
473             }
 
 
475             // Update the battery LED
476             mLed.updateLightsLocked();
 
 
478             // This needs to be done after sendIntent() so that we get the lastest battery stats.
479             if (logOutlier && dischargeDuration != 0) {
480                 logOutlierLocked(dischargeDuration);
481             }
482 
483             mLastBatteryStatus = mBatteryProps.batteryStatus;
484             mLastBatteryHealth = mBatteryProps.batteryHealth;
485             mLastBatteryPresent = mBatteryProps.batteryPresent;
486             mLastBatteryLevel = mBatteryProps.batteryLevel;
487             mLastPlugType = mPlugType;
488             mLastBatteryVoltage = mBatteryProps.batteryVoltage;
489             mLastBatteryTemperature = mBatteryProps.batteryTemperature;
490             mLastMaxChargingCurrent = mBatteryProps.maxChargingCurrent;
491             mLastBatteryLevelCritical = mBatteryLevelCritical;
492             mLastInvalidCharger = mInvalidCharger;
493         }

healtd

healthd是安卓4.4之後提出來的,監聽來自kernel的電池事件,並向上傳遞電池數據給framework層的BatteryService。BatteryService計算電池電量顯示,剩餘電量,電量級別等信息,其代碼位於/system/core/healthd。

根據Android.mk文件。

LOCAL_SRC_FILES := \
    healthd.cpp \
    healthd_mode_android.cpp \
    healthd_mode_charger.cpp \
    BatteryMonitor.cpp \
    BatteryPropertiesRegistrar.cpp

LOCAL_MODULE := healthd
LOCAL_MODULE_TAGS := optional
LOCAL_FORCE_STATIC_EXECUTABLE := true

這個目錄下的文件會被編譯成healthd可執行程式。

int main(int argc, char **argv) {
    int ch;
    int ret;
    static pthread_t thread;//Talen
 
    klog_set_level(KLOG_LEVEL);
//正常開機啟動
 healthd_mode_ops = &android_ops;
 
    if (!strcmp(basename(argv[0]), "charger")) {
//關機充電
      healthd_mode_ops = &charger_ops;
    } else {
        while ((ch = getopt(argc, argv, "cr")) != -1) {
            switch (ch) {
            case 'c':
                healthd_mode_ops = &charger_ops;
                break;
            case 'r':
//recovery下操作
                healthd_mode_ops = &recovery_ops;
                break;
            case '?':
            default:
                KLOG_ERROR(LOG_TAG, "Talen, Unrecognized healthd option: %c\n", optopt);
                exit(1);
            }
        }
    }
 
    ret = healthd_init();

healthd_init的初始化如下:

static int healthd_init() {
    epollfd = epoll_create(MAX_EPOLL_EVENTS);
    if (epollfd == -1) {
        KLOG_ERROR(LOG_TAG,
                   "epoll_create failed; errno=%d\n",
                   errno);
        return -1;
    }
//和板子級別相關的初始化
    healthd_board_init(&healthd_config);
//根據所處的模式,有三種情況的init,分別是正常安卓系統,關機充電以及recovery。
    healthd_mode_ops->init(&healthd_config);
//wakealarm定時器初始化
    wakealarm_init();
//uevent事件初始化,用以監聽電池的uevent事件
    uevent_init();
//BatteryMonitor初始化。
    gBatteryMonitor = new BatteryMonitor();
    gBatteryMonitor->init(&healthd_config);
    return 0;
}

init分為三種情況。

android(healthd_mode_android.cpp)

void healthd_mode_android_init(struct healthd_config* /*config*/) {
    ProcessState::self()->setThreadPoolMaxThreadCount(0);//獲取線程池最大線程數
    IPCThreadState::self()->disableBackgroundScheduling(true);//禁止後臺調用
    IPCThreadState::self()->setupPolling(&gBinderFd);//將gBinderFd加入到epoll中。
 
    if (gBinderFd >= 0) {
//將binder_event事件註冊到gBinderfd文件節點用以監聽Binder事件。
            if (healthd_register_event(gBinderFd, binder_event))
            KLOG_ERROR(LOG_TAG,
                       "Register for binder events failed\n");
    }
 
    gBatteryPropertiesRegistrar = new BatteryPropertiesRegistrar();
//將batteryProperties註冊到ServiceManager中
    gBatteryPropertiesRegistrar->publish();
}

charger情況(healthd_mode_charger.cpp)

1105 void healthd_mode_charger_init(struct healthd_config* config)
1106 {
 
1118     ret = ev_init(input_callback, charger);
1119     if (!ret) {
1120         epollfd = ev_get_epollfd();
1121         healthd_register_event(epollfd, charger_event_handler);
1122     }
1123 
1124     ret = res_create_display_surface("charger/battery_fail", &charger->surf_unknown);
1125     if (ret < 0) {
1126         LOGE("Cannot load battery_fail image\n");
1127         charger->surf_unknown = NULL;
1128     }
1129 
1130     charger->batt_anim = &battery_animation;
1131 
1132     GRSurface** scale_frames;
1133     int scale_count;
1134     ret = res_create_multi_display_surface("charger/battery_scale", &scale_count, &scale_frames);
1135     if (ret < 0) {
1136         LOGE("Cannot load battery_scale image\n");
1137         charger->batt_anim->num_frames = 0;
1138         charger->batt_anim->num_cycles = 1;
1139     } else if (scale_count != charger->batt_anim->num_frames) {
1140         LOGE("battery_scale image has unexpected frame count (%d, expected %d)\n",
1141              scale_count, charger->batt_anim->num_frames);

uevent_init函數

static void uevent_init(void) {
//創建並打開一個64K的socket文件描述符uevent_fd.
    uevent_fd = uevent_open_socket(64*1024, true);
//將其設置為非阻塞模式
    fcntl(uevent_fd, F_SETFL, O_NONBLOCK);
//將其註冊到healthd_init創建的描述符集合里
    if (healthd_register_event(uevent_fd, uevent_event))
        KLOG_ERROR(LOG_TAG,
                   "register for uevent events failed\n");
}

BatteryMonitor.cpp

void BatteryMonitor::init(struct healthd_config *hc) {
    String8 path;
    char pval[PROPERTY_VALUE_MAX];
 
    mHealthdConfig = hc;
//打開/sys/class/power_supply,遍歷該節點下的電池參數初始化healthd的config參數
    DIR* dir = opendir(POWER_SUPPLY_SYSFS_PATH);

接下來main函數調用periodic_chores更新電池狀態信息。

void healthd_battery_update(void) {
    // Fast wake interval when on charger (watch for overheat);
    // slow wake interval when on battery (watch for drained battery).
//獲取新的wakealarm喚醒間隔,fast wake處於充電模式,slow是處於非充電模式的喚醒間隔。
   int new_wake_interval = gBatteryMonitor->update() ?
       healthd_config.periodic_chores_interval_fast :
           healthd_config.periodic_chores_interval_slow;
//判定並跟新新的喚醒間隔
    if (new_wake_interval != wakealarm_wake_interval)
            wakealarm_set_interval(new_wake_interval);
 
    // During awake periods poll at fast rate.  If wake alarm is set at fast
    // rate then just use the alarm; if wake alarm is set at slow rate then
    // poll at fast rate while awake and let alarm wake up at slow rate when
    // asleep.
 
    if (healthd_config.periodic_chores_interval_fast == -1)
        awake_poll_interval = -1;
    else
//輪詢間隔時間調節
         awake_poll_interval =
            new_wake_interval == healthd_config.periodic_chores_interval_fast ?
                -1 : healthd_config.periodic_chores_interval_fast * 1000;
}
 
 
static void periodic_chores() {
    healthd_battery_update();
}

image

uevent_event處理函數如下。

#define UEVENT_MSG_LEN 2048
static void uevent_event(uint32_t /*epevents*/) {
    char msg[UEVENT_MSG_LEN+2];
    char *cp;
    int n;
 
    n = uevent_kernel_multicast_recv(uevent_fd, msg, UEVENT_MSG_LEN);
    if (n <= 0)
        return;
    if (n >= UEVENT_MSG_LEN)   /* overflow -- discard */
        return;
 
    msg[n] = '\0';
    msg[n+1] = '\0';
    cp = msg;
 
    while (*cp) {
//判斷是否是power_supply目錄下的事件,如果是則更新電池狀態。
       if (!strcmp(cp, "SUBSYSTEM=" POWER_SUPPLY_SUBSYSTEM)) {
            healthd_battery_update();
            break;
        }
 
        /* advance to after the next \0 */
        while (*cp++)
            ;
    }
}

update函數里調用了 gBatteryMonitor->update()方法去完成實際意義上的更新。

//BatteryMonitor.cpp
181 bool BatteryMonitor::update(void) {
182     bool logthis;
183 
184     props.chargerAcOnline = false;
185     props.chargerUsbOnline = false;
186     props.chargerWirelessOnline = false;
187     props.batteryStatus = BATTERY_STATUS_UNKNOWN;
188     props.batteryHealth = BATTERY_HEALTH_UNKNOWN;
189     props.maxChargingCurrent = 0;
 
接下來跟新props.batteryPresent,props.batteryLevel,props.batteryVoltage等信息,這些信息的來源是/sys/class/power_supply/battery目錄下的文件節點。

image

接下來將信息保存到dmesgline里。這樣dmesg就可以看到這樣的信息了。

        if (props.batteryPresent) {
            snprintf(dmesgline, sizeof(dmesgline),
                 "battery l=%d v=%d t=%s%d.%d h=%d st=%d",
                 props.batteryLevel, props.batteryVoltage,
                 props.batteryTemperature < 0 ? "-" : "",
                 abs(props.batteryTemperature / 10),
                 abs(props.batteryTemperature % 10), props.batteryHealth,
                 props.batteryStatus);
 
            if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
                int c = getIntField(mHealthdConfig->batteryCurrentNowPath);
                char b[20];
 
                snprintf(b, sizeof(b), " c=%d", c / 1000);
                strlcat(dmesgline, b, sizeof(dmesgline));
            }
        }
    size_t len = strlen(dmesgline);
        snprintf(dmesgline + len, sizeof(dmesgline) - len, " chg=%s%s%s",
                 props.chargerAcOnline ? "a" : "",
                 props.chargerUsbOnline ? "u" : "",
                 props.chargerWirelessOnline ? "w" : "");

獲取到以上信息後,更新電池狀態

    healthd_mode_ops->battery_update(&props);
   //返回電池是否處在充電狀態
    return props.chargerAcOnline | props.chargerUsbOnline |
            props.chargerWirelessOnline;

對於安卓情況的battery update情況如下:

void healthd_mode_android_battery_update(
    struct android::BatteryProperties *props) {
    if (gBatteryPropertiesRegistrar != NULL)
        gBatteryPropertiesRegistrar->notifyListeners(*props);
 
    return;
}
void BatteryPropertiesRegistrar::notifyListeners(struct BatteryProperties props) {
    Mutex::Autolock _l(mRegistrationLock);
    for (size_t i = 0; i < mListeners.size(); i++) {
        mListeners[i]->batteryPropertiesChanged(props);
    }
}

上面mListeners的定義如下:

class BatteryPropertiesRegistrar : public BnBatteryPropertiesRegistrar,
                                   public IBinder::DeathRecipient {
public:
    void publish();
    void notifyListeners(struct BatteryProperties props);
 
private:
    Mutex mRegistrationLock;
    Vector<sp<IBatteryPropertiesListener> > mListeners;
 
    void registerListener(const sp<IBatteryPropertiesListener>& listener);
    void unregisterListener(const sp<IBatteryPropertiesListener>& listener);
    status_t getProperty(int id, struct BatteryProperty *val);
    status_t dump(int fd, const Vector<String16>& args);
    void binderDied(const wp<IBinder>& who);
};

調用batteryPropertiesRegistrar的notifyListeners通知props改變了。這個通知必然是給調用registerListener註冊的,也必然是給framework層的。

@Override
    public void onStart() {
        IBinder b = ServiceManager.getService("batteryproperties");
        final IBatteryPropertiesRegistrar batteryPropertiesRegistrar =
                IBatteryPropertiesRegistrar.Stub.asInterface(b);
        try {
            batteryPropertiesRegistrar.registerListener(new BatteryListener());
        } catch (RemoteException e) {
            // Should never happen.
        }
 
        publishBinderService("battery", new BinderService());
        publishLocalService(BatteryManagerInternal.class, new LocalService());
    }

在初始化時,healthd初始化時候會創建BatteryPropertiesRegister對象,並將其publish到系統服務中去。

void BatteryPropertiesRegistrar::publish() {
    defaultServiceManager()->addService(String16("batteryproperties"), this);
}

framework/base/services/core/java/com/android/server/BatteryService.java

    @Override
    public void onStart() {
        IBinder b = ServiceManager.getService("batteryproperties");
        final IBatteryPropertiesRegistrar batteryPropertiesRegistrar =
                IBatteryPropertiesRegistrar.Stub.asInterface(b);
        try {
            batteryPropertiesRegistrar.registerListener(new BatteryListener());
        } catch (RemoteException e) {
            // Should never happen.
        }
 
        publishBinderService("battery", new BinderService());
        publishLocalService(BatteryManagerInternal.class, new LocalService());

image

kernel層

一個是充電晶元驅動,一個是電量計,這兩個設備統一由power子系統管理。

power子系統主要由如下文件組成:

  • power_supply.h (include\linux)
  • power_supply_core.c (drivers\power)
  • power_supply_sysfs.c (drivers\power)

    charger充電晶元

    適合充電晶元相關的。

一個例子如下https://github.com/tibms/dual-bq2589x;這個並沒有battery。

電量計

對於高通平臺,早期使用了分立的器件,但現在採用AP內部集成了該模塊。高通平臺常用的名詞如下:

qpnp-bms.c(qcom plug n play)

qpnp-charger.c

BMS:battery monitoring system;

ICC:intelligent coulomb counter,基於高通BMS系統。

RC:remaing capacity。當前狀態下的剩餘電量,充滿電時RC=FCC。假設放電電流小於1/20C。

UUC:unusable capacity。由於電池電阻導致的電池壓降而無法使用的電量,其是放電電流的函數。

UC:usable capacity。UC=FCC-UUC

RUC:剩餘可用電量RUC=RC-UUC

SoC:state of charge;SoC=RC/FCC,對於上層應用,包含UUC更好:SoC=RUC/UC=(RC-UUC)/(FCC-UUC)。

C(rate):放電速率的測量方法,一個小時放完電的情況測量;如如果電池等級是1Ah,也就是1A放電能持續1h,也就是按1C標準放電。如果按0.5C放電,則500mA可以持續2h。

OCV:open circuit voltage。近乎於0電流情況下的穩定電壓。電池帶負載工作後,需要5~30min恢復OCV。

FCC:Full-charge capacity

CC:Coulumb counter

### linux 電源子系統核心框架
#include/linux/power_supply.h
struct power_supply 
{
    const char *name;//對應於/sys/class/power_supply/XXX 文件夾
    enum power_supply_type type;//電池類型,UPS/BATTERY/USB等
    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);
//寫屬性值
    int (*set_property)(struct power_supply *psy,
                enum power_supply_property psp,
                const union power_supply_propval *val);
    int (*property_is_writeable)(struct power_supply *psy,
                     enum power_supply_property psp);
//外部電源變化時所作的工作
    void (*external_power_changed)(struct power_supply *psy);
    void (*set_charged)(struct power_supply *psy);
 
    /* For APM emulation, think legacy userspace. */
    int use_for_apm;
 
    /* private */
    struct device *dev;
    struct work_struct changed_work;
    spinlock_t changed_lock;
    bool changed;
    struct wake_lock work_wake_lock;
}
extern int power_supply_register(struct device *parent,
                 struct power_supply *psy);
extern void power_supply_unregister(struct power_supply *psy);

power_supply_register函數

int power_supply_register(struct device *parent, struct power_supply *psy)
{
    struct device *dev;
    int rc;
 
    dev = kzalloc(sizeof(*dev), GFP_KERNEL);
    if (!dev)
        return -ENOMEM;
 
    device_initialize(dev);
 
    dev->class = power_supply_class;
    dev->type = &power_supply_dev_type;
    dev->parent = parent;
    dev->release = power_supply_dev_release;
    dev_set_drvdata(dev, psy);
    psy->dev = dev;
 
    INIT_WORK(&psy->changed_work, power_supply_changed_work);
 
    rc = kobject_set_name(&dev->kobj, "%s", psy->name);
    if (rc)
        goto kobject_set_name_failed;
 
    rc = device_add(dev);//添加電源設備
    if (rc)
        goto device_add_failed;
 
    spin_lock_init(&psy->changed_lock);
    wake_lock_init(&psy->work_wake_lock, WAKE_LOCK_SUSPEND, "power-supply");
//和電源led相關的門限設置
    rc = power_supply_create_triggers(psy);
    if (rc)
        goto create_triggers_failed;
//調度psy的power_supply_changed_work,即上面的INIT_WORK初始化的函數,向用戶空間發送uevent, 通知系統和用戶電源有變化
    power_supply_changed(psy);
 
    goto success;
 
create_triggers_failed:
    wake_lock_destroy(&psy->work_wake_lock);
    device_del(dev);
kobject_set_name_failed:
device_add_failed:
    put_device(dev);
success:
    return rc;
}

總結來說電源驅動一般要做如下幾部分工作:

1.定義struct power_supply,該定義可以是全局的或者是嵌入到驅動中的專有數據,如上面給的參考程式:

struct bq2589x {
    struct device *dev;
    struct i2c_client *client;
 
    enum   bq2589x_part_no part_no;
    int    revision;
 
    unsigned int    status;
    int     vbus_type;
 
    bool    enabled;
 
    bool    interrupt;
 
    int     vbus_volt;
    int     vbat_volt;
 
    int     rsoc;
    struct  bq2589x_config  cfg;
    struct work_struct irq_work;
    struct work_struct adapter_in_work;
    struct work_struct adapter_out_work;
    struct delayed_work monitor_work;
    struct delayed_work ico_work;
    struct delayed_work pe_volt_tune_work;
    struct delayed_work check_pe_tuneup_work;
    struct delayed_work charger2_enable_work;
 
    struct power_supply usb;
    struct power_supply wall;
    struct power_supply *batt_psy;
 
};

在probe函數中初始化並註冊這個電源驅動到
static int bq2589x_charger1_probe(struct i2c_client client,
const struct i2c_device_id
id)
{
struct bq2589x *bq;
//初始化相關欄位
bq = devm_kzalloc(&client->dev, sizeof(struct bq2589x), GFP_KERNEL);

bq->dev = &client->dev;
bq->client = client;
i2c_set_clientdata(client, bq);

ret = bq2589x_detect_device(bq);

bq->batt_psy = power_supply_get_by_name("battery");

g_bq1 = bq;


ret = bq2589x_psy_register(bq);


INIT_WORK(&bq->irq_work, bq2589x_charger1_irq_workfunc);
INIT_WORK(&bq->adapter_in_work, bq2589x_adapter_in_workfunc);
INIT_WORK(&bq->adapter_out_work, bq2589x_adapter_out_workfunc);
INIT_DELAYED_WORK(&bq->monitor_work, bq2589x_monitor_workfunc);
INIT_DELAYED_WORK(&bq->ico_work, bq2589x_ico_workfunc);
INIT_DELAYED_WORK(&bq->pe_volt_tune_work, bq2589x_pe_tune_volt_workfunc);
INIT_DELAYED_WORK(&bq->check_pe_tuneup_work, bq2589x_check_pe_tuneup_workfunc);
INIT_DELAYED_WORK(&bq->charger2_enable_work, bq2589x_charger2_enable_workfunc);

//創建sys下節點
ret = sysfs_create_group(&bq->dev->kobj, &bq2589x_attr_group);

//處理充電晶元電池事件
ret = request_irq(client->irq, bq2589x_charger1_interrupt, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "bq2589x_charger1_irq", bq);

第二步,調用power_supply_register註冊這個psy設備

static int bq2589x_psy_register(struct bq2589x *bq)
{
    int ret;
 
    bq->usb.name = "bq2589x-usb";
    bq->usb.type = POWER_SUPPLY_TYPE_USB;
    bq->usb.properties = bq2589x_charger_props;
    bq->usb.num_properties = ARRAY_SIZE(bq2589x_charger_props);
    bq->usb.get_property = bq2589x_usb_get_property;
    bq->usb.external_power_changed = NULL;
 
    ret = power_supply_register(bq->dev, &bq->usb);
    if (ret < 0) {
        dev_err(bq->dev, "%s:failed to register usb psy:%d\n", __func__, ret);
        return ret;
    }
 
 
    bq->wall.name = "bq2589x-Wall";
    bq->wall.type = POWER_SUPPLY_TYPE_MAINS;
    bq->wall.properties = bq2589x_charger_props;
    bq->wall.num_properties = ARRAY_SIZE(bq2589x_charger_props);
    bq->wall.get_property = bq2589x_wall_get_property;
    bq->wall.external_power_changed = NULL;
 
    ret = power_supply_register(bq->dev, &bq->wall);
    if (ret < 0) {
        dev_err(bq->dev, "%s:failed to register wall psy:%d\n", __func__, ret);
        goto fail_1;
}

對於電量計BMS,也是這個類型的一種設備,照樣子添加。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • maven中的繼承是在子類工程中加入父類的groupId,artifactId,version並用parent標簽囊括 depenentManagement標簽作用: 當父類的pom.xml中沒有dependencyManagement標簽時,子工程將繼承父工程的所有依賴,有此標簽則可自定義繼承所需 ...
  • 對官方提供的內核源碼包進行解壓縮,進入到內核目錄,使用make menuconfig後,發現提示以下錯誤: *** Unable to find the ncurses libraries or the *** required header files. *** 'make menuconfig' ...
  • 1.使用./build編譯boa-0.94.13時出現make:yacc:command not be found 解決方法:apt-get install -y byacc 2.出現make:lex:command not be found 解決方法:apt-get install flex ...
  • 1.解壓好u-boot後,打開uboot根目錄的README文件,在software configuration 里有寫明,如果要針對某個單板進行配置,需要執行:make <board_name>_config 其中uboot支持的board_name可以在根目錄的include/configs/下 ...
  • 記錄乾貨。。。 一、下載、解壓 在centos下載依賴庫: yum install gcc-c++ yum install -y pcre pcre-devel yum install -y zlib zlib-devel yum install -y openssl openssl-devel 在 ...
  • 1、SSH簡介 ssh(安全外殼協議)是Secure Shell的縮寫,是建立在應用層和傳輸層基礎上的安全協議。傳輸的時候是經過加密的,防止信息泄露,比telnet(明文傳遞)要安全很多。 ftp安裝(要先載入上光碟鏡像文件) 安裝的ftp伺服器端 啟動服務 查看 創建一個新用戶 tcpdump抓包 ...
  • 連接 1.安裝xwindow 2.安裝gnome desktop 安裝成功,但不能啟動圖形化,因為沒有安裝桌面KDE或gnome 3.啟動桌面 4.主機上切換圖形桌面 ...
  • 1.關於YUM源 Yum 全稱為 Yellow dog Updater Modified,它是一個線上的軟體安裝命令。 能夠從指定的伺服器自動下載RPM包並且安裝,可以自動處理依賴性關係,並且一次安裝所有依賴的軟體包,無須繁瑣地一次次下載、安裝。yum提供了查找、安裝、刪除某一個、一組甚至全部軟體包 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...