Porting guide

Implement low-level driver

Implementation of low-level driver is an essential part. It links middleware with actual hardware design of the device.

Its implementation must provide 4 functions:

  • To open/configure UART hardware

  • To set UART baudrate on the fly

  • To transmit/receive data over UART

  • To close/de-init UART hardware

After these functions have been implemented (check below for references), driver must link these functions to single driver structure of type lwow_ll_drv_t, later used during instance initialization.

Tip

Check Low-level driver for function prototypes.

Implement system functions

System functions are required only if operating system mode is enabled, with LWOW_CFG_OS.

Its implementation structure is not the same as for low-level driver, customer needs to implement fixed functions, with pre-defined name, starting with ow_sys_ name.

System function must only support OS mutex management and has to provide:

  • ow_sys_mutex_create() function to create new mutex

  • ow_sys_mutex_delete() function to delete existing mutex

  • ow_sys_mutex_wait() function to wait for mutex to be available

  • ow_sys_mutex_release() function to release (give) mutex back

Warning

Application must define LWOW_CFG_OS_MUTEX_HANDLE for mutex type. This shall be done in lwow_opts.h file.

Tip

Check System functions for function prototypes.

Example: Low-level driver for WIN32

Example code for low-level porting on WIN32 platform. It uses native Windows features to open COM port and read/write from/to it.

Actual implementation of low-level driver for WIN32
  1/**
  2 * \file            lwow_ll_win32.c
  3 * \brief           UART implementation for WIN32
  4 */
  5
  6/*
  7 * Copyright (c) 2020 Tilen MAJERLE
  8 *
  9 * Permission is hereby granted, free of charge, to any person
 10 * obtaining a copy of this software and associated documentation
 11 * files (the "Software"), to deal in the Software without restriction,
 12 * including without limitation the rights to use, copy, modify, merge,
 13 * publish, distribute, sublicense, and/or sell copies of the Software,
 14 * and to permit persons to whom the Software is furnished to do so,
 15 * subject to the following conditions:
 16 *
 17 * The above copyright notice and this permission notice shall be
 18 * included in all copies or substantial portions of the Software.
 19 *
 20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 21 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 22 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
 23 * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 24 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 25 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 26 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 27 * OTHER DEALINGS IN THE SOFTWARE.
 28 *
 29 * This file is part of LwOW - Lightweight onewire library.
 30 *
 31 * Author:          Tilen MAJERLE <tilen@majerle.eu>
 32 * Version:         v3.0.2
 33 */
 34#include <stdio.h>
 35#include "lwow/lwow.h"
 36#include "windows.h"
 37
 38#if !__DOXYGEN__
 39
 40/* Function prototypes */
 41static uint8_t init(void* arg);
 42static uint8_t deinit(void* arg);
 43static uint8_t set_baudrate(uint32_t baud, void* arg);
 44static uint8_t transmit_receive(const uint8_t* tx, uint8_t* rx, size_t len, void* arg);
 45
 46/* Win 32 LL driver for OW */
 47const lwow_ll_drv_t
 48lwow_ll_drv_win32 = {
 49    .init = init,
 50    .deinit = deinit,
 51    .set_baudrate = set_baudrate,
 52    .tx_rx = transmit_receive,
 53};
 54
 55static HANDLE com_port;
 56static DCB dcb = { 0 };
 57
 58static uint8_t
 59init(void* arg) {
 60    dcb.DCBlength = sizeof(dcb);
 61
 62    /* Open virtual file as read/write */
 63    com_port = CreateFile(L"\\\\.\\COM4",
 64                          GENERIC_READ | GENERIC_WRITE,
 65                          0,
 66                          0,
 67                          OPEN_EXISTING,
 68                          0,
 69                          NULL
 70                         );
 71
 72    /* First read current values */
 73    if (GetCommState(com_port, &dcb)) {
 74        COMMTIMEOUTS timeouts;
 75
 76        dcb.BaudRate = 115200;
 77        dcb.ByteSize = 8;
 78        dcb.Parity = NOPARITY;
 79        dcb.StopBits = ONESTOPBIT;
 80
 81        /* Try to set com port data */
 82        if (!SetCommState(com_port, &dcb)) {
 83            printf("Cannot get COM port\r\n");
 84            return 0;
 85        }
 86
 87        if (GetCommTimeouts(com_port, &timeouts)) {
 88            /* Set timeout to return immediatelly from ReadFile function */
 89            timeouts.ReadIntervalTimeout = MAXDWORD;
 90            timeouts.ReadTotalTimeoutConstant = 0;
 91            timeouts.ReadTotalTimeoutMultiplier = 0;
 92            if (!SetCommTimeouts(com_port, &timeouts)) {
 93                printf("Cannot set COM PORT timeouts\r\n");
 94            }
 95            GetCommTimeouts(com_port, &timeouts);
 96        }
 97    } else {
 98        printf("Cannot get COM port info\r\n");
 99    }
100
101    return 1;
102}
103
104uint8_t
105deinit(void* arg) {
106    /* Disable UART peripheral */
107
108    return 1;
109}
110
111uint8_t
112set_baudrate(uint32_t baud, void* arg) {
113    /* Configure UART to selected baudrate */
114    dcb.BaudRate = baud;
115
116    /* Try to set com port data */
117    if (!SetCommState(com_port, &dcb)) {
118        printf("Cannot set COM port baudrate to %u bauds\r\n", (unsigned)baud);
119        return 0;
120    }
121
122    return 1;
123}
124
125uint8_t
126transmit_receive(const uint8_t* tx, uint8_t* rx, size_t len, void* arg) {
127    /* Perform data exchange */
128    size_t read = 0;
129    DWORD br;
130
131    if (com_port != NULL) {
132        /*
133         * Flush any data in RX buffer.
134         * This helps to reset communication in case of on-the-fly device management
135         * if one-or-more device(s) are added or removed.
136         *
137         * Any noise on UART level could start byte and put it to Win buffer,
138         * preventing to read aligned data from it
139         */
140        PurgeComm(com_port, PURGE_RXCLEAR | PURGE_RXABORT);
141
142        /* Write file and send data */
143        WriteFile(com_port, tx, len, &br, NULL);
144        FlushFileBuffers(com_port);
145
146        /* Read same amount of data as sent previously (loopback) */
147        do {
148            if (ReadFile(com_port, rx, (DWORD)(len - read), &br, NULL)) {
149                read += (size_t)br;
150                rx += (size_t)br;
151            }
152        } while (read < len);
153    }
154
155    return 1;
156}
157
158#endif /* !__DOXYGEN__ */

