Correct error in clock_gettime(); fix wait for STOP in I2C stm32 driver

git-svn-id: svn://svn.code.sf.net/p/nuttx/code/trunk@3948 42af7a65-404d-4744-a932-0658087f49c3
This commit is contained in:
patacongo 2011-09-11 14:55:31 +00:00
parent bdf8ede547
commit 455b9336fb
6 changed files with 201 additions and 60 deletions

View File

@ -2070,3 +2070,7 @@
temperature sensor driver.
* configs/stm3210e-eval/src/up_lm75.c: Add support for the LM-75 on the
STMicro STM3210E-EVAL board.
* sched/clock_gettime.c: Correct an error in the tv_nsec calculation
that happens only config CONFIG_RTC is enabled.
* arch/arm/src/stm32/stm32_i2c.c: Correct some bugs related to waiting
to the I2C STOP condition to be cleared.

View File

@ -429,12 +429,25 @@ static inline void stm32_i2c_sendstart(FAR struct stm32_i2c_priv_s *priv)
static inline void stm32_i2c_clrstart(FAR struct stm32_i2c_priv_s *priv)
{
/* "This [START] bit is set and cleared by software and cleared by hardware
/* "Note: When the STOP, START or PEC bit is set, the software must
* not perform any write access to I2C_CR1 before this bit is
* cleared by hardware. Otherwise there is a risk of setting a
* second STOP, START or PEC request."
*
* "The [STOP] bit is set and cleared by software, cleared by hardware
* when a Stop condition is detected, set by hardware when a timeout
* error is detected.
*
* "This [START] bit is set and cleared by software and cleared by hardware
* when start is sent or PE=0." The bit must be cleared by software if the
* START is never sent.
*
* "This [PEC] bit is set and cleared by software, and cleared by hardware
* when PEC is transferred or by a START or Stop condition or when PE=0."
*/
stm32_i2c_modifyreg(priv, STM32_I2C_CR1_OFFSET, I2C_CR1_START, 0);
stm32_i2c_modifyreg(priv, STM32_I2C_CR1_OFFSET,
I2C_CR1_START|I2C_CR1_STOP|I2C_CR1_PEC, 0);
}
static inline void stm32_i2c_sendstop(FAR struct stm32_i2c_priv_s *priv)
@ -871,6 +884,7 @@ int stm32_i2c_process(FAR struct i2c_dev_s *dev, FAR struct i2c_msg_s *msgs, int
struct stm32_i2c_inst_s *inst = (struct stm32_i2c_inst_s *)dev;
uint32_t status = 0;
uint32_t ahbenr;
uint16_t regval;
int status_errno = 0;
ASSERT(count);
@ -879,15 +893,43 @@ int stm32_i2c_process(FAR struct i2c_dev_s *dev, FAR struct i2c_msg_s *msgs, int
ahbenr = stm32_i2c_disablefsmc(inst->priv);
/* Wait as stop might still be in progress
*
* \todo GET RID OF THIS PERFORMANCE LOSS and for() loop
/* Wait as stop might still be in progress; but stop might also
* be set because of a timeout error: "The [STOP] bit is set and
* cleared by software, cleared by hardware when a Stop condition is
* detected, set by hardware when a timeout error is detected."
*/
for (; stm32_i2c_getreg(inst->priv, STM32_I2C_CR1_OFFSET) & I2C_CR1_STOP; )
for (;;)
{
up_waste();
/* Check for STOP condition */
regval = stm32_i2c_getreg(inst->priv, STM32_I2C_CR1_OFFSET);
if ((regval & I2C_CR1_STOP) == 0)
{
break;
}
/* Check for timeout error */
regval = stm32_i2c_getreg(inst->priv, STM32_I2C_SR1_OFFSET);
if ((regval & I2C_SR1_TIMEOUT) != 0)
{
break;
}
}
/* Clear any pending error interrupts */
stm32_i2c_putreg(inst->priv, STM32_I2C_SR1_OFFSET, 0);
/* "Note: When the STOP, START or PEC bit is set, the software must
* not perform any write access to I2C_CR1 before this bit is
* cleared by hardware. Otherwise there is a risk of setting a
* second STOP, START or PEC request." However, if the bits are
* not cleared by hardware, then we will have to do that from hardware.
*/
stm32_i2c_clrstart(inst->priv);
/* Old transfers are done */
@ -898,10 +940,6 @@ int stm32_i2c_process(FAR struct i2c_dev_s *dev, FAR struct i2c_msg_s *msgs, int
stm32_i2c_setclock(inst->priv, inst->frequency);
/* Clear any pending error interrupts */
stm32_i2c_putreg(inst->priv, STM32_I2C_SR1_OFFSET, 0);
/* Trigger start condition, then the process moves into the ISR. I2C
* interrupts will be enabled within stm32_i2c_waitisr().
*/
@ -917,10 +955,10 @@ int stm32_i2c_process(FAR struct i2c_dev_s *dev, FAR struct i2c_msg_s *msgs, int
status = stm32_i2c_getstatus(inst->priv);
status_errno = ETIMEDOUT;
/* " Note: When the STOP, START or PEC bit is set, the software must
* not perform any write access to I2C_CR1 before this bit is
* cleared by hardware. Otherwise there is a risk of setting a
* second STOP, START or PEC request."
/* "Note: When the STOP, START or PEC bit is set, the software must
* not perform any write access to I2C_CR1 before this bit is
* cleared by hardware. Otherwise there is a risk of setting a
* second STOP, START or PEC request."
*/
stm32_i2c_clrstart(inst->priv);
@ -936,6 +974,8 @@ int stm32_i2c_process(FAR struct i2c_dev_s *dev, FAR struct i2c_msg_s *msgs, int
if ((status & I2C_SR1_ERRORMASK) != 0)
{
/* I2C_SR1_ERRORMASK is the 'OR' of the following individual bits: */
if (status & I2C_SR1_BERR)
{
/* Bus Error */
@ -975,7 +1015,7 @@ int stm32_i2c_process(FAR struct i2c_dev_s *dev, FAR struct i2c_msg_s *msgs, int
/* This is not an error and should never happen since SMBus is not enabled */
else if (status & I2C_SR1_SMBALERT)
else /* if (status & I2C_SR1_SMBALERT) */
{
/* SMBus alert is an optional signal with an interrupt line for devices
* that want to trade their ability to master for a pin.
@ -987,9 +1027,11 @@ int stm32_i2c_process(FAR struct i2c_dev_s *dev, FAR struct i2c_msg_s *msgs, int
/* This is not an error, but should not happen. The BUSY signal can hang,
* however, if there are unhealthy devices on the bus that need to be reset.
* NOTE: We will only see this buy indication if stm32_i2c_sem_waitisr()
* fails above; Otherwise it is cleared.
*/
else if (status & (I2C_SR2_BUSY << 16))
else if ((status & (I2C_SR2_BUSY << 16)) != 0)
{
/* I2C Bus is for some reason busy */
@ -1016,7 +1058,7 @@ int stm32_i2c_write(FAR struct i2c_dev_s *dev, const uint8_t *buffer, int buflen
.buffer = (uint8_t *)buffer,
.length = buflen
};
return stm32_i2c_process(dev, &msgv, 1);
}

View File

@ -13,6 +13,7 @@ Contents
- NuttX buildroot Toolchain
- DFU
- LEDs
- Temperature Sensor
- STM3210E-EVAL-specific Configuration Options
- Configurations
@ -237,6 +238,32 @@ They are encoded as follows:
on a small proportion of the time.
*** LED2 may also flicker normally if signals are processed.
Temperature Sensor
==================
Support for the on-board LM-75 temperature sensor is available. This supported
has been verified, but has not been included in any of the available the
configurations. To set up the temperature sensor, add the following to the
NuttX configuration file
CONFIG_I2C=y
CONFIG_I2C_LM75=y
Then you can implement logic like the following to use the temperature sensor:
#include <nuttx/sensors/lm75.h>
#include <arch/board/board.h>
ret = stm32_lm75initialize("/dev/temp"); /* Register the temperature sensor */
fd = open("/dev/temp", O_RDONLY); /* Open the temperature sensor device */
ret = ioctl(fd, SNIOC_FAHRENHEIT, 0); /* Select Fahrenheit */
bytesread = read(fd, buffer, 8*sizeof(b16_t)); /* Read temperature samples */
More complex temperature sensor operations are also available. See the IOCTAL
commands enumerated in include/nuttx/sensors/lm75.h. Also read the descriptions
of the stm32_lm75initialize() and stm32_lm75attach() interfaces in the
arch/board/board.h file (sames as configs/stm3210e-eval/include/board.h).
STM3210E-EVAL-specific Configuration Options
============================================

View File

@ -43,6 +43,7 @@
#include <stdlib.h>
#include <fixedmath.h>
#include <errno.h>
#include <debug.h>
#include <nuttx/fs.h>
#include <nuttx/i2c.h>
@ -57,7 +58,19 @@
/* Centigrade to Fahrenheit conversion: F = 9*C/5 + 32 */
#define B16_9DIV5 (9 * 65536 / 5)
#define B16_32 (32 * 65536);
#define B16_32 (32 * 65536)
/* Debug for this file only */
#ifdef CONFIG_DEBUG_LM75
# define lm75dbg dbg
#else
# ifdef CONFIG_CPP_HAVE_VARARGS
# define lm75dbg(x...)
# else
# define lm75dbg (void)
# endif
#endif
/****************************************************************************
* Private
@ -75,14 +88,12 @@ struct lm75_dev_s
****************************************************************************/
/* I2C Helpers */
static int lm75_readb8(FAR struct lm75_dev_s *priv, uint8_t regaddr,
FAR b8_t *regvalue);
#if 0 // Not used
static int lm75_writeb8(FAR struct lm75_dev_s *priv, uint8_t regaddr,
b8_t regval);
#endif
static int lm75_readtemp(FAR struct lm75_dev_s *priv, b16_t *temp);
static int lm75_readconf(FAR struct lm75_dev_s *priv, uint8_t *conf);
static int lm75_readb16(FAR struct lm75_dev_s *priv, uint8_t regaddr,
FAR b16_t *regvalue);
static int lm75_writeb16(FAR struct lm75_dev_s *priv, uint8_t regaddr,
b16_t regval);
static int lm75_readtemp(FAR struct lm75_dev_s *priv, FAR b16_t *temp);
static int lm75_readconf(FAR struct lm75_dev_s *priv, FAR uint8_t *conf);
static int lm75_writeconf(FAR struct lm75_dev_s *priv, uint8_t conf);
/* Character driver methods */
@ -114,15 +125,15 @@ static const struct file_operations lm75_fops =
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: lm75_readb8
* Name: lm75_readb16
*
* Description:
* Read a 16-bit register (LM75_TEMP_REG, LM75_THYS_REG, or LM75_TOS_REG)
*
****************************************************************************/
static int lm75_readb8(FAR struct lm75_dev_s *priv, uint8_t regaddr,
FAR b8_t *regvalue)
static int lm75_readb16(FAR struct lm75_dev_s *priv, uint8_t regaddr,
FAR b16_t *regvalue)
{
uint8_t buffer[2];
int ret;
@ -133,6 +144,7 @@ static int lm75_readb8(FAR struct lm75_dev_s *priv, uint8_t regaddr,
ret = I2C_WRITE(priv->i2c, &regaddr, 1);
if (ret < 0)
{
lm75dbg("I2C_WRITE failed: %d\n", ret);
return ret;
}
@ -141,43 +153,49 @@ static int lm75_readb8(FAR struct lm75_dev_s *priv, uint8_t regaddr,
ret = I2C_READ(priv->i2c, buffer, 2);
if (ret < 0)
{
lm75dbg("I2C_READ failed: %d\n", ret);
return ret;
}
/* Data format is: TTTTTTTT Txxxxxxx where TTTTTTTTT is a nine-bit
* temperature value with LSB = 0.5 degrees centigrade. So the raw
* data is b8_t
/* Data format is: TTTTTTTT Txxxxxxx where TTTTTTTTT is a nine-bit,
* signed temperature value with LSB = 0.5 degrees centigrade. So the
* raw data is b8_t
*/
*regvalue = (b8_t)buffer[0] << 8 | (b8_t)buffer[1];
*regvalue = b8tob16((b8_t)buffer[0] << 8 | (b8_t)buffer[1]);
lm75dbg("addr: %02x value: %08x ret: %d\n", regaddr, *regvalue, ret);
return OK;
}
/****************************************************************************
* Name: lm75_writeb8
* Name: lm75_writeb16
*
* Description:
* Write to a 16-bit register (LM75_TEMP_REG, LM75_THYS_REG, or LM75_TOS_REG)
*
****************************************************************************/
#if 0 // Not used
static int lm75_writeb8(FAR struct lm75_dev_s *priv, uint8_t regaddr,
b8_t regval)
static int lm75_writeb16(FAR struct lm75_dev_s *priv, uint8_t regaddr,
b16_t regval)
{
uint8_t buffer[3];
b8_t regb8;
lm75dbg("addr: %02x value: %08x\n", regaddr, regval);
/* Set up a 3 byte message to send */
buffer[0] = regaddr;
buffer[1] = regval >> 8;
buffer[2] = regval;
regb8 = b16tob8(regval);
buffer[1] = (uint8_t)(regb8 >> 8);
buffer[2] = (uint8_t)regb8;
/* Write the register address followed by the data (no RESTART) */
I2C_SETADDRESS(priv->i2c, priv->addr, 7);
return I2C_WRITE(priv->i2c, buffer, 3);
}
#endif
/****************************************************************************
* Name: lm75_readtemp
@ -187,26 +205,20 @@ static int lm75_writeb8(FAR struct lm75_dev_s *priv, uint8_t regaddr,
*
****************************************************************************/
static int lm75_readtemp(FAR struct lm75_dev_s *priv, b16_t *temp)
static int lm75_readtemp(FAR struct lm75_dev_s *priv, FAR b16_t *temp)
{
b8_t temp8;
b16_t temp16;
int ret;
/* Read the raw temperature data. Data format is: TTTTTTTT Txxxxxxx where
* TTTTTTTTT is a nine-bit temperature value with LSB = 0.5 degrees centigrade.
* So the raw data is b8_t.
*/
ret = lm75_readb8(priv, LM75_TEMP_REG, &temp8);
/* Read the raw temperature data (b16_t) */
ret = lm75_readb16(priv, LM75_TEMP_REG, &temp16);
if (ret < 0)
{
lm75dbg("lm75_readb16 failed: %d\n", ret);
return ret;
}
/* Convert to b16_t */
temp16 = b8tob16(temp8);
lm75dbg("Centigrade: %08x\n", temp16);
/* Was fahrenheit requested? */
@ -215,6 +227,7 @@ static int lm75_readtemp(FAR struct lm75_dev_s *priv, b16_t *temp)
/* Centigrade to Fahrenheit conversion: F = 9*C/5 + 32 */
temp16 = b16mulb16(temp16, B16_9DIV5) + B16_32;
lm75dbg("Fahrenheit: %08x\n", temp16);
}
*temp = temp16;
@ -229,7 +242,7 @@ static int lm75_readtemp(FAR struct lm75_dev_s *priv, b16_t *temp)
*
****************************************************************************/
static int lm75_readconf(FAR struct lm75_dev_s *priv, uint8_t *conf)
static int lm75_readconf(FAR struct lm75_dev_s *priv, FAR uint8_t *conf)
{
uint8_t buffer;
int ret;
@ -242,12 +255,14 @@ static int lm75_readconf(FAR struct lm75_dev_s *priv, uint8_t *conf)
ret = I2C_WRITE(priv->i2c, &buffer, 1);
if (ret < 0)
{
lm75dbg("I2C_WRITE failed: %d\n", ret);
return ret;
}
/* Restart and read 8-bits from the register */
ret = I2C_READ(priv->i2c, conf, 1);
lm75dbg("conf: %02x ret: %d\n", *conf, ret);
return ret;
}
@ -263,6 +278,8 @@ static int lm75_writeconf(FAR struct lm75_dev_s *priv, uint8_t conf)
{
uint8_t buffer[2];
lm75dbg("conf: %02x\n", conf);
/* Set up a 2 byte message to send */
buffer[0] = LM75_CONF_REG;
@ -310,16 +327,19 @@ static ssize_t lm75_read(FAR struct file *filep, FAR char *buffer, size_t buflen
FAR struct lm75_dev_s *priv = inode->i_private;
FAR b16_t *ptr;
ssize_t nsamples;
int i;
int ret;
/* How many samples were requested to get? */
nsamples = buflen/sizeof(b16_t);
nsamples = buflen / sizeof(b16_t);
ptr = (FAR b16_t *)buffer;
lm75dbg("buflen: %d nsamples: %d\n", buflen, nsamples);
/* Get the requested number of samples */
for (; nsamples > 0; nsamples--)
for (i = 0; i < nsamples; i++)
{
b16_t temp;
@ -328,6 +348,7 @@ static ssize_t lm75_read(FAR struct file *filep, FAR char *buffer, size_t buflen
ret = lm75_readtemp(priv, &temp);
if (ret < 0)
{
lm75dbg("lm75_readtemp failed: %d\n",ret);
return (ssize_t)ret;
}
@ -359,7 +380,7 @@ static int lm75_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
FAR struct lm75_dev_s *priv = inode->i_private;
int ret = OK;
switch (arg)
switch (cmd)
{
/* Read from the configuration register. Arg: uint8_t* pointer */
@ -367,6 +388,7 @@ static int lm75_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
{
FAR uint8_t *ptr = (FAR uint8_t *)((uintptr_t)arg);
ret = lm75_readconf(priv, ptr);
lm75dbg("conf: %02x ret: %d\n", *ptr, ret);
}
break;
@ -374,6 +396,7 @@ static int lm75_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
case SNIOC_WRITECONF:
ret = lm75_writeconf(priv, (uint8_t)arg);
lm75dbg("conf: %02x ret: %d\n", *(uint8_t*)arg, ret);
break;
/* Shutdown the LM75, Arg: None */
@ -386,6 +409,7 @@ static int lm75_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
{
ret = lm75_writeconf(priv, conf | LM75_CONF_SHUTDOWN);
}
lm75dbg("conf: %02x ret: %d\n", conf | LM75_CONF_SHUTDOWN, ret);
}
break;
@ -399,6 +423,7 @@ static int lm75_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
{
ret = lm75_writeconf(priv, conf & ~LM75_CONF_SHUTDOWN);
}
lm75dbg("conf: %02x ret: %d\n", conf & ~LM75_CONF_SHUTDOWN, ret);
}
break;
@ -406,15 +431,52 @@ static int lm75_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
case SNIOC_FAHRENHEIT:
priv->fahrenheit = true;
lm75dbg("Fahrenheit\n");
break;
/* Report Samples in Centigrade */
case SNIOC_CENTIGRADE:
priv->fahrenheit = false;
lm75dbg("Centigrade\n");
break;
/* Read THYS temperature register. Arg: b16_t* pointer */
case SNIOC_READTHYS:
{
FAR b16_t *ptr = (FAR b16_t *)((uintptr_t)arg);
ret = lm75_readb16(priv, LM75_THYS_REG, ptr);
lm75dbg("THYS: %08x ret: %d\n", *ptr, ret);
}
break;
/* Write THYS temperature register. Arg: b16_t value */
case SNIOC_WRITETHYS:
ret = lm75_writeb16(priv, LM75_THYS_REG, (b16_t)arg);
lm75dbg("THYS: %08x ret: %d\n", (b16_t)arg, ret);
break;
/* Read TOS (Over-temp Shutdown Threshold) Register. Arg: b16_t* pointer */
case SNIOC_READTOS:
{
FAR b16_t *ptr = (FAR b16_t *)((uintptr_t)arg);
ret = lm75_readb16(priv, LM75_TOS_REG, ptr);
lm75dbg("TOS: %08x ret: %d\n", *ptr, ret);
}
break;
/* Write TOS (Over-temp Shutdown Threshold) Register. Arg: b16_t value */
case SNIOC_WRITRETOS:
ret = lm75_writeb16(priv, LM75_TOS_REG, (b16_t)arg);
lm75dbg("TOS: %08x ret: %d\n", (b16_t)arg, ret);
break;
default:
lm75dbg("Unrecognized cmd: %d\n", cmd);
ret = -ENOTTY;
break;
}
@ -454,6 +516,7 @@ int lm75_register(FAR const char *devpath, FAR struct i2c_dev_s *i2c, uint8_t ad
priv = (FAR struct lm75_dev_s *)malloc(sizeof(struct lm75_dev_s));
if (!priv)
{
lm75dbg("Failed to allocate instance\n");
return -ENOMEM;
}
@ -466,6 +529,7 @@ int lm75_register(FAR const char *devpath, FAR struct i2c_dev_s *i2c, uint8_t ad
ret = register_driver(devpath, &lm75_fops, 0555, priv);
if (ret < 0)
{
lm75dbg("Failed to register driver: %d\n", ret);
free(priv);
}
return ret;

View File

@ -61,6 +61,10 @@
#define SNIOC_POWERUP _SNIOC(0x0004) /* Arg: None */
#define SNIOC_FAHRENHEIT _SNIOC(0x0005) /* Arg: None */
#define SNIOC_CENTIGRADE _SNIOC(0x0006) /* Arg: None */
#define SNIOC_READTHYS _SNIOC(0x0007) /* Arg: b16_t* pointer */
#define SNIOC_WRITETHYS _SNIOC(0x0008) /* Arg: b16_t value */
#define SNIOC_READTOS _SNIOC(0x0009) /* Arg: b16_t* pointer */
#define SNIOC_WRITRETOS _SNIOC(0x000a) /* Arg: b16_t value */
/* LM-75 Register Definitions ***********************************************/
/* LM-75 Registers addresses */
@ -68,7 +72,7 @@
#define LM75_TEMP_REG 0x00 /* Temperature Register */
#define LM75_CONF_REG 0x01 /* Configuration Register */
#define LM75_THYS_REG 0x02 /* Temperature Register */
#define LM75_TOS_REG 0x03 /* Over-temp Shutdown threshold Register */
#define LM75_TOS_REG 0x03 /* Over-temp Shutdown Threshold Register */
/* Configuration Register Bit Definitions */

View File

@ -148,7 +148,7 @@ int clock_gettime(clockid_t clock_id, struct timespec *tp)
if (g_rtc_enabled)
{
tp->tv_sec = up_rtc_gettime();
tp->tv_nsec = (up_rtc_getclock() & (RTC_CLOCKS_PER_SEC-1) ) * (1000000000/TICK_PER_SEC);
tp->tv_nsec = (up_rtc_getclock() & (RTC_CLOCKS_PER_SEC-1) ) * (1000000000/RTC_CLOCKS_PER_SEC);
}
else
#endif