|
/** |
|
* @file stm32h737_hal_i2c_nb.c |
|
* @author CodeDog |
|
* @brief Provides TRULY non-blocking I2C memory I/O. |
|
* @remarks |
|
* See the header file for more details. |
|
* |
|
* @copyright (c)2022 CodeDog, All rights reserved. |
|
*/ |
|
|
|
#include <stm32h7xx_hal_i2c_nb.h> |
|
|
|
static I2C_MemoryArgsTypeDef I2C_MemoryArgsHandles[HAL_I2C_HCOUNT]; |
|
|
|
/** |
|
* @fn I2C_MemoryArgsTypeDef I2C_MemoryArgsCreate*(I2C_HandleTypeDef*, uint16_t, uint16_t, uint16_t, uint32_t, uint32_t) |
|
* @brief Creates arguments for the non-blocking I2C memory I/O. |
|
* @param hi2c I2C handle pointer. |
|
* @param Type The type of the memory I/O operation. |
|
* @param DevAddress Target device address: |
|
* The device 7 bits address value in datasheet must be shifted to the left before calling the interface. |
|
* @param MemAddress Internal memory address. |
|
* @param MemAddSize Size of internal memory address. |
|
* @param Timeout Time limit in milliseconds for the operiation to complete. |
|
* @param XferMode Transfer mode. |
|
* @return A pointer to the agruments for the I/O opertaion or NULL on error. |
|
*/ |
|
static I2C_MemoryArgsTypeDef* I2C_MemoryArgsCreate(I2C_HandleTypeDef* hi2c, |
|
I2C_MemoryIOTypeDef Type, |
|
uint16_t DevAddress, |
|
uint16_t MemAddress, |
|
uint16_t MemAddSize, |
|
uint32_t Timeout, |
|
uint32_t XferMode) |
|
{ |
|
for (uint8_t i = 0; i < HAL_I2C_HCOUNT; i++) |
|
{ |
|
if (!I2C_MemoryArgsHandles[i].hi2c) |
|
{ |
|
I2C_MemoryArgsTypeDef* args = &I2C_MemoryArgsHandles[i]; |
|
args->hi2c = hi2c; |
|
args->Type = Type; |
|
args->DevAddress = DevAddress; |
|
args->MemAddress = MemAddress; |
|
args->MemAddSize = MemAddSize; |
|
args->State = I2C_S_INIT; |
|
args->XferMode = XferMode; |
|
args->CTR1 = Timeout; |
|
args->CTR2 = Timeout; |
|
args->CTR3 = Timeout; |
|
return args; |
|
} |
|
} |
|
return NULL; |
|
} |
|
|
|
/** |
|
* @fn I2C_MemoryArgsTypeDef I2C_MemoryArgsGet*(I2C_HandleTypeDef*) |
|
* @brief Gets the arguments for the non-blocking I2C memory I/O set for the specified handle. |
|
* @param hi2c I2C handle pointer. |
|
* @return A pointer to the agruments for the I/O opertaion or NULL on error. |
|
*/ |
|
static I2C_MemoryArgsTypeDef* I2C_MemoryArgsGet(I2C_HandleTypeDef* hi2c) |
|
{ |
|
for (uint8_t i = 0; i < HAL_I2C_HCOUNT; i++) |
|
if (I2C_MemoryArgsHandles[i].hi2c == hi2c) |
|
return &I2C_MemoryArgsHandles[i]; |
|
return NULL; |
|
} |
|
|
|
/** |
|
* @fn void I2C_MemoryArgsDispose(I2C_HandleTypeDef*) |
|
* @brief Disposes the arguments for the non-blocking I2C memory I/O set for the specified handle. |
|
* @param hi2c I2C handle pointer. |
|
*/ |
|
static void I2C_MemoryArgsDispose(I2C_HandleTypeDef* hi2c) |
|
{ |
|
for (uint8_t i = 0; i < HAL_I2C_HCOUNT; i++) |
|
if (I2C_MemoryArgsHandles[i].hi2c == hi2c) |
|
{ |
|
I2C_MemoryArgsHandles[i].hi2c = NULL; |
|
return; |
|
} |
|
} |
|
|
|
/** |
|
* @fn void I2C_RequestMemoryIO_NB(I2C_HandleTypeDef*) |
|
* @brief Executes non blocking memory read or write sequence. |
|
* @remarks Non blocking hack. |
|
* @param hi2c I2C handle pointer. |
|
*/ |
|
static void I2C_RequestMemoryIO_NB(I2C_HandleTypeDef* hi2c) |
|
{ |
|
I2C_MemoryArgsTypeDef* args = I2C_MemoryArgsGet(hi2c); |
|
if (!args) |
|
{ |
|
hi2c->State = HAL_I2C_STATE_READY; |
|
hi2c->Mode = HAL_I2C_MODE_NONE; |
|
hi2c->Lock = 0; |
|
return; |
|
} |
|
uint8_t completed = 0; |
|
|
|
#define WAIT() delay_async(1, hi2c, (DelayAsync_CallbackTypeDef)I2C_RequestMemoryIO_NB) == HAL_OK |
|
#define CHANGE_STATE(s) { args->State = s; goto next_state; } |
|
#define WAIT_FOR_FLAG_UNTIL_TIMEOUT(ctr, flag)\ |
|
if (I2C_IsErrorOccurred(hi2c, 0, 0) != HAL_OK) CHANGE_STATE(I2C_S_ERROR);\ |
|
if (__HAL_I2C_GET_FLAG(hi2c, flag) == RESET)\ |
|
{\ |
|
if (ctr-- && WAIT()) return;\ |
|
else CHANGE_STATE(I2C_S_TIMEOUT);\ |
|
}\ |
|
|
|
next_state: |
|
switch (args->State) |
|
{ |
|
case I2C_S_INIT: |
|
I2C_TransferConfig(hi2c, args->DevAddress, (uint8_t)args->MemAddSize, |
|
args->Type == I2C_IO_READ ? I2C_SOFTEND_MODE : I2C_RELOAD_MODE, I2C_GENERATE_START_WRITE); |
|
args->State = I2C_S_WAIT_TXS1; |
|
case I2C_S_WAIT_TXS1: |
|
WAIT_FOR_FLAG_UNTIL_TIMEOUT(args->CTR1, I2C_FLAG_TXIS); |
|
if (args->MemAddSize == I2C_MEMADD_SIZE_8BIT) |
|
{ |
|
hi2c->Instance->TXDR = I2C_MEM_ADD_LSB(args->MemAddress); |
|
CHANGE_STATE(I2C_S_WAIT_TC); |
|
} |
|
hi2c->Instance->TXDR = I2C_MEM_ADD_MSB(args->MemAddress); |
|
args->State = I2C_S_WAIT_TXS2; |
|
case I2C_S_WAIT_TXS2: |
|
WAIT_FOR_FLAG_UNTIL_TIMEOUT(args->CTR2, I2C_FLAG_TXIS); |
|
args->State = I2C_S_WAIT_TC; |
|
hi2c->Instance->TXDR = I2C_MEM_ADD_LSB(args->MemAddress); |
|
case I2C_S_WAIT_TC: |
|
WAIT_FOR_FLAG_UNTIL_TIMEOUT(args->CTR3, args->Type == I2C_IO_READ ? I2C_FLAG_TC : I2C_FLAG_TCR); |
|
I2C_TransferConfig(hi2c, args->DevAddress, (uint8_t)hi2c->XferSize, args->XferMode, |
|
args->Type == I2C_IO_READ ? I2C_GENERATE_START_READ : I2C_NO_STARTSTOP); |
|
completed = 1; |
|
CHANGE_STATE(I2C_S_DONE); |
|
case I2C_S_TIMEOUT: |
|
hi2c->ErrorCode |= HAL_I2C_ERROR_TIMEOUT; |
|
case I2C_S_ERROR: |
|
hi2c->State = HAL_I2C_STATE_READY; |
|
hi2c->Mode = HAL_I2C_MODE_NONE; |
|
case I2C_S_DONE: |
|
I2C_MemoryArgsDispose(hi2c); |
|
hi2c->Lock = 0; |
|
if (completed) I2C_Enable_IRQ(hi2c, args->Type == I2C_IO_READ ? I2C_XFER_RX_IT : I2C_XFER_TX_IT); |
|
break; |
|
} |
|
|
|
#undef CHANGE_STATE |
|
#undef WAIT |
|
#undef WAIT_FOR_FLAG_UNTIL_TIMEOUT |
|
} |
|
|
|
/** |
|
* @brief Reads an amount of data in non-blocking mode with Interrupt from a specific memory address |
|
* @remarks Truly non blocking hack. |
|
* @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 MemAddress Internal memory address |
|
* @param MemAddSize Size of internal memory address |
|
* @param pData Pointer to data buffer |
|
* @param Size Amount of data to be sent |
|
* @retval HAL status |
|
*/ |
|
HAL_StatusTypeDef HAL_I2C_Mem_Read_IT(I2C_HandleTypeDef* hi2c, |
|
uint16_t DevAddress, uint16_t MemAddress, |
|
uint16_t MemAddSize, uint8_t* pData, uint16_t Size) |
|
{ |
|
if (hi2c->State != HAL_I2C_STATE_READY || __HAL_I2C_GET_FLAG(hi2c, I2C_FLAG_BUSY) == SET) |
|
return HAL_BUSY; |
|
if (!pData || !Size) |
|
{ |
|
hi2c->ErrorCode = HAL_I2C_ERROR_INVALID_PARAM; |
|
return HAL_ERROR; |
|
} |
|
assert_param(IS_I2C_MEMADD_SIZE(MemAddSize)); |
|
|
|
hi2c->Lock = 1; |
|
hi2c->State = HAL_I2C_STATE_BUSY_RX; |
|
hi2c->Mode = HAL_I2C_MODE_MEM; |
|
hi2c->ErrorCode = HAL_I2C_ERROR_NONE; |
|
|
|
hi2c->pBuffPtr = pData; |
|
hi2c->XferCount = Size; |
|
hi2c->XferOptions = I2C_NO_OPTION_FRAME; |
|
hi2c->XferISR = I2C_Master_ISR_IT; |
|
|
|
uint32_t xfermode; |
|
if (hi2c->XferCount > MAX_NBYTE_SIZE) |
|
{ |
|
hi2c->XferSize = MAX_NBYTE_SIZE; |
|
xfermode = I2C_RELOAD_MODE; |
|
} |
|
else |
|
{ |
|
hi2c->XferSize = hi2c->XferCount; |
|
xfermode = I2C_AUTOEND_MODE; |
|
} |
|
|
|
if (!I2C_MemoryArgsCreate(hi2c, I2C_IO_READ, DevAddress, MemAddress, MemAddSize, I2C_TIMEOUT_FLAG, xfermode)) |
|
{ |
|
hi2c->State = HAL_I2C_STATE_READY; |
|
hi2c->Mode = HAL_I2C_MODE_NONE; |
|
hi2c->Lock = 0; |
|
return HAL_ERROR; |
|
} |
|
I2C_RequestMemoryIO_NB(hi2c); // I2C is locked at this point, must be unlocked by the state machine. |
|
return HAL_OK; |
|
} |
|
|
|
/** |
|
* @brief Writes an amount of data in non-blocking mode with Interrupt from a specific memory address |
|
* @remarks Truly non blocking hack. |
|
* @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 MemAddress Internal memory address |
|
* @param MemAddSize Size of internal memory address |
|
* @param pData Pointer to data buffer |
|
* @param Size Amount of data to be sent |
|
* @retval HAL status |
|
*/ |
|
HAL_StatusTypeDef HAL_I2C_Mem_Write_IT(I2C_HandleTypeDef* hi2c, |
|
uint16_t DevAddress, uint16_t MemAddress, |
|
uint16_t MemAddSize, uint8_t* pData, uint16_t Size) |
|
{ |
|
if (hi2c->State != HAL_I2C_STATE_READY || __HAL_I2C_GET_FLAG(hi2c, I2C_FLAG_BUSY) == SET) |
|
return HAL_BUSY; |
|
if (!pData || !Size) |
|
{ |
|
hi2c->ErrorCode = HAL_I2C_ERROR_INVALID_PARAM; |
|
return HAL_ERROR; |
|
} |
|
assert_param(IS_I2C_MEMADD_SIZE(MemAddSize)); |
|
|
|
hi2c->Lock = 1; |
|
hi2c->State = HAL_I2C_STATE_BUSY_TX; |
|
hi2c->Mode = HAL_I2C_MODE_MEM; |
|
hi2c->ErrorCode = HAL_I2C_ERROR_NONE; |
|
|
|
hi2c->pBuffPtr = pData; |
|
hi2c->XferCount = Size; |
|
hi2c->XferOptions = I2C_NO_OPTION_FRAME; |
|
hi2c->XferISR = I2C_Master_ISR_IT; |
|
|
|
uint32_t xfermode; |
|
if (hi2c->XferCount > MAX_NBYTE_SIZE) |
|
{ |
|
hi2c->XferSize = MAX_NBYTE_SIZE; |
|
xfermode = I2C_RELOAD_MODE; |
|
} |
|
else |
|
{ |
|
hi2c->XferSize = hi2c->XferCount; |
|
xfermode = I2C_AUTOEND_MODE; |
|
} |
|
|
|
if (!I2C_MemoryArgsCreate(hi2c, I2C_IO_WRITE, DevAddress, MemAddress, MemAddSize, I2C_TIMEOUT_FLAG, xfermode)) |
|
{ |
|
hi2c->State = HAL_I2C_STATE_READY; |
|
hi2c->Mode = HAL_I2C_MODE_NONE; |
|
hi2c->Lock = 0; |
|
return HAL_ERROR; |
|
} |
|
I2C_RequestMemoryIO_NB(hi2c); // I2C is locked at this point, must be unlocked by the state machine. |
|
return HAL_OK; |
|
} |