zoukankan      html  css  js  c++  java
  • 读取bq26500电池电量 Driver

        bq26500是用于锂离子电池、锂聚合物电池充电、放电控制和监控的集成电路芯片。利用它能够准确提供电池充电、放电、电池温度、充放电电压、电池电量等相关数据。不须要外接微处理器參与电池充电、放电等相关数据的计算。

       本文简单的读取了电池的电量百分比和电压。如要读取其它參数依照datasheet给出的寄存器一一读取就可以。


    连接示意图:


    引脚pack+ 和 pack-分别连接正极和负极用来冲放电,HDQ是数据交互引脚,用来读取电池的參数,一般用一个gpio连接就能够。


    时序图:



    看图可知。逻辑1和0并非简单的高低电平,而是依据高低电平占空比来推断是逻辑1还是0的。

    基本通信过程:先发break 进入通信, 然后主机发送地址,电池返回数据。


    寄存器列表:


    读取电量百分比为0x0b寄存器。读取电压为0x09和0x08寄存器的值拼接的。


    依据上诉信息编写读取电量的代码:

    #define ADDR_PERCENT 0x0b
    
    void bat_set_gpio(int en)
    {
        if (en) {
            mt_set_gpio_mode(GPIO155, GPIO_MODE_00);
            mt_set_gpio_dir(GPIO155, GPIO_DIR_OUT);
            mt_set_gpio_out(GPIO155, GPIO_OUT_ONE);
        } else {
            mt_set_gpio_mode(GPIO155, GPIO_MODE_00);
            mt_set_gpio_dir(GPIO155, GPIO_DIR_OUT);
            mt_set_gpio_out(GPIO155, GPIO_OUT_ZERO);
        }
        return;
    }
    
    int bat_get_gpio(void)
    {
        int ret = 0;
        mt_set_gpio_mode(GPIO155, GPIO_MODE_00);
        mt_set_gpio_dir(GPIO155, GPIO_DIR_IN);
        ret = mt_get_gpio_in(GPIO155);
    
        return ret;
    }
    
    static int send_addr_to_bq26500(unsigned int addr,int rw)
    {
        int i;
        unsigned int  cmd = 0;
    
        cmd = addr | rw << 7;
    
        // break and break recovery timing
        bat_set_gpio(1);
        udelay(100);
        bat_set_gpio(0);
        udelay(190);//190us
        bat_set_gpio(1);
        udelay(40); //40us
    
        for (i=0;i<8;i++) {
            if(cmd >> i & 0x01) { //hw1
                bat_set_gpio(0);
                udelay(40); //40us
                bat_set_gpio(1);
                udelay(150); //150  
            } else {             //hw0
                bat_set_gpio(0);
                udelay(125); //125
                bat_set_gpio(1);
                udelay(65); //65        
            }
        }
        bat_get_gpio();
    
        return 0;
    }
    
    static unsigned char data_from_bq26500(void)
    {
        volatile  unsigned char value = 0, low=0, temp = 0, temp_old = 0;
        int i, j;
    
        //wait response time
        for(i=0; i < 32; i++) { //320us
            udelay(10);
            temp = bat_get_gpio();
            if (temp == 0)
                break;
        }
    
        //read data
    	//依据占空比推断是逻辑1还是逻辑0
        for (i=0; i < 8; i++) {
            for (j=0, low = 0; j < 26; j++) {//260us
                udelay(10);
                temp = bat_get_gpio();
                if (temp == 0) {
                    low++;
                }
                if ((temp == 1) && (temp_old == 0)){
                    break;
                }
    
                temp_old = temp;
            }
    
            if (low < 6 && low != 0){
                value = value | 1<< i;
            }
    
            temp = 1; 
            temp_old = 1; 
        }    
        
        return value;
    }
    
    static DEFINE_SPINLOCK(bq26500_spinlock);
    
    // transmit address to bq26500
    static unsigned char read_data_bq26500(unsigned int addr)
    {
        volatile unsigned char value = 0, temp[3] = {0};    
        unsigned long flags;
        int i, j;
    
    	//读取三次取中间值上报,假设平台频率低仅仅读取一次就可以。
        for (i=0; i < 3; i++) {
            spin_lock_irqsave(&bq26500_spinlock, flags);
            value = send_addr_to_bq26500(addr,0);
            temp[i] = data_from_bq26500();
            spin_unlock_irqrestore(&bq26500_spinlock, flags);
            usleep_range(10, 20); 
        }    
    
        for (i=0; i < 3; i++) {
            for (j=i; j < 3; j++) {
                if (temp[i] > temp[j]) {
                    value = temp[i];
                    temp[i] = temp[j];
                    temp[j] = value;
                }    
            }    
        }    
    
        value = temp[1];
        return value;
    } 

    printk("Battery percent: %d
    ", read_data_bq26500(0x0b))

    我实现的平台是MTK 平台,操作GPIO的函数跟标准的kernel有些出入,但不影响理解代码。

    上诉代码是读取电量百分的,读取电压的方法例如以下:

        voltage_h = read_data_bq26500(0x09/*BATTERYH_VOLTAGE*/);
        voltage_l = read_data_bq26500(0x08/*BATTERYL_VOLTAGE*/);
        printk("Battery Vol:%d
    ",voltage_l | voltage_h << 8);

        使用spin_lock_irqsave是由于读取过程必须严格遵循时序,不能被中断或进程调度打断。一但打断将不能正确从电池中读出数据。由于使用了spin_lock所以期间的睡眠也仅仅能是忙等,不能使用sleep这类的系统调度的睡眠函数。


  • 相关阅读:
    HDU 1312 Red and Black(经典DFS)
    POJ 1274 The Perfect Stall(二分图 && 匈牙利 && 最小点覆盖)
    POJ 3041 Asteroids(二分图 && 匈牙利算法 && 最小点覆盖)
    HDU 1016 素数环(dfs + 回溯)
    HDU 1035 Robot Motion(dfs + 模拟)
    vjudge Trailing Zeroes (III) (二分答案 && 数论)
    openjudge 和为给定数(二分答案)
    图的存储
    二分查找
    快速选择算法
  • 原文地址:https://www.cnblogs.com/cynchanpin/p/7287270.html
Copyright © 2011-2022 走看看