From 53679e81c721e4714978ed701b8275900f8114ae Mon Sep 17 00:00:00 2001 From: patacongo Date: Mon, 23 Jan 2012 17:19:43 +0000 Subject: [PATCH] Completes first (untested) cut at MAX1704x battery driver git-svn-id: svn://svn.code.sf.net/p/nuttx/code/trunk@4324 42af7a65-404d-4744-a932-0658087f49c3 --- TODO | 40 +++- drivers/power/Make.defs | 3 +- drivers/power/battery.c | 16 +- drivers/power/max1704x.c | 367 +++++++++++++++++++++++++++++++--- include/nuttx/power/battery.h | 22 +- 5 files changed, 401 insertions(+), 47 deletions(-) diff --git a/TODO b/TODO index 01f808cdbd..3165a72bfd 100644 --- a/TODO +++ b/TODO @@ -22,7 +22,7 @@ nuttx/ (1) Documentation (Documentation/) (7) Build system / Toolchains (5) Linux/Cywgin simulation (arch/sim) - (4) ARM (arch/arm/) + (5) ARM (arch/arm/) (1) ARM/C5471 (arch/arm/src/c5471/) (3) ARM/DM320 (arch/arm/src/dm320/) (2) ARM/i.MX (arch/arm/src/imx/) @@ -842,6 +842,44 @@ o ARM (arch/arm/) Priority: Low until I get around to implementng security or kernel mode for the ARM platform. + Title: CORTEX-M3 STACK OVERFLOW + Description: There is bit bit logic inf up_fullcontextrestore() that executes on + return from interrupts (and other context switches) that looks like: + + ldr r1, [r0, #(4*REG_CPSR)] /* Fetch the stored CPSR value */ + msr cpsr, r1 /* Set the CPSR */ + + /* Now recover r0 and r1 */ + + ldr r0, [sp] + ldr r1, [sp, #4] + add sp, sp, #(2*4) + + /* Then return to the address at the stop of the stack, + * destroying the stack frame + */ + + ldr pc, [sp], #4 + + Under conditions of excessivley high interrupt conditions, many + nested interrupts can oocur just after the 'msr cpsr' instruction. + At that time, there are 4 bytes on the stack and, with each + interrupt, the stack pointer may increment and possibly overflow. + + This can happen only under conditions of continuous interrupts. + See this email thread: http://tech.groups.yahoo.com/group/nuttx/message/1261 + On suggested change is: + + ldr r1, [r0, #(4*REG_CPSR)] /* Fetch the stored CPSR value */ + msr spsr_cxsf, r1 /* Set the CPSR */ + ldmia r0, {r0-r15}^ + + But this has not been proven to be a solution. + Status: Open + Priority: Low. The conditions of continous interrupts is really the problem. + If your design needs continous interrupts like this, please try + the above change and, please, submit a patch with the working fix. + o ARM/C5471 (arch/arm/src/c5471/) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/drivers/power/Make.defs b/drivers/power/Make.defs index 261e7525c2..45c6aebc32 100644 --- a/drivers/power/Make.defs +++ b/drivers/power/Make.defs @@ -81,5 +81,4 @@ endif DEPPATH += $(POWER_DEPPATH) VPATH += $(POWER_VPATH) -CFLAGS += $(POWER_CFLAGS); - +CFLAGS += $(POWER_CFLAGS) diff --git a/drivers/power/battery.c b/drivers/power/battery.c index a0d07956a4..ce93974083 100644 --- a/drivers/power/battery.c +++ b/drivers/power/battery.c @@ -170,8 +170,7 @@ static int bat_ioctl(FAR struct file *filep, int cmd, unsigned long arg) FAR int *ptr = (FAR int *)((uintptr_t)arg)); if (ptr) { - *ptr = dev->ops->state(dev); - ret = OK; + ret = dev->ops->state(dev, ptr); } } break; @@ -180,29 +179,26 @@ static int bat_ioctl(FAR struct file *filep, int cmd, unsigned long arg) FAR bool *ptr = (FAR bool *)((uintptr_t)arg)); if (ptr) { - *ptr = dev->ops->online(dev); - ret = OK; + ret = dev->ops->online(dev, ptr); } break; case BATIOC_VOLTAGE: { - FAR int *ptr = (FAR int *)((uintptr_t)arg)); + FAR b16_t *ptr = (FAR b16_t *)((uintptr_t)arg)); if (ptr) { - *ptr = dev->ops->voltage(dev); - ret = OK; + ret = dev->ops->voltage(dev, ptr); } } break; case BATIOC_CAPACITY: { - FAR int *ptr = (FAR int *)((uintptr_t)arg)); + FAR b16_t *ptr = (FAR b16_t *)((uintptr_t)arg)); if (ptr) { - *ptr = dev->ops->capacity(dev); - ret = OK; + ret = dev->ops->capacity(dev, ptr); } } break; diff --git a/drivers/power/max1704x.c b/drivers/power/max1704x.c index f8a09aec7b..58f48aaa3c 100644 --- a/drivers/power/max1704x.c +++ b/drivers/power/max1704x.c @@ -34,6 +34,12 @@ * ****************************************************************************/ +/* "The MAX17040/MAX17041 are ultra-compact, low-cost, host-side fuel-gauge + * systems for lithium-ion (Li+) batteries in handheld and portable equipment. + * The MAX17040 is configured to operate with a single lithium cell and the + * MAX17041 is configured for a dual-cell 2S pack. + */ + /**************************************************************************** * Included Files ****************************************************************************/ @@ -57,6 +63,89 @@ /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ +/* MAX1704x Register Definitions ********************************************/ +/* "All host interaction with the MAX17040/MAX17041 is handled by writing to + * and reading from register locations. The MAX17040/MAX17041 have six 16-bit + * registers: SOC, VCELL, MODE, VERSION, RCOMP, and COMMAND. Register reads + * and writes are only valid if all 16 bits are transferred..." + */ + +/* "VCELL Register. Battery voltage is measured at the CELL pin input with + * respect to GND over a 0 to 5.00V range for the MAX17040 and 0 to 10.00V + * for the MAX17041 with resolutions of 1.25mV and 2.50mV, respectively..." + */ + +#define MAX1407X_VCELL_ADDR 0x02 /* Bits 4-15: Bits 0-11 of the battery voltage */ + +/* VCELL conversion macros */ + +#define MAX14700_VCELL_CONV 82 /* 0.00125 v * 65536 */ +#define MAX14070_VCELL(v) ((b16_t)(v) * MAX14700_VCELL_CONV) + +#define MAX14701_VCELL_CONV 163 /* 0.0025 v * 65536 */ +#define MAX14071_VCELL(v) ((b16_t)(v) * MAX14701_VCELL_CONV) + +/* "SOC Register. The SOC register is a read-only register that displays the + * state of charge of the cell as calculated by the ModelGauge algorithm. The + * result is displayed as a percentage of the cell’s full capacity... + * + * "...Units of % can be directly determined by observing only the high byte + * of the SOC register. The low byte provides additional resolution in units + * 1/256%. + */ + +#define MAX1407X_SOC_ADDR 0x04 /* Bits 0-15: Full SOC */ + +/* SoC conversion macros */ + +#define MAX1407X_SOC(s) ((b16_t)MAX1407X_SOCB8(s) << 8) +#define MAX17040_SOC_FULL itob16(95) /* We say full if Soc >= 95% */ + +/* "MODE Register.The MODE register allows the host processor to send special + * commands to the IC." + */ + +#define MAX1407X_MODE_ADDR 0x06 /* Bits 0-15: 16-bit MODE */ + +/* Supported modes */ + +#define MAX1407X_MODE_QUICKSTART 0x4000 + +/* "The VERSION register is a read-only register that contains a value + * indicating the production version of the MAX17040/MAX17041." + */ + +#define MAX1407X_VERSION_ADDR 0x08 /* Bits 0-15: 16-bit VERSION */ + +/* "RCOMP Register. RCOMP is a 16-bit value used to compensate the ModelGauge + * algorithm. RCOMP can be adjusted to optimize performance for different + * lithium chemistries or different operating temperatures... The factory- + * default value for RCOMP is 9700h." + */ + +#define MAX1407X_RCOMP_ADDR 0x0c /* Bits 0-15: 16-bit RCOMP */ + +/* "COMMAND Register. The COMMAND register allows the host processor to send + * special commands to the IC..." + */ + +#define MAX1407X_COMMAND_ADDR 0xfe /* Bits 0-7: 16-bit COMMAND */ + +/* Supported copmmands */ + +#define MAX1407X_COMMAND_POR 0x5400 + +/* Debug ********************************************************************/ + +#ifdef CONFIG_DEBUG_MAX1704X +# define batdbg dbg +#else +# ifdef CONFIG_CPP_HAVE_VARARGS +# define batdbg(x...) +# else +# define batdbg (void) +# endif +#endif /**************************************************************************** * Private @@ -78,13 +167,28 @@ struct max1704x_dev_s /**************************************************************************** * Private Function Prototypes ****************************************************************************/ +/* I2C support */ + +static int max1704x_getreg16(FAR struct max1704x_dev_s *priv, uint8_t regaddr) + FAR uint16_t *regval); +static int max1704x_putreg16(FAR struct max1704x_dev_s *priv, uint8_t regaddr, + uint16_t regval); + +static inline int max1704x_getvcell(FAR struct max1704x_dev_s *priv, + b16_t *vcell); +static inline int max1704x_getsoc(FAR struct max1704x_dev_s *priv, + b16_t *soc); +static inline int max1704x_setquikstart(FAR struct max1704x_dev_s *priv); +static inline int max1704x_getversion(FAR struct max1704x_dev_s *priv, + uint16_t *version); +static inline int max1704x_reset(FAR struct max1704x_dev_s *priv); /* Battery driver lower half methods */ -static enum battery_status_e mx1704x_state(struct battery_dev_s *lower); -static bool mx1704x_online(struct battery_dev_s *lower); -static int mx1704x_voltage(struct battery_dev_s *lower); -static int mx1704x_capacity(struct battery_dev_s *lower); +static int max1704x_state(struct battery_dev_s *dev, int *status); +static int max1704x_online(struct battery_dev_s *dev, bool *status); +static int max1704x_voltage(struct battery_dev_s *dev, b16_t *value); +static int max1704x_capacity(struct battery_dev_s *dev, b16_t *value); /**************************************************************************** * Private Data @@ -92,10 +196,10 @@ static int mx1704x_capacity(struct battery_dev_s *lower); static const struct battery_operations_s g_max1704xops = { - mx1704x_state, - mx1704x_online, - mx1704x_voltage, - mx1704x_capacity + max1704x_state, + max1704x_online, + max1704x_voltage, + max1704x_capacity }; /**************************************************************************** @@ -103,59 +207,260 @@ static const struct battery_operations_s g_max1704xops = ****************************************************************************/ /**************************************************************************** - * Name: max1704x_open + * Name: max1704x_getreg16 + * + * Description: + * Read a 16-bit value from a MAX1704x register pair. + * + * START ACK ACK + * REPEATED-START ACK Data0 ACK Data1 NO-ACK STOP + * + ****************************************************************************/ + +static int max1704x_getreg16(FAR struct max1704x_dev_s *priv, uint8_t regaddr) + FAR uint16_t *regval) +{ + uint8_t buffer[2]; + int ret; + + /* Write the register address */ + + I2C_SETADDRESS(priv->i2c, priv->addr, 7); + ret = I2C_WRITE(priv->i2c, ®addr, 1); + if (ret < 0) + { + batdbg("I2C_WRITE failed: %d\n", ret); + return ret; + } + + /* Restart and read 16-bits from the register */ + + ret = I2C_READ(priv->i2c, buffer, 2); + if (ret < 0) + { + batdbg("I2C_READ failed: %d\n", ret); + return ret; + } + + /* Return the 16-bit value */ + + return (uint16_t)buffer[0] << 8 | (uint16_t)buffer[1]; + return OK; +} + +/**************************************************************************** + * Name: max1704x_putreg16 + * + * Description: + * Write a 16-bit value to a MAX1704x register pair. + * + * START ACK ACK Data0 ACK Data1 ACK STOP + * + ****************************************************************************/ + +static int max1704x_putreg16(FAR struct max1704x_dev_s *priv, uint8_t regaddr, + uint16_t regval) +{ + uint8_t buffer[3]; + b8_t regb8; + + batdbg("addr: %02x regval: %08x\n", regaddr, regval); + + /* Set up a 3 byte message to send */ + + buffer[0] = regaddr; + buffer[1] = (uint8_t)(regval >> 8); + buffer[2] = (uint8_t)(regval & 0xff); + + /* Write the register address followed by the data (no RESTART) */ + + I2C_SETADDRESS(priv->i2c, priv->addr, 7); + return I2C_WRITE(priv->i2c, buffer, 3); +} + +/**************************************************************************** + * Name: max1704x_getvcell + * + * Description: + * Read the VCELL register and scale the returned value + * + ****************************************************************************/ + +static inline int max1704x_getvcell(FAR struct max1704x_dev_s *priv, + b16_t *vcell) +{ + uint16_t regval; + int ret; + + ret = max1704x_getreg16(priv, MAX1407X_VCELL_ADDR, ®val); + if (ret == OK) + { + *vcell = MAX14070_VCELL(regval); + } + return ret; +} + +/**************************************************************************** + * Name: max1704x_getsoc + * + * Description: + * Read the SOC register and scale the returned value + * + ****************************************************************************/ + +static inline int max1704x_getsoc(FAR struct max1704x_dev_s *priv, + b16_t *soc) +{ + uint16_t regval; + int ret; + + ret = max1704x_getreg16(priv, MAX1407X_VCELL_ADDR, ®val); + if (ret == OK) + { + *soc = MAX1407X_SOC(regval); + } + return ret; +} + +/**************************************************************************** + * Name: max1704x_setquikstart + * + * Description: + * Set Quickstart mode + * + ****************************************************************************/ + +static inline int max1704x_setquikstart(FAR struct max1704x_dev_s *priv) +{ + return max1704x_putreg16(priv, MAX1407X_MODE_ADDR, MAX1407X_MODE_QUICKSTART); +} + +/**************************************************************************** + * Name: max1704x_getversion + * + * Description: + * Read the SOC register and scale the returned value + * + ****************************************************************************/ + +static inline int max1704x_getversion(FAR struct max1704x_dev_s *priv, + uint16_t *version) +{ + return max1704x_getreg16(priv, MAX1407X_VCELL_ADDR, version); +} + +/**************************************************************************** + * Name: max1704x_setrcomp + * + * Description: + * Set Quickstart mode + * + ****************************************************************************/ + +static inline int max1704x_setrcomp(FAR struct max1704x_dev_s *priv, uint16_t rcomp) +{ + return max1704x_putreg16(priv, MAX1407X_RCOMP_ADDR, rcomp); +} + +/**************************************************************************** + * Name: max1704x_reset + * + * Description: + * Reset the MAX1704x + * + ****************************************************************************/ + +static inline int max1704x_reset(FAR struct max1704x_dev_s *priv) +{ + return max1704x_putreg16(priv, MAX1407X_COMMAND_ADDR, MAX1407X_COMMAND_POR); +} + +/**************************************************************************** + * Name: max1704x_state * * Description: * Return the current battery state * ****************************************************************************/ -static enum battery_status_e state(struct battery_dev_s *lower) +static int max1704x_state(struct battery_dev_s *dev, int *status) { -#warning "Missing logic" - return BATTERY_UNKNOWN; + FAR struct max1704x_dev_s *priv = (FAR struct max1704x_dev_s *)dev; + b16_t soc; + int ret; + + /* Only a few of the possible battery states are supported by this driver: + * + * BATTERY_UNKNOWN - Returned on error conditions + * BATTERY_IDLE - This is what will usually be reported + * BATTERY_FULL - This will be reported if the SoC is greater than 95% + * BATTERY_CHARGING and BATTERY_DISCHARGING - I don't think this hardware + * knows anything about current (charging or dischargin). + */ + + ret = max1704x_getsoc(priv, &soc); + if (ret < 0) + { + *status = BATTERY_UNKNOWN; + return ret; + } + + /* Is the battery fully charged? */ + + if (soc > MAX17040_SOC_FULL) + { + *status = BATTERY_FULL; + } + else + { + *status = BATTERY_IDLE; + } + + return OK; } /**************************************************************************** - * Name: max1704x_open + * Name: max1704x_online * * Description: * Return true if the batter is online * ****************************************************************************/ -static bool online(struct battery_dev_s *lower) +static int max1704x_online(struct battery_dev_s *dev, bool *status); { -#warning "Missing logic" - return false; + /* There is no concept of online/offline in this driver */ + + *status = true + return OK; } /**************************************************************************** - * Name: max1704x_open + * Name: max1704x_voltage * * Description: * Current battery voltage * ****************************************************************************/ -static int voltage(struct battery_dev_s *lower); +static int max1704x_voltage(struct battery_dev_s *dev, b16_t *value); { -#warning "Missing logic" - return 0; + FAR struct max1704x_dev_s *priv = (FAR struct max1704x_dev_s *)dev; + return max1704x_getvcell(priv, value); } /**************************************************************************** - * Name: max1704x_open + * Name: max1704x_capacity * * Description: * Battery capacity * ****************************************************************************/ -static int capacity(struct battery_dev_s *lower); +static int max1704x_capacity(struct battery_dev_s *dev, b16_t *value); { -#warning "Missing logic" - return 0; + FAR struct max1704x_dev_s *priv = (FAR struct max1704x_dev_s *)dev; + return max1704x_getsoc(priv, value); } /**************************************************************************** @@ -191,15 +496,27 @@ FAR struct battery_dev_s *max1704x_initialize(FAR struct i2c_dev_s *i2c, FAR struct max1704x_dev_s *priv; int ret; - /* Initialize theMAX1704x device structure */ + /* Initialize the MAX1704x device structure */ priv = (FAR struct max1704x_dev_s *)kzalloc(sizeof(struct max1704x_dev_s)); if (priv) { + /* Initialize the MAX1704x device structure */ + sem_init(&priv->batsem, 0, 1); priv->ops = &g_max1704xops; priv->i2c = i2c; priv->addr = addr; + + /* Reset the MAX1704x (mostly just to make sure that we can talk to it) */ + + ret = max1704x_reset(priv); + if (ret < 0) + { + batdbg("Failed to reset the MAX1704x: %d\n", ret); + kfree(priv); + return NULL; + } } return (FAR struct battery_dev_s *)priv; } diff --git a/include/nuttx/power/battery.h b/include/nuttx/power/battery.h index eb2fa05028..b1a06fd626 100644 --- a/include/nuttx/power/battery.h +++ b/include/nuttx/power/battery.h @@ -45,6 +45,7 @@ #include #include +#include #ifdef CONFIG_BATTERY @@ -74,10 +75,13 @@ * Input value: A pointer to type int. * BATIOC_ONLINE - Return 1 if the battery is online; 0 if offline. * Input value: A pointer to type bool. - * BATIOC_VOLTAGE - Return the current battery voltage. - * Input value: A pointer to type int. - * BATIOC_CAPACITY - Return the current battery capacity. - * Input value: A pointer to type int. + * BATIOC_VOLTAGE - Return the current battery voltage. The returned value + * is a fixed preceision number in units of volts. + * Input value: A pointer to type b16_t. + * BATIOC_CAPACITY - Return the current battery capacity or State of Charge + * (SoC). The returned value is a fixed precision percentage of the + * batteries full capacity. + * Input value: A pointer to type b16_t. */ #define BATIOC_STATE _BATIOC(0x0001) @@ -104,21 +108,21 @@ enum battery_status_e struct battery_dev_s; struct battery_operations_s { - /* Return the current battery state */ + /* Return the current battery state (see enum battery_status_e) */ - enum battery_status_e state(struct battery_dev_s *dev); + int state(struct battery_dev_s *dev, int *status); /* Return true if the batter is online */ - bool online(struct battery_dev_s *dev); + int online(struct battery_dev_s *dev, bool *status); /* Current battery voltage */ - int voltage(struct battery_dev_s *dev); + int voltage(struct battery_dev_s *dev, b16_t *value); /* Battery capacity */ - int capacity(struct battery_dev_s *dev); + int capacity(struct battery_dev_s *dev, b16_t *value); }; /* This structure defines the battery driver state structure */