blob: 9671b8b3e9eaec02bb002f26bcdc27c0900366d5 [file] [log] [blame]
/**
* <h2><center>&copy; COPYRIGHT(c) 2016 STMicroelectronics</center></h2>
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of STMicroelectronics nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
******************************************************************************
*/
#include <os/os_time.h>
#include <mcu/stm32_hal.h>
#include <syscfg/syscfg.h>
#if !MYNEWT_VAL(STM32_HAL_I2C_HAS_CLOCKSPEED)
#define I2C_TIMEOUT_BUSY (25U) /*!< 25 ms */
#define MAX_NBYTE_SIZE 255U
static const uint8_t HAL_I2C_MODE_MASTER_SEL = 0x11;
static const uint8_t HAL_NACK = HAL_TIMEOUT + 1;
/**
* @brief This function handles I2C Communication Timeout.
* @param hi2c Pointer to a I2C_HandleTypeDef structure that contains
* the configuration information for I2C module
* @param Flag specifies the I2C flag to check.
* @param Status The new Flag status (SET or RESET).
* @param Timeout Timeout duration
* @param Tickstart Tick start value
* @retval HAL status
*/
/*
* FIXME: this function is similar enough that it could be shared between
* both i2c drivers.
*/
static HAL_StatusTypeDef I2C_WaitOnFlagUntilTimeout(I2C_HandleTypeDef *hi2c,
uint32_t Flag, FlagStatus Status, uint32_t Timeout, uint32_t Tickstart)
{
while (__HAL_I2C_GET_FLAG(hi2c, Flag) == Status)
{
/* Check for the Timeout */
if (Timeout != HAL_MAX_DELAY)
{
if ((Timeout == 0U) || ((HAL_GetTick() - Tickstart) > Timeout))
{
hi2c->State = HAL_I2C_STATE_READY;
hi2c->Mode = HAL_I2C_MODE_NONE;
/* Process Unlocked */
__HAL_UNLOCK(hi2c);
return HAL_TIMEOUT;
}
}
}
return HAL_OK;
}
/**
* @brief Handles I2Cx communication when starting transfer or during transfer (TC or TCR flag are set).
* @param hi2c I2C handle.
* @param DevAddress Specifies the slave address to be programmed.
* @param Size Specifies the number of bytes to be programmed.
* This parameter must be a value between 0 and 255.
* @param Mode New state of the I2C START condition generation.
* This parameter can be one of the following values:
* @arg @ref I2C_RELOAD_MODE Enable Reload mode .
* @arg @ref I2C_AUTOEND_MODE Enable Automatic end mode.
* @arg @ref I2C_SOFTEND_MODE Enable Software end mode.
* @param Request New state of the I2C START condition generation.
* This parameter can be one of the following values:
* @arg @ref I2C_NO_STARTSTOP Don't Generate stop and start condition.
* @arg @ref I2C_GENERATE_STOP Generate stop condition (Size should be set to 0).
* @arg @ref I2C_GENERATE_START_READ Generate Restart for read request.
* @arg @ref I2C_GENERATE_START_WRITE Generate Restart for write request.
* @retval None
*/
static void I2C_TransferConfig(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t Size, uint32_t Mode, uint32_t Request)
{
uint32_t tmpreg = 0U;
/* Check the parameters */
assert_param(IS_I2C_ALL_INSTANCE(hi2c->Instance));
assert_param(IS_TRANSFER_MODE(Mode));
assert_param(IS_TRANSFER_REQUEST(Request));
/* Get the CR2 register value */
tmpreg = hi2c->Instance->CR2;
/* clear tmpreg specific bits */
tmpreg &= (uint32_t)~((uint32_t)(I2C_CR2_SADD | I2C_CR2_NBYTES | I2C_CR2_RELOAD | I2C_CR2_AUTOEND | I2C_CR2_RD_WRN | I2C_CR2_START | I2C_CR2_STOP));
/* update tmpreg */
tmpreg |= (uint32_t)(((uint32_t)DevAddress & I2C_CR2_SADD) | (((uint32_t)Size << 16) & I2C_CR2_NBYTES) | \
(uint32_t)Mode | (uint32_t)Request);
/* update CR2 register */
hi2c->Instance->CR2 = tmpreg;
}
/**
* @brief I2C Tx data register flush process.
* @param hi2c I2C handle.
* @retval None
*/
static void I2C_Flush_TXDR(I2C_HandleTypeDef *hi2c)
{
/* If a pending TXIS flag is set */
/* Write a dummy data in TXDR to clear it */
if (__HAL_I2C_GET_FLAG(hi2c, I2C_FLAG_TXIS) != RESET)
{
hi2c->Instance->TXDR = 0x00U;
}
/* Flush TX register if not empty */
if (__HAL_I2C_GET_FLAG(hi2c, I2C_FLAG_TXE) == RESET)
{
__HAL_I2C_CLEAR_FLAG(hi2c, I2C_FLAG_TXE);
}
}
/**
* @brief This function handles Acknowledge failed detection during an I2C Communication.
* @param hi2c Pointer to a I2C_HandleTypeDef structure that contains
* the configuration information for the specified I2C.
* @param Timeout Timeout duration
* @param Tickstart Tick start value
* @retval HAL status
*/
static HAL_StatusTypeDef I2C_IsAcknowledgeFailed(I2C_HandleTypeDef *hi2c, uint32_t Timeout, uint32_t Tickstart)
{
if (__HAL_I2C_GET_FLAG(hi2c, I2C_FLAG_AF) == SET)
{
/* Wait until STOP Flag is reset */
/* AutoEnd should be initiate after AF */
while (__HAL_I2C_GET_FLAG(hi2c, I2C_FLAG_STOPF) == RESET)
{
/* Check for the Timeout */
if (Timeout != HAL_MAX_DELAY)
{
if ((Timeout == 0U) || ((HAL_GetTick() - Tickstart) > Timeout))
{
hi2c->State = HAL_I2C_STATE_READY;
hi2c->Mode = HAL_I2C_MODE_NONE;
/* Process Unlocked */
__HAL_UNLOCK(hi2c);
return HAL_TIMEOUT;
}
}
}
/* Clear NACKF Flag */
__HAL_I2C_CLEAR_FLAG(hi2c, I2C_FLAG_AF);
/* Clear STOP Flag */
__HAL_I2C_CLEAR_FLAG(hi2c, I2C_FLAG_STOPF);
/* Flush TX register */
I2C_Flush_TXDR(hi2c);
/* Clear Configuration Register 2 */
I2C_RESET_CR2(hi2c);
hi2c->ErrorCode = HAL_I2C_ERROR_AF;
hi2c->State = HAL_I2C_STATE_READY;
hi2c->Mode = HAL_I2C_MODE_NONE;
/* Process Unlocked */
__HAL_UNLOCK(hi2c);
return HAL_ERROR;
}
return HAL_OK;
}
/**
* @brief This function handles I2C Communication Timeout for specific usage of TXIS flag.
* @param hi2c Pointer to a I2C_HandleTypeDef structure that contains
* the configuration information for the specified I2C.
* @param Timeout Timeout duration
* @param Tickstart Tick start value
* @retval HAL status
*/
static HAL_StatusTypeDef I2C_WaitOnTXISFlagUntilTimeout(I2C_HandleTypeDef *hi2c, uint32_t Timeout, uint32_t Tickstart)
{
while (__HAL_I2C_GET_FLAG(hi2c, I2C_FLAG_TXIS) == RESET)
{
/* Check if a NACK is detected */
if (I2C_IsAcknowledgeFailed(hi2c, Timeout, Tickstart) != HAL_OK)
{
return HAL_ERROR;
}
/* Check for the Timeout */
if (Timeout != HAL_MAX_DELAY)
{
if ((Timeout == 0U) || ((HAL_GetTick() - Tickstart) > Timeout))
{
hi2c->ErrorCode |= HAL_I2C_ERROR_TIMEOUT;
hi2c->State = HAL_I2C_STATE_READY;
hi2c->Mode = HAL_I2C_MODE_NONE;
/* Process Unlocked */
__HAL_UNLOCK(hi2c);
return HAL_TIMEOUT;
}
}
}
return HAL_OK;
}
/**
* @brief This function handles I2C Communication Timeout for specific usage of STOP flag.
* @param hi2c Pointer to a I2C_HandleTypeDef structure that contains
* the configuration information for the specified I2C.
* @param Timeout Timeout duration
* @param Tickstart Tick start value
* @retval HAL status
*/
static HAL_StatusTypeDef I2C_WaitOnSTOPFlagUntilTimeout(I2C_HandleTypeDef *hi2c, uint32_t Timeout, uint32_t Tickstart)
{
while (__HAL_I2C_GET_FLAG(hi2c, I2C_FLAG_STOPF) == RESET)
{
/* Check if a NACK is detected */
if (I2C_IsAcknowledgeFailed(hi2c, Timeout, Tickstart) != HAL_OK)
{
return HAL_ERROR;
}
/* Check for the Timeout */
if ((Timeout == 0U) || ((HAL_GetTick() - Tickstart) > Timeout))
{
hi2c->ErrorCode |= HAL_I2C_ERROR_TIMEOUT;
hi2c->State = HAL_I2C_STATE_READY;
hi2c->Mode = HAL_I2C_MODE_NONE;
/* Process Unlocked */
__HAL_UNLOCK(hi2c);
return HAL_TIMEOUT;
}
}
return HAL_OK;
}
/**
* @brief Transmits in master mode an amount of data in blocking mode.
* @param hi2c Pointer to a I2C_HandleTypeDef structure that contains
* the configuration information for the specified I2C.
* @param DevAddress Target device address The device 7 bits address value
* in datasheet must be shifted to the left before calling the interface
* @param pData Pointer to data buffer
* @param Size Amount of data to be sent
* @param Timeout Timeout duration
* @param LastOp If set sends STOP, otherwise no STOP
* @retval HAL status
*/
HAL_StatusTypeDef HAL_I2C_Master_Transmit_Custom(I2C_HandleTypeDef *hi2c,
uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout,
uint8_t LastOp)
{
uint32_t tickstart = 0U;
uint8_t prev_mode = 0;
if (hi2c->State == HAL_I2C_STATE_READY)
{
/* Process Locked */
__HAL_LOCK(hi2c);
/* Init tickstart for timeout management*/
tickstart = HAL_GetTick();
prev_mode = hi2c->Mode;
if (prev_mode != HAL_I2C_MODE_MASTER_SEL)
{
if (I2C_WaitOnFlagUntilTimeout(hi2c, I2C_FLAG_BUSY, SET, I2C_TIMEOUT_BUSY, tickstart) != HAL_OK)
{
return HAL_TIMEOUT;
}
}
hi2c->State = HAL_I2C_STATE_BUSY_TX;
hi2c->Mode = HAL_I2C_MODE_MASTER;
hi2c->ErrorCode = HAL_I2C_ERROR_NONE;
/* Prepare transfer parameters */
hi2c->pBuffPtr = pData;
hi2c->XferCount = Size;
hi2c->XferISR = NULL;
/* Send Slave Address */
/* Set NBYTES to write and reload if hi2c->XferCount > MAX_NBYTE_SIZE and generate RESTART */
if (hi2c->XferCount > MAX_NBYTE_SIZE)
{
hi2c->XferSize = MAX_NBYTE_SIZE;
I2C_TransferConfig(hi2c, DevAddress, hi2c->XferSize, I2C_RELOAD_MODE, I2C_GENERATE_START_WRITE);
}
else if (!LastOp)
{
hi2c->XferSize = hi2c->XferCount;
I2C_TransferConfig(hi2c, DevAddress, hi2c->XferSize, I2C_SOFTEND_MODE, I2C_GENERATE_START_WRITE);
}
else
{
hi2c->XferSize = hi2c->XferCount;
I2C_TransferConfig(hi2c, DevAddress, hi2c->XferSize, I2C_AUTOEND_MODE, I2C_GENERATE_START_WRITE);
}
/*
* Wait RESTART to be accepted
*/
if (prev_mode == HAL_I2C_MODE_MASTER_SEL)
{
if (I2C_WaitOnFlagUntilTimeout(hi2c, I2C_FLAG_TC, RESET, Timeout, tickstart) != HAL_OK)
{
return HAL_TIMEOUT;
}
}
while (hi2c->XferCount > 0U)
{
/* Wait until TXIS flag is set */
if (I2C_WaitOnTXISFlagUntilTimeout(hi2c, Timeout, tickstart) != HAL_OK)
{
if (hi2c->ErrorCode == HAL_I2C_ERROR_AF)
{
return HAL_ERROR;
}
else
{
return HAL_TIMEOUT;
}
}
/* Write data to TXDR */
hi2c->Instance->TXDR = (*hi2c->pBuffPtr++);
hi2c->XferCount--;
hi2c->XferSize--;
if ((hi2c->XferSize == 0U) && (hi2c->XferCount != 0U))
{
/* Wait until TCR flag is set */
if (I2C_WaitOnFlagUntilTimeout(hi2c, I2C_FLAG_TCR, RESET, Timeout, tickstart) != HAL_OK)
{
return HAL_TIMEOUT;
}
if (hi2c->XferCount > MAX_NBYTE_SIZE)
{
hi2c->XferSize = MAX_NBYTE_SIZE;
I2C_TransferConfig(hi2c, DevAddress, hi2c->XferSize, I2C_RELOAD_MODE, I2C_NO_STARTSTOP);
}
else if (!LastOp)
{
hi2c->XferSize = hi2c->XferCount;
I2C_TransferConfig(hi2c, DevAddress, hi2c->XferSize, I2C_SOFTEND_MODE, I2C_NO_STARTSTOP);
}
else
{
hi2c->XferSize = hi2c->XferCount;
I2C_TransferConfig(hi2c, DevAddress, hi2c->XferSize, I2C_AUTOEND_MODE, I2C_NO_STARTSTOP);
}
}
}
if (LastOp)
{
/* No need to Check TC flag, with AUTOEND mode the stop is automatically generated */
/* Wait until STOPF flag is set */
if (I2C_WaitOnSTOPFlagUntilTimeout(hi2c, Timeout, tickstart) != HAL_OK)
{
if (hi2c->ErrorCode == HAL_I2C_ERROR_AF)
{
return HAL_ERROR;
}
else
{
return HAL_TIMEOUT;
}
}
/* Clear STOP Flag */
__HAL_I2C_CLEAR_FLAG(hi2c, I2C_FLAG_STOPF);
}
else
{
/* No autoend/reload was requested, make sure transmission of last byte
* has finished... */
if (I2C_WaitOnFlagUntilTimeout(hi2c, I2C_FLAG_TC, SET, Timeout, tickstart) != HAL_OK)
{
return HAL_TIMEOUT;
}
}
/* Clear Configuration Register 2 */
I2C_RESET_CR2(hi2c);
hi2c->State = HAL_I2C_STATE_READY;
if (LastOp)
{
hi2c->Mode = HAL_I2C_MODE_NONE;
}
else
{
hi2c->Mode = HAL_I2C_MODE_MASTER_SEL;
}
/* Process Unlocked */
__HAL_UNLOCK(hi2c);
return HAL_OK;
}
else
{
return HAL_BUSY;
}
}
/**
* @brief This function handles I2C Communication Timeout for specific usage of RXNE flag.
* @param hi2c Pointer to a I2C_HandleTypeDef structure that contains
* the configuration information for the specified I2C.
* @param Timeout Timeout duration
* @param Tickstart Tick start value
* @retval HAL status
*/
static HAL_StatusTypeDef I2C_WaitOnRXNEFlagUntilTimeout(I2C_HandleTypeDef *hi2c, uint32_t Timeout, uint32_t Tickstart, uint8_t prev_mode)
{
while (__HAL_I2C_GET_FLAG(hi2c, I2C_FLAG_RXNE) == RESET)
{
/* Check if a NACK is detected */
if (I2C_IsAcknowledgeFailed(hi2c, Timeout, Tickstart) != HAL_OK)
{
return HAL_NACK;
}
/*
* FIXME: need to re-check if what is supposed to be a RESTART is not also
* triggering a STOP along...
*/
/* Check if a STOPF is detected */
if (prev_mode != HAL_I2C_MODE_MASTER_SEL && __HAL_I2C_GET_FLAG(hi2c, I2C_FLAG_STOPF) == SET)
{
/* Clear STOP Flag */
__HAL_I2C_CLEAR_FLAG(hi2c, I2C_FLAG_STOPF);
/* Clear Configuration Register 2 */
I2C_RESET_CR2(hi2c);
hi2c->ErrorCode = HAL_I2C_ERROR_NONE;
hi2c->State = HAL_I2C_STATE_READY;
hi2c->Mode = HAL_I2C_MODE_NONE;
/* Process Unlocked */
__HAL_UNLOCK(hi2c);
return HAL_ERROR;
}
/* Check for the Timeout */
if ((Timeout == 0U) || ((HAL_GetTick() - Tickstart) > Timeout))
{
hi2c->ErrorCode |= HAL_I2C_ERROR_TIMEOUT;
hi2c->State = HAL_I2C_STATE_READY;
/* Process Unlocked */
__HAL_UNLOCK(hi2c);
return HAL_TIMEOUT;
}
}
return HAL_OK;
}
/**
* @brief Receives in master mode an amount of data in blocking mode.
* @param hi2c Pointer to a I2C_HandleTypeDef structure that contains
* the configuration information for the specified I2C.
* @param DevAddress Target device address The device 7 bits address value
* in datasheet must be shifted to the left before calling the interface
* @param pData Pointer to data buffer
* @param Size Amount of data to be sent
* @param Timeout Timeout duration
* @param LastOp If set sends STOP, otherwise no STOP
* @retval HAL status
*/
HAL_StatusTypeDef HAL_I2C_Master_Receive_Custom(I2C_HandleTypeDef *hi2c,
uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout,
uint8_t LastOp)
{
uint32_t tickstart = 0U;
uint8_t prev_mode = 0;
HAL_StatusTypeDef rc;
if (hi2c->State == HAL_I2C_STATE_READY)
{
/* Process Locked */
__HAL_LOCK(hi2c);
/* Init tickstart for timeout management */
tickstart = HAL_GetTick();
prev_mode = hi2c->Mode;
if (prev_mode != HAL_I2C_MODE_MASTER_SEL)
{
if (I2C_WaitOnFlagUntilTimeout(hi2c, I2C_FLAG_BUSY, SET, I2C_TIMEOUT_BUSY, tickstart) != HAL_OK)
{
return HAL_TIMEOUT;
}
}
hi2c->State = HAL_I2C_STATE_BUSY_RX;
hi2c->Mode = HAL_I2C_MODE_MASTER;
hi2c->ErrorCode = HAL_I2C_ERROR_NONE;
/* Prepare transfer parameters */
hi2c->pBuffPtr = pData;
hi2c->XferCount = Size;
hi2c->XferISR = NULL;
/* Send Slave Address */
/* Set NBYTES to write and reload if hi2c->XferCount > MAX_NBYTE_SIZE and generate RESTART */
if (hi2c->XferCount > MAX_NBYTE_SIZE)
{
hi2c->XferSize = MAX_NBYTE_SIZE;
I2C_TransferConfig(hi2c, DevAddress, hi2c->XferSize, I2C_RELOAD_MODE, I2C_GENERATE_START_READ);
}
else if (!LastOp)
{
hi2c->XferSize = hi2c->XferCount;
I2C_TransferConfig(hi2c, DevAddress, hi2c->XferSize, I2C_SOFTEND_MODE, I2C_GENERATE_START_READ);
}
else
{
hi2c->XferSize = hi2c->XferCount;
I2C_TransferConfig(hi2c, DevAddress, hi2c->XferSize, I2C_AUTOEND_MODE, I2C_GENERATE_START_READ);
}
while (hi2c->XferCount > 0U)
{
/* Wait until RXNE flag is set */
rc = I2C_WaitOnRXNEFlagUntilTimeout(hi2c, Timeout, tickstart, prev_mode);
if (rc != HAL_OK && rc != HAL_NACK)
{
if (hi2c->ErrorCode == HAL_I2C_ERROR_AF)
{
return HAL_ERROR;
}
else
{
return HAL_TIMEOUT;
}
}
/* Read data from RXDR */
(*hi2c->pBuffPtr++) = hi2c->Instance->RXDR;
hi2c->XferSize--;
hi2c->XferCount--;
if (rc == HAL_NACK)
{
break;
}
if ((hi2c->XferSize == 0U) && (hi2c->XferCount != 0U))
{
/* Wait until TCR flag is set */
if (I2C_WaitOnFlagUntilTimeout(hi2c, I2C_FLAG_TCR, RESET, Timeout, tickstart) != HAL_OK)
{
return HAL_TIMEOUT;
}
if (hi2c->XferCount > MAX_NBYTE_SIZE)
{
hi2c->XferSize = MAX_NBYTE_SIZE;
I2C_TransferConfig(hi2c, DevAddress, hi2c->XferSize, I2C_RELOAD_MODE, I2C_NO_STARTSTOP);
}
else if (!LastOp)
{
hi2c->XferSize = hi2c->XferCount;
I2C_TransferConfig(hi2c, DevAddress, hi2c->XferSize, I2C_SOFTEND_MODE, I2C_NO_STARTSTOP);
}
else
{
hi2c->XferSize = hi2c->XferCount;
I2C_TransferConfig(hi2c, DevAddress, hi2c->XferSize, I2C_AUTOEND_MODE, I2C_NO_STARTSTOP);
}
}
}
if (LastOp)
{
/* No need to Check TC flag, with AUTOEND mode the stop is automatically generated */
/* Wait until STOPF flag is set */
if (I2C_WaitOnSTOPFlagUntilTimeout(hi2c, Timeout, tickstart) != HAL_OK)
{
if (hi2c->ErrorCode == HAL_I2C_ERROR_AF)
{
return HAL_ERROR;
}
else
{
return HAL_TIMEOUT;
}
}
/* Clear STOP Flag */
__HAL_I2C_CLEAR_FLAG(hi2c, I2C_FLAG_STOPF);
}
else
{
/* No autoend/reload was requested, make sure transmission of last byte
* has finished... */
if (I2C_WaitOnFlagUntilTimeout(hi2c, I2C_FLAG_TC, SET, Timeout, tickstart) != HAL_OK)
{
return HAL_TIMEOUT;
}
}
/* Clear Configuration Register 2 */
I2C_RESET_CR2(hi2c);
hi2c->State = HAL_I2C_STATE_READY;
if (LastOp)
{
hi2c->Mode = HAL_I2C_MODE_NONE;
}
else
{
hi2c->Mode = HAL_I2C_MODE_MASTER_SEL;
}
/* Process Unlocked */
__HAL_UNLOCK(hi2c);
return HAL_OK;
}
else
{
return HAL_BUSY;
}
}
#endif /* !MYNEWT_VAL(STM32_HAL_I2C_HAS_CLOCKSPEED) */