From 4765efbaa9d939601a65b63a7f43cbe44e947aec Mon Sep 17 00:00:00 2001 From: Giovanni Di Sirio Date: Tue, 17 Jan 2017 13:20:48 +0000 Subject: I2C fallback driver finished, to be tested. git-svn-id: svn://svn.code.sf.net/p/chibios/svn/trunk@10059 35acf78f-673a-0410-8e92-d51de3d6d3f4 --- os/hal/lib/fallback/I2C/hal_i2c_lld.c | 252 +++++++++++++++++++++++++--------- os/hal/lib/fallback/I2C/hal_i2c_lld.h | 4 + 2 files changed, 193 insertions(+), 63 deletions(-) (limited to 'os/hal') diff --git a/os/hal/lib/fallback/I2C/hal_i2c_lld.c b/os/hal/lib/fallback/I2C/hal_i2c_lld.c index 82b17663a..77b9e2e29 100644 --- a/os/hal/lib/fallback/I2C/hal_i2c_lld.c +++ b/os/hal/lib/fallback/I2C/hal_i2c_lld.c @@ -71,48 +71,110 @@ I2CDriver I2CD4; /* Driver local functions. */ /*===========================================================================*/ -static msg_t i2c_delay(I2CDriver *i2cp) { +static msg_t i2c_write_stop(I2CDriver *i2cp); - if (!osalOsIsTimeWithinX(osalOsGetSystemTimeX(), i2cp->start, i2cp->end)) { - return MSG_TIMEOUT; - } +static inline void i2c_delay(I2CDriver *i2cp) { #if SW_I2C_USE_OSAL_DELAY || defined(__DOXYGEN__) osalThreadSleep(i2cp->config->ticks); #else i2cp->config->delay(); #endif +} + +static inline msg_t i2c_check_arbitration(I2CDriver *i2cp) { + + if (palReadLine(i2cp->config->sda) == PAL_LOW) { + i2cp->errors |= I2C_ARBITRATION_LOST; + return MSG_RESET; + } + + return MSG_OK; +} + +static inline msg_t i2c_check_timeout(I2CDriver *i2cp) { + + if (!osalOsIsTimeWithinX(osalOsGetSystemTimeX(), i2cp->start, i2cp->end)) { + i2c_write_stop(i2cp); + return MSG_TIMEOUT; + } + + return MSG_OK; +} + +static msg_t i2c_wait_clock(I2CDriver *i2cp) { + + while (palReadLine(i2cp->config->scl) == PAL_LOW) { + if (!osalOsIsTimeWithinX(osalOsGetSystemTimeX(), i2cp->start, i2cp->end)) { + return MSG_TIMEOUT; + } + i2c_delay(i2cp); + } + return MSG_OK; } -static msg_t i2c_write_start(I2CDriver *i2cp) { +static inline msg_t i2c_write_start(I2CDriver *i2cp) { + + /* Arbitration check.*/ + CHECK_ERROR(i2c_check_arbitration(i2cp)); + palClearLine(i2cp->config->sda); + i2c_delay(i2cp); + palClearLine(i2cp->config->scl); + + return MSG_OK; } static msg_t i2c_write_restart(I2CDriver *i2cp) { + palSetLine(i2cp->config->sda); + i2c_delay(i2cp); + palSetLine(i2cp->config->scl); + + /* Clock stretching.*/ + CHECK_ERROR(i2c_wait_clock(i2cp)); + + i2c_delay(i2cp); + i2c_write_start(i2cp); + + return MSG_OK; } static msg_t i2c_write_stop(I2CDriver *i2cp) { + palClearLine(i2cp->config->sda); + i2c_delay(i2cp); + palSetLine(i2cp->config->scl); + + /* Clock stretching.*/ + CHECK_ERROR(i2c_wait_clock(i2cp)); + + i2c_delay(i2cp); + palSetLine(i2cp->config->sda); + i2c_delay(i2cp); + + /* Arbitration check.*/ + CHECK_ERROR(i2c_check_arbitration(i2cp)); + + i2c_delay(i2cp); + + return MSG_OK; } -static msg_t i2c_writebit(I2CDriver *i2cp, unsigned bit) { +static msg_t i2c_write_bit(I2CDriver *i2cp, unsigned bit) { palWriteLine(i2cp->config->sda, bit); - CHECK_ERROR(i2c_delay(i2cp)); + i2c_delay(i2cp); palSetLine(i2cp->config->scl); - CHECK_ERROR(i2c_delay(i2cp)); + i2c_delay(i2cp); /* Clock stretching.*/ - while (palReadLine(i2cp->config->scl) == PAL_LOW) { - CHECK_ERROR(i2c_delay(i2cp)); - } + CHECK_ERROR(i2c_wait_clock(i2cp)); /* Arbitration check.*/ - if ((bit == PAL_HIGH) && (palReadLine(i2cp->config->sda) == PAL_LOW)) { - i2cp->errors |= I2C_ARBITRATION_LOST; - return MSG_RESET; + if (bit == PAL_HIGH) { + CHECK_ERROR(i2c_check_arbitration(i2cp)); } palClearLine(i2cp->config->scl); @@ -120,51 +182,95 @@ static msg_t i2c_writebit(I2CDriver *i2cp, unsigned bit) { return MSG_OK; } -static msg_t i2c_readbit(I2CDriver *i2cp) { +static msg_t i2c_read_bit(I2CDriver *i2cp) { msg_t bit; palSetLine(i2cp->config->sda); - CHECK_ERROR(i2c_delay(i2cp)); + i2c_delay(i2cp); palSetLine(i2cp->config->scl); /* Clock stretching.*/ - while (palReadLine(i2cp->config->scl) == PAL_LOW) { - CHECK_ERROR(i2c_delay(i2cp)); - } + CHECK_ERROR(i2c_wait_clock(i2cp)); - CHECK_ERROR(i2c_delay(i2cp)); + i2c_delay(i2cp); bit = palReadLine(i2cp->config->sda); palClearLine(i2cp->config->scl); return bit; } -static msg_t i2c_writebyte(I2CDriver *i2cp, uint8_t byte) { +static msg_t i2c_write_byte(I2CDriver *i2cp, uint8_t byte) { + msg_t msg; uint8_t mask; + CHECK_ERROR(i2c_check_timeout(i2cp)); + for (mask = 0x80U; mask > 0U; mask >>= 1U) { - CHECK_ERROR(i2c_writebit(i2cp, (byte & mask) != 0)); + CHECK_ERROR(i2c_write_bit(i2cp, (byte & mask) != 0)); } - return i2c_readbit(i2cp); + msg = i2c_read_bit(i2cp); + CHECK_ERROR(msg); + + /* Checking for NACK.*/ + if (msg == PAL_HIGH) { + i2cp->errors |= I2C_ACK_FAILURE; + return MSG_RESET; + } + + return MSG_OK; } -static msg_t i2c_readbyte(I2CDriver *i2cp, unsigned nack) { +static msg_t i2c_read_byte(I2CDriver *i2cp, unsigned nack) { msg_t byte; unsigned i; + CHECK_ERROR(i2c_check_timeout(i2cp)); + byte = 0U; for (i = 0; i < 8; i++) { - msg_t msg = i2c_readbit(i2cp); + msg_t msg = i2c_read_bit(i2cp); CHECK_ERROR(msg); byte = (byte << 1U) | msg; } - CHECK_ERROR(i2c_writebit(i2cp, PAL_LOW)); + CHECK_ERROR(i2c_write_bit(i2cp, nack)); return byte; } +static msg_t i2c_write_header(I2CDriver *i2cp, i2caddr_t addr, bool rw) { + + CHECK_ERROR(i2c_write_start(i2cp)); + + /* Check for 10 bits addressing.*/ + if (i2cp->config->addr10) { + /* It is 10 bits.*/ + uint8_t b1, b2; + + b1 = 0xF0U | ((addr >> 8U) << 1U); + b2 = (uint8_t)(addr & 255U); + if (rw) { + b1 |= 1U; + } + CHECK_ERROR(i2c_write_byte(i2cp, b1)); + CHECK_ERROR(i2c_write_byte(i2cp, b2)); + } + else { + /* It is 7 bits.*/ + if (rw) { + /* Read.*/ + CHECK_ERROR(i2c_write_byte(i2cp, (addr << 1U) | 1U)); + } + else { + /* Write.*/ + CHECK_ERROR(i2c_write_byte(i2cp, (addr << 1U) | 0U)); + } + } + + return MSG_OK; +} + /*===========================================================================*/ /* Driver interrupt handlers. */ /*===========================================================================*/ @@ -182,7 +288,16 @@ void i2c_lld_init(void) { #if SW_I2C_USE_I2C1 i2cObjectInit(&I2CD1); -#endif /* SW_I2C_USE_I2C1 */ +#endif +#if SW_I2C_USE_I2C2 + i2cObjectInit(&I2CD2); +#endif +#if SW_I2C_USE_I2C3 + i2cObjectInit(&I2CD3); +#endif +#if SW_I2C_USE_I2C4 + i2cObjectInit(&I2CD4); +#endif } /** @@ -194,14 +309,8 @@ void i2c_lld_init(void) { */ void i2c_lld_start(I2CDriver *i2cp) { - /* If in stopped state then enables the I2C and DMA clocks.*/ - if (i2cp->state == I2C_STOP) { - -#if SW_I2C_USE_I2C1 - if (&I2CD1 == i2cp) { - } -#endif /* SW_I2C_USE_I2C1 */ - } + /* Does nothing.*/ + (void)i2cp; } /** @@ -213,17 +322,8 @@ void i2c_lld_start(I2CDriver *i2cp) { */ void i2c_lld_stop(I2CDriver *i2cp) { - /* If not in stopped state then disables the I2C clock.*/ - if (i2cp->state != I2C_STOP) { - - /* I2C disable.*/ - //i2c_lld_abort_operation(i2cp); - -#if SW_I2C_USE_I2C1 - if (&I2CD1 == i2cp) { - } -#endif - } + /* Does nothing.*/ + (void)i2cp; } /** @@ -250,15 +350,25 @@ void i2c_lld_stop(I2CDriver *i2cp) { msg_t i2c_lld_master_receive_timeout(I2CDriver *i2cp, i2caddr_t addr, uint8_t *rxbuf, size_t rxbytes, systime_t timeout) { - msg_t msg = MSG_OK; - (void)i2cp; - (void)addr; - (void)rxbuf; - (void)rxbytes; - (void)timeout; + /* Setting timeout fields.*/ + i2cp->start = osalOsGetSystemTimeX(); + i2cp->end = i2cp->start; + if (timeout != TIME_INFINITE) { + i2cp->end += timeout; + } + + /* Sending anddress and mode.*/ + CHECK_ERROR(i2c_write_header(i2cp, addr, true)); + + do { + /* Last byte sends a NACK.*/ + msg_t msg = i2c_read_byte(i2cp, rxbytes > 1U ? 0U : 1U); + CHECK_ERROR(msg); + *rxbuf++ = (uint8_t)msg; + } while (--rxbytes); - return msg; + return i2c_write_stop(i2cp); } /** @@ -290,17 +400,33 @@ msg_t i2c_lld_master_transmit_timeout(I2CDriver *i2cp, i2caddr_t addr, const uint8_t *txbuf, size_t txbytes, uint8_t *rxbuf, size_t rxbytes, systime_t timeout) { - msg_t msg = MSG_OK; - (void)i2cp; - (void)addr; - (void)txbuf; - (void)txbytes; - (void)rxbuf; - (void)rxbytes; - (void)timeout; - - return msg; + /* Setting timeout fields.*/ + i2cp->start = osalOsGetSystemTimeX(); + i2cp->end = i2cp->start; + if (timeout != TIME_INFINITE) { + i2cp->end += timeout; + } + + /* Sending anddress and mode.*/ + CHECK_ERROR(i2c_write_header(i2cp, addr, false)); + + do { + CHECK_ERROR(i2c_write_byte(i2cp, *txbuf++)); + } while (--txbytes); + + /* Is there a read phase? */ + if (rxbytes > 0U) { + CHECK_ERROR(i2c_write_restart(i2cp)); + do { + /* Last byte sends a NACK.*/ + msg_t msg = i2c_read_byte(i2cp, rxbytes > 1U ? 0U : 1U); + CHECK_ERROR(msg); + *rxbuf++ = (uint8_t)msg; + } while (--rxbytes); + } + + return i2c_write_stop(i2cp); } #endif /* HAL_USE_I2C */ diff --git a/os/hal/lib/fallback/I2C/hal_i2c_lld.h b/os/hal/lib/fallback/I2C/hal_i2c_lld.h index c6c4908a4..5b63bc6cd 100644 --- a/os/hal/lib/fallback/I2C/hal_i2c_lld.h +++ b/os/hal/lib/fallback/I2C/hal_i2c_lld.h @@ -113,6 +113,10 @@ typedef void (*i2c_delay_t)(void); * @brief Type of I2C driver configuration structure. */ typedef struct { + /** + * @brief 10 bits addressing switch. + */ + bool addr10; /** * @brief I2C clock line. */ -- cgit v1.2.3