Skip to content

Instantly share code, notes, and snippets.

@royratcliffe
Last active April 9, 2023 16:43
Show Gist options
  • Save royratcliffe/11fa5620e051371cc7bcce2a691946d0 to your computer and use it in GitHub Desktop.
Save royratcliffe/11fa5620e051371cc7bcce2a691946d0 to your computer and use it in GitHub Desktop.
STM32xx I2C Slave

STM32xx I2C Slave

Copyright (c) 2023, Roy Ratcliffe, Northumberland, United Kingdom

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

/*
* list_ex.c
*/
#include "list_ex.h"
#include "task.h"
ListItem_t *pxListItemNew(void *pxOwner, TickType_t xValue) {
ListItem_t *pxListItem = pvPortMalloc(sizeof(*pxListItem));
if (pxListItem == NULL)
return NULL;
vListInitialiseItem(pxListItem);
listSET_LIST_ITEM_OWNER(pxListItem, pxOwner);
listSET_LIST_ITEM_VALUE(pxListItem, xValue);
return pxListItem;
}
ListItem_t *pxListInsertNew(List_t *pxList, void *pxOwner, TickType_t xValue) {
ListItem_t *pxListItem = pxListItemNew(pxOwner, xValue);
configASSERT(pxListItem);
vListInsert(pxList, pxListItem);
return pxListItem;
}
ListItem_t *pxListInsertNewEnd(List_t *pxList, void *pxOwner,
TickType_t xValue) {
ListItem_t *pxListItem = pxListItemNew(pxOwner, xValue);
configASSERT(pxListItem);
vListInsertEnd(pxList, pxListItem);
return pxListItem;
}
const ListItem_t *pxListYield(List_t *const pxList,
BaseType_t (*pxYield)(void *pxOwner,
TickType_t xValue)) {
const ListItem_t *pxListEnd = listGET_END_MARKER(pxList),
*pxListItem = listGET_HEAD_ENTRY(pxList);
while (pxListItem != pxListEnd) {
if (pxYield(listGET_LIST_ITEM_OWNER(pxListItem),
listGET_LIST_ITEM_VALUE(pxListItem)) == pdPASS)
return pxListItem;
pxListItem = listGET_NEXT(pxListItem);
}
return NULL;
}
void vListYield(List_t *const pxList,
BaseType_t (*pxYield)(void *pxOwner, TickType_t xValue)) {
const ListItem_t *pxListEnd = listGET_END_MARKER(pxList),
*pxListItem = listGET_HEAD_ENTRY(pxList);
while (pxListItem != pxListEnd) {
if (pxYield(listGET_LIST_ITEM_OWNER(pxListItem),
listGET_LIST_ITEM_VALUE(pxListItem)) == pdFAIL)
return;
pxListItem = listGET_NEXT(pxListItem);
}
}
/*
* list_ex.h
*/
#pragma once
// FreeRTOS
#include "FreeRTOS.h"
#include "list.h"
/*!
* \brief Constructs new list item.
*
* \returns New dynamically-allocated list item or \c NULL if out of memory.
*
* \note Be aware the item value \c xValue typically spans 32-bits but _could_
* have 16 bits on some smaller systems. If the FreeRTOS configuration defines
* \c configUSE_16_BIT_TICKS as 1, the tick type is an unsigned 16-bit integer
* type.
*/
ListItem_t *pxListItemNew(void *pxOwner, TickType_t xValue);
/*!
* \brief Inserts new list item.
*
* \note Asserts successful allocation.
*/
ListItem_t *pxListInsertNew(List_t *pxList, void *pxOwner, TickType_t xValue);
/*!
* \brief Inserts item at end of list.
*
* Inserts a new item at the very end of the list _without_ sorting by item
* value. Thus, the \c xValue parameter is non-functional with respect to the
* container albeit appearing in list yields.
*/
ListItem_t *pxListInsertNewEnd(List_t *pxList, void *pxOwner,
TickType_t xValue);
/*!
* \brief Yields list item owners and item values as pairs while yielding fails.
*
* \returns Answers the passing list item, else \c NULL if no matching list
* item. The \c pxYield function determines the match, success or failure, pass
* or fail.
*
* The return value is \c const by design for compatibility; if not \c NULL,
* the result is a immutable list item. Such items reference their container
* list and even if immutable can detach using \c uxListRemove.
*/
const ListItem_t *pxListYield(List_t *const pxList,
BaseType_t (*pxYield)(void *pxOwner,
TickType_t xValue));
/*!
* \brief Yields owners and their values.
*
* \param pxYield Pointer to yield function that receives the owner and item
* value. The yield continues for each item while the function returns \c
* pdPASS.
*
* \returns Answers the passing item.
*/
void vListYield(List_t *const pxList,
BaseType_t (*pxYield)(void *pxOwner, TickType_t xValue));
/*
* registered_opaques.c
*
* The registered opaque structure has no counter. The number of registered
* opaque pointers corresponds to the total number of pointers less the number
* of \c NULL pointers.
*/
#include "registered_opaques.h"
/*
* Only for \c configASSERT macro.
*/
#include "FreeRTOS.h"
#include "task.h"
static void **ppvRegisteredOpaque(RegisteredOpaques_t xRegisteredOpaques,
void *pvOpaque, size_t xCardinal);
static size_t xRegisteredHashOfOpaque(RegisteredOpaques_t xRegisteredOpaques,
void *pvOpaque);
size_t xRegisteredCardinalOfOpaque(RegisteredOpaques_t xRegisteredOpaques,
void *pvOpaque) {
size_t xCardinal = xRegisteredHashOfOpaque(xRegisteredOpaques, pvOpaque);
void **ppvOpaque =
ppvRegisteredOpaque(xRegisteredOpaques, pvOpaque, xCardinal);
if (ppvOpaque == NULL) {
ppvOpaque = ppvRegisteredOpaque(xRegisteredOpaques, NULL, xCardinal);
configASSERT(ppvOpaque);
*ppvOpaque = pvOpaque;
}
return ppvOpaque - xRegisteredOpaques->ppvOpaques;
}
/*!
* \brief Searches for an opaque pointer.
*
* \param xCardinal Starting cardinal typically based on the opaque's hash. Must
* always be less than the number of opaque pointers.
*
* \returns Pointer to opaque pointer.
*
* The implementation design optimises for quickly finding a
* previously-registered opaque. For example, it would be possible to collate
* the \c NULL opaques while iterating.
*/
static void **ppvRegisteredOpaque(RegisteredOpaques_t xRegisteredOpaques,
void *pvOpaque, size_t xCardinal) {
for (size_t xOrdinal = xRegisteredOpaques->xNumberOfOpaques; xOrdinal;
xOrdinal--) {
void **ppvRegisteredOpaque = xRegisteredOpaques->ppvOpaques + xCardinal;
if (*ppvRegisteredOpaque == pvOpaque)
return ppvRegisteredOpaque;
if (++xCardinal == xRegisteredOpaques->xNumberOfOpaques)
xCardinal = 0U;
}
return NULL;
}
/*!
* \brief Computes the hash of an opaque pointer.
* \returns Optimised cardinal offset of opaque pointer based on hash function,
* or zero by default.
*/
static size_t xRegisteredHashOfOpaque(RegisteredOpaques_t xRegisteredOpaques,
void *pvOpaque) {
if (xRegisteredOpaques->pxHashOfOpaqueFunction == NULL)
return 0U;
return xRegisteredOpaques->pxHashOfOpaqueFunction(pvOpaque) %
xRegisteredOpaques->xNumberOfOpaques;
}
/*
* registered_opaques.h (Abstractions)
*/
/*!
* \file
*
* The link to FreeRTOS is tenuous. Only the asserts utilise FreeRTOS \c
* configASSERT for memory overruns. The naming conventions echo FreeRTOS
* Hungarian naming conventions.
*/
#pragma once
#include <stddef.h>
struct RegisteredOpaques {
void **ppvOpaques;
size_t xNumberOfOpaques;
size_t (*pxHashOfOpaqueFunction)(void *pvOpaque);
};
typedef struct RegisteredOpaques *RegisteredOpaques_t;
/*!
* \brief Cardinal for opaque pointer.
*
* Registers the opaque pointer if not already registered. Uses the hash
* function, if available, to place the opaque in a hash-optimised registry
* location.
*
* Asserts if full. By design, always provide sufficient space for registered
* opaque pointers. No function exists to unregister a pointer by design.
*/
size_t xRegisteredCardinalOfOpaque(RegisteredOpaques_t xRegisteredOpaques,
void *pvOpaque);
/*
* stm32xx_i2c.h
*/
#pragma once
#include "stm32xx_mcu.h"
#define stm32xx_i2cMAX_INSTANCES 4U
typedef I2C_TypeDef *I2C_t;
/*!
* Note that the \c xI2C parameter refers to the I2C peripheral base address;
* it does not refer to the HAL structure.
*/
uint8_t ucCardinalForI2C(I2C_t xI2C);
uint8_t ucOrdinalForI2C(I2C_t xI2C);
/*
* The STM32 headers do not define \c I2C_HandleTypeDef unless the project
* enables the I2C module. Hence make its dependencies likewise compile-time
* elided by the same manifest constant.
*/
#ifdef HAL_I2C_MODULE_ENABLED
typedef I2C_HandleTypeDef *I2CHandle_t;
#endif
/*
* stm32xx_i2c_cb.c
*/
#include "stm32xx_i2c_cb.h"
#ifdef HAL_I2C_MODULE_ENABLED
#include "stm32xx_registered_i2c.h"
#include "list_ex.h"
static List_t xMasterTxCpltForI2C[stm32xx_i2cMAX_INSTANCES];
static List_t xMasterRxCpltForI2C[stm32xx_i2cMAX_INSTANCES];
static List_t xSlaveTxCpltForI2C[stm32xx_i2cMAX_INSTANCES];
static List_t xSlaveRxCpltForI2C[stm32xx_i2cMAX_INSTANCES];
static List_t xAddrForI2C[stm32xx_i2cMAX_INSTANCES];
static List_t xListenCpltForI2C[stm32xx_i2cMAX_INSTANCES];
static List_t xErrorForI2C[stm32xx_i2cMAX_INSTANCES];
static List_t xAbortForI2C[stm32xx_i2cMAX_INSTANCES];
static void __attribute__((constructor)) prvListInitialise() {
for (size_t xCardinal = 0U; xCardinal < stm32xx_i2cMAX_INSTANCES;
xCardinal++) {
vListInitialise(xMasterTxCpltForI2C + xCardinal);
vListInitialise(xMasterRxCpltForI2C + xCardinal);
vListInitialise(xSlaveTxCpltForI2C + xCardinal);
vListInitialise(xSlaveRxCpltForI2C + xCardinal);
vListInitialise(xAddrForI2C + xCardinal);
vListInitialise(xListenCpltForI2C + xCardinal);
vListInitialise(xErrorForI2C + xCardinal);
vListInitialise(xAbortForI2C + xCardinal);
}
}
/*!
* \defgroup halI2C_MasterTxCpltCallback Master Transmit Complete Callback
* \{
*/
#if USE_HAL_I2C_REGISTER_CALLBACKS
static void prvMasterTxCplt(I2CHandle_t xI2C)
#else
void HAL_I2C_MasterTxCpltCallback(I2CHandle_t xI2C)
#endif
{
BaseType_t xYield(void *pxOwner, TickType_t xValue) {
((I2CHandler_t)pxOwner)(xI2C);
return pdPASS;
}
vListYield(xMasterTxCpltForI2C + xRegisteredCardinalOfI2C(xI2C), xYield);
}
ListItem_t *pxI2CRegisterMasterTxCpltHandler(I2CHandle_t xI2C,
I2CHandler_t xHandler,
TickType_t xDelay) {
List_t *pxForI2C = xMasterTxCpltForI2C + xRegisteredCardinalOfI2C(xI2C);
#if USE_HAL_I2C_REGISTER_CALLBACKS
if (listLIST_IS_EMPTY(pxForI2C))
HAL_I2C_RegisterCallback(xI2C, HAL_I2C_MASTER_TX_COMPLETE_CB_ID,
prvMasterTxCplt);
#endif
return pxListInsertNew(pxForI2C, xHandler, xDelay);
}
/*!
* \}
*/
/*!
* \defgroup halI2C_MasterRxCpltCallback Master Receive Complete Callback
* \{
*/
#if USE_HAL_I2C_REGISTER_CALLBACKS
static void prvMasterRxCplt(I2CHandle_t xI2C)
#else
void HAL_I2C_MasterRxCpltCallback(I2CHandle_t xI2C)
#endif
{
BaseType_t xYield(void *pxOwner, TickType_t xValue) {
((I2CHandler_t)pxOwner)(xI2C);
return pdPASS;
}
vListYield(xMasterRxCpltForI2C + xRegisteredCardinalOfI2C(xI2C), xYield);
}
ListItem_t *pxI2CRegisterMasterRxCpltHandler(I2CHandle_t xI2C,
I2CHandler_t xHandler,
TickType_t xDelay) {
List_t *pxForI2C = xMasterRxCpltForI2C + xRegisteredCardinalOfI2C(xI2C);
#if USE_HAL_I2C_REGISTER_CALLBACKS
if (listLIST_IS_EMPTY(pxForI2C))
HAL_I2C_RegisterCallback(xI2C, HAL_I2C_MASTER_RX_COMPLETE_CB_ID,
prvMasterRxCplt);
#endif
return pxListInsertNew(pxForI2C, xHandler, xDelay);
}
/*!
* \}
*/
/*!
* \defgroup halI2C_SlaveTxCpltCallback Slave Transmit Complete Callback
* \{
*/
#if USE_HAL_I2C_REGISTER_CALLBACKS
static void prvSlaveTxCplt(I2CHandle_t xI2C)
#else
void HAL_I2C_SlaveTxCpltCallback(I2CHandle_t xI2C)
#endif
{
BaseType_t xYield(void *pxOwner, TickType_t xValue) {
((I2CHandler_t)pxOwner)(xI2C);
return pdPASS;
}
vListYield(xSlaveTxCpltForI2C + xRegisteredCardinalOfI2C(xI2C), xYield);
}
ListItem_t *pxI2CRegisterSlaveTxCpltHandler(I2CHandle_t xI2C,
I2CHandler_t xHandler,
TickType_t xDelay) {
List_t *pxForI2C = xSlaveTxCpltForI2C + xRegisteredCardinalOfI2C(xI2C);
#if USE_HAL_I2C_REGISTER_CALLBACKS
if (listLIST_IS_EMPTY(pxForI2C))
HAL_I2C_RegisterCallback(xI2C, HAL_I2C_SLAVE_TX_COMPLETE_CB_ID,
prvSlaveTxCplt);
#endif
return pxListInsertNew(pxForI2C, xHandler, xDelay);
}
/*!
* \}
*/
/*!
* \defgroup halI2C_SlaveRxCpltCallback Slave Receive Complete Callback
* \{
*/
#if USE_HAL_I2C_REGISTER_CALLBACKS
static void prvSlaveRxCplt(I2CHandle_t xI2C)
#else
void HAL_I2C_SlaveRxCpltCallback(I2CHandle_t xI2C)
#endif
{
BaseType_t xYield(void *pxOwner, TickType_t xValue) {
((I2CHandler_t)pxOwner)(xI2C);
return pdPASS;
}
vListYield(xSlaveRxCpltForI2C + xRegisteredCardinalOfI2C(xI2C), xYield);
}
ListItem_t *pxI2CRegisterSlaveRxCpltHandler(I2CHandle_t xI2C,
I2CHandler_t xHandler,
TickType_t xDelay) {
List_t *pxForI2C = xSlaveRxCpltForI2C + xRegisteredCardinalOfI2C(xI2C);
#if USE_HAL_I2C_REGISTER_CALLBACKS
if (listLIST_IS_EMPTY(pxForI2C))
HAL_I2C_RegisterCallback(xI2C, HAL_I2C_SLAVE_RX_COMPLETE_CB_ID,
prvSlaveRxCplt);
#endif
return pxListInsertNew(pxForI2C, xHandler, xDelay);
}
/*!
* \}
*/
/*!
* \defgroup halI2C_AddrCallback Address Callback
* \{
*/
#if USE_HAL_I2C_REGISTER_CALLBACKS
static void prvAddr(I2CHandle_t xI2C, uint8_t ucTransferDirection,
uint16_t usAddrMatchCode)
#else
void HAL_I2C_AddrCallback(I2CHandle_t xI2C, uint8_t ucTransferDirection,
uint16_t usAddrMatchCode)
#endif
{
BaseType_t xYield(void *pxOwner, TickType_t xValue) {
((I2CAddrHandler_t)pxOwner)(xI2C, ucTransferDirection, usAddrMatchCode);
return pdPASS;
}
vListYield(xAddrForI2C + xRegisteredCardinalOfI2C(xI2C), xYield);
}
ListItem_t *pxI2CRegisterAddrHandler(I2CHandle_t xI2C,
I2CAddrHandler_t xHandler,
TickType_t xDelay) {
List_t *pxForI2C = xAddrForI2C + xRegisteredCardinalOfI2C(xI2C);
#if USE_HAL_I2C_REGISTER_CALLBACKS
if (listLIST_IS_EMPTY(pxForI2C))
HAL_I2C_RegisterAddrCallback(xI2C, prvAddr);
#endif
return pxListInsertNew(pxForI2C, xHandler, xDelay);
}
/*!
* \}
*/
/*!
* \defgroup halI2C_ListenCpltCallback Listen Complete Callback
* \{
*/
#if USE_HAL_I2C_REGISTER_CALLBACKS
static void prvListenCplt(I2CHandle_t xI2C)
#else
void HAL_I2C_ListenCpltCallback(I2CHandle_t xI2C)
#endif
{
BaseType_t xYield(void *pxOwner, TickType_t xValue) {
((I2CHandler_t)pxOwner)(xI2C);
return pdPASS;
}
vListYield(xListenCpltForI2C + xRegisteredCardinalOfI2C(xI2C), xYield);
}
ListItem_t *pxI2CRegisterListenCpltHandler(I2CHandle_t xI2C,
I2CHandler_t xHandler,
TickType_t xDelay) {
List_t *pxForI2C = xListenCpltForI2C + xRegisteredCardinalOfI2C(xI2C);
#if USE_HAL_I2C_REGISTER_CALLBACKS
if (listLIST_IS_EMPTY(pxForI2C))
HAL_I2C_RegisterCallback(xI2C, HAL_I2C_LISTEN_COMPLETE_CB_ID,
prvListenCplt);
#endif
return pxListInsertNew(pxForI2C, xHandler, xDelay);
}
/*!
* \}
*/
/*!
* \defgroup halI2C_ErrorCallback Error Callback
* \{
*/
#if USE_HAL_I2C_REGISTER_CALLBACKS
static void prvError(I2CHandle_t xI2C)
#else
void HAL_I2C_ErrorCallback(I2CHandle_t xI2C)
#endif
{
BaseType_t xYield(void *pxOwner, TickType_t xValue) {
((I2CHandler_t)pxOwner)(xI2C);
return pdPASS;
}
vListYield(xErrorForI2C + xRegisteredCardinalOfI2C(xI2C), xYield);
}
ListItem_t *pxI2CRegisterErrorHandler(I2CHandle_t xI2C, I2CHandler_t xHandler,
TickType_t xDelay) {
List_t *pxForI2C = xErrorForI2C + xRegisteredCardinalOfI2C(xI2C);
#if USE_HAL_I2C_REGISTER_CALLBACKS
if (listLIST_IS_EMPTY(pxForI2C))
HAL_I2C_RegisterCallback(xI2C, HAL_I2C_ERROR_CB_ID, prvError);
#endif
return pxListInsertNew(pxForI2C, xHandler, xDelay);
}
/*!
* \}
*/
/*!
* \defgroup halI2C_AbortCallback Abort Callback
* \{
*/
#if USE_HAL_I2C_REGISTER_CALLBACKS
static void prvAbort(I2CHandle_t xI2C)
#else
void HAL_I2C_AbortCallback(I2CHandle_t xI2C)
#endif
{
BaseType_t xYield(void *pxOwner, TickType_t xValue) {
((I2CHandler_t)pxOwner)(xI2C);
return pdPASS;
}
vListYield(xAbortForI2C + xRegisteredCardinalOfI2C(xI2C), xYield);
}
ListItem_t *pxI2CRegisterAbortHandler(I2CHandle_t xI2C, I2CHandler_t xHandler,
TickType_t xDelay) {
List_t *pxForI2C = xAbortForI2C + xRegisteredCardinalOfI2C(xI2C);
#if USE_HAL_I2C_REGISTER_CALLBACKS
if (listLIST_IS_EMPTY(pxForI2C))
HAL_I2C_RegisterCallback(xI2C, HAL_I2C_ABORT_CB_ID, prvAbort);
#endif
return pxListInsertNew(pxForI2C, xHandler, xDelay);
}
/*!
* \}
*/
#endif
/*
* stm32xx_i2c_cb.h
*/
#pragma once
#include "stm32xx_i2c.h"
#include "FreeRTOS.h"
#include "list.h"
typedef void (*I2CHandler_t)(I2CHandle_t xI2C);
ListItem_t *pxI2CRegisterMasterTxCpltHandler(I2CHandle_t xI2C,
I2CHandler_t xHandler,
TickType_t xDelay);
ListItem_t *pxI2CRegisterMasterRxCpltHandler(I2CHandle_t xI2C,
I2CHandler_t xHandler,
TickType_t xDelay);
ListItem_t *pxI2CRegisterSlaveTxCpltHandler(I2CHandle_t xI2C,
I2CHandler_t xHandler,
TickType_t xDelay);
ListItem_t *pxI2CRegisterSlaveRxCpltHandler(I2CHandle_t xI2C,
I2CHandler_t xHandler,
TickType_t xDelay);
/*!
* \param usAddrMatchCode 16-bit slave address.
*/
typedef void (*I2CAddrHandler_t)(I2CHandle_t xI2C, uint8_t ucTransferDirection,
uint16_t usAddrMatchCode);
ListItem_t *pxI2CRegisterAddrHandler(I2CHandle_t xI2C,
I2CAddrHandler_t xHandler,
TickType_t xDelay);
/*!
* \brief Registers an I2C listen-complete handler.
* \param[in] xDelay Nominal relative delay for handler used for the handler
* list priority ordering. Pass \c portMAX_DELAY to add the handler to the end
* of the handlers' list.
*/
ListItem_t *pxI2CRegisterListenCpltHandler(I2CHandle_t xI2C,
I2CHandler_t xHandler,
TickType_t xDelay);
ListItem_t *pxI2CRegisterErrorHandler(I2CHandle_t xI2C, I2CHandler_t xHandler,
TickType_t xDelay);
ListItem_t *pxI2CRegisterAbortHandler(I2CHandle_t xI2C, I2CHandler_t xHandler,
TickType_t xDelay);
/*
* stm32xx_i2c_seq.c
*/
#include "stm32xx_i2c_seq.h"
#include "FreeRTOS.h"
#include "task.h"
#include <memory.h>
/*!
* \brief I2C sequencer for STM32.
*
* Wraps an abstract sequential I2C transfer. It encapsulates an I2C handle, a
* dynamically-allocated buffer and virtualised transmit-receive methods. The
* current version only supports dynamic buffering although future work could
* add support for static buffers. Virtual transmit and receive support
* interrupt-driven or DMA-driven transfers.
*
* The encapsulation provides persistent buffering using dynamic memory for
* asynchronous transfers. It also performs slave transfer direction inversion.
*/
struct I2CSeq {
I2CHandle_t xI2C;
/*!
* \brief Transmit and receive buffer for persisting transfer bytes.
*
* The underlying hardware abstraction layer fails to apply \c const
* correctness to the transmit data pointer. It does not modify the transmit
* bytes.
*/
void *pvBuffer;
size_t xBufferLengthBytes;
HAL_StatusTypeDef (*pxTransfer[2U])(struct I2CSeq *pxI2CSeq, uint32_t ulOptions);
HAL_StatusTypeDef (*pxNoOptionTransfer[2U])(struct I2CSeq *pxI2CSeq);
uint8_t ucTransferDirection;
/*!
* \brief Master-mode I2C address without shift.
*
* Not used in slave mode except for introspection.
*/
uint8_t ucAddr;
};
static HAL_StatusTypeDef prvMasterTransmitIT(struct I2CSeq *pxI2CSeq, uint32_t ulOptions) {
return HAL_I2C_Master_Seq_Transmit_IT(pxI2CSeq->xI2C, pxI2CSeq->ucAddr << 1U, pxI2CSeq->pvBuffer,
pxI2CSeq->xBufferLengthBytes, ulOptions);
}
static HAL_StatusTypeDef prvMasterReceiveIT(struct I2CSeq *pxI2CSeq, uint32_t ulOptions) {
return HAL_I2C_Master_Seq_Receive_IT(pxI2CSeq->xI2C, pxI2CSeq->ucAddr << 1U, pxI2CSeq->pvBuffer,
pxI2CSeq->xBufferLengthBytes, ulOptions);
}
static HAL_StatusTypeDef prvSlaveTransmitIT(struct I2CSeq *pxI2CSeq, uint32_t ulOptions) {
return HAL_I2C_Slave_Seq_Transmit_IT(pxI2CSeq->xI2C, pxI2CSeq->pvBuffer, pxI2CSeq->xBufferLengthBytes, ulOptions);
}
static HAL_StatusTypeDef prvSlaveReceiveIT(struct I2CSeq *pxI2CSeq, uint32_t ulOptions) {
return HAL_I2C_Slave_Seq_Receive_IT(pxI2CSeq->xI2C, pxI2CSeq->pvBuffer, pxI2CSeq->xBufferLengthBytes, ulOptions);
}
static HAL_StatusTypeDef prvMasterNoOptionTransmitIT(struct I2CSeq *pxI2CSeq) {
return HAL_I2C_Master_Transmit_IT(pxI2CSeq->xI2C, pxI2CSeq->ucAddr << 1U, pxI2CSeq->pvBuffer,
pxI2CSeq->xBufferLengthBytes);
}
static HAL_StatusTypeDef prvMasterNoOptionReceiveIT(struct I2CSeq *pxI2CSeq) {
return HAL_I2C_Master_Receive_IT(pxI2CSeq->xI2C, pxI2CSeq->ucAddr << 1U, pxI2CSeq->pvBuffer,
pxI2CSeq->xBufferLengthBytes);
}
static HAL_StatusTypeDef prvSlaveNoOptionTransmitIT(struct I2CSeq *pxI2CSeq) {
return HAL_I2C_Slave_Transmit_IT(pxI2CSeq->xI2C, pxI2CSeq->pvBuffer, pxI2CSeq->xBufferLengthBytes);
}
static HAL_StatusTypeDef prvSlaveNoOptionReceiveIT(struct I2CSeq *pxI2CSeq) {
return HAL_I2C_Slave_Receive_IT(pxI2CSeq->xI2C, pxI2CSeq->pvBuffer, pxI2CSeq->xBufferLengthBytes);
}
I2CSeqHandle_t xI2CSeqCreate(I2CHandle_t xI2C) {
I2CSeqHandle_t xI2CSeq = pvPortMalloc(sizeof(*xI2CSeq));
configASSERT(xI2CSeq);
(void)memset(xI2CSeq, 0, sizeof(*xI2CSeq));
xI2CSeq->xI2C = xI2C;
return xI2CSeq;
}
void vI2CSeqDelete(I2CSeqHandle_t xI2CSeq) {
if (xI2CSeq) {
vPortFree(xI2CSeq->pvBuffer);
vPortFree(xI2CSeq);
}
}
void vI2CSeqTransferDirection(I2CSeqHandle_t xI2CSeq, uint8_t ucTransferDirection) {
xI2CSeq->ucTransferDirection = ucTransferDirection & 0x01U;
}
void vI2CSeqTransmit(I2CSeqHandle_t xI2CSeq) { xI2CSeq->ucTransferDirection = I2C_DIRECTION_TRANSMIT; }
void vI2CSeqReceive(I2CSeqHandle_t xI2CSeq) { xI2CSeq->ucTransferDirection = I2C_DIRECTION_RECEIVE; }
uint8_t ucI2CSeqTransferDirection(I2CSeqHandle_t xI2CSeq) { return xI2CSeq->ucTransferDirection; }
void vI2CSeqAddr(I2CSeqHandle_t xI2CSeq, uint8_t ucAddr) { xI2CSeq->ucAddr = ucAddr & 0x7fU; }
uint8_t ucI2CSeqAddr(I2CSeqHandle_t xI2CSeq) { return xI2CSeq->ucAddr; }
void vI2CSeqMasterIT(I2CSeqHandle_t xI2CSeq) {
xI2CSeq->pxTransfer[I2C_DIRECTION_TRANSMIT] = prvMasterTransmitIT;
xI2CSeq->pxTransfer[I2C_DIRECTION_RECEIVE] = prvMasterReceiveIT;
xI2CSeq->pxNoOptionTransfer[I2C_DIRECTION_TRANSMIT] = prvMasterNoOptionTransmitIT;
xI2CSeq->pxNoOptionTransfer[I2C_DIRECTION_RECEIVE] = prvMasterNoOptionReceiveIT;
}
void vI2CSeqSlaveIT(I2CSeqHandle_t xI2CSeq) {
xI2CSeq->pxTransfer[I2C_DIRECTION_TRANSMIT] = prvSlaveReceiveIT;
xI2CSeq->pxTransfer[I2C_DIRECTION_RECEIVE] = prvSlaveTransmitIT;
xI2CSeq->pxNoOptionTransfer[I2C_DIRECTION_TRANSMIT] = prvSlaveNoOptionReceiveIT;
xI2CSeq->pxNoOptionTransfer[I2C_DIRECTION_RECEIVE] = prvSlaveNoOptionTransmitIT;
}
void vI2CSeqBufferLengthBytes(I2CSeqHandle_t xI2CSeq, size_t xBufferLengthBytes) {
if (xI2CSeq->pvBuffer) {
if (xI2CSeq->xBufferLengthBytes == xBufferLengthBytes) return;
vPortFree(xI2CSeq->pvBuffer);
}
xI2CSeq->pvBuffer = pvPortMalloc(xI2CSeq->xBufferLengthBytes = xBufferLengthBytes);
configASSERT(xI2CSeq->pvBuffer);
}
void vI2CSeqCopyFrom(I2CSeqHandle_t xI2CSeq, const void *pvData) {
memcpy(xI2CSeq->pvBuffer, pvData, xI2CSeq->xBufferLengthBytes);
}
void vI2CSeqCopyTo(I2CSeqHandle_t xI2CSeq, void *pvData) {
memcpy(pvData, xI2CSeq->pvBuffer, xI2CSeqXferBytes(xI2CSeq));
}
void *pvI2CSeqBuffer(I2CSeqHandle_t xI2CSeq) { return xI2CSeq->pvBuffer; }
size_t xI2CSeqBufferLengthBytes(I2CSeqHandle_t xI2CSeq) { return xI2CSeq->xBufferLengthBytes; }
size_t xI2CSeqXferBytes(I2CSeqHandle_t xI2CSeq) { return xI2CSeq->xBufferLengthBytes - xI2CSeq->xI2C->XferSize; }
int xI2CSeqFrame(I2CSeqHandle_t xI2CSeq, uint32_t ulOptions) {
return xI2CSeq->pxTransfer[xI2CSeq->ucTransferDirection](xI2CSeq, ulOptions);
}
int xI2CSeqFirstFrame(I2CSeqHandle_t xI2CSeq) { return xI2CSeqFrame(xI2CSeq, I2C_FIRST_FRAME); }
int xI2CSeqNextFrame(I2CSeqHandle_t xI2CSeq) { return xI2CSeqFrame(xI2CSeq, I2C_NEXT_FRAME); }
int xI2CSeqLastFrame(I2CSeqHandle_t xI2CSeq) { return xI2CSeqFrame(xI2CSeq, I2C_LAST_FRAME); }
int xI2CSeqNoOptionFrame(I2CSeqHandle_t xI2CSeq) {
return xI2CSeq->pxNoOptionTransfer[xI2CSeq->ucTransferDirection](xI2CSeq);
}
uint32_t xI2CSeqError(I2CSeqHandle_t xI2CSeq) { return HAL_I2C_GetError(xI2CSeq->xI2C); }
/*
* stm32xx_i2c_seq.h
*/
#pragma once
#include "stm32xx_i2c.h"
/*!
* \brief I2C sequencer opaque handle.
*
* Use a forward structure declaration *without* definition hence opaque.
*
* Abstract STM32 I2C slave transfer.
* The transfer has a direction, transmit or receive, representing the master's
* first frame transfer bit. Transmit means master wants write. Receive means
* master wants to read. The transfer also includes a matching address code.
*/
typedef struct I2CSeq *I2CSeqHandle_t;
/*!
* \brief Creates a new dynamic I2C sequencer.
*/
I2CSeqHandle_t xI2CSeqCreate(I2CHandle_t xI2C);
/*!
* \brief Deletes a previously-created I2C sequencer.
*/
void vI2CSeqDelete(I2CSeqHandle_t xI2CSeq);
/*!
* \brief Sets up transfer direction, transmit or receive.
*
* Performs sanity validation. Only the least-significant bit determines the
* direction: 0 for transmit, 1 for receive.
*/
void vI2CSeqTransferDirection(I2CSeqHandle_t xI2CSeq,
uint8_t ucTransferDirection);
void vI2CSeqTransmit(I2CSeqHandle_t xI2CSeq);
void vI2CSeqReceive(I2CSeqHandle_t xI2CSeq);
uint8_t ucI2CSeqTransferDirection(I2CSeqHandle_t xI2CSeq);
void vI2CSeqAddr(I2CSeqHandle_t xI2CSeq, uint8_t ucAddr);
uint8_t ucI2CSeqAddr(I2CSeqHandle_t xI2CSeq);
/*!
* \brief Sets up a sequential master-mode interrupt-driven transfer.
*/
void vI2CSeqMasterIT(I2CSeqHandle_t xI2CSeq);
/*!
* \brief Set up slave transfer for interrupt-driven usage.
*
* Notice the important transfer direction inversion for slaves. The slave
* transmits during receive transfers.
*/
void vI2CSeqSlaveIT(I2CSeqHandle_t xI2CSeq);
/*!
* \brief Allocates transfer buffer.
* \param[in] xBufferLengthBytes Length of the buffer in bytes.
*/
void vI2CSeqBufferLengthBytes(I2CSeqHandle_t xI2CSeq,
size_t xBufferLengthBytes);
/*!
* \brief Copies from data to buffer.
*/
void vI2CSeqCopyFrom(I2CSeqHandle_t xI2CSeq, const void *pvData);
/*!
* \brief Copies transferred bytes only.
*/
void vI2CSeqCopyTo(I2CSeqHandle_t xI2CSeq, void *pvData);
/*!
* Note that the buffer is mutable if allocated.
*/
void *pvI2CSeqBuffer(I2CSeqHandle_t xI2CSeq);
size_t xI2CSeqBufferLengthBytes(I2CSeqHandle_t xI2CSeq);
/*!
* The I2C handle's transfer size is a down counter starting at the initial
* transfer size.
*/
size_t xI2CSeqXferBytes(I2CSeqHandle_t xI2CSeq);
int xI2CSeqFrame(I2CSeqHandle_t xI2CSeq, uint32_t ulOptions);
/*!
* \brief Transfers first or first-and-next frame.
*/
int xI2CSeqFirstFrame(I2CSeqHandle_t xI2CSeq);
int xI2CSeqNextFrame(I2CSeqHandle_t xI2CSeq);
int xI2CSeqLastFrame(I2CSeqHandle_t xI2CSeq);
int xI2CSeqNoOptionFrame(I2CSeqHandle_t xI2CSeq);
uint32_t xI2CSeqError(I2CSeqHandle_t xI2CSeq);
/*
* stm32xx_i2c_slave.c
*/
#include "stm32xx_i2c_slave.h"
#include "stm32xx_i2c_cb.h"
#include "stm32xx_i2c_seq.h"
#include "stm32xx_registered_i2c.h"
#include "task.h"
#include <limits.h>
#include <stdio.h>
#include <string.h>
struct I2CDevice {
void (*pvSlaveTxCplt)(I2CSeqHandle_t xI2CSeq);
void (*pvSlaveRxCplt)(I2CSeqHandle_t xI2CSeq);
void (*pvAddr)(I2CSeqHandle_t xI2CSeq);
void (*pvError)(I2CSeqHandle_t xI2CSeq);
};
/*!
* \brief I2C slave internal structure.
*
* Persists the I2C handle, the slave task handle and its virtual I2C devices.
*/
struct I2CSlave {
I2CHandle_t xI2C;
TaskHandle_t xTask;
struct I2CDevice *pxDevices[1U << 7U];
};
#define SLAVE_TX_CPLT_NOTIFIED (1UL << 0U)
#define SLAVE_RX_CPLT_NOTIFIED (1UL << 1U)
#define ADDR_NOTIFIED (1UL << 2U)
#define ERROR_NOTIFIED (1UL << 3U)
#define STOP_NOTIFIED (1UL << 31U)
/*!
* Slave mode is straightforward: listen, receive then transmit, repeat forever.
*/
static portTASK_FUNCTION(prvI2CSlaveTask, pvParameters) {
I2CSlaveHandle_t xI2CSlave = pvParameters;
/*
* Set up the transfer. Transmit and receive implementations capture the slave
* context. There can be only one transfer at any one time.
*
* The underlying hardware abstraction layer fails to apply \c const
* correctness to the transmit data pointer. It does not modify the transmit
* bytes.
*/
/*
* Reuse an I2C sequencer.
*
* Set up the sequencer for interrupt-driven slave transfers.
*/
I2CSeqHandle_t xI2CSeq = xI2CSeqCreate(xI2CSlave->xI2C);
vI2CSeqSlaveIT(xI2CSeq);
/*
* Capture slave transmit complete, slave receive complete, listen complete,
* address and error.
*
* Bounce the interrupt signals to the slave task. This has an important side
* effect. The interrupts trigger a task-time response and the device relay
* runs at within a task rather than within an interrupt service routine.
*/
TaskHandle_t xTask = xTaskGetCurrentTaskHandle();
void prvSlaveTxCplt(I2CHandle_t xI2C) {
BaseType_t xSwitchRequired;
xTaskNotifyFromISR(xTask, SLAVE_TX_CPLT_NOTIFIED, eSetBits,
&xSwitchRequired);
portYIELD_FROM_ISR(xSwitchRequired);
}
void prvSlaveRxCplt(I2CHandle_t xI2C) {
BaseType_t xSwitchRequired;
xTaskNotifyFromISR(xTask, SLAVE_RX_CPLT_NOTIFIED, eSetBits,
&xSwitchRequired);
portYIELD_FROM_ISR(xSwitchRequired);
}
void prvListenCplt(I2CHandle_t xI2C) {
HAL_StatusTypeDef xStatus = HAL_I2C_EnableListen_IT(xI2C);
configASSERT(xStatus == HAL_OK);
}
/*
* The transfer direction relates to the master. Transmit direction means that
* the master wants to transmit and therefore the slave needs to receive.
*
* The implementation captures the I2C sequencer and updates its transfer
* direction and address. The updating runs at interrupt time.
*/
void prvAddr(I2CHandle_t xI2C, uint8_t ucTransferDirection,
uint16_t usAddrMatchCode) {
vI2CSeqTransferDirection(xI2CSeq, ucTransferDirection);
vI2CSeqAddr(xI2CSeq, usAddrMatchCode >> 1U);
BaseType_t xSwitchRequired;
xTaskNotifyFromISR(xTask, ADDR_NOTIFIED, eSetBits, &xSwitchRequired);
portYIELD_FROM_ISR(xSwitchRequired);
}
void prvError(I2CHandle_t xI2C) {
BaseType_t xSwitchRequired;
xTaskNotifyFromISR(xTask, ERROR_NOTIFIED, eSetBits, &xSwitchRequired);
portYIELD_FROM_ISR(xSwitchRequired);
}
ListItem_t *pxSlaveTxCplt = pxI2CRegisterSlaveTxCpltHandler(
xI2CSlave->xI2C, prvSlaveTxCplt, portMAX_DELAY);
ListItem_t *pxSlaveRxCplt = pxI2CRegisterSlaveRxCpltHandler(
xI2CSlave->xI2C, prvSlaveRxCplt, portMAX_DELAY);
ListItem_t *pxListenCplt = pxI2CRegisterListenCpltHandler(
xI2CSlave->xI2C, prvListenCplt, portMAX_DELAY);
ListItem_t *pxAddr =
pxI2CRegisterAddrHandler(xI2CSlave->xI2C, prvAddr, portMAX_DELAY);
ListItem_t *pxError =
pxI2CRegisterErrorHandler(xI2CSlave->xI2C, prvError, portMAX_DELAY);
/*
* Wait indefinitely for stop notification. Nothing else to do.
*/
if (HAL_I2C_EnableListen_IT(xI2CSlave->xI2C) == HAL_OK) {
uint32_t ulNotified;
do {
xTaskNotifyWait(0UL, ULONG_MAX, &ulNotified, portMAX_DELAY);
uint8_t ucAddr = ucI2CSeqAddr(xI2CSeq);
struct I2CDevice *pxDevice = xI2CSlave->pxDevices[ucAddr];
if (pxDevice) {
if ((ulNotified & SLAVE_TX_CPLT_NOTIFIED) && pxDevice->pvSlaveTxCplt)
pxDevice->pvSlaveTxCplt(xI2CSeq);
if ((ulNotified & SLAVE_RX_CPLT_NOTIFIED) && pxDevice->pvSlaveRxCplt)
pxDevice->pvSlaveRxCplt(xI2CSeq);
if ((ulNotified & ADDR_NOTIFIED) && pxDevice->pvAddr)
pxDevice->pvAddr(xI2CSeq);
if ((ulNotified & ERROR_NOTIFIED) && pxDevice->pvError)
pxDevice->pvError(xI2CSeq);
}
} while ((ulNotified & STOP_NOTIFIED) == 0UL);
}
/*
* Tear down the slave. Remove all its handlers before deleting its task.
* Do not forget to dispose of the sequencer.
*
* Always stop the slave *before* deleting it. Deleting frees the internal
* structure and therefore avoid accessing the structure members after the
* stop notification.
*/
(void)uxListRemove(pxSlaveTxCplt);
(void)uxListRemove(pxSlaveRxCplt);
(void)uxListRemove(pxListenCplt);
(void)uxListRemove(pxAddr);
(void)uxListRemove(pxError);
vI2CSeqDelete(xI2CSeq);
vTaskDelete(NULL);
}
I2CSlaveHandle_t xI2CSlaveCreate(I2CHandle_t xI2C) {
I2CSlaveHandle_t xI2CSlave = pvPortMalloc(sizeof(*xI2CSlave));
configASSERT(xI2CSlave);
xI2CSlave->xI2C = xI2C;
return xI2CSlave;
}
BaseType_t xI2CSlaveStart(I2CSlaveHandle_t xI2CSlave, const char *pcName) {
configASSERT(xI2CSlave->xTask == NULL);
char name[configMAX_TASK_NAME_LEN];
snprintf(name, sizeof(name), "%s-%d", pcName ? pcName : i2cslaveTASK_NAME,
xRegisteredCardinalOfI2C(xI2CSlave->xI2C));
return xTaskCreate(prvI2CSlaveTask, name, i2cslaveSTACK_DEPTH, xI2CSlave,
i2cslavePRIORITY, &xI2CSlave->xTask);
}
void vI2CSlaveStop(I2CSlaveHandle_t xI2CSlave) {
configASSERT(xI2CSlave->xTask);
xTaskNotify(xI2CSlave->xTask, STOP_NOTIFIED, eSetBits);
xI2CSlave->xTask = NULL;
}
void vI2CSlaveDelete(I2CSlaveHandle_t xI2CSlave) { vPortFree(xI2CSlave); }
/*!
* \param[in] ucAddr Unshift 7-bit slave address of virtual device.
*
* Constructs the slave device lazily. The slave is an association entity
* linking a slave with its bus address.
*
* \note Watch out for race conditions.
*/
static struct I2CDevice *prvI2CSlaveDevice(struct I2CSlave *pxI2CSlave,
uint8_t ucAddr) {
configASSERT((ucAddr & 0b10000000U) == 0b00000000U);
struct I2CDevice *pxDevice = pxI2CSlave->pxDevices[ucAddr];
if (pxDevice == NULL) {
pxDevice = pvPortMalloc(sizeof(*pxDevice));
configASSERT(pxDevice);
pxI2CSlave->pxDevices[ucAddr] = pxDevice;
}
return pxDevice;
}
void vI2CSlaveDeviceTxComplete(I2CSlaveHandle_t xI2CSlave, uint8_t ucAddr,
void (*pvSlaveTxCplt)(I2CSeqHandle_t xI2CSeq)) {
prvI2CSlaveDevice(xI2CSlave, ucAddr)->pvSlaveTxCplt = pvSlaveTxCplt;
}
void vI2CSlaveDeviceRxComplete(I2CSlaveHandle_t xI2CSlave, uint8_t ucAddr,
void (*pvSlaveRxCplt)(I2CSeqHandle_t xI2CSeq)) {
prvI2CSlaveDevice(xI2CSlave, ucAddr)->pvSlaveRxCplt = pvSlaveRxCplt;
}
void vI2CSlaveDeviceAddr(I2CSlaveHandle_t xI2CSlave, uint8_t ucAddr,
void (*pvAddr)(I2CSeqHandle_t xI2CSeq)) {
prvI2CSlaveDevice(xI2CSlave, ucAddr)->pvAddr = pvAddr;
}
void vI2CSlaveDeviceError(I2CSlaveHandle_t xI2CSlave, uint8_t ucAddr,
void (*pvError)(I2CSeqHandle_t xI2CSeq)) {
prvI2CSlaveDevice(xI2CSlave, ucAddr)->pvError = pvError;
}
/*
* stm32xx_i2c_slave.h
*/
#pragma once
#include "stm32xx_mcu.h"
#include "stm32xx_i2c_seq.h"
#include "FreeRTOS.h"
typedef I2C_HandleTypeDef *I2CHandle_t;
#ifndef i2cslaveBUFFER_LENGTH_BYTES
#define i2cslaveBUFFER_LENGTH_BYTES 256U
#endif
#ifndef i2cslaveSTACK_DEPTH
#define i2cslaveSTACK_DEPTH 2048U
#endif
#ifndef i2cslavePRIORITY
#define i2cslavePRIORITY 16U
#endif
#ifndef i2cslaveTASK_NAME
#define i2cslaveTASK_NAME "I2Cslave"
#endif
typedef struct I2CSlave *I2CSlaveHandle_t;
/*!
* \brief Creates a new I2C slave.
*
* The slave operates in interrupt-driven mode by default. Creating does not
* automatically start the slave; see \c xI2CSlaveStart.
*
* Creates and asserts a new slave. Halts if out of heap space.
*/
I2CSlaveHandle_t xI2CSlaveCreate(I2CHandle_t xI2C);
/*!
* \brief Starts the I2C slave task.
* \param[in] xI2CSlave Slave to start.
* \param[in] pcName Name of slave task or \c NULL for default name.
*
* \note Assumes that the service task does not already exist.
*/
BaseType_t xI2CSlaveStart(I2CSlaveHandle_t xI2CSlave, const char *pcName);
/*!
* \brief Stops the slave.
*/
void vI2CSlaveStop(I2CSlaveHandle_t xI2CSlave);
void vI2CSlaveDelete(I2CSlaveHandle_t xI2CSlave);
/*!
* \defgroup stm32xxI2CSlaveDevice I2C Slave Device for STM32xx
* \{
*/
void vI2CSlaveDeviceTxComplete(I2CSlaveHandle_t xI2CSlave, uint8_t ucAddr,
void (*pvSlaveTxCplt)(I2CSeqHandle_t xI2CSeq));
void vI2CSlaveDeviceRxComplete(I2CSlaveHandle_t xI2CSlave, uint8_t ucAddr,
void (*pvSlaveRxCplt)(I2CSeqHandle_t xI2CSeq));
/*!
* \brief Sets up a slave device address handler.
*/
void vI2CSlaveDeviceAddr(I2CSlaveHandle_t xI2CSlave, uint8_t ucAddr,
void (*pvAddr)(I2CSeqHandle_t xI2CSeq));
void vI2CSlaveDeviceError(I2CSlaveHandle_t xI2CSlave, uint8_t ucAddr,
void (*pvError)(I2CSeqHandle_t xI2CSeq));
/*!
* \}
*/
/*!
* \file
*
* Indirectly includes the HAL driver, assuming that the compiler has also
* defined the \c USE_HAL_DRIVER manifest constant.
*
* The list of STM32 microprocessors and micro-controllers is large. The defines
* below do not entirely encompass the currently existing range. ST continuously
* expands its variants. Please add your own particular version.
*
* Should the "defined" block below run selectively with mutual exclusion using
* \c elif pre-precessor directives? The design below makes them overlap in order
* to trigger multiple-defintion errors if the compiler defines more than one
* manifest constant simultaneously; choose one only.
*/
#pragma once
#if defined(STM32L452xx)
#define STM32xx L4
#define STM32L4xx 52
#endif
#if defined(STM32L4A6xx)
#define STM32xx L4
#define STM32L4xx A6
#endif
#if STM32xx == L4
#include "stm32l4xx.h"
#endif
/*
* stm32xx_registered_i2c.c
*/
#include "stm32xx_registered_i2c.h"
#ifdef HAL_I2C_MODULE_ENABLED
#include "registered_opaques.h"
static size_t prvHashOfOpaque(void *pvOpaque) {
I2C_HandleTypeDef *pxI2C = pvOpaque;
return (uint32_t)pxI2C->Instance >> 10U;
}
size_t xRegisteredCardinalOfI2C(I2C_HandleTypeDef *pxI2C) {
static void *pvOpaques[stm32xx_i2cMAX_INSTANCES];
static struct RegisteredOpaques prvRegisteredOpaques = {
.ppvOpaques = pvOpaques,
.xNumberOfOpaques = stm32xx_i2cMAX_INSTANCES,
.pxHashOfOpaqueFunction = prvHashOfOpaque};
return xRegisteredCardinalOfOpaque(&prvRegisteredOpaques, pxI2C);
}
#endif
/*
* stm32xx_registered_i2c.h
*/
#pragma once
#include "stm32xx_i2c.h"
#ifdef HAL_I2C_MODULE_ENABLED
size_t xRegisteredCardinalOfI2C(I2CHandle_t xI2C);
#endif
/*
* tmp10x.c
*/
#include "tmp10x.h"
#include "FreeRTOS.h"
#include "task.h"
#include "stm32xx_i2c_seq.h"
#include "stm32xx_i2c_slave.h"
#include <memory.h>
#include <stdio.h>
struct TMP10x {
TaskHandle_t xTask;
I2CSlaveHandle_t xI2CSlave;
uint8_t ucAddr;
};
/*!
* \brief Set up device at address.
*
* For the sake of simplicity, all errors assert. Assume the happy path.
*/
static portTASK_FUNCTION(prvTMP10xTask, pvParameters) {
struct TMP10x *xTMP10x = pvParameters;
/*
* 16-bit register file.
*/
uint16_t usFile[] = {0x1234U, 0xff00U, 0x1111U, 0xffffU};
void prvAddr(I2CSeqHandle_t xI2CSeq) {
switch (ucI2CSeqTransferDirection(xI2CSeq)) {
HAL_StatusTypeDef xStatus;
case I2C_DIRECTION_TRANSMIT:
/*
* Receive the first and next frames.
*/
vI2CSeqBufferLengthBytes(xI2CSeq, 3U);
xStatus = xI2CSeqFirstFrame(xI2CSeq);
configASSERT(xStatus == HAL_OK);
break;
case I2C_DIRECTION_RECEIVE:
/*
* Transmit the last frames.
*/
switch (xI2CSeqXferBytes(xI2CSeq)) {
uint8_t ucPtr;
case 1U:
vI2CSeqCopyTo(xI2CSeq, &ucPtr);
vI2CSeqCopyFrom(xI2CSeq, usFile + (ucPtr & 0x03U));
xStatus = xI2CSeqLastFrame(xI2CSeq);
configASSERT(xStatus == HAL_OK);
}
}
}
/*
* The slave buffer receive completes.
*/
void prvSlaveRxCplt(I2CSeqHandle_t xI2CSeq) {
switch (ucI2CSeqTransferDirection(xI2CSeq)) {
case I2C_DIRECTION_TRANSMIT:
switch (xI2CSeqXferBytes(xI2CSeq)) {
uint8_t *pcBuffer;
case 3U:
pcBuffer = pvI2CSeqBuffer(xI2CSeq);
usFile[pcBuffer[0U] & 0x03U] = (pcBuffer[1U] << 8U) | pcBuffer[2U];
}
}
}
vI2CSlaveDeviceAddr(xTMP10x->xI2CSlave, xTMP10x->ucAddr, prvAddr);
vI2CSlaveDeviceRxComplete(xTMP10x->xI2CSlave, xTMP10x->ucAddr,
prvSlaveRxCplt);
uint32_t ulNotified;
xTaskNotifyWait(0UL, 1UL, &ulNotified, portMAX_DELAY);
vI2CSlaveDeviceAddr(xTMP10x->xI2CSlave, xTMP10x->ucAddr, NULL);
vI2CSlaveDeviceRxComplete(xTMP10x->xI2CSlave, xTMP10x->ucAddr, NULL);
}
TMP10xHandle_t xTMP10xCreate(I2CSlaveHandle_t xI2CSlave, uint8_t ucAddr) {
TMP10xHandle_t xTMP10x = pvPortMalloc(sizeof(*xTMP10x));
configASSERT(xTMP10x);
xTMP10x->xI2CSlave = xI2CSlave;
xTMP10x->ucAddr = ucAddr;
return xTMP10x;
}
BaseType_t xTMP10xStart(TMP10xHandle_t xTMP10x) {
configASSERT(xTMP10x->xTask == NULL);
char name[configMAX_TASK_NAME_LEN];
snprintf(name, sizeof(name), "%s-%02x", tmp10xTASK_NAME, xTMP10x->ucAddr);
return xTaskCreate(prvTMP10xTask, name, tmp10xSTACK_DEPTH, xTMP10x,
tmp10xPRIORITY, &xTMP10x->xTask);
}
void vTMP10xStop(TMP10xHandle_t xTMP10x) {
configASSERT(xTMP10x->xTask);
xTaskNotify(xTMP10x->xTask, 1UL << 0U, eSetBits);
xTMP10x->xTask = NULL;
}
void vTMP10xDelete(TMP10xHandle_t xTMP10x) { vPortFree(xTMP10x); }
/*
* tmp10x.h
*/
#pragma once
#include "stm32xx_i2c_slave.h"
#ifndef tmp10xTASK_NAME
#define tmp10xTASK_NAME "TMP10x"
#endif
#ifndef tmp10xSTACK_DEPTH
#define tmp10xSTACK_DEPTH 1024U
#endif
#ifndef tmp10xPRIORITY
#define tmp10xPRIORITY 32U
#endif
typedef struct TMP10x *TMP10xHandle_t;
TMP10xHandle_t xTMP10xCreate(I2CSlaveHandle_t xI2CSlave, uint8_t ucAddr);
BaseType_t xTMP10xStart(TMP10xHandle_t xTMP10x);
void vTMP10xStop(TMP10xHandle_t xTMP10x);
void vTMP10xDelete(TMP10xHandle_t xTMP10x);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment