Porting guide
High level of ESP-AT library is platform independent, written in ANSI C99, however there is an important part where middleware needs to communicate with target ESP device and it must work under different optional operating systems selected by final customer.
Porting consists of:
Implementation of low-level part, for actual communication between host device and ESP device
Implementation of system functions, link between target operating system and middleware functions
Assignment of memory for allocation manager
Implement low-level driver
To successfully prepare all parts of low-level driver, application must take care of:
Implementing
lwesp_ll_init()
andlwesp_ll_deinit()
callback functionsImplement and assign send data and optional hardware reset function callbacks
Assign memory for allocation manager when using default allocator or use custom allocator
Process received data from ESP device and send it to input module for further processing
Tip
Port examples are available for STM32 and WIN32 architectures. Both actual working and up-to-date implementations are available within the library.
Note
Check Input module for more information about direct & indirect input processing.
Implement system functions
System functions are bridge between operating system calls and ESP middleware. ESP library relies on stable operating system features and its implementation and does not require any special features which do not normally come with operating systems.
Operating system must support:
Thread management functions
Mutex management functions
Binary semaphores only, no need for counting semaphores
Message queue management functions
Warning
If any of the features are not available within targeted operating system, customer needs to resolve it with care. As an example, message queue is not available in WIN32 OS API therefore custom message queue has been implemented using binary semaphores
Application needs to implement all system call functions, starting with lwesp_sys_
.
It must also prepare header file for standard types in order to support OS types within ESP middleware.
An example code is provided latter section of this page for WIN32 and STM32.
Steps to follow
Copy
lwesp/src/system/lwesp_sys_template.c
to the same folder and rename it to application port, eg.lwesp_sys_win32.c
Open newly created file and implement all system functions
Copy folder
lwesp/src/include/system/port/template/*
to the same folder and rename folder name to application port, eg.cmsis_os
Open
lwesp_sys_port.h
file from newly created folder and implement all typedefs and macros for specific targetAdd source file to compiler sources and add path to header file to include paths in compiler options
Note
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.
Notes:
It uses separate thread for received data processing. It uses
lwesp_input_process()
orlwesp_input()
functions, based on application configuration ofLWESP_CFG_INPUT_USE_PROCESS
parameter.When
LWESP_CFG_INPUT_USE_PROCESS
is disabled, dedicated receive buffer is created by ESP-AT library andlwesp_input()
function just writes data to it and does not process received characters immediately. This is handled by Processing thread at later stage instead.When
LWESP_CFG_INPUT_USE_PROCESS
is enabled,lwesp_input_process()
is used, which directly processes input data and sends potential callback/event functions to application layer.
Memory manager has been assigned to
1
region ofLWESP_MEM_SIZE
sizeIt sets send and reset callback functions for ESP-AT library
1/**
2 * \file lwesp_ll_win32.c
3 * \brief Low-level communication with ESP device 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 LwESP - Lightweight ESP-AT parser library.
30 *
31 * Author: Tilen MAJERLE <tilen@majerle.eu>
32 * Version: v1.1.2-dev
33 */
34#include "lwesp/lwesp.h"
35#include "lwesp/lwesp_input.h"
36#include "lwesp/lwesp_mem.h"
37#include "system/lwesp_ll.h"
38
39#if !__DOXYGEN__
40
41volatile uint8_t lwesp_ll_win32_driver_ignore_data;
42static uint8_t initialized = 0;
43static HANDLE thread_handle;
44static volatile HANDLE com_port; /*!< COM port handle */
45static uint8_t data_buffer[0x1000]; /*!< Received data array */
46
47static void uart_thread(void* param);
48
49/**
50 * \brief Send data to ESP device, function called from ESP stack when we have data to send
51 */
52static size_t
53send_data(const void* data, size_t len) {
54 DWORD written;
55 if (com_port != NULL) {
56#if !LWESP_CFG_AT_ECHO
57 const uint8_t* d = data;
58 HANDLE hConsole;
59
60 hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
61 SetConsoleTextAttribute(hConsole, FOREGROUND_RED);
62 for (DWORD i = 0; i < len; ++i) {
63 printf("%c", d[i]);
64 }
65 SetConsoleTextAttribute(hConsole, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
66#endif /* !LWESP_CFG_AT_ECHO */
67
68 WriteFile(com_port, data, len, &written, NULL);
69 FlushFileBuffers(com_port);
70 return written;
71 }
72 return 0;
73}
74
75/**
76 * \brief Configure UART (USB to UART)
77 * \return `1` if initialized, `0` otherwise
78 */
79static uint8_t
80configure_uart(uint32_t baudrate) {
81 size_t i;
82 DCB dcb = {.DCBlength = sizeof(dcb)};
83
84 /*
85 * List of COM ports to probe for ESP devices
86 * This may be different on your computer
87 */
88 static const char* com_port_names[] = {"\\\\.\\COM17", "\\\\.\\COM4", "\\\\.\\COM8", "\\\\.\\COM9", "\\\\.\\COM10"};
89
90 /* Try to open one of listed COM ports */
91 if (!initialized) {
92 printf("Initializing COM port first time\r\n");
93 for (i = 0; i < LWESP_ARRAYSIZE(com_port_names); ++i) {
94 printf("Trying to open COM port %s\r\n", com_port_names[i]);
95 com_port = CreateFileA(com_port_names[i], GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, NULL);
96 if (GetCommState(com_port, &dcb)) {
97 printf("Successfully received info for COM port %s. Using this one..\r\n", com_port_names[i]);
98 break;
99 } else {
100 printf("Could not get info for COM port %s\r\n", com_port_names[i]);
101 }
102 }
103 if (i == LWESP_ARRAYSIZE(com_port_names)) {
104 printf("Could not get info for any COM port. Entering while loop\r\n");
105 while (1) {
106 Sleep(1000);
107 }
108 }
109 }
110
111 /* Configure COM port parameters */
112 if (GetCommState(com_port, &dcb)) {
113 COMMTIMEOUTS timeouts;
114
115 /* Set port config */
116 dcb.BaudRate = baudrate;
117 dcb.ByteSize = 8;
118 dcb.Parity = NOPARITY;
119 dcb.StopBits = ONESTOPBIT;
120 if (SetCommState(com_port, &dcb)) {
121 /* Set timeouts config */
122 if (GetCommTimeouts(com_port, &timeouts)) {
123 /* Set timeout to return immediately from ReadFile function */
124 timeouts.ReadIntervalTimeout = MAXDWORD;
125 timeouts.ReadTotalTimeoutConstant = 0;
126 timeouts.ReadTotalTimeoutMultiplier = 0;
127 if (SetCommTimeouts(com_port, &timeouts)) {
128 GetCommTimeouts(com_port, &timeouts);
129 } else {
130 printf("[LWESP LL] Could not set port timeout config\r\n");
131 }
132 } else {
133 printf("[LWESP LL] Could not get port timeout config\r\n");
134 }
135 } else {
136 printf("[LWESP LL] Could not set port config\r\n");
137 }
138 } else {
139 printf("[LWESP LL] Could not get port info\r\n");
140 }
141
142 /* On first function call, create a thread to read data from COM port */
143 if (!initialized) {
144 lwesp_sys_thread_create(&thread_handle, "lwesp_ll_thread", uart_thread, NULL, 0, 0);
145 }
146 return 1;
147}
148
149/**
150 * \brief UART thread
151 */
152static void
153uart_thread(void* param) {
154 DWORD bytes_read;
155 lwesp_sys_sem_t sem;
156 FILE* file = NULL;
157
158 LWESP_UNUSED(param);
159
160 lwesp_sys_sem_create(&sem, 0); /* Create semaphore for delay functions */
161 while (com_port == NULL) {
162 lwesp_sys_sem_wait(&sem, 1); /* Add some delay with yield */
163 }
164
165 fopen_s(&file, "log_file.txt", "w+"); /* Open debug file in write mode */
166 while (1) {
167 while (com_port == NULL) {
168 lwesp_sys_sem_wait(&sem, 1);
169 }
170
171 /*
172 * Try to read data from COM port
173 * and send it to upper layer for processing
174 */
175 do {
176 ReadFile(com_port, data_buffer, sizeof(data_buffer), &bytes_read, NULL);
177 if (bytes_read > 0) {
178 HANDLE hConsole;
179 hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
180 SetConsoleTextAttribute(hConsole, FOREGROUND_GREEN);
181 for (DWORD i = 0; i < bytes_read; ++i) {
182 printf("%c", data_buffer[i]);
183 }
184 SetConsoleTextAttribute(hConsole, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
185
186 if (lwesp_ll_win32_driver_ignore_data) {
187 printf("IGNORING..\r\n");
188 continue;
189 }
190
191 /* Send received data to input processing module */
192#if LWESP_CFG_INPUT_USE_PROCESS
193 lwesp_input_process(data_buffer, (size_t)bytes_read);
194#else /* LWESP_CFG_INPUT_USE_PROCESS */
195 lwesp_input(data_buffer, (size_t)bytes_read);
196#endif /* !LWESP_CFG_INPUT_USE_PROCESS */
197
198 /* Write received data to output debug file */
199 if (file != NULL) {
200 fwrite(data_buffer, 1, bytes_read, file);
201 fflush(file);
202 }
203 }
204 } while (bytes_read == (DWORD)sizeof(data_buffer));
205
206 /* Implement delay to allow other tasks processing */
207 lwesp_sys_sem_wait(&sem, 1);
208 }
209}
210
211/**
212 * \brief Reset device GPIO management
213 */
214static uint8_t
215reset_device(uint8_t state) {
216 LWESP_UNUSED(state);
217 return 0; /* Hardware reset was not successful */
218}
219
220/**
221 * \brief Callback function called from initialization process
222 */
223lwespr_t
224lwesp_ll_init(lwesp_ll_t* ll) {
225#if !LWESP_CFG_MEM_CUSTOM
226 /* Step 1: Configure memory for dynamic allocations */
227 static uint8_t memory[0x10000]; /* Create memory for dynamic allocations with specific size */
228
229 /*
230 * Create memory region(s) of memory.
231 * If device has internal/external memory available,
232 * multiple memories may be used
233 */
234 lwesp_mem_region_t mem_regions[] = {{memory, sizeof(memory)}};
235 if (!initialized) {
236 lwesp_mem_assignmemory(mem_regions,
237 LWESP_ARRAYSIZE(mem_regions)); /* Assign memory for allocations to ESP library */
238 }
239#endif /* !LWESP_CFG_MEM_CUSTOM */
240
241 /* Step 2: Set AT port send function to use when we have data to transmit */
242 if (!initialized) {
243 ll->send_fn = send_data; /* Set callback function to send data */
244 ll->reset_fn = reset_device;
245 }
246
247 /* Step 3: Configure AT port to be able to send/receive data to/from ESP device */
248 if (!configure_uart(ll->uart.baudrate)) { /* Initialize UART for communication */
249 return lwespERR;
250 }
251 initialized = 1;
252 return lwespOK;
253}
254
255/**
256 * \brief Callback function to de-init low-level communication part
257 */
258lwespr_t
259lwesp_ll_deinit(lwesp_ll_t* ll) {
260 LWESP_UNUSED(ll);
261 if (thread_handle != NULL) {
262 lwesp_sys_thread_terminate(&thread_handle);
263 thread_handle = NULL;
264 }
265 initialized = 0; /* Clear initialized flag */
266 return lwespOK;
267}
268
269#endif /* !__DOXYGEN__ */
Example: Low-level driver for STM32
Example code for low-level porting on STM32 platform. It uses CMSIS-OS based application layer functions for implementing threads & other OS dependent features.
Notes:
It uses separate thread for received data processing. It uses
lwesp_input_process()
function to directly process received data without using intermediate receive bufferMemory manager has been assigned to
1
region ofLWESP_MEM_SIZE
sizeIt sets send and reset callback functions for ESP-AT library
1/**
2 * \file lwesp_ll_stm32.c
3 * \brief Generic STM32 driver, included in various STM32 driver variants
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 LwESP - Lightweight ESP-AT parser library.
30 *
31 * Author: Tilen MAJERLE <tilen@majerle.eu>
32 * Version: v1.1.2-dev
33 */
34
35/*
36 * How it works
37 *
38 * On first call to \ref lwesp_ll_init, new thread is created and processed in usart_ll_thread function.
39 * USART is configured in RX DMA mode and any incoming bytes are processed inside thread function.
40 * DMA and USART implement interrupt handlers to notify main thread about new data ready to send to upper layer.
41 *
42 * More about UART + RX DMA: https://github.com/MaJerle/stm32-usart-dma-rx-tx
43 *
44 * \ref LWESP_CFG_INPUT_USE_PROCESS must be enabled in `lwesp_config.h` to use this driver.
45 */
46#include "lwesp/lwesp.h"
47#include "lwesp/lwesp_input.h"
48#include "lwesp/lwesp_mem.h"
49#include "system/lwesp_ll.h"
50
51#if !__DOXYGEN__
52
53#if !LWESP_CFG_INPUT_USE_PROCESS
54#error "LWESP_CFG_INPUT_USE_PROCESS must be enabled in `lwesp_config.h` to use this driver."
55#endif /* LWESP_CFG_INPUT_USE_PROCESS */
56
57#if !defined(LWESP_USART_DMA_RX_BUFF_SIZE)
58#define LWESP_USART_DMA_RX_BUFF_SIZE 0x1000
59#endif /* !defined(LWESP_USART_DMA_RX_BUFF_SIZE) */
60
61#if !defined(LWESP_MEM_SIZE)
62#define LWESP_MEM_SIZE 0x1000
63#endif /* !defined(LWESP_MEM_SIZE) */
64
65#if !defined(LWESP_USART_RDR_NAME)
66#define LWESP_USART_RDR_NAME RDR
67#endif /* !defined(LWESP_USART_RDR_NAME) */
68
69/* USART memory */
70static uint8_t usart_mem[LWESP_USART_DMA_RX_BUFF_SIZE];
71static uint8_t is_running, initialized;
72static size_t old_pos;
73
74/* USART thread */
75static void usart_ll_thread(void* arg);
76static osThreadId_t usart_ll_thread_id;
77
78/* Message queue */
79static osMessageQueueId_t usart_ll_mbox_id;
80
81/**
82 * \brief USART data processing
83 */
84static void
85usart_ll_thread(void* arg) {
86 size_t pos;
87
88 LWESP_UNUSED(arg);
89
90 while (1) {
91 void* d;
92 /* Wait for the event message from DMA or USART */
93 osMessageQueueGet(usart_ll_mbox_id, &d, NULL, osWaitForever);
94
95 /* Read data */
96#if defined(LWESP_USART_DMA_RX_STREAM)
97 pos = sizeof(usart_mem) - LL_DMA_GetDataLength(LWESP_USART_DMA, LWESP_USART_DMA_RX_STREAM);
98#else
99 pos = sizeof(usart_mem) - LL_DMA_GetDataLength(LWESP_USART_DMA, LWESP_USART_DMA_RX_CH);
100#endif /* defined(LWESP_USART_DMA_RX_STREAM) */
101 if (pos != old_pos && is_running) {
102 if (pos > old_pos) {
103 lwesp_input_process(&usart_mem[old_pos], pos - old_pos);
104 } else {
105 lwesp_input_process(&usart_mem[old_pos], sizeof(usart_mem) - old_pos);
106 if (pos > 0) {
107 lwesp_input_process(&usart_mem[0], pos);
108 }
109 }
110 old_pos = pos;
111 }
112 }
113}
114
115/**
116 * \brief Configure UART using DMA for receive in double buffer mode and IDLE line detection
117 */
118static void
119prv_configure_uart(uint32_t baudrate) {
120 static LL_USART_InitTypeDef usart_init;
121 static LL_DMA_InitTypeDef dma_init;
122 LL_GPIO_InitTypeDef gpio_init;
123
124 if (!initialized) {
125 /* Enable peripheral clocks */
126 LWESP_USART_CLK;
127 LWESP_USART_DMA_CLK;
128 LWESP_USART_TX_PORT_CLK;
129 LWESP_USART_RX_PORT_CLK;
130
131#if defined(LWESP_RESET_PIN)
132 LWESP_RESET_PORT_CLK;
133#endif /* defined(LWESP_RESET_PIN) */
134
135#if defined(LWESP_GPIO0_PIN)
136 LWESP_GPIO0_PORT_CLK;
137#endif /* defined(LWESP_GPIO0_PIN) */
138
139#if defined(LWESP_GPIO2_PIN)
140 LWESP_GPIO2_PORT_CLK;
141#endif /* defined(LWESP_GPIO2_PIN) */
142
143#if defined(LWESP_CH_PD_PIN)
144 LWESP_CH_PD_PORT_CLK;
145#endif /* defined(LWESP_CH_PD_PIN) */
146
147 /* Global pin configuration */
148 LL_GPIO_StructInit(&gpio_init);
149 gpio_init.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
150 gpio_init.Pull = LL_GPIO_PULL_UP;
151 gpio_init.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH;
152 gpio_init.Mode = LL_GPIO_MODE_OUTPUT;
153
154#if defined(LWESP_RESET_PIN)
155 /* Configure RESET pin */
156 gpio_init.Pin = LWESP_RESET_PIN;
157 LL_GPIO_Init(LWESP_RESET_PORT, &gpio_init);
158#endif /* defined(LWESP_RESET_PIN) */
159
160#if defined(LWESP_GPIO0_PIN)
161 /* Configure GPIO0 pin */
162 gpio_init.Pin = LWESP_GPIO0_PIN;
163 LL_GPIO_Init(LWESP_GPIO0_PORT, &gpio_init);
164 LL_GPIO_SetOutputPin(LWESP_GPIO0_PORT, LWESP_GPIO0_PIN);
165#endif /* defined(LWESP_GPIO0_PIN) */
166
167#if defined(LWESP_GPIO2_PIN)
168 /* Configure GPIO2 pin */
169 gpio_init.Pin = LWESP_GPIO2_PIN;
170 LL_GPIO_Init(LWESP_GPIO2_PORT, &gpio_init);
171 LL_GPIO_SetOutputPin(LWESP_GPIO2_PORT, LWESP_GPIO2_PIN);
172#endif /* defined(LWESP_GPIO2_PIN) */
173
174#if defined(LWESP_CH_PD_PIN)
175 /* Configure CH_PD pin */
176 gpio_init.Pin = LWESP_CH_PD_PIN;
177 LL_GPIO_Init(LWESP_CH_PD_PORT, &gpio_init);
178 LL_GPIO_SetOutputPin(LWESP_CH_PD_PORT, LWESP_CH_PD_PIN);
179#endif /* defined(LWESP_CH_PD_PIN) */
180
181 /* Configure USART pins */
182 gpio_init.Mode = LL_GPIO_MODE_ALTERNATE;
183
184 /* TX PIN */
185 gpio_init.Alternate = LWESP_USART_TX_PIN_AF;
186 gpio_init.Pin = LWESP_USART_TX_PIN;
187 LL_GPIO_Init(LWESP_USART_TX_PORT, &gpio_init);
188
189 /* RX PIN */
190 gpio_init.Alternate = LWESP_USART_RX_PIN_AF;
191 gpio_init.Pin = LWESP_USART_RX_PIN;
192 LL_GPIO_Init(LWESP_USART_RX_PORT, &gpio_init);
193
194 /* Configure UART */
195 LL_USART_DeInit(LWESP_USART);
196 LL_USART_StructInit(&usart_init);
197 usart_init.BaudRate = baudrate;
198 usart_init.DataWidth = LL_USART_DATAWIDTH_8B;
199 usart_init.HardwareFlowControl = LL_USART_HWCONTROL_NONE;
200 usart_init.OverSampling = LL_USART_OVERSAMPLING_16;
201 usart_init.Parity = LL_USART_PARITY_NONE;
202 usart_init.StopBits = LL_USART_STOPBITS_1;
203 usart_init.TransferDirection = LL_USART_DIRECTION_TX_RX;
204 LL_USART_Init(LWESP_USART, &usart_init);
205
206 /* Enable USART interrupts and DMA request */
207 LL_USART_EnableIT_IDLE(LWESP_USART);
208 LL_USART_EnableIT_PE(LWESP_USART);
209 LL_USART_EnableIT_ERROR(LWESP_USART);
210 LL_USART_EnableDMAReq_RX(LWESP_USART);
211
212 /* Enable USART interrupts */
213 NVIC_SetPriority(LWESP_USART_IRQ, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 0x07, 0x00));
214 NVIC_EnableIRQ(LWESP_USART_IRQ);
215
216 /* Configure DMA */
217 is_running = 0;
218#if defined(LWESP_USART_DMA_RX_STREAM)
219 LL_DMA_DeInit(LWESP_USART_DMA, LWESP_USART_DMA_RX_STREAM);
220 dma_init.Channel = LWESP_USART_DMA_RX_CH;
221#else
222 LL_DMA_DeInit(LWESP_USART_DMA, LWESP_USART_DMA_RX_CH);
223 dma_init.PeriphRequest = LWESP_USART_DMA_RX_REQ_NUM;
224#endif /* defined(LWESP_USART_DMA_RX_STREAM) */
225 dma_init.PeriphOrM2MSrcAddress = (uint32_t)&LWESP_USART->LWESP_USART_RDR_NAME;
226 dma_init.MemoryOrM2MDstAddress = (uint32_t)usart_mem;
227 dma_init.Direction = LL_DMA_DIRECTION_PERIPH_TO_MEMORY;
228 dma_init.Mode = LL_DMA_MODE_CIRCULAR;
229 dma_init.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT;
230 dma_init.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT;
231 dma_init.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_BYTE;
232 dma_init.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_BYTE;
233 dma_init.NbData = sizeof(usart_mem);
234 dma_init.Priority = LL_DMA_PRIORITY_MEDIUM;
235#if defined(LWESP_USART_DMA_RX_STREAM)
236 LL_DMA_Init(LWESP_USART_DMA, LWESP_USART_DMA_RX_STREAM, &dma_init);
237#else
238 LL_DMA_Init(LWESP_USART_DMA, LWESP_USART_DMA_RX_CH, &dma_init);
239#endif /* defined(LWESP_USART_DMA_RX_STREAM) */
240
241 /* Enable DMA interrupts */
242#if defined(LWESP_USART_DMA_RX_STREAM)
243 LL_DMA_EnableIT_HT(LWESP_USART_DMA, LWESP_USART_DMA_RX_STREAM);
244 LL_DMA_EnableIT_TC(LWESP_USART_DMA, LWESP_USART_DMA_RX_STREAM);
245 LL_DMA_EnableIT_TE(LWESP_USART_DMA, LWESP_USART_DMA_RX_STREAM);
246 LL_DMA_EnableIT_FE(LWESP_USART_DMA, LWESP_USART_DMA_RX_STREAM);
247 LL_DMA_EnableIT_DME(LWESP_USART_DMA, LWESP_USART_DMA_RX_STREAM);
248#else
249 LL_DMA_EnableIT_HT(LWESP_USART_DMA, LWESP_USART_DMA_RX_CH);
250 LL_DMA_EnableIT_TC(LWESP_USART_DMA, LWESP_USART_DMA_RX_CH);
251 LL_DMA_EnableIT_TE(LWESP_USART_DMA, LWESP_USART_DMA_RX_CH);
252#endif /* defined(LWESP_USART_DMA_RX_STREAM) */
253
254 /* Enable DMA interrupts */
255 NVIC_SetPriority(LWESP_USART_DMA_RX_IRQ, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 0x07, 0x00));
256 NVIC_EnableIRQ(LWESP_USART_DMA_RX_IRQ);
257
258 old_pos = 0;
259 is_running = 1;
260
261 /* Start DMA and USART */
262#if defined(LWESP_USART_DMA_RX_STREAM)
263 LL_DMA_EnableStream(LWESP_USART_DMA, LWESP_USART_DMA_RX_STREAM);
264#else
265 LL_DMA_EnableChannel(LWESP_USART_DMA, LWESP_USART_DMA_RX_CH);
266#endif /* defined(LWESP_USART_DMA_RX_STREAM) */
267 LL_USART_Enable(LWESP_USART);
268 } else {
269 osDelay(10);
270 LL_USART_Disable(LWESP_USART);
271 usart_init.BaudRate = baudrate;
272 LL_USART_Init(LWESP_USART, &usart_init);
273 LL_USART_Enable(LWESP_USART);
274 }
275
276 /* Create mbox and start thread */
277 if (usart_ll_mbox_id == NULL) {
278 usart_ll_mbox_id = osMessageQueueNew(10, sizeof(void*), NULL);
279 }
280 if (usart_ll_thread_id == NULL) {
281 const osThreadAttr_t attr = {.stack_size = 1024};
282 usart_ll_thread_id = osThreadNew(usart_ll_thread, usart_ll_mbox_id, &attr);
283 }
284}
285
286#if defined(LWESP_RESET_PIN)
287/**
288 * \brief Hardware reset callback
289 */
290static uint8_t
291prv_reset_device(uint8_t state) {
292 if (state) { /* Activate reset line */
293 LL_GPIO_ResetOutputPin(LWESP_RESET_PORT, LWESP_RESET_PIN);
294 } else {
295 LL_GPIO_SetOutputPin(LWESP_RESET_PORT, LWESP_RESET_PIN);
296 }
297 return 1;
298}
299#endif /* defined(LWESP_RESET_PIN) */
300
301/**
302 * \brief Send data to ESP device
303 * \param[in] data: Pointer to data to send
304 * \param[in] len: Number of bytes to send
305 * \return Number of bytes sent
306 */
307static size_t
308prv_send_data(const void* data, size_t len) {
309 const uint8_t* d = data;
310
311 for (size_t i = 0; i < len; ++i, ++d) {
312 LL_USART_TransmitData8(LWESP_USART, *d);
313 while (!LL_USART_IsActiveFlag_TXE(LWESP_USART)) {}
314 }
315 return len;
316}
317
318/**
319 * \brief Callback function called from initialization process
320 */
321lwespr_t
322lwesp_ll_init(lwesp_ll_t* ll) {
323#if !LWESP_CFG_MEM_CUSTOM
324 static uint8_t memory[LWESP_MEM_SIZE];
325 const lwesp_mem_region_t mem_regions[] = {{memory, sizeof(memory)}, {NULL, 0}};
326
327 if (!initialized) {
328 lwesp_mem_assignmemory(mem_regions, LWESP_ARRAYSIZE(mem_regions)); /* Assign memory for allocations */
329 }
330#endif /* !LWESP_CFG_MEM_CUSTOM */
331
332 if (!initialized) {
333 ll->send_fn = prv_send_data; /* Set callback function to send data */
334#if defined(LWESP_RESET_PIN)
335 ll->reset_fn = prv_reset_device; /* Set callback for hardware reset */
336#endif /* defined(LWESP_RESET_PIN) */
337 }
338
339 prv_configure_uart(ll->uart.baudrate); /* Initialize UART for communication */
340 initialized = 1;
341 return lwespOK;
342}
343
344/**
345 * \brief Callback function to de-init low-level communication part
346 */
347lwespr_t
348lwesp_ll_deinit(lwesp_ll_t* ll) {
349 if (usart_ll_mbox_id != NULL) {
350 osMessageQueueId_t tmp = usart_ll_mbox_id;
351 usart_ll_mbox_id = NULL;
352 osMessageQueueDelete(tmp);
353 }
354 if (usart_ll_thread_id != NULL) {
355 osThreadId_t tmp = usart_ll_thread_id;
356 usart_ll_thread_id = NULL;
357 osThreadTerminate(tmp);
358 }
359 initialized = 0;
360 LWESP_UNUSED(ll);
361 return lwespOK;
362}
363
364/**
365 * \brief UART global interrupt handler
366 */
367void
368LWESP_USART_IRQHANDLER(void) {
369 LL_USART_ClearFlag_IDLE(LWESP_USART);
370 LL_USART_ClearFlag_PE(LWESP_USART);
371 LL_USART_ClearFlag_FE(LWESP_USART);
372 LL_USART_ClearFlag_ORE(LWESP_USART);
373 LL_USART_ClearFlag_NE(LWESP_USART);
374
375 if (usart_ll_mbox_id != NULL) {
376 void* d = (void*)1;
377 osMessageQueuePut(usart_ll_mbox_id, &d, 0, 0);
378 }
379}
380
381/**
382 * \brief UART DMA stream/channel handler
383 */
384void
385LWESP_USART_DMA_RX_IRQHANDLER(void) {
386 LWESP_USART_DMA_RX_CLEAR_TC;
387 LWESP_USART_DMA_RX_CLEAR_HT;
388
389 if (usart_ll_mbox_id != NULL) {
390 void* d = (void*)1;
391 osMessageQueuePut(usart_ll_mbox_id, &d, 0, 0);
392 }
393}
394
395#endif /* !__DOXYGEN__ */
Example: System functions for WIN32
1/**
2 * \file lwesp_sys_port.h
3 * \brief WIN32 based system file implementation
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 LwESP - Lightweight ESP-AT parser library.
30 *
31 * Author: Tilen MAJERLE <tilen@majerle.eu>
32 * Version: v1.1.2-dev
33 */
34#ifndef LWESP_SYSTEM_PORT_HDR_H
35#define LWESP_SYSTEM_PORT_HDR_H
36
37#include <stdint.h>
38#include <stdlib.h>
39#include "lwesp/lwesp_opt.h"
40#include "windows.h"
41
42#ifdef __cplusplus
43extern "C" {
44#endif /* __cplusplus */
45
46#if LWESP_CFG_OS && !__DOXYGEN__
47
48typedef HANDLE lwesp_sys_mutex_t;
49typedef HANDLE lwesp_sys_sem_t;
50typedef HANDLE lwesp_sys_mbox_t;
51typedef HANDLE lwesp_sys_thread_t;
52typedef int lwesp_sys_thread_prio_t;
53
54#define LWESP_SYS_MBOX_NULL ((HANDLE)0)
55#define LWESP_SYS_SEM_NULL ((HANDLE)0)
56#define LWESP_SYS_MUTEX_NULL ((HANDLE)0)
57#define LWESP_SYS_TIMEOUT (INFINITE)
58#define LWESP_SYS_THREAD_PRIO (0)
59#define LWESP_SYS_THREAD_SS (1024)
60
61#endif /* LWESP_CFG_OS && !__DOXYGEN__ */
62
63#ifdef __cplusplus
64}
65#endif /* __cplusplus */
66
67#endif /* LWESP_SYSTEM_PORT_HDR_H */
1/**
2 * \file lwesp_sys_win32.c
3 * \brief System dependant 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 LwESP - Lightweight ESP-AT parser library.
30 *
31 * Author: Tilen MAJERLE <tilen@majerle.eu>
32 * Version: v1.1.2-dev
33 */
34#include <stdlib.h>
35#include <string.h>
36#include "lwesp/lwesp_private.h"
37#include "system/lwesp_sys.h"
38#include "windows.h"
39
40#if !__DOXYGEN__
41
42/**
43 * \brief Custom message queue implementation for WIN32
44 */
45typedef struct {
46 lwesp_sys_sem_t sem_not_empty; /*!< Semaphore indicates not empty */
47 lwesp_sys_sem_t sem_not_full; /*!< Semaphore indicates not full */
48 lwesp_sys_sem_t sem; /*!< Semaphore to lock access */
49 size_t in, out, size;
50 void* entries[1];
51} win32_mbox_t;
52
53static LARGE_INTEGER freq, sys_start_time;
54static lwesp_sys_mutex_t sys_mutex; /* Mutex ID for main protection */
55
56/**
57 * \brief Check if message box is full
58 * \param[in] m: Message box handle
59 * \return 1 if full, 0 otherwise
60 */
61static uint8_t
62mbox_is_full(win32_mbox_t* m) {
63 size_t size = 0;
64 if (m->in > m->out) {
65 size = (m->in - m->out);
66 } else if (m->out > m->in) {
67 size = m->size - m->out + m->in;
68 }
69 return size == m->size - 1;
70}
71
72/**
73 * \brief Check if message box is empty
74 * \param[in] m: Message box handle
75 * \return 1 if empty, 0 otherwise
76 */
77static uint8_t
78mbox_is_empty(win32_mbox_t* m) {
79 return m->in == m->out;
80}
81
82/**
83 * \brief Get current kernel time in units of milliseconds
84 */
85static uint32_t
86osKernelSysTick(void) {
87 LONGLONG ret;
88 LARGE_INTEGER now;
89
90 QueryPerformanceFrequency(&freq); /* Get frequency */
91 QueryPerformanceCounter(&now); /* Get current time */
92 ret = now.QuadPart - sys_start_time.QuadPart;
93 return (uint32_t)(((ret)*1000) / freq.QuadPart);
94}
95
96uint8_t
97lwesp_sys_init(void) {
98 QueryPerformanceFrequency(&freq);
99 QueryPerformanceCounter(&sys_start_time);
100
101 lwesp_sys_mutex_create(&sys_mutex);
102 return 1;
103}
104
105uint32_t
106lwesp_sys_now(void) {
107 return osKernelSysTick();
108}
109
110#if LWESP_CFG_OS
111uint8_t
112lwesp_sys_protect(void) {
113 lwesp_sys_mutex_lock(&sys_mutex);
114 return 1;
115}
116
117uint8_t
118lwesp_sys_unprotect(void) {
119 lwesp_sys_mutex_unlock(&sys_mutex);
120 return 1;
121}
122
123uint8_t
124lwesp_sys_mutex_create(lwesp_sys_mutex_t* p) {
125 *p = CreateMutex(NULL, FALSE, NULL);
126 return *p != NULL;
127}
128
129uint8_t
130lwesp_sys_mutex_delete(lwesp_sys_mutex_t* p) {
131 return CloseHandle(*p);
132}
133
134uint8_t
135lwesp_sys_mutex_lock(lwesp_sys_mutex_t* p) {
136 DWORD ret;
137 ret = WaitForSingleObject(*p, INFINITE);
138 if (ret != WAIT_OBJECT_0) {
139 return 0;
140 }
141 return 1;
142}
143
144uint8_t
145lwesp_sys_mutex_unlock(lwesp_sys_mutex_t* p) {
146 return ReleaseMutex(*p);
147}
148
149uint8_t
150lwesp_sys_mutex_isvalid(lwesp_sys_mutex_t* p) {
151 return p != NULL && *p != NULL;
152}
153
154uint8_t
155lwesp_sys_mutex_invalid(lwesp_sys_mutex_t* p) {
156 *p = LWESP_SYS_MUTEX_NULL;
157 return 1;
158}
159
160uint8_t
161lwesp_sys_sem_create(lwesp_sys_sem_t* p, uint8_t cnt) {
162 HANDLE h;
163 h = CreateSemaphore(NULL, !!cnt, 1, NULL);
164 *p = h;
165 return *p != NULL;
166}
167
168uint8_t
169lwesp_sys_sem_delete(lwesp_sys_sem_t* p) {
170 return CloseHandle(*p);
171}
172
173uint32_t
174lwesp_sys_sem_wait(lwesp_sys_sem_t* p, uint32_t timeout) {
175 DWORD ret;
176
177 if (timeout == 0) {
178 ret = WaitForSingleObject(*p, INFINITE);
179 return 1;
180 } else {
181 ret = WaitForSingleObject(*p, timeout);
182 if (ret == WAIT_OBJECT_0) {
183 return 1;
184 } else {
185 return LWESP_SYS_TIMEOUT;
186 }
187 }
188}
189
190uint8_t
191lwesp_sys_sem_release(lwesp_sys_sem_t* p) {
192 return ReleaseSemaphore(*p, 1, NULL);
193}
194
195uint8_t
196lwesp_sys_sem_isvalid(lwesp_sys_sem_t* p) {
197 return p != NULL && *p != NULL;
198}
199
200uint8_t
201lwesp_sys_sem_invalid(lwesp_sys_sem_t* p) {
202 *p = LWESP_SYS_SEM_NULL;
203 return 1;
204}
205
206uint8_t
207lwesp_sys_mbox_create(lwesp_sys_mbox_t* b, size_t size) {
208 win32_mbox_t* mbox;
209
210 *b = 0;
211
212 mbox = malloc(sizeof(*mbox) + size * sizeof(void*));
213 if (mbox != NULL) {
214 memset(mbox, 0x00, sizeof(*mbox));
215 mbox->size = size + 1; /* Set it to 1 more as cyclic buffer has only one less than size */
216 lwesp_sys_sem_create(&mbox->sem, 1);
217 lwesp_sys_sem_create(&mbox->sem_not_empty, 0);
218 lwesp_sys_sem_create(&mbox->sem_not_full, 0);
219 *b = mbox;
220 }
221 return *b != NULL;
222}
223
224uint8_t
225lwesp_sys_mbox_delete(lwesp_sys_mbox_t* b) {
226 win32_mbox_t* mbox = *b;
227 lwesp_sys_sem_delete(&mbox->sem);
228 lwesp_sys_sem_delete(&mbox->sem_not_full);
229 lwesp_sys_sem_delete(&mbox->sem_not_empty);
230 free(mbox);
231 return 1;
232}
233
234uint32_t
235lwesp_sys_mbox_put(lwesp_sys_mbox_t* b, void* m) {
236 win32_mbox_t* mbox = *b;
237 uint32_t time = osKernelSysTick(); /* Get start time */
238
239 lwesp_sys_sem_wait(&mbox->sem, 0); /* Wait for access */
240
241 /*
242 * Since function is blocking until ready to write something to queue,
243 * wait and release the semaphores to allow other threads
244 * to process the queue before we can write new value.
245 */
246 while (mbox_is_full(mbox)) {
247 lwesp_sys_sem_release(&mbox->sem); /* Release semaphore */
248 lwesp_sys_sem_wait(&mbox->sem_not_full, 0); /* Wait for semaphore indicating not full */
249 lwesp_sys_sem_wait(&mbox->sem, 0); /* Wait availability again */
250 }
251 mbox->entries[mbox->in] = m;
252 if (++mbox->in >= mbox->size) {
253 mbox->in = 0;
254 }
255 lwesp_sys_sem_release(&mbox->sem_not_empty); /* Signal non-empty state */
256 lwesp_sys_sem_release(&mbox->sem); /* Release access for other threads */
257 return osKernelSysTick() - time;
258}
259
260uint32_t
261lwesp_sys_mbox_get(lwesp_sys_mbox_t* b, void** m, uint32_t timeout) {
262 win32_mbox_t* mbox = *b;
263 uint32_t time;
264
265 time = osKernelSysTick();
266
267 /* Get exclusive access to message queue */
268 if (lwesp_sys_sem_wait(&mbox->sem, timeout) == LWESP_SYS_TIMEOUT) {
269 return LWESP_SYS_TIMEOUT;
270 }
271 while (mbox_is_empty(mbox)) {
272 lwesp_sys_sem_release(&mbox->sem);
273 if (lwesp_sys_sem_wait(&mbox->sem_not_empty, timeout) == LWESP_SYS_TIMEOUT) {
274 return LWESP_SYS_TIMEOUT;
275 }
276 lwesp_sys_sem_wait(&mbox->sem, timeout);
277 }
278 *m = mbox->entries[mbox->out];
279 if (++mbox->out >= mbox->size) {
280 mbox->out = 0;
281 }
282 lwesp_sys_sem_release(&mbox->sem_not_full);
283 lwesp_sys_sem_release(&mbox->sem);
284
285 return osKernelSysTick() - time;
286}
287
288uint8_t
289lwesp_sys_mbox_putnow(lwesp_sys_mbox_t* b, void* m) {
290 win32_mbox_t* mbox = *b;
291
292 lwesp_sys_sem_wait(&mbox->sem, 0);
293 if (mbox_is_full(mbox)) {
294 lwesp_sys_sem_release(&mbox->sem);
295 return 0;
296 }
297 mbox->entries[mbox->in] = m;
298 if (mbox->in == mbox->out) {
299 lwesp_sys_sem_release(&mbox->sem_not_empty);
300 }
301 if (++mbox->in >= mbox->size) {
302 mbox->in = 0;
303 }
304 lwesp_sys_sem_release(&mbox->sem);
305 return 1;
306}
307
308uint8_t
309lwesp_sys_mbox_getnow(lwesp_sys_mbox_t* b, void** m) {
310 win32_mbox_t* mbox = *b;
311
312 lwesp_sys_sem_wait(&mbox->sem, 0); /* Wait exclusive access */
313 if (mbox->in == mbox->out) {
314 lwesp_sys_sem_release(&mbox->sem); /* Release access */
315 return 0;
316 }
317
318 *m = mbox->entries[mbox->out];
319 if (++mbox->out >= mbox->size) {
320 mbox->out = 0;
321 }
322 lwesp_sys_sem_release(&mbox->sem_not_full); /* Queue not full anymore */
323 lwesp_sys_sem_release(&mbox->sem); /* Release semaphore */
324 return 1;
325}
326
327uint8_t
328lwesp_sys_mbox_isvalid(lwesp_sys_mbox_t* b) {
329 return b != NULL && *b != NULL;
330}
331
332uint8_t
333lwesp_sys_mbox_invalid(lwesp_sys_mbox_t* b) {
334 *b = LWESP_SYS_MBOX_NULL;
335 return 1;
336}
337
338uint8_t
339lwesp_sys_thread_create(lwesp_sys_thread_t* t, const char* name, lwesp_sys_thread_fn thread_func, void* const arg,
340 size_t stack_size, lwesp_sys_thread_prio_t prio) {
341 HANDLE h;
342 DWORD id;
343
344 LWESP_UNUSED(name);
345 LWESP_UNUSED(stack_size);
346 LWESP_UNUSED(prio);
347 h = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)thread_func, arg, 0, &id);
348 if (t != NULL) {
349 *t = h;
350 }
351 return h != NULL;
352}
353
354uint8_t
355lwesp_sys_thread_terminate(lwesp_sys_thread_t* t) {
356 if (t == NULL) { /* Shall we terminate ourself? */
357 ExitThread(0);
358 } else {
359 /* We have known thread, find handle by looking at ID */
360 TerminateThread(*t, 0);
361 }
362 return 1;
363}
364
365uint8_t
366lwesp_sys_thread_yield(void) {
367 /* Not implemented */
368 return 1;
369}
370
371#endif /* LWESP_CFG_OS */
372#endif /* !__DOXYGEN__ */
Example: System functions for CMSIS-OS
1/**
2 * \file lwesp_sys_port.h
3 * \brief CMSIS-OS based system file
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 LwESP - Lightweight ESP-AT parser library.
30 *
31 * Author: Tilen MAJERLE <tilen@majerle.eu>
32 * Version: v1.1.2-dev
33 */
34#ifndef LWESP_SYSTEM_PORT_HDR_H
35#define LWESP_SYSTEM_PORT_HDR_H
36
37#include <stdint.h>
38#include <stdlib.h>
39#include "cmsis_os.h"
40#include "lwesp/lwesp_opt.h"
41
42#ifdef __cplusplus
43extern "C" {
44#endif /* __cplusplus */
45
46#if LWESP_CFG_OS && !__DOXYGEN__
47
48typedef osMutexId_t lwesp_sys_mutex_t;
49typedef osSemaphoreId_t lwesp_sys_sem_t;
50typedef osMessageQueueId_t lwesp_sys_mbox_t;
51typedef osThreadId_t lwesp_sys_thread_t;
52typedef osPriority_t lwesp_sys_thread_prio_t;
53
54#define LWESP_SYS_MUTEX_NULL ((lwesp_sys_mutex_t)0)
55#define LWESP_SYS_SEM_NULL ((lwesp_sys_sem_t)0)
56#define LWESP_SYS_MBOX_NULL ((lwesp_sys_mbox_t)0)
57#define LWESP_SYS_TIMEOUT ((uint32_t)osWaitForever)
58#define LWESP_SYS_THREAD_PRIO (osPriorityNormal)
59#define LWESP_SYS_THREAD_SS (512)
60
61#endif /* LWESP_CFG_OS && !__DOXYGEN__ */
62
63#ifdef __cplusplus
64}
65#endif /* __cplusplus */
66
67#endif /* LWESP_SYSTEM_PORT_HDR_H */
1/**
2 * \file lwesp_sys_cmsis_os.c
3 * \brief System dependent functions for CMSIS 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 LwESP - Lightweight ESP-AT parser library.
30 *
31 * Author: Tilen MAJERLE <tilen@majerle.eu>
32 * Version: v1.1.2-dev
33 */
34#include "cmsis_os.h"
35#include "system/lwesp_sys.h"
36
37#if !__DOXYGEN__
38
39static osMutexId_t sys_mutex;
40
41uint8_t
42lwesp_sys_init(void) {
43 lwesp_sys_mutex_create(&sys_mutex);
44 return 1;
45}
46
47uint32_t
48lwesp_sys_now(void) {
49 return osKernelGetTickCount();
50}
51
52uint8_t
53lwesp_sys_protect(void) {
54 lwesp_sys_mutex_lock(&sys_mutex);
55 return 1;
56}
57
58uint8_t
59lwesp_sys_unprotect(void) {
60 lwesp_sys_mutex_unlock(&sys_mutex);
61 return 1;
62}
63
64uint8_t
65lwesp_sys_mutex_create(lwesp_sys_mutex_t* p) {
66 const osMutexAttr_t attr = {
67 .attr_bits = osMutexRecursive,
68 .name = "lwesp_mutex",
69 };
70 return (*p = osMutexNew(&attr)) != NULL;
71}
72
73uint8_t
74lwesp_sys_mutex_delete(lwesp_sys_mutex_t* p) {
75 return osMutexDelete(*p) == osOK;
76}
77
78uint8_t
79lwesp_sys_mutex_lock(lwesp_sys_mutex_t* p) {
80 return osMutexAcquire(*p, osWaitForever) == osOK;
81}
82
83uint8_t
84lwesp_sys_mutex_unlock(lwesp_sys_mutex_t* p) {
85 return osMutexRelease(*p) == osOK;
86}
87
88uint8_t
89lwesp_sys_mutex_isvalid(lwesp_sys_mutex_t* p) {
90 return p != NULL && *p != NULL;
91}
92
93uint8_t
94lwesp_sys_mutex_invalid(lwesp_sys_mutex_t* p) {
95 *p = LWESP_SYS_MUTEX_NULL;
96 return 1;
97}
98
99uint8_t
100lwesp_sys_sem_create(lwesp_sys_sem_t* p, uint8_t cnt) {
101 const osSemaphoreAttr_t attr = {
102 .name = "lwesp_sem",
103 };
104 return (*p = osSemaphoreNew(1, cnt > 0 ? 1 : 0, &attr)) != NULL;
105}
106
107uint8_t
108lwesp_sys_sem_delete(lwesp_sys_sem_t* p) {
109 return osSemaphoreDelete(*p) == osOK;
110}
111
112uint32_t
113lwesp_sys_sem_wait(lwesp_sys_sem_t* p, uint32_t timeout) {
114 uint32_t tick = osKernelSysTick();
115 return (osSemaphoreAcquire(*p, timeout == 0 ? osWaitForever : timeout) == osOK) ? (osKernelSysTick() - tick)
116 : LWESP_SYS_TIMEOUT;
117}
118
119uint8_t
120lwesp_sys_sem_release(lwesp_sys_sem_t* p) {
121 return osSemaphoreRelease(*p) == osOK;
122}
123
124uint8_t
125lwesp_sys_sem_isvalid(lwesp_sys_sem_t* p) {
126 return p != NULL && *p != NULL;
127}
128
129uint8_t
130lwesp_sys_sem_invalid(lwesp_sys_sem_t* p) {
131 *p = LWESP_SYS_SEM_NULL;
132 return 1;
133}
134
135uint8_t
136lwesp_sys_mbox_create(lwesp_sys_mbox_t* b, size_t size) {
137 const osMessageQueueAttr_t attr = {
138 .name = "lwesp_mbox",
139 };
140 return (*b = osMessageQueueNew(size, sizeof(void*), &attr)) != NULL;
141}
142
143uint8_t
144lwesp_sys_mbox_delete(lwesp_sys_mbox_t* b) {
145 if (osMessageQueueGetCount(*b) > 0) {
146 return 0;
147 }
148 return osMessageQueueDelete(*b) == osOK;
149}
150
151uint32_t
152lwesp_sys_mbox_put(lwesp_sys_mbox_t* b, void* m) {
153 uint32_t tick = osKernelSysTick();
154 return osMessageQueuePut(*b, &m, 0, osWaitForever) == osOK ? (osKernelSysTick() - tick) : LWESP_SYS_TIMEOUT;
155}
156
157uint32_t
158lwesp_sys_mbox_get(lwesp_sys_mbox_t* b, void** m, uint32_t timeout) {
159 uint32_t tick = osKernelSysTick();
160 return (osMessageQueueGet(*b, m, NULL, timeout == 0 ? osWaitForever : timeout) == osOK) ? (osKernelSysTick() - tick)
161 : LWESP_SYS_TIMEOUT;
162}
163
164uint8_t
165lwesp_sys_mbox_putnow(lwesp_sys_mbox_t* b, void* m) {
166 return osMessageQueuePut(*b, &m, 0, 0) == osOK;
167}
168
169uint8_t
170lwesp_sys_mbox_getnow(lwesp_sys_mbox_t* b, void** m) {
171 return osMessageQueueGet(*b, m, NULL, 0) == osOK;
172}
173
174uint8_t
175lwesp_sys_mbox_isvalid(lwesp_sys_mbox_t* b) {
176 return b != NULL && *b != NULL;
177}
178
179uint8_t
180lwesp_sys_mbox_invalid(lwesp_sys_mbox_t* b) {
181 *b = LWESP_SYS_MBOX_NULL;
182 return 1;
183}
184
185uint8_t
186lwesp_sys_thread_create(lwesp_sys_thread_t* t, const char* name, lwesp_sys_thread_fn thread_func, void* const arg,
187 size_t stack_size, lwesp_sys_thread_prio_t prio) {
188 lwesp_sys_thread_t id;
189 const osThreadAttr_t thread_attr = {.name = (char*)name,
190 .priority = (osPriority)prio,
191 .stack_size = stack_size > 0 ? stack_size : LWESP_SYS_THREAD_SS};
192
193 id = osThreadNew(thread_func, arg, &thread_attr);
194 if (t != NULL) {
195 *t = id;
196 }
197 return id != NULL;
198}
199
200uint8_t
201lwesp_sys_thread_terminate(lwesp_sys_thread_t* t) {
202 if (t != NULL) {
203 osThreadTerminate(*t);
204 } else {
205 osThreadExit();
206 }
207 return 1;
208}
209
210uint8_t
211lwesp_sys_thread_yield(void) {
212 osThreadYield();
213 return 1;
214}
215
216#endif /* !__DOXYGEN__ */