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 lwow_sys_ name.

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

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) 2024 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 lwow_ll_drv_win32 = {
 48    .init = init,
 49    .deinit = deinit,
 50    .set_baudrate = set_baudrate,
 51    .tx_rx = transmit_receive,
 52};
 53
 54static HANDLE com_port;
 55static DCB dcb = {0};
 56
 57static uint8_t
 58init(void* arg) {
 59    dcb.DCBlength = sizeof(dcb);
 60    LWOW_UNUSED(arg);
 61
 62    /* Open virtual file as read/write */
 63    com_port = CreateFileA("\\\\.\\COM4", GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, NULL);
 64
 65    /* First read current values */
 66    if (GetCommState(com_port, &dcb)) {
 67        COMMTIMEOUTS timeouts;
 68
 69        dcb.BaudRate = 115200;
 70        dcb.ByteSize = 8;
 71        dcb.Parity = NOPARITY;
 72        dcb.StopBits = ONESTOPBIT;
 73
 74        /* Try to set com port data */
 75        if (!SetCommState(com_port, &dcb)) {
 76            printf("Cannot get COM port..\r\n");
 77            return 0;
 78        }
 79
 80        if (GetCommTimeouts(com_port, &timeouts)) {
 81            /* Set timeout to return immediatelly from ReadFile function */
 82            timeouts.ReadIntervalTimeout = MAXDWORD;
 83            timeouts.ReadTotalTimeoutConstant = 0;
 84            timeouts.ReadTotalTimeoutMultiplier = 0;
 85            if (!SetCommTimeouts(com_port, &timeouts)) {
 86                printf("Cannot set COM PORT timeouts..\r\n");
 87            }
 88            GetCommTimeouts(com_port, &timeouts);
 89        } else {
 90            printf("Cannot get communication timeouts..\r\n");
 91            return 0;
 92        }
 93    } else {
 94        printf("Cannot get COM port info..\r\n");
 95        return 0;
 96    }
 97
 98    return 1;
 99}
