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);
}
Exemple #3
0
/**
 * @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;
}
Exemple #4
0
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);
}