Skip to content

Instantly share code, notes, and snippets.

@HTD
Last active September 28, 2025 01:30
Show Gist options
  • Select an option

  • Save HTD/e36fb68488742f27a737a5d096170623 to your computer and use it in GitHub Desktop.

Select an option

Save HTD/e36fb68488742f27a737a5d096170623 to your computer and use it in GitHub Desktop.
HAL I2C truly non-blocking reads.

STM32 HAL I2C TRULY NON-BLOCKING MEMORY I/O

Makes HAL_I2C_Mem_Read_IT and HAL_I2C_Mem_Write_IT truly non-blocking.

Known issues:

  • needs a configured system timer to provide interrupts,
  • needs a modification in original HAL driver,
  • needs an extra helper (delay_async.c / delay_async.h).
  • internal delays depend on the timer tick time (default 1ms here) so smaller intervals require smaller timer period.

The 1ms timer period is a compromise between CPU load with interrupts and I2C I/O latency. Decrease the period to decrease latency at the cost of increased CPU load.

Note that un-modified HAL driver is much worse for overall application performance and introduces extremely long lags!

If you have an idea how to provide a feature similar to what delay_async provides, please let me know.

The feature provides wait handles, so callbacks can be scheduled to be called after a specified time elapses. It is, in other words, a non-blocking delay function.

As I2C registers must be polled, the only non-blocking option for it is to limit the polling to a time interval. Polling in a spinning loop cannot be used in an interrupt handler.