100
101uint8_t
102deinit(void* arg) {
103    /* Disable UART peripheral */
104    LWOW_UNUSED(arg);
105
106    return 1;
107}
108
109uint8_t
110set_baudrate(uint32_t baud, void* arg) {
111    LWOW_UNUSED(arg);
112    /* Configure UART to selected baudrate */
113    dcb.BaudRate = baud;
114
115    /* Try to set com port data */
116    if (!SetCommState(com_port, &dcb)) {
117        printf("Cannot set COM port baudrate to %u bauds\r\n", (unsigned)baud);
118        return 0;
119    }
120
121    return 1;
122}
123
124uint8_t
125transmit_receive(const uint8_t* tx, uint8_t* rx, size_t len, void* arg) {
126    /* Perform data exchange */
127    size_t read = 0;
128    DWORD br;
129    LWOW_UNUSED(arg);
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) 2024 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 lwow_ll_drv_stm32 = {
 51    .init = init,
 52    .deinit = deinit,
 53    .set_baudrate = set_baudrate,
 54    .tx_rx = transmit_receive,
 55};
 56
 57static LL_USART_InitTypeDef usart_init;
 58
 59static uint8_t
 60init(void* arg) {
 61    LL_GPIO_InitTypeDef gpio_init;
 62
 63    /* Peripheral clock enable */
 64    ONEWIRE_USART_CLK_EN;
 65    ONEWIRE_TX_PORT_CLK_EN;
 66    ONEWIRE_RX_PORT_CLK_EN;
 67
 68    /* Configure GPIO pins */
 69    LL_GPIO_StructInit(&gpio_init);
 70    gpio_init.Mode = LL_GPIO_MODE_ALTERNATE;
 71    gpio_init.Speed = LL_GPIO_SPEED_FREQ_HIGH;
 72    gpio_init.OutputType = LL_GPIO_OUTPUT_OPENDRAIN;
 73    gpio_init.Pull = LL_GPIO_PULL_UP;
 74
 75    /* TX pin */
 76    gpio_init.Alternate = ONEWIRE_TX_PIN_AF;
 77    gpio_init.Pin = ONEWIRE_TX_PIN;
 78    LL_GPIO_Init(ONEWIRE_TX_PORT, &gpio_init);
 79
 80    /* RX pin */
 81    gpio_init.Alternate = ONEWIRE_RX_PIN_AF;
 82    gpio_init.Pin = ONEWIRE_RX_PIN;
 83    LL_GPIO_Init(ONEWIRE_RX_PORT, &gpio_init);
 84
 85    /* Configure UART peripherals */
 86    LL_USART_DeInit(ONEWIRE_USART);
 87    LL_USART_StructInit(&usart_init);
 88    usart_init.BaudRate = 9600;
 89    usart_init.DataWidth = LL_USART_DATAWIDTH_8B;
 90    usart_init.StopBits = LL_USART_STOPBITS_1;
 91    usart_init.Parity = LL_USART_PARITY_NONE;
 92    usart_init.TransferDirection = LL_USART_DIRECTION_TX_RX;
 93    usart_init.HardwareFlowControl = LL_USART_HWCONTROL_NONE;
 94    usart_init.OverSampling = LL_USART_OVERSAMPLING_16;
 95    LL_USART_Init(ONEWIRE_USART, &usart_init);
 96    LL_USART_ConfigAsyncMode(ONEWIRE_USART);
 97
 98    LWOW_UNUSED(arg);
 99
100    return 1;
101}
102
103static uint8_t
104deinit(void* arg) {
105    LL_USART_DeInit(ONEWIRE_USART);
106    LWOW_UNUSED(arg);
107    return 1;
108}
109
110static uint8_t
111set_baudrate(uint32_t baud, void* arg) {
112    usart_init.BaudRate = baud;
113    LL_USART_Init(ONEWIRE_USART, &usart_init);
114    LL_USART_ConfigAsyncMode(ONEWIRE_USART);
115    LWOW_UNUSED(arg);
116
117    return 1;
118}
119
120static uint8_t
121transmit_receive(const uint8_t* tx, uint8_t* rx, size_t len, void* arg) {
122    const uint8_t* t = tx;
123    uint8_t* r = rx;
124
125    /* Send byte with polling */
126    LL_USART_Enable(ONEWIRE_USART);
127    for (; len > 0; --len, ++t, ++r) {
128        LL_USART_TransmitData8(ONEWIRE_USART, *t);
129        while (!LL_USART_IsActiveFlag_TXE(ONEWIRE_USART)) {
130            ;
131        }
132        while (!LL_USART_IsActiveFlag_RXNE(ONEWIRE_USART)) {
133            ;
134        }
135        *r = LL_USART_ReceiveData8(ONEWIRE_USART);
136    }
137    while (!LL_USART_IsActiveFlag_TC(ONEWIRE_USART)) {}
138    LL_USART_Disable(ONEWIRE_USART);
139    LWOW_UNUSED(arg);
140    return 1;
141}
142
143#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) 2024 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 "system/lwow_sys.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    LWOW_UNUSED(arg);
42    *mutex = CreateMutex(NULL, 0, NULL);
43    return 1;
44}
45
46uint8_t
47lwow_sys_mutex_delete(LWOW_CFG_OS_MUTEX_HANDLE* mutex, void* arg) {
48    LWOW_UNUSED(arg);
49    CloseHandle(*mutex);
50    *mutex = NULL;
51    return 1;
52}
53
54uint8_t
55lwow_sys_mutex_wait(LWOW_CFG_OS_MUTEX_HANDLE* mutex, void* arg) {
56    LWOW_UNUSED(arg);
57    return WaitForSingleObject(*mutex, INFINITE) == WAIT_OBJECT_0;
58}
59
60uint8_t
61lwow_sys_mutex_release(LWOW_CFG_OS_MUTEX_HANDLE* mutex, void* arg) {
62    LWOW_UNUSED(arg);
63    return ReleaseMutex(*mutex);
64}
65
66#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) 2024 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 "system/lwow_sys.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 lwow_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) 2024 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 lwow_ll_drv_stm32_hal = {
 62    .init = init,
 63    .deinit = deinit,
 64    .set_baudrate = set_baudrate,
 65    .tx_rx = transmit_receive,
 66};
 67
 68static uint8_t
 69init(void* arg) {
 70    UART_HandleTypeDef* huart = arg;
 71
 72    LWOW_ASSERT0("arg != NULL", arg != NULL);
 73
 74    /* Initialize UART */
 75    HAL_UART_DeInit(huart);
 76    return HAL_UART_Init(huart) == HAL_OK;
 77}
 78
 79static uint8_t
 80deinit(void* arg) {
 81    UART_HandleTypeDef* huart = arg;
 82
 83    LWOW_ASSERT0("arg != NULL", arg != NULL);
 84
 85    return HAL_UART_DeInit(huart);
 86}
 87
 88static uint8_t
 89set_baudrate(uint32_t baud, void* arg) {
 90    UART_HandleTypeDef* huart = arg;
 91
 92    LWOW_ASSERT0("arg != NULL", arg != NULL);
 93
 94    huart->Init.BaudRate = baud;
 95    return init(huart);
 96}
 97
 98static uint8_t
 99transmit_receive(const uint8_t* tx, uint8_t* rx, size_t len, void* arg) {
100    UART_HandleTypeDef* huart = arg;
101    uint32_t start;
102
103    LWOW_ASSERT0("arg != NULL", arg != NULL);
104
105    /* Get current HAL tick */
106    start = HAL_GetTick();
107
108    /* Start RX in interrupt mode */
109    HAL_UART_Receive_IT(huart, rx, len);
110
111    /* Process TX in polling mode */
112    HAL_UART_Transmit(huart, (void*)tx, len, 100);
113
114    /* Wait RX to finish */
115    while (huart->RxState != HAL_UART_STATE_READY) {
116        if (HAL_GetTick() - start > 100) {
117            return 0;
118        }
119    }
120
121    return 1;
122}
123
124#endif /* !__DOXYGEN__ */