Example: Low-level driver for STM32

Example code for low-level porting on STM32 platform.

Actual implementation of low-level driver for STM32
  1/**
  2 * \file            lwow_ll_stm32.c
  3 * \brief           Generic UART implementation for STM32 MCUs
  4 */
  5
  6/*
  7 * Copyright (c) 2020 Tilen MAJERLE
  8 *
  9 * Permission is hereby granted, free of charge, to any person
 10 * obtaining a copy of this software and associated documentation
 11 * files (the "Software"), to deal in the Software without restriction,
 12 * including without limitation the rights to use, copy, modify, merge,
 13 * publish, distribute, sublicense, and/or sell copies of the Software,
 14 * and to permit persons to whom the Software is furnished to do so,
 15 * subject to the following conditions:
 16 *
 17 * The above copyright notice and this permission notice shall be
 18 * included in all copies or substantial portions of the Software.
 19 *
 20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 21 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 22 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
 23 * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 24 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 25 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 26 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 27 * OTHER DEALINGS IN THE SOFTWARE.
 28 *
 29 * This file is part of LwOW - Lightweight onewire library.
 30 *
 31 * Author:          Tilen MAJERLE <tilen@majerle.eu>
 32 * Version:         v3.0.2
 33 */
 34
 35/*
 36 * How it works
 37 *
 38 * https://docs.majerle.eu/projects/lwow/en/latest/user-manual/hw_connection.html#
 39 */
 40#include "lwow/lwow.h"
 41
 42#if !__DOXYGEN__
 43
 44static uint8_t init(void* arg);
 45static uint8_t deinit(void* arg);
 46static uint8_t set_baudrate(uint32_t baud, void* arg);
 47static uint8_t transmit_receive(const uint8_t* tx, uint8_t* rx, size_t len, void* arg);
 48
 49/* STM32 LL driver for OW */
 50const lwow_ll_drv_t
 51lwow_ll_drv_stm32 = {
 52    .init = init,
 53    .deinit = deinit,
 54    .set_baudrate = set_baudrate,
 55    .tx_rx = transmit_receive,
 56};
 57
 58static LL_USART_InitTypeDef
 59usart_init;
 60
 61static uint8_t
 62init(void* arg) {
 63    LL_GPIO_InitTypeDef gpio_init;
 64
 65    /* Peripheral clock enable */
 66    ONEWIRE_USART_CLK_EN;
 67    ONEWIRE_TX_PORT_CLK_EN;
 68    ONEWIRE_RX_PORT_CLK_EN;
 69
 70    /* Configure GPIO pins */
 71    LL_GPIO_StructInit(&gpio_init);
 72    gpio_init.Mode = LL_GPIO_MODE_ALTERNATE;
 73    gpio_init.Speed = LL_GPIO_SPEED_FREQ_HIGH;
 74    gpio_init.OutputType = LL_GPIO_OUTPUT_OPENDRAIN;
 75    gpio_init.Pull = LL_GPIO_PULL_UP;
 76
 77    /* TX pin */
 78    gpio_init.Alternate = ONEWIRE_TX_PIN_AF;
 79    gpio_init.Pin = ONEWIRE_TX_PIN;
 80    LL_GPIO_Init(ONEWIRE_TX_PORT, &gpio_init);
 81
 82    /* RX pin */
 83    gpio_init.Alternate = ONEWIRE_RX_PIN_AF;
 84    gpio_init.Pin = ONEWIRE_RX_PIN;
 85    LL_GPIO_Init(ONEWIRE_RX_PORT, &gpio_init);
 86
 87    /* Configure UART peripherals */
 88    LL_USART_DeInit(ONEWIRE_USART);
 89    LL_USART_StructInit(&usart_init);
 90    usart_init.BaudRate = 9600;
 91    usart_init.DataWidth = LL_USART_DATAWIDTH_8B;
 92    usart_init.StopBits = LL_USART_STOPBITS_1;
 93    usart_init.Parity = LL_USART_PARITY_NONE;
 94    usart_init.TransferDirection = LL_USART_DIRECTION_TX_RX;
 95    usart_init.HardwareFlowControl = LL_USART_HWCONTROL_NONE;
 96    usart_init.OverSampling = LL_USART_OVERSAMPLING_16;
 97    LL_USART_Init(ONEWIRE_USART, &usart_init);
 98    LL_USART_ConfigAsyncMode(ONEWIRE_USART);
 99
100    LWOW_UNUSED(arg);
101
102    return 1;
103}
104
105static uint8_t
106deinit(void* arg) {
107    LL_USART_DeInit(ONEWIRE_USART);
108    LWOW_UNUSED(arg);
109    return 1;
110}
111
112static uint8_t
113set_baudrate(uint32_t baud, void* arg) {
114    usart_init.BaudRate = baud;
115    LL_USART_Init(ONEWIRE_USART, &usart_init);
116    LL_USART_ConfigAsyncMode(ONEWIRE_USART);
117    LWOW_UNUSED(arg);
118
119    return 1;
120}
121
122static uint8_t
123transmit_receive(const uint8_t* tx, uint8_t* rx, size_t len, void* arg) {
124    const uint8_t* t = tx;
125    uint8_t* r = rx;
126
127    /* Send byte with polling */
128    LL_USART_Enable(ONEWIRE_USART);
129    for (; len > 0; --len, ++t, ++r) {
130        LL_USART_TransmitData8(ONEWIRE_USART, *t);
131        while (!LL_USART_IsActiveFlag_TXE(ONEWIRE_USART));
132        while (!LL_USART_IsActiveFlag_RXNE(ONEWIRE_USART));
133        *r = LL_USART_ReceiveData8(ONEWIRE_USART);
134    }
135    while (!LL_USART_IsActiveFlag_TC(ONEWIRE_USART)) {}
136    LL_USART_Disable(ONEWIRE_USART);
137    LWOW_UNUSED(arg);
138    return 1;
139}
140
141#endif /* !__DOXYGEN__ */

Example: System functions for WIN32

Actual implementation of system functions for WIN32
 1/**
 2 * \file            lwow_sys_win32.c
 3 * \brief           System functions for WIN32
 4 */
 5
 6/*
 7 * Copyright (c) 2020 Tilen MAJERLE
 8 *
 9 * Permission is hereby granted, free of charge, to any person
10 * obtaining a copy of this software and associated documentation
11 * files (the "Software"), to deal in the Software without restriction,
12 * including without limitation the rights to use, copy, modify, merge,
13 * publish, distribute, sublicense, and/or sell copies of the Software,
14 * and to permit persons to whom the Software is furnished to do so,
15 * subject to the following conditions:
16 *
17 * The above copyright notice and this permission notice shall be
18 * included in all copies or substantial portions of the Software.
19 *
20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
22 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
23 * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
24 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
25 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
27 * OTHER DEALINGS IN THE SOFTWARE.
28 *
29 * This file is part of LwOW - Lightweight onewire library.
30 *
31 * Author:          Tilen MAJERLE <tilen@majerle.eu>
32 * Version:         v3.0.2
33 */
34#include "lwow/lwow.h"
35#include "windows.h"
36
37#if LWOW_CFG_OS && !__DOXYGEN__
38
39uint8_t
40lwow_sys_mutex_create(LWOW_CFG_OS_MUTEX_HANDLE* mutex, void* arg) {
41    *mutex = CreateMutex(NULL, 0, NULL);
42    return 1;
43}
44
45uint8_t
46lwow_sys_mutex_delete(LWOW_CFG_OS_MUTEX_HANDLE* mutex, void* arg) {
47    CloseHandle(*mutex);
48    *mutex = NULL;
49    return 1;
50}
51
52uint8_t
53lwow_sys_mutex_wait(LWOW_CFG_OS_MUTEX_HANDLE* mutex, void* arg) {
54    return WaitForSingleObject(*mutex, INFINITE) == WAIT_OBJECT_0;
55}
56
57uint8_t
58lwow_sys_mutex_release(LWOW_CFG_OS_MUTEX_HANDLE* mutex, void* arg) {
59    return ReleaseMutex(*mutex);
60}
61
62#endif /* LWOW_CFG_OS && !__DOXYGEN__ */

Example: System functions for CMSIS-OS

Actual implementation of system functions for CMSIS-OS
 1/**
 2 * \file            lwow_sys_cmsis_os.c
 3 * \brief           System functions for CMSIS-OS based operating system
 4 */
 5
 6/*
 7 * Copyright (c) 2020 Tilen MAJERLE
 8 *
 9 * Permission is hereby granted, free of charge, to any person
10 * obtaining a copy of this software and associated documentation
11 * files (the "Software"), to deal in the Software without restriction,
12 * including without limitation the rights to use, copy, modify, merge,
13 * publish, distribute, sublicense, and/or sell copies of the Software,
14 * and to permit persons to whom the Software is furnished to do so,
15 * subject to the following conditions:
16 *
17 * The above copyright notice and this permission notice shall be
18 * included in all copies or substantial portions of the Software.
19 *
20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
22 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
23 * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
24 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
25 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
27 * OTHER DEALINGS IN THE SOFTWARE.
28 *
29 * This file is part of LwOW - Lightweight onewire library.
30 *
31 * Author:          Tilen MAJERLE <tilen@majerle.eu>
32 * Version:         v3.0.2
33 */
34#include "lwow/lwow.h"
35
36#if LWOW_CFG_OS && !__DOXYGEN__
37
38#include "cmsis_os.h"
39
40uint8_t
41lwow_sys_mutex_create(LWOW_CFG_OS_MUTEX_HANDLE* m, void* arg) {
42    LWOW_UNUSED(arg);
43    const osMutexAttr_t attr = {
44        .attr_bits = osMutexRecursive,
45        .name = "lwow_mutex",
46    };
47    return (*m = osMutexNew(&attr)) != NULL;
48}
49
50uint8_t
51lwow_sys_mutex_delete(LWOW_CFG_OS_MUTEX_HANDLE* m, void* arg) {
52    LWOW_UNUSED(arg);
53    return osMutexDelete(*m) == osOK;
54}
55
56uint8_t
57lwow_sys_mutex_wait(LWOW_CFG_OS_MUTEX_HANDLE* m, void* arg) {
58    LWOW_UNUSED(arg);
59    return osMutexAcquire(*m, osWaitForever) == osOK;
60}
61
62uint8_t
63lwow_sys_mutex_release(LWOW_CFG_OS_MUTEX_HANDLE* m, void* arg) {
64    LWOW_UNUSED(arg);
65    return osMutexRelease(*m) == osOK;
66}
67
68#endif /* LWOW_CFG_OS && !__DOXYGEN__ */

Low-Level driver for STM32 with STM32CubeMX

Specific low-level driver has been implemented for STM32 series of microcontrollers, to allow easy and simple link of LwOW library with projects generated with STM32CubeMX or STm32CubeIDE development tools.

Driver is based on HAL (Hardware Abstraction Layer) and it uses interrupt configuration to transmit/receive data. When customer starts a new project using CubeMX, it must:

  • Configure specific UART IP as async mode both directions

  • UART must have enabled global interrupts, to allow transmitting/receiving data using interrupts

  • Application must pass pointer to UART handle when calling ow_init function

Tip

Special example has been developed to demonstrate how can application use multiple OneWire instances on multiple UART ports at the same time. It uses custom argument to determine which UART handle shall be used for data transmit. Check /examples/stm32/ folder for actual implementation.

