/** * @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); }
/** * @brief Transmits data via the I2C bus as master. * @details Number of receiving bytes must be 0 or 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[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 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_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; 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 /* TX DMA setup.*/ dmaStreamSetMode(i2cp->dmatx, i2cp->txdmamode); dmaStreamSetMemory0(i2cp->dmatx, txbuf); dmaStreamSetTransactionSize(i2cp->dmatx, txbytes); /* RX DMA setup, note, rxbytes can be zero but we write the value anyway.*/ dmaStreamSetMode(i2cp->dmarx, i2cp->rxdmamode); dmaStreamSetMemory0(i2cp->dmarx, rxbuf); dmaStreamSetTransactionSize(i2cp->dmarx, rxbytes); #else i2cp->txptr = txbuf; i2cp->txbytes = txbytes; 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); /* Preparing the transfer.*/ i2c_lld_setup_tx_transfer(i2cp); #if STM32_I2C_USE_DMA == TRUE /* Enabling TX DMA.*/ dmaStreamEnable(i2cp->dmatx); /* Transfer complete interrupt enabled.*/ dp->CR1 |= I2C_CR1_TCIE; #else /* Transfer complete and TX interrupts enabled.*/ dp->CR1 |= I2C_CR1_TCIE | I2C_CR1_TXIE; #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; }