Low-Level driver for manual GPIO control

it is possible to use LwOW library even without available UARTs in the device (or if UARTs are being used for something else). Demo driver, that manipulates GPIO toggling is available in the repository.

LwOW low-level driver for manual GPIO control without UART
  1/**
  2 * \file            lwow_ll_stm32_single_gpio_driver.c
  3 * \brief           Driver for non-UART use, with single GPIO
  4 */
  5
  6/*
  7 * Copyright (c) 2024 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 "stm32l4xx_hal.h"
 35/* And all other includes */
 36
 37#if !__DOXYGEN__
 38
 39/* Pin setting */
 40#define GPIO_CLK_EN     LL_AHB2_GRP1_PERIPH_GPIOB
 41#define GPIO_PORT       GPIOB
 42#define GPIO_PIN        LL_GPIO_PIN_13
 43#define OW_PIN_LOW      LL_GPIO_ResetOutputPin(GPIO_PORT, GPIO_PIN)
 44#define OW_PIN_HIGH     LL_GPIO_SetOutputPin(GPIO_PORT, GPIO_PIN)
 45#define OW_PIN_INPUT    LL_GPIO_SetPinMode(GPIO_PORT, GPIO_PIN, LL_GPIO_MODE_INPUT)
 46#define OW_PIN_OUTPUT   LL_GPIO_SetPinMode(GPIO_PORT, GPIO_PIN, LL_GPIO_MODE_OUTPUT)
 47
 48/* Macros for irq lock */
 49#define IRQ_LOCK_DEFINE uint32_t primask = __get_PRIMASK()
 50#define IRQ_LOCK        __disable_irq()
 51#define IRQ_UNLOCK      __set_PRIMASK(primask)
 52
 53/* Function prototypes for driver */
 54static uint8_t prv_init(void* arg);
 55static uint8_t prv_deinit(void* arg);
 56static uint8_t prv_set_baudrate(uint32_t baud, void* arg);
 57static uint8_t prv_transmit_receive(const uint8_t* tx, uint8_t* rx, size_t len, void* arg);
 58
 59/* Global driver structure for application use */
 60const lwow_ll_drv_t ow_driver_gpio = {
 61    .init = prv_init, .deinit = prv_deinit, .set_baudrate = prv_set_baudrate, .tx_rx = prv_transmit_receive};
 62static uint32_t baudrate; /* Expected baudrate set by the application */
 63
 64/**
 65 * \brief           Actual data exchange function
 66 * 
 67 * This is the demo for STM32L4xx; with slight modifications it will work on any other architecture.
 68 * 
 69 * Requirements to be provided for application
 70 * - microseconds timing function (example below uses 16-bit timer as source)
 71 * - interrupt locking mechanism
 72 * - GPIO manupulation features (open-drain mode, pull, etc)
 73 * 
 74 * \param           low_init_pulse_time: Time in microseconds for initial low pulse width
 75 * \param           pre_sample_time: Time in us to wait after pin release.
 76 *                      Bus is samples after time expiration
 77 * \param           post_sample_time: Time in us to wait after sample has been completed
 78 * \return          Bus value, `1 == high`, `0 == low`
 79 */
 80static uint8_t
 81prv_exch(uint16_t low_init_pulse_time, uint16_t pre_sample_time, uint16_t post_sample_time) {
 82    uint8_t b = 0;
 83    uint16_t time, start_time;
 84    IRQ_LOCK_DEFINE;
 85
 86    /* Lock interrupts and start execution */
 87    IRQ_LOCK;
 88    time = timebase_get_us_tick();
 89
 90    /* Initiate start low pulse */
 91    start_time = time;
 92    OW_PIN_LOW;
 93    OW_PIN_OUTPUT;
 94    while ((uint16_t)((time = timebase_get_us_tick()) - start_time) < low_init_pulse_time) {}
 95
 96    /* Release line and wait for mid pulse */
 97    start_time = time;
 98    OW_PIN_INPUT;
 99    while ((uint16_t)((time = timebase_get_us_tick()) - start_time) < pre_sample_time) {}
100
101    /* Read pin state */
102    b = LL_GPIO_IsInputPinSet(GPIO_PORT, GPIO_PIN);
103
104    /* Interrupts can now be enabled from this point */
105    IRQ_UNLOCK;
106
107    /* Wait remaining time */
108    start_time = time;
109    while ((uint16_t)((time = timebase_get_us_tick()) - start_time) < post_sample_time) {}
110
111    return b;
112}
113
114/*******************************************/
115/* LwOW driver interface functions         */
116/*******************************************/
117static uint8_t
118prv_init(void* arg) {
119    LL_GPIO_InitTypeDef gpio_init;
120
121    /* Peripheral clock enable */
122    LL_AHB2_GRP1_EnableClock(GPIO_CLK_EN);
123
124    /* Configure GPIO pin with open-drain mode */
125    LL_GPIO_StructInit(&gpio_init);
126    gpio_init.Mode = LL_GPIO_MODE_INPUT;
127    gpio_init.Speed = LL_GPIO_SPEED_FREQ_HIGH;
128    gpio_init.OutputType = LL_GPIO_OUTPUT_OPENDRAIN;
129    gpio_init.Pull = LL_GPIO_PULL_UP;
130    gpio_init.Pin = GPIO_PIN;
131    LL_GPIO_Init(GPIO_PORT, &gpio_init);
132
133    LWOW_UNUSED(arg);
134    return 1;
135}
136
137static uint8_t
138prv_deinit(void* arg) {
139    LWOW_UNUSED(arg);
140    return 1;
141}
142
143static uint8_t
144prv_set_baudrate(uint32_t baud, void* arg) {
145    LWOW_UNUSED(arg);
146    baudrate = baud;
147    return 1;
148}
149
150static uint8_t
151prv_transmit_receive(const uint8_t* tx, uint8_t* rx, size_t len, void* arg) {
152    const uint8_t* t = tx;
153    uint8_t* r = rx;
154
155    /* 
156     * For baudrate set at 9600 - by UART definition
157     * this fits timing only for reset sequence at onewire level
158     * 
159     * Length must always be zero, or error is returned
160     */
161    if (baudrate == 9600) {
162        if (len == 1) {
163            uint8_t v = prv_exch(480, 70, 410);
164            *r = v ? *t : 0x01;
165        } else {
166            return 0;
167        }
168    } else if (baudrate == 115200) {
169        /*
170         * Regular transmission process
171         *
172         * Exchange values and set timings for different events,
173         * according to the byte value to be transmitted
174         */
175        for (size_t i = 0; i < len; ++i, ++r, ++t) {
176            uint8_t v = prv_exch(*t ? 6 : 60, *t ? 9 : 0, *t ? 55 : 10);
177
178            /*
179             * Set value as 0xFF in case of positive reading, 0x00 otherwise.
180             * This is to be compliant with LwOW UART expectations 
181             */
182            *r = v ? 0xFF : 0x00;
183        }
184    } else {
185        return 0;
186    }
187    LWOW_UNUSED(arg);
188    return 1;
189}
190
191#endif /* !__DOXYGEN__ */