Actual implementation of low-level driver for STM32 with HAL drivers
  1/**
  2 * \file            lwow_ll_stm32_hal.c
  3 * \brief           UART driver implementation for STM32 with HAL code
  4 */
  5
  6/*
  7 * Copyright (c) 2020 Tilen MAJERLE
  8 *
  9 * Permission is hereby granted, free of charge, to any person
 10 * obtaining a copy of this software and associated documentation
 11 * files (the "Software"), to deal in the Software without restriction,
 12 * including without limitation the rights to use, copy, modify, merge,
 13 * publish, distribute, sublicense, and/or sell copies of the Software,
 14 * and to permit persons to whom the Software is furnished to do so,
 15 * subject to the following conditions:
 16 *
 17 * The above copyright notice and this permission notice shall be
 18 * included in all copies or substantial portions of the Software.
 19 *
 20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 21 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 22 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
 23 * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 24 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 25 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 26 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 27 * OTHER DEALINGS IN THE SOFTWARE.
 28 *
 29 * This file is part of LwOW - Lightweight onewire library.
 30 *
 31 * Author:          Tilen MAJERLE <tilen@majerle.eu>
 32 * Version:         v3.0.2
 33 */
 34
 35/*
 36 * How it works (general)
 37 *
 38 * https://docs.majerle.eu/projects/lwow/en/latest/user-manual/hw_connection.html#
 39 *
 40 * This specific driver is optimized for proejcts generated by STM32CubeMX or STM32CubeIDE with HAL drivers
 41 * It can be used w/ or w/o operating system and it uses interrupts & polling for data receive and data transmit.
 42 *
 43 * Application must pass pointer to UART handle as argument to ow_init function in order
 44 * to link OW instance with actual UART hardware used for OW instance.
 45 *
 46 * To use this driver, application must:
 47 * - Enable interrupt in CubeMX to allow HAL_UART_Receive_IT functionality
 48 * - Use pointer to UART handle when initializing ow with ow_init
 49 */
 50#include "lwow/lwow.h"
 51#include "main.h"                               /* Generated normally by CubeMX */
 52
 53#if !__DOXYGEN__
 54
 55static uint8_t init(void* arg);
 56static uint8_t deinit(void* arg);
 57static uint8_t set_baudrate(uint32_t baud, void* arg);
 58static uint8_t transmit_receive(const uint8_t* tx, uint8_t* rx, size_t len, void* arg);
 59
 60/* STM32 LL driver for OW */
 61const lwow_ll_drv_t
 62lwow_ll_drv_stm32_hal = {
 63    .init = init,
 64    .deinit = deinit,
 65    .set_baudrate = set_baudrate,
 66    .tx_rx = transmit_receive,
 67};
 68
 69static uint8_t
 70init(void* arg) {
 71    UART_HandleTypeDef* huart = arg;
 72
 73    LWOW_ASSERT0("arg != NULL", arg != NULL);
 74
 75    /* Initialize UART */
 76    HAL_UART_DeInit(huart);
 77    return HAL_UART_Init(huart) == HAL_OK;
 78}
 79
 80static uint8_t
 81deinit(void* arg) {
 82    UART_HandleTypeDef* huart = arg;
 83
 84    LWOW_ASSERT0("arg != NULL", arg != NULL);
 85
 86    return HAL_UART_DeInit(huart);
 87}
 88
 89static uint8_t
 90set_baudrate(uint32_t baud, void* arg) {
 91    UART_HandleTypeDef* huart = arg;
 92
 93    LWOW_ASSERT0("arg != NULL", arg != NULL);
 94
 95    huart->Init.BaudRate = baud;
 96    return init(huart);
 97}
 98
 99static uint8_t
100transmit_receive(const uint8_t* tx, uint8_t* rx, size_t len, void* arg) {
101    UART_HandleTypeDef* huart = arg;
102    uint32_t start;
103
104    LWOW_ASSERT0("arg != NULL", arg != NULL);
105
106    /* Get current HAL tick */
107    start = HAL_GetTick();
108
109    /* Start RX in interrupt mode */
110    HAL_UART_Receive_IT(huart, rx, len);
111
112    /* Process TX in polling mode */
113    HAL_UART_Transmit(huart, (void*)tx, len, 100);
114
115    /* Wait RX to finish */
116    while (huart->RxState != HAL_UART_STATE_READY) {
117        if (HAL_GetTick() - start > 100) {
118            return 0;
119        }
120    }
121
122    return 1;
123}
124
125#endif /* !__DOXYGEN__ */