/**
* @file delay_async.c
* @author CodeDog
* @brief Hardware timer interrupt delay provider.
* @remarks
* Uses HAL TIM interrupt to schedule events.
*
* @copyright (c)2022 CodeDog, All rights reserved.
*/
#include "delay_async.h"
#if (defined(HAL_TIM_MODULE_ENABLED) && USE_HAL_TIM_REGISTER_CALLBACKS > 0)
/**
* Timer configuration.
*/
static DelayAsync_ConfigTypeDef _delay_async_config = { };
/**
* Events queue AKA wait handles.
*/
static DelayAsync_EventTypeDef wait_handles[WAIT_HANDLES_N];
/**
* @fn void _delay_async_tim_callback(TIM_HandleTypeDef*)
* @brief Callback for TIM period elapsed.
* @param htim TIM handle pointer.
*/
static void _delay_async_tim_callback(TIM_HandleTypeDef* htim)
{
if (!htim || htim != _delay_async_config.htim) return;
for (uint8_t i = 0; i < WAIT_HANDLES_N; i++)
{
DelayAsync_EventTypeDef* event = &wait_handles[i];
if (event->active && event->callback)
{
if (event->ticks_left > 0) {
event->ticks_left--;
continue;
}
if (event->reset) {
event->ticks_left = event->reset - 1;
event->callback(event->binding);
}
else {
event->active = 0;
event->callback(event->binding);
}
}
}
}
/**
* @fn uint16_t _delay_get_ticks_per_ms(double)
* @brief Calculates the number of ticks for t milliseconds.
* @param t Number of milliseconds.
* @return Number of ticks.
*/
static uint16_t _delay_get_ticks(double t)
{
return round(t * pow(10, _delay_async_config.tick_time_exp - 6));
}
/**
* @fn HAL_StatusTypeDef _delay_async(double, uint32_t, void*, void(*)(void*))
* @brief Delays or repeats the callback in interrupt mode.
* @param ms Number of millisecods to wait. Fractions are valid if configured tick time is small enough.
* @param reset_ms When time elapses the delay will be restarted after this number of millseconds.
* @param binding A pointer that is passed as the callback argument.
* @param callback A function to call when the time elapses.
* @return HAL status.
*/
static HAL_StatusTypeDef _delay_async(double ms, double reset_ms, void* binding, DelayAsync_CallbackTypeDef callback)
{
uint32_t ticks = _delay_get_ticks(ms);
uint32_t reset = reset_ms == 0 ? 0 : _delay_get_ticks(reset_ms);
if (ticks < 1) return HAL_ERROR;
for (uint8_t i = 0; i < WAIT_HANDLES_N; i++)
{
DelayAsync_EventTypeDef* event = &wait_handles[i];
if (!event->active)
{
event->ticks_left = ticks;
event->reset = reset;
event->binding = binding;
event->callback = callback;
event->active = 1;
return HAL_OK;
}
}
return HAL_ERROR;
}
/**
* @fn HAL_StatusTypeDef delay_async_init(TIM_HandleTypeDef*, uint16_t, uint8_t)
* @brief Configure one system timer as a time base source for precise delays.
* @remarks Reconfigures hardware timer, use with caution.
* @param htim Timer not used for anything else, but enabled with IRQ.
* @param time_base Timer clock in MHz.
* @param tick_time_exp 0: 1ns, 3: 1μs, 6: 1ms, 9: 1s.
* @return HAL status, error on invalid values.
*/
HAL_StatusTypeDef delay_async_init(TIM_HandleTypeDef* htim, uint16_t time_base, uint8_t tick_time_exp)
{
if (!htim || !time_base || _delay_async_config.htim) return HAL_ERROR;
_delay_async_config.htim = htim;
_delay_async_config.time_base = time_base;
_delay_async_config.tick_time_exp = tick_time_exp;
double d = (double)time_base * pow(10, tick_time_exp - 3); // time base division
if (d < 1) return HAL_ERROR;
double m = floor(log10(d) / 2); // division magnitude
uint16_t arr = pow(10, m); // auto reload register value
uint16_t psc = d / arr; // prescaler register value
if (arr < 1 || psc < 1) return HAL_ERROR;
htim->Instance->ARR = arr - 1;
htim->Instance->PSC = psc - 1;
HAL_StatusTypeDef status
= HAL_TIM_RegisterCallback(htim, HAL_TIM_PERIOD_ELAPSED_CB_ID, _delay_async_tim_callback);
if (status != HAL_OK) return status;
status = HAL_TIM_Base_Start_IT(htim);
if (status != HAL_OK) return status;
return HAL_OK;
}
/**
* @fn HAL_StatusTypeDef delay_async(double, void*, DelayAsync_CallbackTypeDef)
* @brief Delays the callback in interrupt mode.
* @param ms Number of millisecods to wait. Fractions are valid if configured tick time is small enough.
* @param binding A pointer that is passed as the callback argument.
* @param callback A function to call when the time elapses.
* @return HAL status.
*/
HAL_StatusTypeDef delay_async(double ms, void* binding, DelayAsync_CallbackTypeDef callback)
{
return _delay_async(ms, 0, binding, callback);
}
/**
* @fn HAL_StatusTypeDef repeat_async(double, void*, DelayAsync_CallbackTypeDef)
* @brief Repeats the callback in interrupt mode.
* @param ms Number of millisecods to wait. Fractions are valid if configured tick time is small enough.
* @param binding A pointer that is passed as the callback argument.
* @param callback A function to call when the time elapses.
* @return HAL status.
*/
HAL_StatusTypeDef repeat_async(double ms, void* binding, DelayAsync_CallbackTypeDef callback)
{
return _delay_async(ms, ms, binding, callback);
}
#endif
/**
* @file delay_async.h
* @author CodeDog
* @brief Hardware timer interrupt delay provider header file.
* @remarks
* Uses HAL TIM interrupt to schedule events.
*
* @copyright (c)2022 CodeDog, All rights reserved.
*/
#pragma once
#include "hal.h"
#if (!defined(HAL_TIM_MODULE_ENABLED) || USE_HAL_TIM_REGISTER_CALLBACKS < 1)
#warning "PSSSST! Look here:"
/**
* This driver depends on HAL TIM with global interrupt and REGISTER CALLBACK features enabled.
* In STM32CubeIde at least one TIM must be enabled with its global interrupt on.
* IOC Project Manager > Advanced Settings > Register CallBack
* change TIM settings to ENABLED.
*/
#endif
#if (defined(HAL_TIM_MODULE_ENABLED) && USE_HAL_TIM_REGISTER_CALLBACKS > 0)
#define DELAY_ASYNC_ENABLED
/**
* @def WAIT_HANDLES_N
* @brief Number of reserved delay events.
*/
#define WAIT_HANDLES_N 128
/**
* @typedef DelayAsync_CallbackTypeDef
* @brief Callback for delay fuction with any pointer passed as argument.
* @param Any pointer.
*/
typedef void(*DelayAsync_CallbackTypeDef)(void*);
/**
* @typedef DelayAsync_ConfigTypeDef
* @brief Contains DelayAsync timing configuration.
*/
typedef struct
{
TIM_HandleTypeDef* htim; ///< Timer handle pointer.
uint16_t time_base; ///< Timer time base (timer clock) in MHz.
uint8_t tick_time_exp; ///< t = 1ns * 10^(tick_time_exp); 0: 1ns, 3: 1μs, 6: 1ms, 9: 1s.
} DelayAsync_ConfigTypeDef;
/**
* Asynchronous delay event.
*/
typedef struct
{
uint8_t active; ///< 0: free, 1: active.
uint32_t ticks_left; ///< Ticks left to complete the delay.
uint32_t reset; ///< If set, the ticks_left will be reset to that value when the time elapses.
void* binding; ///< Binding passed to the callback.
DelayAsync_CallbackTypeDef callback; ///< Callback to call when the time elapses.
} DelayAsync_EventTypeDef;
HAL_StatusTypeDef delay_async_init(TIM_HandleTypeDef* htim, uint16_t time_base, uint8_t tick_time_exp);
HAL_StatusTypeDef delay_async(double ms, void* binding, void (*callback)(void*));
HAL_StatusTypeDef repeat_async(double ms, void* binding, void (*callback)(void*));
#endif
/**
* @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;
}
/**
* @file stm32h7xx_hal_i2c_common.h
* @author CodeDog
* @brief Common definitions for original HAL I2C driver to be shared with the driver hack.
* @remarks
* WARNING: the "static" keyword MUST be removed from the original driver
* for all functions declared here!
*
* @copyright (c)2022 CodeDog, All rights reserved.
*/
#pragma once
#include "stm32h7xx_hal.h"
/** @defgroup I2C_Private_Define I2C Private Define
* @{
*/
#define TIMING_CLEAR_MASK (0xF0FFFFFFU) /*!< I2C TIMING clear register Mask */
#define I2C_TIMEOUT_ADDR (10000U) /*!< 10 s */
#define I2C_TIMEOUT_BUSY (25U) /*!< 25 ms */
#define I2C_TIMEOUT_DIR (25U) /*!< 25 ms */
#define I2C_TIMEOUT_RXNE (25U) /*!< 25 ms */
#define I2C_TIMEOUT_STOPF (25U) /*!< 25 ms */
#define I2C_TIMEOUT_TC (25U) /*!< 25 ms */
#define I2C_TIMEOUT_TCR (25U) /*!< 25 ms */
#define I2C_TIMEOUT_TXIS (25U) /*!< 25 ms */
#define I2C_TIMEOUT_FLAG (25U) /*!< 25 ms */
#define MAX_NBYTE_SIZE 255U
#define SLAVE_ADDR_SHIFT 7U
#define SLAVE_ADDR_MSK 0x06U
/* Private define for @ref PreviousState usage */
#define I2C_STATE_MSK ((uint32_t)((uint32_t)((uint32_t)HAL_I2C_STATE_BUSY_TX | \
(uint32_t)HAL_I2C_STATE_BUSY_RX) & \
(uint32_t)(~((uint32_t)HAL_I2C_STATE_READY))))
/*!< Mask State define, keep only RX and TX bits */
#define I2C_STATE_NONE ((uint32_t)(HAL_I2C_MODE_NONE))
/*!< Default Value */
#define I2C_STATE_MASTER_BUSY_TX ((uint32_t)(((uint32_t)HAL_I2C_STATE_BUSY_TX & I2C_STATE_MSK) | \
(uint32_t)HAL_I2C_MODE_MASTER))
/*!< Master Busy TX, combinaison of State LSB and Mode enum */
#define I2C_STATE_MASTER_BUSY_RX ((uint32_t)(((uint32_t)HAL_I2C_STATE_BUSY_RX & I2C_STATE_MSK) | \
(uint32_t)HAL_I2C_MODE_MASTER))
/*!< Master Busy RX, combinaison of State LSB and Mode enum */
#define I2C_STATE_SLAVE_BUSY_TX ((uint32_t)(((uint32_t)HAL_I2C_STATE_BUSY_TX & I2C_STATE_MSK) | \
(uint32_t)HAL_I2C_MODE_SLAVE))
/*!< Slave Busy TX, combinaison of State LSB and Mode enum */
#define I2C_STATE_SLAVE_BUSY_RX ((uint32_t)(((uint32_t)HAL_I2C_STATE_BUSY_RX & I2C_STATE_MSK) | \
(uint32_t)HAL_I2C_MODE_SLAVE))
/*!< Slave Busy RX, combinaison of State LSB and Mode enum */
#define I2C_STATE_MEM_BUSY_TX ((uint32_t)(((uint32_t)HAL_I2C_STATE_BUSY_TX & I2C_STATE_MSK) | \
(uint32_t)HAL_I2C_MODE_MEM))
/*!< Memory Busy TX, combinaison of State LSB and Mode enum */
#define I2C_STATE_MEM_BUSY_RX ((uint32_t)(((uint32_t)HAL_I2C_STATE_BUSY_RX & I2C_STATE_MSK) | \
(uint32_t)HAL_I2C_MODE_MEM))
/*!< Memory Busy RX, combinaison of State LSB and Mode enum */
/* Private define to centralize the enable/disable of Interrupts */
#define I2C_XFER_TX_IT (uint16_t)(0x0001U) /*!< Bit field can be combinated with
@ref I2C_XFER_LISTEN_IT */
#define I2C_XFER_RX_IT (uint16_t)(0x0002U) /*!< Bit field can be combinated with
@ref I2C_XFER_LISTEN_IT */
#define I2C_XFER_LISTEN_IT (uint16_t)(0x8000U) /*!< Bit field can be combinated with @ref I2C_XFER_TX_IT
and @ref I2C_XFER_RX_IT */
#define I2C_XFER_ERROR_IT (uint16_t)(0x0010U) /*!< Bit definition to manage addition of global Error
and NACK treatment */
#define I2C_XFER_CPLT_IT (uint16_t)(0x0020U) /*!< Bit definition to manage only STOP evenement */
#define I2C_XFER_RELOAD_IT (uint16_t)(0x0040U) /*!< Bit definition to manage only Reload of NBYTE */
/* Private define Sequential Transfer Options default/reset value */
#define I2C_NO_OPTION_FRAME (0xFFFF0000U)
/**
* @}
*/
HAL_StatusTypeDef I2C_Master_ISR_IT(struct __I2C_HandleTypeDef *hi2c, uint32_t ITFlags, uint32_t ITSources);
HAL_StatusTypeDef I2C_IsErrorOccurred(I2C_HandleTypeDef *hi2c, uint32_t Timeout, uint32_t Tickstart);
void I2C_TransferConfig(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t Size, uint32_t Mode, uint32_t Request);
void I2C_Enable_IRQ(I2C_HandleTypeDef *hi2c, uint16_t InterruptRequest);
void I2C_Disable_IRQ(I2C_HandleTypeDef *hi2c, uint16_t InterruptRequest);
/**
* @file stm32h737_hal_i2c_nb.h
* @author CodeDog
* @brief Provides TRULY non-blocking I2C memory I/O.
* @remarks
* The original driver file needs to be hacked as follows in order for this to work:
* 1. This file must be included instead of the HAL header.
* 2. Functiona HAL_I2C_Mem_Read_IT and HAL_I2C_Mem_Write_IT must be made __weak.
* 3. Functions declared in stm32h7xx_hal_i2c_common.h must be made non-static.
*
* @copyright (c)2022 CodeDog, All rights reserved.
*/
#pragma once
#include "delay_async.h"
#include "stm32h7xx_hal_i2c_common.h"
#define HAL_I2C_HCOUNT 4 ///< The number of the configured I2C ports in the system.
/**
* @typedef I2C_MemoryIOTypeDef
* @enum
* @brief Types of the I2C memory I/O operations.
*/
typedef enum
{
I2C_IO_READ,///< Read operation.
I2C_IO_WRITE///< Write operation.
} I2C_MemoryIOTypeDef;
/**
* @typedef I2C_MemoryIOStateTypeDef
* @enum
* @brief A state of the non-blocking I2C I/O.
*/
typedef enum
{
I2C_S_INIT, ///< Transfer start.
I2C_S_WAIT_TXS1,///< Awaiting the first TXS to read the first (or only) byte of the register.
I2C_S_WAIT_TXS2,///< Awaiting the second TXS to read the second byte of the register.
I2C_S_WAIT_TC, ///< Awaiting TC / TCR to complete the operation.
I2C_S_TIMEOUT, ///< A timeout occured.
I2C_S_ERROR, ///< An error occured.
I2C_S_DONE ///< Operation compeleted.
} I2C_MemoryIOStateTypeDef;
/**
* @typedef I2C_MemoryArgsTypeDef
* @struct
* @brief Arguments and state for I2C memory non-blocking functions.
*/
typedef struct
{
I2C_HandleTypeDef* hi2c; ///< I2C handle pointer.
I2C_MemoryIOTypeDef Type; ///< I/O operation type.
uint16_t DevAddress; ///< Target device address.
uint16_t MemAddress; ///< Internal memory address.
uint16_t MemAddSize; ///< Size of internal memory address.
I2C_MemoryIOStateTypeDef State; ///< I/O operation state.
uint32_t XferMode; ///< Transfer mode.
uint8_t CTR1; ///< Timeout countdown counter 1.
uint8_t CTR2; ///< Timeout countdown counter 2.
uint8_t CTR3; ///< Timeout countdown counter 3.
} I2C_MemoryArgsTypeDef;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment