前段时间比较烦躁,各种不想学习不想工作,于是休息了几天。这几天又下来任务了--调试充电电路和电池电量检测电路,于是又开始工作,顺便把调试过程记录下来。

平台: cpu 飞思卡尔imx6q 4核

充电芯片 MAX8903

电量检测芯片 MAX11801

android版本 android4.0

一、电量检测

我们用的电池电量检测芯片MAX11801其实是一款电阻触摸屏的驱动芯片,它外带一个AD采集引脚,因此我们用这个引脚来检测电池电压。MAX11801电源为3.3V而电池电压范围可能是0~4.2V,因此我们需要给电池电压分压。我们所用的电路如下



知道了硬件电路下面来 添加这个芯片的驱动,这是一个i2c的芯片,因此首先在board文件中添加i2c设备

I2C_BOARD_INFO("max11801", 0x48),.platform_data = (void *)&max11801_mode,.irq = gpio_to_irq(SABRESD_TS_INT),},
然后添加这个芯片的驱动文件放在/drivers/input/touchiscreen/max11801_ts.c

对于这个驱动文件我们只要读取出AD的值就可以了,对于触摸屏部分我们并不需要,因此主要是下面几个函数

static u32 max11801_dcm_sample_aux(struct i2c_client *client){u8 temp_buf;int ret;int aux = 0;u32 sample_data = 0;/* AUX_measurement*/max11801_dcm_write_command(client, AUX_measurement);//发送AD采集命令mdelay(5);ret = i2c_smbus_read_i2c_block_data(client, FIFO_RD_AUX_MSB, //读取高字节数据1, &temp_buf);if (ret < 1)printk(KERN_DEBUG "FIFO_RD_AUX_MSB read fails\n");elseaux_buf[0] = temp_buf;mdelay(5);ret = i2c_smbus_read_i2c_block_data(client, FIFO_RD_AUX_LSB,    //读取低字节数据1, &temp_buf);if (ret < 1)printk(KERN_DEBUG "FIFO_RD_AUX_LSB read fails\n");elseaux_buf[1] = temp_buf;aux = (aux_buf[0] << 4) +           //视最低4位无效并去掉(aux_buf[1] >> 4);/*10k和18.7k并联后电阻 R=18.7*10/(18.7+10)=6.516V(aux) = V(bat)*6.516/(6.516+18.7)V(aux) = aux*3300/0xfffV(bat) = aux*1386880/444717*/sample_data = (aux*1386880)/444717;//计算出电池电压return sample_data;}u32  max11801_read_adc(void){u32 adc_data;adc_data = max11801_dcm_sample_aux(max11801_client);//printk("----%s %d\n",__func__,adc_data);//lijianzhangreturn adc_data;}EXPORT_SYMBOL_GPL(max11801_read_adc);

由于电池电量检测的驱动非常简单,而且和充电驱动关系非常密切,因此一般都卸载充电驱动里面,我们也是这么做的。下面的代码都是从充电驱动中摘出来的,因此当大家看到,一些设备文件和函数参数类型 都是充电驱动中的 时候不要太奇怪。

通过上面的max11801_read_adc函数我们已经得到了理论计算的电池的电压,但实际应用中由于分压电阻误差,焊接问题等,这个电压会有一定的误差因此需要一个校正函数

u32 calibration_voltage(struct max8903_data *data){int volt[ADC_SAMPLE_COUNT];u32 voltage_data;int i;for (i = 0; i < ADC_SAMPLE_COUNT; i++) {    //多次采样,防止AD误差if (data->charger_online == 0 && data->usb_charger_online == 0) {/* ADC offset when battery is discharger*/volt[i] = max11801_read_adc()-offset_discharger;    //没有充电情况下 电压误差} else {if (data->charger_online == 1)volt[i] = max11801_read_adc()-offset_charger;//DC充电式 电压误差else if (data->usb_charger_online == 1)volt[i] = max11801_read_adc()-offset_usb_charger;//usb充电  电压误差else if (data->charger_online == 1 && data->usb_charger_online == 1)volt[i] = max11801_read_adc()-offset_charger;}}sort(volt, i, 4, cmp_func, NULL);//对电压排序for (i = 0; i < ADC_SAMPLE_COUNT; i++)pr_debug("volt_sorted[%2d]: %d\n", i, volt[i]);/* get the average of second max/min of remained. */voltage_data = (volt[2] + volt[ADC_SAMPLE_COUNT - 3]) / 2;//去掉最大值最小值 并对剩余数据求平均return voltage_data;}
从上面函数我们读取到了正确的电压值。电池电压是随时变化的,我们要检测电池电量,必须随时采集,因此用一个定时器来做这件事情,代码如下:

INIT_DELAYED_WORK(&data->work, max8903_battery_work);schedule_delayed_work(&data->work, data->interval);

电压采集完成后就是将电压上报出去,上报的过程是:我们读取到电压变化->告诉android端电池电压变化了->android会通过power_supply设备文件来读取具体的电压值。
我们来看定时器回调函数

static void max8903_battery_work(struct work_struct *work){struct max8903_data *data;data = container_of(work, struct max8903_data, work.work);data->interval = HZ * BATTERY_UPDATE_INTERVAL;max8903_charger_update_status(data);    //检测充电状态max8903_battery_update_status(data);    //检测电池状态/* reschedule for the next time */schedule_delayed_work(&data->work, data->interval);//定时器继续}
检测电池状态函数

static void max8903_battery_update_status(struct max8903_data *data){int temp;static int temp_last;bool changed_flag;changed_flag = false;mutex_lock(&data->work_lock);temp = calibration_voltage(data);if (temp_last == 0) {data->voltage_uV = temp;temp_last = temp;}if (data->charger_online == 0 && temp_last != 0) {//DC充电状态if (temp < temp_last) {temp_last = temp;data->voltage_uV = temp;} else {data->voltage_uV = temp_last;}}if (data->charger_online == 1 || data->usb_charger_online == 1) {//USB充电状态和DC充电状态data->voltage_uV = temp;temp_last = temp;}data->percent = calibrate_battery_capability_percent(data);//计算电量的百分比if (data->percent != data->old_percent) {   //电池电压有变化data->old_percent = data->percent;changed_flag = true;}if (changed_flag) {         //如果有变化changed_flag = false;power_supply_changed(&data->bat);//告诉android端 电池电量改变了}/*    because boot time gap between led framwork and charger    framwork,when system boots with charger attatched, charger    led framwork loses the first charger online event,add once extra    power_supply_changed can fix this issure*/if (data->first_delay_count < 200) {data->first_delay_count = data->first_delay_count + 1 ;power_supply_changed(&data->bat);}mutex_unlock(&data->work_lock);}
这里我们看到了 power_supply_changed(&data->bat);告诉android端 电池电量改变了,那么下一步android来读取具体电压,就涉及到了power_supply设备文件。
来看设备文件的建立过程

data->bat.name = "max8903-charger";data->bat.type = POWER_SUPPLY_TYPE_BATTERY;data->bat.properties = max8903_battery_props;data->bat.num_properties = ARRAY_SIZE(max8903_battery_props);data->bat.get_property = max8903_battery_get_property;data->bat.use_for_apm = 1;retval = power_supply_register(&pdev->dev, &data->bat);//注册设备文件if (retval) {dev_err(data->dev, "failed to register battery\n");goto battery_failed;}
这里注册了一个名为max8903-charger的 power_supply设备文件,这个设备文件包含了ARRAY_SIZE(max8903_battery_props)个操作分别为

static enum power_supply_property max8903_battery_props[] = {POWER_SUPPLY_PROP_VOLTAGE_NOW,//当前电压POWER_SUPPLY_PROP_STATUS,       //当前充电状态POWER_SUPPLY_PROP_PRESENT,      //不太清除POWER_SUPPLY_PROP_CAPACITY,     //电量百分比POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,//电池极限电压 最大值POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,//电池极限电压 最小值POWER_SUPPLY_PROP_HEALTH,       //电池健康状态POWER_SUPPLY_PROP_CAPACITY_LEVEL,//电量水平,low或者normal};
这些状态是通过max8903_battery_get_property()这个函数来读取的

static int max8903_battery_get_property(struct power_supply *bat,       enum power_supply_property psp,       union power_supply_propval *val){struct max8903_data *di = container_of(bat,struct max8903_data, bat);switch (psp) {case POWER_SUPPLY_PROP_STATUS:val->intval = POWER_SUPPLY_STATUS_UNKNOWN;if (gpio_get_value(di->pdata->chg) == 0) {di->battery_status = POWER_SUPPLY_STATUS_CHARGING;  //正在充电} else if (di->ta_in &&gpio_get_value(di->pdata->chg) == 1) {if (di->percent >= 99)di->battery_status = POWER_SUPPLY_STATUS_FULL;//电量大于99就充满了elsedi->battery_status = POWER_SUPPLY_STATUS_NOT_CHARGING;}  else if (di->usb_in &&gpio_get_value(di->pdata->chg) == 1) {if (di->percent >= 99)    di->battery_status = POWER_SUPPLY_STATUS_FULL;else  di->battery_status = POWER_SUPPLY_STATUS_NOT_CHARGING;}val->intval = di->battery_status;return 0;default:break;}switch (psp) {case POWER_SUPPLY_PROP_VOLTAGE_NOW:val->intval = di->voltage_uV;break;case POWER_SUPPLY_PROP_CHARGE_NOW:val->intval = 0;break;case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:val->intval = HIGH_VOLT_THRESHOLD;break;case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:val->intval = LOW_VOLT_THRESHOLD;break;case POWER_SUPPLY_PROP_PRESENT:val->intval = 1;break;case POWER_SUPPLY_PROP_CAPACITY:val->intval = di->percent < 0 ? 0 :(di->percent > 100 ? 100 : di->percent);break;case POWER_SUPPLY_PROP_HEALTH:val->intval = POWER_SUPPLY_HEALTH_GOOD;if (di->fault)val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;break;case POWER_SUPPLY_PROP_CAPACITY_LEVEL:if (di->battery_status == POWER_SUPPLY_STATUS_FULL)val->intval = POWER_SUPPLY_CAPACITY_LEVEL_FULL;else if (di->percent <= 15)val->intval = POWER_SUPPLY_CAPACITY_LEVEL_LOW;//电量小于15%就报低电量elseval->intval = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL;//否则就报正常break;default:return -EINVAL;}return 0;}
当我们注册设备文件以后,可以在/sys/devices/platform/max8903-charger.1/power_supply/max8903-charger目录下找到其设备文件如下


我们通过cat命令就可以随时查看电池状态。

二、电池电压校正参数

上面我们知道根据硬件实际情况不同,AD采集出来的电池电压需要校正参数。也就是

static int offset_discharger;
static int offset_charger;
static int offset_usb_charger;

对于这三个参数,当然我们可以在驱动力写死,但是为了以后的兼容性我们可以通过android上层来设置,当我们设备出厂时候,通过一配置文件方便的来修改这三个参数,下面我们就来介绍一下,怎么用设备文件和脚本,来修改者三个参数:

我们用的是sys文件系统的设备文件,创建代码为

ret = device_create_file(&pdev->dev, &max8903_discharger_dev_attr);if (ret)dev_err(&pdev->dev, "create device file failed!\n");ret = device_create_file(&pdev->dev, &max8903_charger_dev_attr);if (ret)dev_err(&pdev->dev, "create device file failed!\n");ret = device_create_file(&pdev->dev, &max8903_usb_charger_dev_attr);if (ret)dev_err(&pdev->dev, "create device file failed!\n");
设备文件的实现代码为

static ssize_t max8903_voltage_offset_discharger_show(struct device *dev,    struct device_attribute *attr, char *buf){return sprintf(buf, "read offset_discharger:%04d\n",offset_discharger);}static ssize_t max8903_voltage_offset_discharger_store(struct device *dev,     struct device_attribute *attr, const char *buf,     size_t count){offset_discharger = simple_strtoul(buf, NULL, 10);pr_info("read offset_discharger:%04d\n", offset_discharger);return count;}static ssize_t max8903_voltage_offset_charger_show(struct device *dev,    struct device_attribute *attr, char *buf){return sprintf(buf, "read offset_charger:%04d\n",offset_charger);}static ssize_t max8903_voltage_offset_charger_store(struct device *dev,     struct device_attribute *attr, const char *buf,     size_t count){offset_charger = simple_strtoul(buf, NULL, 10);pr_info("read offset_charger:%04d\n", offset_charger);return count;}static ssize_t max8903_voltage_offset_usb_charger_show(struct device *dev,    struct device_attribute *attr, char *buf){return sprintf(buf, "read offset_usb_charger:%04d\n",offset_usb_charger);}static ssize_t max8903_voltage_offset_usb_charger_store(struct device *dev,     struct device_attribute *attr, const char *buf,     size_t count){offset_usb_charger = simple_strtoul(buf, NULL, 10);pr_info("read offset_charger:%04d\n", offset_usb_charger);return count;}static struct device_attribute max8903_discharger_dev_attr = {.attr = { .name = "max8903_ctl_offset_discharger", .mode = S_IRUSR | S_IWUSR, },.show = max8903_voltage_offset_discharger_show,.store = max8903_voltage_offset_discharger_store,};static struct device_attribute max8903_charger_dev_attr = {.attr = { .name = "max8903_ctl_offset_charger", .mode = S_IRUSR | S_IWUSR, },.show = max8903_voltage_offset_charger_show,.store = max8903_voltage_offset_charger_store,};static struct device_attribute max8903_usb_charger_dev_attr = {.attr = { .name = "max8903_ctl_offset_usb_charger", .mode = S_IRUSR | S_IWUSR, },.show = max8903_voltage_offset_usb_charger_show,.store = max8903_voltage_offset_usb_charger_store,};
这样,我们就可以在/sys/devices/platform/max8903-charger.1目录下看到这样三个设备文件


我们用cat命令可以读出当前值,
用echo "500">>max8903_ctl_offset_charger 可以修改当前值

这样我们就可以在系统启动的时候,用脚本来自动修改者三个值,我用的办法是在init.rc的on boot阶段增加这么三行

#battery charge    write /sys/devices/platform/max8903-charger.1/max8903_ctl_offset_charger 150    write /sys/devices/platform/max8903-charger.1/max8903_ctl_offset_discharger 200    write /sys/devices/platform/max8903-charger.1/max8903_ctl_offset_usb_charger 250
当然大家也可以把这三行命令写在另外一个脚本里,然后init.rc中调用


三、电池充电

电池充电的电路


一共有4个引脚输出到cpu中:

CHG_FLT1_B 电池检测错误

UOK_B usb插入

DOK_B DC插入

CHG_STATUS1_B 充电状态

对于充电状态的检测过程,和电量检测基本相同, 检测到状态变化->告诉android层发生变化->android层通过设备文件来读取变化值

知道了这些我们来看驱动,首先在board文件中添加max8903设备

static struct max8903_pdata charger1_data = {.dok = SABRESD_CHARGE_DOK_B,.uok = SABRESD_CHARGE_UOK_B,.chg = CHARGE_STATE2,.flt = CHARGE_STATE1,.dcm_always_high = true,.dc_valid = true,.usb_valid = true, };static struct platform_device sabresd_max8903_charger_1 = {.name= "max8903-charger",.id= 1,.dev= {.platform_data = &charger1_data,},};
platform_device_register(&sabresd_max8903_charger_1);
然后在/derivers/power/目录下添加驱动文件。充电状态的变化都是IO电平的变化,我们来看驱动是怎么处理这4个io的,首先在probe函数中

申请IO

if (pdata->dc_valid) {if (pdata->dok && gpio_is_valid(pdata->dok)) {gpio = pdata->dok; /* PULL_UPed Interrupt *//* set DOK gpio input */ret = gpio_request(gpio, "max8903-DOK");if (ret) {printk(KERN_ERR"request max8903-DOK error!!\n");goto err;} else {gpio_direction_input(gpio);}ta_in = gpio_get_value(gpio) ? 0 : 1;} else if (pdata->dok && gpio_is_valid(pdata->dok) && pdata->dcm_always_high) {ta_in = pdata->dok; /* PULL_UPed Interrupt */ta_in = gpio_get_value(gpio) ? 0 : 1;} else {dev_err(dev, "When DC is wired, DOK and DCM should"" be wired as well."" or set dcm always high\n");ret = -EINVAL;goto err;}}if (pdata->usb_valid) {if (pdata->uok && gpio_is_valid(pdata->uok)) {gpio = pdata->uok;/* set UOK gpio input */ret = gpio_request(gpio, "max8903-UOK");if (ret) {printk(KERN_ERR"request max8903-UOK error!!\n");goto err;} else {gpio_direction_input(gpio);}usb_in = gpio_get_value(gpio) ? 0 : 1;} else {dev_err(dev, "When USB is wired, UOK should be wired.""as well.\n");ret = -EINVAL;goto err;}}if (pdata->chg) {if (!gpio_is_valid(pdata->chg)) {dev_err(dev, "Invalid pin: chg.\n");ret = -EINVAL;goto err;}/* set CHG gpio input */ret = gpio_request(pdata->chg, "max8903-CHG");if (ret) {printk(KERN_ERR"request max8903-CHG error!!\n");goto err;} else {gpio_direction_input(pdata->chg);}}if (pdata->flt) {if (!gpio_is_valid(pdata->flt)) {dev_err(dev, "Invalid pin: flt.\n");ret = -EINVAL;goto err;}/* set FLT gpio input */ret = gpio_request(pdata->flt, "max8903-FLT");if (ret) {printk(KERN_ERR"request max8903-FLT error!!\n");goto err;} else {gpio_direction_input(pdata->flt);}}if (pdata->usus) {if (!gpio_is_valid(pdata->usus)) {dev_err(dev, "Invalid pin: usus.\n");ret = -EINVAL;goto err;}}
注册DC充电的设备文件

mutex_init(&data->work_lock);data->fault = false;data->ta_in = ta_in;data->usb_in = usb_in;data->psy.name = "max8903-ac";data->psy.type = POWER_SUPPLY_TYPE_MAINS;data->psy.get_property = max8903_get_property;data->psy.properties = max8903_charger_props;data->psy.num_properties = ARRAY_SIZE(max8903_charger_props);ret = power_supply_register(dev, &data->psy);if (ret) {dev_err(dev, "failed: power supply register.\n");goto err_psy;}
注册USB充电的设备文件

data->usb.name = "max8903-usb";data->usb.type = POWER_SUPPLY_TYPE_USB;data->usb.get_property = max8903_get_usb_property;data->usb.properties = max8903_charger_props;data->usb.num_properties = ARRAY_SIZE(max8903_charger_props);ret = power_supply_register(dev, &data->usb);if (ret) {dev_err(dev, "failed: power supply register.\n");goto err_psy;}
这两个设备文件都只有一个操作:检测充电器是否在线

static enum power_supply_property max8903_charger_props[] = {POWER_SUPPLY_PROP_ONLINE,};
操作函数也很简单
static int max8903_get_property(struct power_supply *psy,enum power_supply_property psp,union power_supply_propval *val){struct max8903_data *data = container_of(psy,struct max8903_data, psy);switch (psp) {case POWER_SUPPLY_PROP_ONLINE:val->intval = 0;if (data->ta_in)val->intval = 1;data->charger_online = val->intval;break;default:return -EINVAL;}return 0;}static int max8903_get_usb_property(struct power_supply *usb,enum power_supply_property psp,union power_supply_propval *val){struct max8903_data *data = container_of(usb,struct max8903_data, usb);switch (psp) {case POWER_SUPPLY_PROP_ONLINE:val->intval = 0;if (data->usb_in)val->intval = 1;data->usb_charger_online = val->intval;break;default:return -EINVAL;}return 0;}
我们可以通过/sys/devices/platform/max8903-charger.1/power_supply/max8903-ac 目录和/sys/devices/platform/max8903-charger.1/power_supply/max8903-usb目录下的设备文件来访问充电器的状态



接下来是IO中断

if (pdata->dc_valid) {ret = request_threaded_irq(gpio_to_irq(pdata->dok),NULL, max8903_dcin,IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,"MAX8903 DC IN", data);if (ret) {dev_err(dev, "Cannot request irq %d for DC (%d)\n",gpio_to_irq(pdata->dok), ret);goto err_usb_irq;}}if (pdata->usb_valid) {ret = request_threaded_irq(gpio_to_irq(pdata->uok),NULL, max8903_usbin,IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,"MAX8903 USB IN", data);if (ret) {dev_err(dev, "Cannot request irq %d for USB (%d)\n",gpio_to_irq(pdata->uok), ret);goto err_dc_irq;}}if (pdata->flt) {ret = request_threaded_irq(gpio_to_irq(pdata->flt),NULL, max8903_fault,IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,"MAX8903 Fault", data);if (ret) {dev_err(dev, "Cannot request irq %d for Fault (%d)\n",gpio_to_irq(pdata->flt), ret);goto err_flt_irq;}}if (pdata->chg) {ret = request_threaded_irq(gpio_to_irq(pdata->chg),NULL, max8903_chg,IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,"MAX8903 Fault", data);if (ret) {dev_err(dev, "Cannot request irq %d for Fault (%d)\n",gpio_to_irq(pdata->flt), ret);goto err_chg_irq;}}
这4个IO的中断处理函数很类似

static irqreturn_t max8903_dcin(int irq, void *_data){struct max8903_data *data = _data;struct max8903_pdata *pdata = data->pdata;bool ta_in;ta_in = gpio_get_value(pdata->dok) ? false : true;   //保存当前dok值if (ta_in == data->ta_in)return IRQ_HANDLED;data->ta_in = ta_in;pr_info("TA(DC-IN) Charger %s.\n", ta_in ?"Connected" : "Disconnected");max8903_charger_update_status(data);    max8903_battery_update_status(data);    power_supply_changed(&data->psy);   //报告状态改变power_supply_changed(&data->bat);return IRQ_HANDLED;}static irqreturn_t max8903_usbin(int irq, void *_data){struct max8903_data *data = _data;struct max8903_pdata *pdata = data->pdata;bool usb_in;usb_in = gpio_get_value(pdata->uok) ? false : true; //保存当前uok值if (usb_in == data->usb_in)return IRQ_HANDLED;data->usb_in = usb_in;max8903_charger_update_status(data);max8903_battery_update_status(data);pr_info("USB Charger %s.\n", usb_in ?"Connected" : "Disconnected");power_supply_changed(&data->bat);power_supply_changed(&data->usb);  //报告状态改变return IRQ_HANDLED;}static irqreturn_t max8903_fault(int irq, void *_data){struct max8903_data *data = _data;struct max8903_pdata *pdata = data->pdata;bool fault;fault = gpio_get_value(pdata->flt) ? false : true;  //保存当前电池错误值if (fault == data->fault)return IRQ_HANDLED;data->fault = fault;if (fault)dev_err(data->dev, "Charger suffers a fault and stops.\n");elsedev_err(data->dev, "Charger recovered from a fault.\n");max8903_charger_update_status(data);max8903_battery_update_status(data);power_supply_changed(&data->psy);power_supply_changed(&data->bat);power_supply_changed(&data->usb);   //报告状态改变return IRQ_HANDLED;}static irqreturn_t max8903_chg(int irq, void *_data){struct max8903_data *data = _data;struct max8903_pdata *pdata = data->pdata;int chg_state;chg_state = gpio_get_value(pdata->chg) ? false : true;//保存电池充电状态if (chg_state == data->chg_state)return IRQ_HANDLED;data->chg_state = chg_state;max8903_charger_update_status(data);max8903_battery_update_status(data);power_supply_changed(&data->psy);power_supply_changed(&data->bat);power_supply_changed(&data->usb);//报告状态改变return IRQ_HANDLED;}
到了这里电池充电的流程就走完了。









更多相关文章

  1. Cocos2d-x学习之创建Android工程和编译
  2. Android用户界面开发(11):Menu
  3. android解析xml文件的方式(其三)
  4. 纯ant命令行打包android apk之图文从原理角度完全详解android打
  5. mac Android(安卓)Studio 真机测试 配置
  6. android 写入文件到SD卡中去
  7. 如何将打开res raw目录中的数据库文件?
  8. 将Android移植到FS2410开发板上
  9. Android(安卓)Bluetooth HID实现详解

随机推荐

  1. Android 通过http协议数据交互
  2. Android中ContextMenu的使用
  3. Android Notes 05 - Tasks and Back Stac
  4. android传送照片到FTP服务器
  5. android 抽奖盘动画 自定义View
  6. Android Bitmap与DrawAble与byte[]与Inpu
  7. Android之使用Activity与Fragment通信
  8. Android之水平ProgressBar多彩背景颜色
  9. Android中图片的处理(放大缩小,去色,转换格
  10. Sina微博的开发心得-1 logo界面