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:
lwow_sys_mutex_create()
function to create new mutexlwow_sys_mutex_delete()
function to delete existing mutexlwow_sys_mutex_wait()
function to wait for mutex to be availablelwow_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.
1/**
2 * \file lwow_ll_win32.c
3 * \brief UART implementation for WIN32
4 */
5
6/*
7 * Copyright (c) 2023 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.
1/**
2 * \file lwow_ll_stm32.c
3 * \brief Generic UART implementation for STM32 MCUs
4 */
5
6/*
7 * Copyright (c) 2023 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
1/**
2 * \file lwow_sys_win32.c
3 * \brief System functions for WIN32
4 */
5
6/*
7 * Copyright (c) 2023 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
1/**
2 * \file lwow_sys_cmsis_os.c
3 * \brief System functions for CMSIS-OS based operating system
4 */
5
6/*
7 * Copyright (c) 2023 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.
1/**
2 * \file lwow_ll_stm32_hal.c
3 * \brief UART driver implementation for STM32 with HAL code
4 */
5
6/*
7 * Copyright (c) 2023 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.
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) 2023 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__ */