From eaa8d738e5ed87ea923f0ffd7acb5c0cd264e8b6 Mon Sep 17 00:00:00 2001 From: Elisabeth Friedrich Date: Thu, 30 Mar 2023 20:35:03 +0200 Subject: [PATCH] Feature: Allow RTOS CPU yielding MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The biggest advantage of using a RTOS (FreeRTOS, Zephyr, …) is their ability to hide the complexity of asynchronous systems, but this often requires cooperation by the threads. This library makes extensive use of busy waiting when it is being used in blocking mode, which in the way it was implemented broke the cooperation principle. To solve this this patch introduces a function named `Mcb_RelinquishCPU` to `mcb_usr.h`. This function is executed on every iteration in the libraries busy waiting loops. When using an RTOS, the user can override it in order to execute their native thread yield / wait / … functions and allow the OS reschedule the task. On non RTOS systems this is of course a no-op. Signed-off-by: Elisabeth Friedrich --- mcb.c | 34 ++++++++++++++++++++++++++++++++++ mcb_usr.c | 5 +++++ mcb_usr.h | 12 ++++++++++++ 3 files changed, 51 insertions(+) diff --git a/mcb.c b/mcb.c index 8e1b97c..0e5e2e8 100644 --- a/mcb.c +++ b/mcb.c @@ -181,6 +181,8 @@ static void Mcb_BlockingGetInfo(Mcb_TInst* ptInst, Mcb_TInfoMsg* pMcbInfoMsg) pMcbInfoMsg->eStatus = Mcb_IntfGetInfo(&ptInst->tIntf, pMcbInfoMsg->u16Node, pMcbInfoMsg->u16Addr, (uint16_t*)&pMcbInfoMsg->tInfoMsgData, &pMcbInfoMsg->u16Size); + Mcb_RelinquishCPU(); + if ((Mcb_GetMillis() - u32Millis) > ptInst->u32Timeout) { pMcbInfoMsg->eStatus = MCB_GETINFO_ERROR; @@ -199,6 +201,8 @@ static void Mcb_BlockingGetInfo(Mcb_TInst* ptInst, Mcb_TInfoMsg* pMcbInfoMsg) do { + Mcb_RelinquishCPU(); + if ((Mcb_GetMillis() - u32Millis) > ptInst->u32Timeout) { pMcbInfoMsg->eStatus = MCB_GETINFO_ERROR; @@ -231,6 +235,8 @@ static void Mcb_BlockingRead(Mcb_TInst* ptInst, Mcb_TMsg* pMcbMsg) pMcbMsg->eStatus = Mcb_IntfRead(&ptInst->tIntf, pMcbMsg->u16Node, pMcbMsg->u16Addr, &pMcbMsg->u16Data[0], &pMcbMsg->u16Size); + Mcb_RelinquishCPU(); + if ((Mcb_GetMillis() - u32Millis) > ptInst->u32Timeout) { pMcbMsg->eStatus = MCB_READ_ERROR; @@ -249,6 +255,8 @@ static void Mcb_BlockingRead(Mcb_TInst* ptInst, Mcb_TMsg* pMcbMsg) do { + Mcb_RelinquishCPU(); + if ((Mcb_GetMillis() - u32Millis) > ptInst->u32Timeout) { pMcbMsg->eStatus = MCB_READ_ERROR; @@ -281,6 +289,8 @@ static void Mcb_BlockingWrite(Mcb_TInst* ptInst, Mcb_TMsg* pMcbMsg) pMcbMsg->eStatus = Mcb_IntfWrite(&ptInst->tIntf, pMcbMsg->u16Node, pMcbMsg->u16Addr, &pMcbMsg->u16Data[0], &pMcbMsg->u16Size); + Mcb_RelinquishCPU(); + if ((Mcb_GetMillis() - u32Millis) > ptInst->u32Timeout) { pMcbMsg->eStatus = MCB_WRITE_ERROR; @@ -299,6 +309,8 @@ static void Mcb_BlockingWrite(Mcb_TInst* ptInst, Mcb_TMsg* pMcbMsg) do { + Mcb_RelinquishCPU(); + if ((Mcb_GetMillis() - u32Millis) > ptInst->u32Timeout) { pMcbMsg->eStatus = MCB_WRITE_ERROR; @@ -447,6 +459,8 @@ void* Mcb_TxMap(Mcb_TInst* ptInst, uint16_t u16Addr, uint16_t u16Sz) { ptInst->Mcb_Write(ptInst, &tMcbMsg); + Mcb_RelinquishCPU(); + if ((Mcb_GetMillis() - u32Millis) > ptInst->u32Timeout) { tMcbMsg.eStatus = MCB_WRITE_ERROR; @@ -513,6 +527,8 @@ void* Mcb_RxMap(Mcb_TInst* ptInst, uint16_t u16Addr, uint16_t u16Sz) { ptInst->Mcb_Write(ptInst, &tMcbMsg); + Mcb_RelinquishCPU(); + if ((Mcb_GetMillis() - u32Millis) > ptInst->u32Timeout) { tMcbMsg.eStatus = MCB_WRITE_ERROR; @@ -560,6 +576,8 @@ uint8_t Mcb_TxUnmap(Mcb_TInst* ptInst) { ptInst->Mcb_Write(ptInst, &tMcbMsg); + Mcb_RelinquishCPU(); + if ((Mcb_GetMillis() - u32Millis) > ptInst->u32Timeout) { tMcbMsg.eStatus = MCB_WRITE_ERROR; @@ -605,6 +623,8 @@ uint8_t Mcb_RxUnmap(Mcb_TInst* ptInst) { ptInst->Mcb_Write(ptInst, &tMcbMsg); + Mcb_RelinquishCPU(); + if ((Mcb_GetMillis() - u32Millis) > ptInst->u32Timeout) { tMcbMsg.eStatus = MCB_WRITE_ERROR; @@ -648,6 +668,8 @@ void Mcb_UnmapAll(Mcb_TInst* ptInst) { ptInst->Mcb_Write(ptInst, &tMcbMsg); + Mcb_RelinquishCPU(); + if ((Mcb_GetMillis() - u32Millis) > ptInst->u32Timeout) { tMcbMsg.eStatus = MCB_WRITE_ERROR; @@ -680,6 +702,8 @@ void Mcb_UnmapAll(Mcb_TInst* ptInst) { ptInst->Mcb_Write(ptInst, &tMcbMsg); + Mcb_RelinquishCPU(); + if ((Mcb_GetMillis() - u32Millis) > ptInst->u32Timeout) { tMcbMsg.eStatus = MCB_WRITE_ERROR; @@ -720,6 +744,8 @@ int32_t Mcb_EnableCyclic(Mcb_TInst* ptInst) { ptInst->Mcb_Write(ptInst, &tMcbMsg); + Mcb_RelinquishCPU(); + if ((Mcb_GetMillis() - u32Millis) > ptInst->u32Timeout) { tMcbMsg.eStatus = MCB_WRITE_ERROR; @@ -753,6 +779,8 @@ int32_t Mcb_EnableCyclic(Mcb_TInst* ptInst) { ptInst->Mcb_Write(ptInst, &tMcbMsg); + Mcb_RelinquishCPU(); + if ((Mcb_GetMillis() - u32Millis) > ptInst->u32Timeout) { tMcbMsg.eStatus = MCB_WRITE_ERROR; @@ -787,6 +815,8 @@ int32_t Mcb_EnableCyclic(Mcb_TInst* ptInst) { ptInst->Mcb_Write(ptInst, &tMcbMsg); + Mcb_RelinquishCPU(); + if ((Mcb_GetMillis() - u32Millis) > ptInst->u32Timeout) { tMcbMsg.eStatus = MCB_WRITE_ERROR; @@ -872,6 +902,8 @@ Mcb_ECyclicMode Mcb_GetCyclicMode(Mcb_TInst* ptInst) { ptInst->Mcb_Read(ptInst, &tMcbMsg); + Mcb_RelinquishCPU(); + if ((Mcb_GetMillis() - u32Millis) > ptInst->u32Timeout) { tMcbMsg.eStatus = MCB_READ_ERROR; @@ -908,6 +940,8 @@ Mcb_ECyclicMode Mcb_SetCyclicMode(Mcb_TInst* ptInst, Mcb_ECyclicMode eNewCycMode { ptInst->Mcb_Write(ptInst, &tMcbMsg); + Mcb_RelinquishCPU(); + if ((Mcb_GetMillis() - u32Millis) > ptInst->u32Timeout) { tMcbMsg.eStatus = MCB_WRITE_ERROR; diff --git a/mcb_usr.c b/mcb_usr.c index b39d611..2be3b01 100644 --- a/mcb_usr.c +++ b/mcb_usr.c @@ -28,6 +28,11 @@ __attribute__((weak))uint32_t Mcb_GetMillis(void) return (uint32_t)0U; } +__attribute__((weak))void Mcb_RelinquishCPU(void) +{ + return; +} + __attribute__((weak))bool Mcb_IntfIsReady(uint16_t u16Id) { /** Check if SPI instance is ready for initiate a new transmission */ diff --git a/mcb_usr.h b/mcb_usr.h index 2aa559d..b205e58 100644 --- a/mcb_usr.h +++ b/mcb_usr.h @@ -169,6 +169,18 @@ Mcb_IntfCheckCrc(uint16_t u16Id, const uint16_t* pu16Buf, uint16_t u16Sz); uint32_t Mcb_GetMillis(void); +/** + * Relinquish (give up / allow others to execute) CPU. + * This is especially useful on RTOS systems which rely on cooperative + * threads in order share the CPU time. + * This routine will be invoked on every iteration if a blocking method + * uses busy waiting (i.e. timeouts). + * + * This should be a no-op on non RTOS systems. + */ +void +Mcb_RelinquishCPU(void); + /** * Executes a SPI transfer *