bool _test_assert_time_window(systime_t start, systime_t end, const char *msg) { return _test_assert(osalOsIsTimeWithinX(osalOsGetSystemTimeX(), start, end), msg); }
/** * @brief Receives data via the I2C bus as master. * * @param[in] i2cp pointer to the @p I2CDriver object * @param[in] addr slave device address * @param[out] rxbuf pointer to the receive buffer * @param[in] rxbytes number of bytes to be received * @param[in] timeout the number of ticks before the operation timeouts, * the following special values are allowed: * - @a TIME_INFINITE no timeout. * . * @return The operation status. * @retval RDY_OK if the function succeeded. * @retval RDY_RESET if one or more I2C errors occurred, the errors can * be retrieved using @p i2cGetErrors(). * @retval RDY_TIMEOUT if a timeout occurred before operation end. <b>After a * timeout the driver must be stopped and restarted * because the bus is in an uncertain state</b>. * * @notapi */ msg_t i2c_lld_master_receive_timeout(I2CDriver *i2cp, i2caddr_t addr, uint8_t *rxbuf, size_t rxbytes, systime_t timeout) { I2C_TypeDef *dp = i2cp->i2c; systime_t start, end; i2cp->rxbuf = rxbuf; i2cp->rxbytes = rxbytes; /* Resetting error flags for this transfer.*/ i2cp->errors = I2C_NO_ERROR; /* Initializes driver fields, LSB = 1 -> receive.*/ i2cp->addr = (addr << 1) | 0x01; /* Releases the lock from high level driver.*/ osalSysUnlock(); /* Calculating the time window for the timeout on the busy bus condition.*/ start = osalOsGetSystemTimeX(); end = start + OSAL_MS2ST(TIVA_I2C_BUSY_TIMEOUT); /* Waits until BUSY flag is reset or, alternatively, for a timeout condition.*/ while (true) { osalSysLock(); /* If the bus is not busy then the operation can continue, note, the loop is exited in the locked state.*/ if ((dp->MCS & TIVA_MCS_BUSY) == 0) break; /* If the system time went outside the allowed window then a timeout condition is returned.*/ if (!osalOsIsTimeWithinX(osalOsGetSystemTimeX(), start, end)) return MSG_TIMEOUT; osalSysUnlock(); } /* set slave address */ dp->MSA = addr; /* Starts the operation.*/ dp->MCS = TIVA_I2C_SINGLE_RECEIVE; /* Waits for the operation completion or a timeout.*/ return osalThreadSuspendTimeoutS(&i2cp->thread, timeout); }
/** * @brief Receives data via the I2C bus as master. * @details Number of receiving bytes must be more than 1 on STM32F1x. This is * hardware restriction. * * @param[in] i2cp pointer to the @p I2CDriver object * @param[in] addr slave device address * @param[out] rxbuf pointer to the receive buffer * @param[in] rxbytes number of bytes to be received * @param[in] timeout the number of ticks before the operation timeouts, * the following special values are allowed: * - @a TIME_INFINITE no timeout. * . * @return The operation status. * @retval MSG_OK if the function succeeded. * @retval MSG_RESET if one or more I2C errors occurred, the errors can * be retrieved using @p i2cGetErrors(). * @retval MSG_TIMEOUT if a timeout occurred before operation end. <b>After a * timeout the driver must be stopped and restarted * because the bus is in an uncertain state</b>. * * @notapi */ msg_t i2c_lld_master_receive_timeout(I2CDriver *i2cp, i2caddr_t addr, uint8_t *rxbuf, size_t rxbytes, systime_t timeout) { msg_t msg; I2C_TypeDef *dp = i2cp->i2c; systime_t start, end; /* Resetting error flags for this transfer.*/ i2cp->errors = I2C_NO_ERROR; /* Releases the lock from high level driver.*/ osalSysUnlock(); #if STM32_I2C_USE_DMA == TRUE /* RX DMA setup.*/ dmaStreamSetMode(i2cp->dmarx, i2cp->rxdmamode); dmaStreamSetMemory0(i2cp->dmarx, rxbuf); dmaStreamSetTransactionSize(i2cp->dmarx, rxbytes); #else i2cp->rxptr = rxbuf; i2cp->rxbytes = rxbytes; #endif /* Calculating the time window for the timeout on the busy bus condition.*/ start = osalOsGetSystemTimeX(); end = start + OSAL_MS2ST(STM32_I2C_BUSY_TIMEOUT); /* Waits until BUSY flag is reset or, alternatively, for a timeout condition.*/ while (true) { osalSysLock(); /* If the bus is not busy then the operation can continue, note, the loop is exited in the locked state.*/ if ((dp->ISR & I2C_ISR_BUSY) == 0) break; /* If the system time went outside the allowed window then a timeout condition is returned.*/ if (!osalOsIsTimeWithinX(osalOsGetSystemTimeX(), start, end)) { return MSG_TIMEOUT; } osalSysUnlock(); } /* Setting up the slave address.*/ i2c_lld_set_address(i2cp, addr); /* Setting up the peripheral.*/ i2c_lld_setup_rx_transfer(i2cp); #if STM32_I2C_USE_DMA == TRUE /* Enabling RX DMA.*/ dmaStreamEnable(i2cp->dmarx); /* Transfer complete interrupt enabled.*/ dp->CR1 |= I2C_CR1_TCIE; #else /* Transfer complete and RX interrupts enabled.*/ dp->CR1 |= I2C_CR1_TCIE | I2C_CR1_RXIE; #endif /* Starts the operation.*/ dp->CR2 |= I2C_CR2_START; /* Waits for the operation completion or a timeout.*/ msg = osalThreadSuspendTimeoutS(&i2cp->thread, timeout); /* In case of a software timeout a STOP is sent as an extreme attempt to release the bus.*/ if (msg == MSG_TIMEOUT) { dp->CR2 |= I2C_CR2_STOP; } return msg; }
static inline msg_t _i2c_txrx_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; systime_t start, end; uint8_t op = (i2cp->intstate == STATE_SEND) ? 0 : 1; i2cp->errors = I2C_NO_ERROR; i2cp->addr = addr; i2cp->txbuf = txbuf; i2cp->txbytes = txbytes; i2cp->txidx = 0; i2cp->rxbuf = rxbuf; i2cp->rxbytes = rxbytes; i2cp->rxidx = 0; #if defined(KL27Zxxx) || defined(KL27Zxx) /* KL27Z RST workaround */ i2cp->rsta_workaround = RSTA_WORKAROUND_OFF; #endif /* KL27Z RST workaround */ /* clear status flags */ #if defined(I2Cx_FLT_STOPF) /* extra flags on KL26Z and KL27Z */ i2cp->i2c->FLT |= I2Cx_FLT_STOPF; #endif #if defined(I2Cx_FLT_STARTF) /* extra flags on KL27Z */ i2cp->i2c->FLT |= I2Cx_FLT_STARTF; #endif i2cp->i2c->S = I2Cx_S_IICIF|I2Cx_S_ARBL; /* acquire the bus */ /* check to see if we already have the bus */ if(i2cp->i2c->C1 & I2Cx_C1_MST) { #if defined(KL27Zxxx) || defined(KL27Zxx) /* KL27Z RST workaround */ /* need to wait for STARTF interrupt after issuing repeated start, * otherwise the double buffering mechanism sends the last sent byte * instead of the slave address. * https://community.freescale.com/thread/377611 */ i2cp->rsta_workaround = RSTA_WORKAROUND_ON; /* clear any interrupt bits and enable STARTF/STOPF interrupts */ i2cp->i2c->FLT |= I2Cx_FLT_STOPF|I2Cx_FLT_STARTF; i2cp->i2c->S |= I2Cx_S_IICIF|I2Cx_S_ARBL; i2cp->i2c->FLT |= I2Cx_FLT_SSIE; #endif /* KL27Z RST workaround */ /* send repeated start */ i2cp->i2c->C1 |= I2Cx_C1_RSTA | I2Cx_C1_TX; #if defined(KL27Zxxx) || defined(KL27Zxx) /* KL27Z RST workaround */ /* wait for the STARTF interrupt */ msg = osalThreadSuspendTimeoutS(&i2cp->thread, timeout); /* abort if this didn't go well (timed out) */ if (msg != MSG_OK) { /* release bus - RX mode, send STOP */ i2cp->i2c->C1 &= ~(I2Cx_C1_TX | I2Cx_C1_MST); return msg; } #endif /* KL27Z RST workaround */ } else { /* unlock during the wait, so that tasks with * higher priority can get attention */ osalSysUnlock(); /* wait until the bus is released */ /* Calculating the time window for the timeout on the busy bus condition.*/ start = osalOsGetSystemTimeX(); end = start + OSAL_MS2ST(KINETIS_I2C_BUSY_TIMEOUT); while(true) { osalSysLock(); /* If the bus is not busy then the operation can continue, note, the loop is exited in the locked state.*/ if(!(i2cp->i2c->S & I2Cx_S_BUSY)) break; /* If the system time went outside the allowed window then a timeout condition is returned.*/ if (!osalOsIsTimeWithinX(osalOsGetSystemTimeX(), start, end)) { return MSG_TIMEOUT; } osalSysUnlock(); } /* send START */ i2cp->i2c->C1 |= I2Cx_C1_MST|I2Cx_C1_TX; } /* send slave address */ i2cp->i2c->D = addr << 1 | op; /* wait for the ISR to signal that the transmission (or receive if no transmission) phase is complete */ msg = osalThreadSuspendTimeoutS(&i2cp->thread, timeout); /* FIXME */ //if (i2cp->i2c->S & I2Cx_S_RXAK) // i2cp->errors |= I2C_ACK_FAILURE; /* the transmitting (or receiving if no transmission) phase has finished, * do we expect to receive something? */ if (msg == MSG_OK && rxbuf != NULL && rxbytes > 0 && i2cp->rxidx < rxbytes) { #if defined(KL27Zxxx) || defined(KL27Zxx) /* KL27Z RST workaround */ /* the same KL27Z RST workaround as above */ i2cp->rsta_workaround = RSTA_WORKAROUND_ON; /* clear any interrupt bits and enable STARTF/STOPF interrupts */ i2cp->i2c->FLT |= I2Cx_FLT_STOPF|I2Cx_FLT_STARTF; i2cp->i2c->S |= I2Cx_S_IICIF|I2Cx_S_ARBL; i2cp->i2c->FLT |= I2Cx_FLT_SSIE; #endif /* KL27Z RST workaround */ /* send repeated start */ i2cp->i2c->C1 |= I2Cx_C1_RSTA; #if defined(KL27Zxxx) || defined(KL27Zxx) /* KL27Z RST workaround */ /* wait for the STARTF interrupt */ msg = osalThreadSuspendTimeoutS(&i2cp->thread, timeout); /* abort if this didn't go well (timed out) */ if (msg != MSG_OK) { /* release bus - RX mode, send STOP */ i2cp->i2c->C1 &= ~(I2Cx_C1_TX | I2Cx_C1_MST); return msg; } #endif /* KL27Z RST workaround */ /* FIXME */ // while (!(i2cp->i2c->S & I2Cx_S_BUSY)); i2cp->intstate = STATE_RECV; i2cp->i2c->D = i2cp->addr << 1 | 1; msg = osalThreadSuspendTimeoutS(&i2cp->thread, timeout); } /* release bus - RX mode, send STOP */ // other kinetis I2C drivers wait here for 1us. is this needed? i2cp->i2c->C1 &= ~(I2Cx_C1_TX | I2Cx_C1_MST); /* FIXME */ // while (i2cp->i2c->S & I2Cx_S_BUSY); return msg; }
/** * @brief Transmits data via the I2C bus as master. * * @param[in] i2cp pointer to the @p I2CDriver object * @param[in] addr slave device address * @param[in] txbuf pointer to the transmit buffer * @param[in] txbytes number of bytes to be transmitted * @param[out] rxbuf pointer to the receive buffer * @param[in] rxbytes number of bytes to be received * @param[in] timeout the number of ticks before the operation timeouts, * the following special values are allowed: * - @a TIME_INFINITE no timeout. * . * @return The operation status. * @retval RDY_OK if the function succeeded. * @retval RDY_RESET if one or more I2C errors occurred, the errors can * be retrieved using @p i2cGetErrors(). * @retval RDY_TIMEOUT if a timeout occurred before operation end. <b>After a * timeout the driver must be stopped and restarted * because the bus is in an uncertain state</b>. * * @notapi */ 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) { I2C_TypeDef *dp = i2cp->i2c; systime_t start, end; i2cp->rxbuf = rxbuf; i2cp->rxbytes = rxbytes; i2cp->txbuf = txbuf; i2cp->txbytes = txbytes; /* Resetting error flags for this transfer.*/ i2cp->errors = I2C_NO_ERROR; /* Releases the lock from high level driver.*/ osalSysUnlock(); /* Calculating the time window for the timeout on the busy bus condition.*/ start = osalOsGetSystemTimeX(); end = start + OSAL_MS2ST(TIVA_I2C_BUSY_TIMEOUT); /* Waits until BUSY flag is reset or, alternatively, for a timeout condition.*/ while (true) { osalSysLock(); /* If the bus is not busy then the operation can continue, note, the loop is exited in the locked state.*/ if ((dp->MCS & TIVA_MCS_BUSY) == 0) break; /* If the system time went outside the allowed window then a timeout condition is returned.*/ if (!osalOsIsTimeWithinX(osalOsGetSystemTimeX(), start, end)) return MSG_TIMEOUT; osalSysUnlock(); } /* Initializes driver fields, LSB = 0 -> write.*/ i2cp->addr = addr << 1 | 0; /* set slave address */ dp->MSA = i2cp->addr; /* enable interrupts */ dp->MIMR = TIVA_MIMR_IM; /* put data in register */ dp->MDR = *(i2cp->txbuf); /* check if 1 or more bytes */ if (i2cp->txbytes == 1) { if (i2cp->rxbytes == 1) { // one byte read i2cp->intstate = STATE_READ_ONE; } else { // multiple byte read i2cp->intstate = STATE_READ_FIRST; } // single byte send dp->MCS = TIVA_I2C_SIGNLE_SEND; } else { if (i2cp->txbytes == 2) { // 2 bytes i2cp->intstate = STATE_WRITE_FINAL; } else { // more then 2 bytes i2cp->intstate = STATE_WRITE_NEXT; } // multiple bytes start send dp->MCS = TIVA_I2C_BURST_SEND_START; } i2cp->txbuf++; i2cp->txbytes--; /* Waits for the operation completion or a timeout.*/ return osalThreadSuspendTimeoutS(&i2cp->thread, timeout); }