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) 2020 Tilen MAJERLE
8 *
9 * Permission is hereby granted, free of charge, to any person
10 * obtaining a copy of this software and associated documentation
11 * files (the "Software"), to deal in the Software without restriction,
12 * including without limitation the rights to use, copy, modify, merge,
13 * publish, distribute, sublicense, and/or sell copies of the Software,
14 * and to permit persons to whom the Software is furnished to do so,
15 * subject to the following conditions:
16 *
17 * The above copyright notice and this permission notice shall be
18 * included in all copies or substantial portions of the Software.
19 *
20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
22 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
23 * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
24 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
25 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
27 * OTHER DEALINGS IN THE SOFTWARE.
28 *
29 * This file is part of LwESP - Lightweight ESP-AT parser library.
30 *
31 * Author: Tilen MAJERLE <tilen@majerle.eu>
32 * Version: v1.1.2-dev
33 */
34#include "system/lwesp_ll.h"
35#include "lwesp/lwesp.h"
36#include "lwesp/lwesp_mem.h"
37#include "lwesp/lwesp_input.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 */
78static uint8_t
79configure_uart(uint32_t baudrate) {
80 DCB dcb = { 0 };
81 dcb.DCBlength = sizeof(dcb);
82 size_t i;
83
84 /* List of COM ports to probe */
85 static const char* com_port_names[] = {
86 "\\\\.\\COM16",
87 "\\\\.\\COM4"
88 };
89
90 /* TODO: Needs proper work to run for loop only if not initialized */
91
92 for (i = 0; i < LWESP_ARRAYSIZE(com_port_names); ++i) {
93 /*
94 * On first call,
95 * create virtual file on selected COM port and open it
96 * as generic read and write
97 */
98 if (!initialized) {
99 printf("Probing COM port %s\r\n", com_port_names[i]);
100 com_port = CreateFileA(com_port_names[i], GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, NULL);
101 }
102
103 /* Configure COM port parameters */
104 if (GetCommState(com_port, &dcb)) {
105 COMMTIMEOUTS timeouts;
106
107 dcb.BaudRate = baudrate;
108 dcb.ByteSize = 8;
109 dcb.Parity = NOPARITY;
110 dcb.StopBits = ONESTOPBIT;
111
112 if (!SetCommState(com_port, &dcb)) {
113 printf("Cannot set COM PORT info for port: %s\r\n", com_port_names[i]);
114 continue;
115 }
116 if (GetCommTimeouts(com_port, &timeouts)) {
117 /* Set timeout to return immediately from ReadFile function */
118 timeouts.ReadIntervalTimeout = MAXDWORD;
119 timeouts.ReadTotalTimeoutConstant = 0;
120 timeouts.ReadTotalTimeoutMultiplier = 0;
121 if (!SetCommTimeouts(com_port, &timeouts)) {
122 printf("Cannot set COM PORT timeouts: %s\r\n", com_port_names[i]);
123 }
124 GetCommTimeouts(com_port, &timeouts);
125 break;
126 } else {
127 printf("Cannot get COM PORT timeouts: %s\r\n", com_port_names[i]);
128 }
129 } else {
130 printf("Cannot get COM PORT info: %s\r\n", com_port_names[i]);
131 }
132 }
133 if (i == LWESP_ARRAYSIZE(com_port_names)) {
134 printf("Failed to open any COM port\r\n");
135 return 0;
136 }
137
138 /* On first function call, create a thread to read data from COM port */
139 if (!initialized) {
140 lwesp_sys_thread_create(&thread_handle, "lwesp_ll_thread", uart_thread, NULL, 0, 0);
141 }
142 return 1;
143}
144
145/**
146 * \brief UART thread
147 */
148static void
149uart_thread(void* param) {
150 DWORD bytes_read;
151 lwesp_sys_sem_t sem;
152 FILE* file = NULL;
153
154 lwesp_sys_sem_create(&sem, 0); /* Create semaphore for delay functions */
155
156 while (com_port == NULL) {
157 lwesp_sys_sem_wait(&sem, 1); /* Add some delay with yield */
158 }
159
160 fopen_s(&file, "log_file.txt", "w+"); /* Open debug file in write mode */
161 while (1) {
162 /*
163 * Try to read data from COM port
164 * and send it to upper layer for processing
165 */
166 do {
167 ReadFile(com_port, data_buffer, sizeof(data_buffer), &bytes_read, NULL);
168 if (bytes_read > 0) {
169 HANDLE hConsole;
170 hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
171 SetConsoleTextAttribute(hConsole, FOREGROUND_GREEN);
172 for (DWORD i = 0; i < bytes_read; ++i) {
173 printf("%c", data_buffer[i]);
174 }
175 SetConsoleTextAttribute(hConsole, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
176
177 if (lwesp_ll_win32_driver_ignore_data) {
178 printf("IGNORING..\r\n");
179 continue;
180 }
181
182 /* Send received data to input processing module */
183#if LWESP_CFG_INPUT_USE_PROCESS
184 lwesp_input_process(data_buffer, (size_t)bytes_read);
185#else /* LWESP_CFG_INPUT_USE_PROCESS */
186 lwesp_input(data_buffer, (size_t)bytes_read);
187#endif /* !LWESP_CFG_INPUT_USE_PROCESS */
188
189 /* Write received data to output debug file */
190 if (file != NULL) {
191 fwrite(data_buffer, 1, bytes_read, file);
192 fflush(file);
193 }
194 }
195 } while (bytes_read == (DWORD)sizeof(data_buffer));
196
197 /* Implement delay to allow other tasks processing */
198 lwesp_sys_sem_wait(&sem, 1);
199 }
200}
201
202/**
203 * \brief Reset device GPIO management
204 */
205static uint8_t
206reset_device(uint8_t state) {
207 return 0; /* Hardware reset was not successful */
208}
209
210/**
211 * \brief Callback function called from initialization process
212 */
213lwespr_t
214lwesp_ll_init(lwesp_ll_t* ll) {
215#if !LWESP_CFG_MEM_CUSTOM
216 /* Step 1: Configure memory for dynamic allocations */
217 static uint8_t memory[0x10000]; /* Create memory for dynamic allocations with specific size */
218
219 /*
220 * Create memory region(s) of memory.
221 * If device has internal/external memory available,
222 * multiple memories may be used
223 */
224 lwesp_mem_region_t mem_regions[] = {
225 { memory, sizeof(memory) }
226 };
227 if (!initialized) {
228 lwesp_mem_assignmemory(mem_regions, LWESP_ARRAYSIZE(mem_regions)); /* Assign memory for allocations to ESP library */
229 }
230#endif /* !LWESP_CFG_MEM_CUSTOM */
231
232 /* Step 2: Set AT port send function to use when we have data to transmit */
233 if (!initialized) {
234 ll->send_fn = send_data; /* Set callback function to send data */
235 ll->reset_fn = reset_device;
236 }
237
238 /* Step 3: Configure AT port to be able to send/receive data to/from ESP device */
239 if (!configure_uart(ll->uart.baudrate)) { /* Initialize UART for communication */
240 return lwespERR;
241 }
242 initialized = 1;
243 return lwespOK;
244}
245
246/**
247 * \brief Callback function to de-init low-level communication part
248 */
249lwespr_t
250lwesp_ll_deinit(lwesp_ll_t* ll) {
251 if (thread_handle != NULL) {
252 lwesp_sys_thread_terminate(&thread_handle);
253 thread_handle = NULL;
254 }
255 initialized = 0; /* Clear initialized flag */
256 return lwespOK;
257}
258
259#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) 2020 Tilen MAJERLE
8 *
9 * Permission is hereby granted, free of charge, to any person
10 * obtaining a copy of this software and associated documentation
11 * files (the "Software"), to deal in the Software without restriction,
12 * including without limitation the rights to use, copy, modify, merge,
13 * publish, distribute, sublicense, and/or sell copies of the Software,
14 * and to permit persons to whom the Software is furnished to do so,
15 * subject to the following conditions:
16 *
17 * The above copyright notice and this permission notice shall be
18 * included in all copies or substantial portions of the Software.
19 *
20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
22 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
23 * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
24 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
25 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
27 * OTHER DEALINGS IN THE SOFTWARE.
28 *
29 * This file is part of 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_mem.h"
48#include "lwesp/lwesp_input.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 if (old_pos == sizeof(usart_mem)) {
112 old_pos = 0;
113 }
114 }
115 }
116}
117
118/**
119 * \brief Configure UART using DMA for receive in double buffer mode and IDLE line detection
120 */
121static void
122configure_uart(uint32_t baudrate) {
123 static LL_USART_InitTypeDef usart_init;
124 static LL_DMA_InitTypeDef dma_init;
125 LL_GPIO_InitTypeDef gpio_init;
126
127 if (!initialized) {
128 /* Enable peripheral clocks */
129 LWESP_USART_CLK;
130 LWESP_USART_DMA_CLK;
131 LWESP_USART_TX_PORT_CLK;
132 LWESP_USART_RX_PORT_CLK;
133
134#if defined(LWESP_RESET_PIN)
135 LWESP_RESET_PORT_CLK;
136#endif /* defined(LWESP_RESET_PIN) */
137
138#if defined(LWESP_GPIO0_PIN)
139 LWESP_GPIO0_PORT_CLK;
140#endif /* defined(LWESP_GPIO0_PIN) */
141
142#if defined(LWESP_GPIO2_PIN)
143 LWESP_GPIO2_PORT_CLK;
144#endif /* defined(LWESP_GPIO2_PIN) */
145
146#if defined(LWESP_CH_PD_PIN)
147 LWESP_CH_PD_PORT_CLK;
148#endif /* defined(LWESP_CH_PD_PIN) */
149
150 /* Global pin configuration */
151 LL_GPIO_StructInit(&gpio_init);
152 gpio_init.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
153 gpio_init.Pull = LL_GPIO_PULL_UP;
154 gpio_init.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH;
155 gpio_init.Mode = LL_GPIO_MODE_OUTPUT;
156
157#if defined(LWESP_RESET_PIN)
158 /* Configure RESET pin */
159 gpio_init.Pin = LWESP_RESET_PIN;
160 LL_GPIO_Init(LWESP_RESET_PORT, &gpio_init);
161#endif /* defined(LWESP_RESET_PIN) */
162
163#if defined(LWESP_GPIO0_PIN)
164 /* Configure GPIO0 pin */
165 gpio_init.Pin = LWESP_GPIO0_PIN;
166 LL_GPIO_Init(LWESP_GPIO0_PORT, &gpio_init);
167 LL_GPIO_SetOutputPin(LWESP_GPIO0_PORT, LWESP_GPIO0_PIN);
168#endif /* defined(LWESP_GPIO0_PIN) */
169
170#if defined(LWESP_GPIO2_PIN)
171 /* Configure GPIO2 pin */
172 gpio_init.Pin = LWESP_GPIO2_PIN;
173 LL_GPIO_Init(LWESP_GPIO2_PORT, &gpio_init);
174 LL_GPIO_SetOutputPin(LWESP_GPIO2_PORT, LWESP_GPIO2_PIN);
175#endif /* defined(LWESP_GPIO2_PIN) */
176
177#if defined(LWESP_CH_PD_PIN)
178 /* Configure CH_PD pin */
179 gpio_init.Pin = LWESP_CH_PD_PIN;
180 LL_GPIO_Init(LWESP_CH_PD_PORT, &gpio_init);
181 LL_GPIO_SetOutputPin(LWESP_CH_PD_PORT, LWESP_CH_PD_PIN);
182#endif /* defined(LWESP_CH_PD_PIN) */
183
184 /* Configure USART pins */
185 gpio_init.Mode = LL_GPIO_MODE_ALTERNATE;
186
187 /* TX PIN */
188 gpio_init.Alternate = LWESP_USART_TX_PIN_AF;
189 gpio_init.Pin = LWESP_USART_TX_PIN;
190 LL_GPIO_Init(LWESP_USART_TX_PORT, &gpio_init);
191
192 /* RX PIN */
193 gpio_init.Alternate = LWESP_USART_RX_PIN_AF;
194 gpio_init.Pin = LWESP_USART_RX_PIN;
195 LL_GPIO_Init(LWESP_USART_RX_PORT, &gpio_init);
196
197 /* Configure UART */
198 LL_USART_DeInit(LWESP_USART);
199 LL_USART_StructInit(&usart_init);
200 usart_init.BaudRate = baudrate;
201 usart_init.DataWidth = LL_USART_DATAWIDTH_8B;
202 usart_init.HardwareFlowControl = LL_USART_HWCONTROL_NONE;
203 usart_init.OverSampling = LL_USART_OVERSAMPLING_16;
204 usart_init.Parity = LL_USART_PARITY_NONE;
205 usart_init.StopBits = LL_USART_STOPBITS_1;
206 usart_init.TransferDirection = LL_USART_DIRECTION_TX_RX;
207 LL_USART_Init(LWESP_USART, &usart_init);
208
209 /* Enable USART interrupts and DMA request */
210 LL_USART_EnableIT_IDLE(LWESP_USART);
211 LL_USART_EnableIT_PE(LWESP_USART);
212 LL_USART_EnableIT_ERROR(LWESP_USART);
213 LL_USART_EnableDMAReq_RX(LWESP_USART);
214
215 /* Enable USART interrupts */
216 NVIC_SetPriority(LWESP_USART_IRQ, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 0x07, 0x00));
217 NVIC_EnableIRQ(LWESP_USART_IRQ);
218
219 /* Configure DMA */
220 is_running = 0;
221#if defined(LWESP_USART_DMA_RX_STREAM)
222 LL_DMA_DeInit(LWESP_USART_DMA, LWESP_USART_DMA_RX_STREAM);
223 dma_init.Channel = LWESP_USART_DMA_RX_CH;
224#else
225 LL_DMA_DeInit(LWESP_USART_DMA, LWESP_USART_DMA_RX_CH);
226 dma_init.PeriphRequest = LWESP_USART_DMA_RX_REQ_NUM;
227#endif /* defined(LWESP_USART_DMA_RX_STREAM) */
228 dma_init.PeriphOrM2MSrcAddress = (uint32_t)&LWESP_USART->LWESP_USART_RDR_NAME;
229 dma_init.MemoryOrM2MDstAddress = (uint32_t)usart_mem;
230 dma_init.Direction = LL_DMA_DIRECTION_PERIPH_TO_MEMORY;
231 dma_init.Mode = LL_DMA_MODE_CIRCULAR;
232 dma_init.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT;
233 dma_init.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT;
234 dma_init.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_BYTE;
235 dma_init.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_BYTE;
236 dma_init.NbData = sizeof(usart_mem);
237 dma_init.Priority = LL_DMA_PRIORITY_MEDIUM;
238#if defined(LWESP_USART_DMA_RX_STREAM)
239 LL_DMA_Init(LWESP_USART_DMA, LWESP_USART_DMA_RX_STREAM, &dma_init);
240#else
241 LL_DMA_Init(LWESP_USART_DMA, LWESP_USART_DMA_RX_CH, &dma_init);
242#endif /* defined(LWESP_USART_DMA_RX_STREAM) */
243
244 /* Enable DMA interrupts */
245#if defined(LWESP_USART_DMA_RX_STREAM)
246 LL_DMA_EnableIT_HT(LWESP_USART_DMA, LWESP_USART_DMA_RX_STREAM);
247 LL_DMA_EnableIT_TC(LWESP_USART_DMA, LWESP_USART_DMA_RX_STREAM);
248 LL_DMA_EnableIT_TE(LWESP_USART_DMA, LWESP_USART_DMA_RX_STREAM);
249 LL_DMA_EnableIT_FE(LWESP_USART_DMA, LWESP_USART_DMA_RX_STREAM);
250 LL_DMA_EnableIT_DME(LWESP_USART_DMA, LWESP_USART_DMA_RX_STREAM);
251#else
252 LL_DMA_EnableIT_HT(LWESP_USART_DMA, LWESP_USART_DMA_RX_CH);
253 LL_DMA_EnableIT_TC(LWESP_USART_DMA, LWESP_USART_DMA_RX_CH);
254 LL_DMA_EnableIT_TE(LWESP_USART_DMA, LWESP_USART_DMA_RX_CH);
255#endif /* defined(LWESP_USART_DMA_RX_STREAM) */
256
257 /* Enable DMA interrupts */
258 NVIC_SetPriority(LWESP_USART_DMA_RX_IRQ, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 0x07, 0x00));
259 NVIC_EnableIRQ(LWESP_USART_DMA_RX_IRQ);
260
261 old_pos = 0;
262 is_running = 1;
263
264 /* Start DMA and USART */
265#if defined(LWESP_USART_DMA_RX_STREAM)
266 LL_DMA_EnableStream(LWESP_USART_DMA, LWESP_USART_DMA_RX_STREAM);
267#else
268 LL_DMA_EnableChannel(LWESP_USART_DMA, LWESP_USART_DMA_RX_CH);
269#endif /* defined(LWESP_USART_DMA_RX_STREAM) */
270 LL_USART_Enable(LWESP_USART);
271 } else {
272 osDelay(10);
273 LL_USART_Disable(LWESP_USART);
274 usart_init.BaudRate = baudrate;
275 LL_USART_Init(LWESP_USART, &usart_init);
276 LL_USART_Enable(LWESP_USART);
277 }
278
279 /* Create mbox and start thread */
280 if (usart_ll_mbox_id == NULL) {
281 usart_ll_mbox_id = osMessageQueueNew(10, sizeof(void*), NULL);
282 }
283 if (usart_ll_thread_id == NULL) {
284 const osThreadAttr_t attr = {
285 .stack_size = 1024
286 };
287 usart_ll_thread_id = osThreadNew(usart_ll_thread, usart_ll_mbox_id, &attr);
288 }
289}
290
291#if defined(LWESP_RESET_PIN)
292/**
293 * \brief Hardware reset callback
294 */
295static uint8_t
296reset_device(uint8_t state) {
297 if (state) { /* Activate reset line */
298 LL_GPIO_ResetOutputPin(LWESP_RESET_PORT, LWESP_RESET_PIN);
299 } else {
300 LL_GPIO_SetOutputPin(LWESP_RESET_PORT, LWESP_RESET_PIN);
301 }
302 return 1;
303}
304#endif /* defined(LWESP_RESET_PIN) */
305
306/**
307 * \brief Send data to ESP device
308 * \param[in] data: Pointer to data to send
309 * \param[in] len: Number of bytes to send
310 * \return Number of bytes sent
311 */
312static size_t
313send_data(const void* data, size_t len) {
314 const uint8_t* d = data;
315
316 for (size_t i = 0; i < len; ++i, ++d) {
317 LL_USART_TransmitData8(LWESP_USART, *d);
318 while (!LL_USART_IsActiveFlag_TXE(LWESP_USART)) {}
319 }
320 return len;
321}
322
323/**
324 * \brief Callback function called from initialization process
325 */
326lwespr_t
327lwesp_ll_init(lwesp_ll_t* ll) {
328#if !LWESP_CFG_MEM_CUSTOM
329 static uint8_t memory[LWESP_MEM_SIZE];
330 lwesp_mem_region_t mem_regions[] = {
331 { memory, sizeof(memory) }
332 };
333
334 if (!initialized) {
335 lwesp_mem_assignmemory(mem_regions, LWESP_ARRAYSIZE(mem_regions)); /* Assign memory for allocations */
336 }
337#endif /* !LWESP_CFG_MEM_CUSTOM */
338
339 if (!initialized) {
340 ll->send_fn = send_data; /* Set callback function to send data */
341#if defined(LWESP_RESET_PIN)
342 ll->reset_fn = reset_device; /* Set callback for hardware reset */
343#endif /* defined(LWESP_RESET_PIN) */
344 }
345
346 configure_uart(ll->uart.baudrate); /* Initialize UART for communication */
347 initialized = 1;
348 return lwespOK;
349}
350
351/**
352 * \brief Callback function to de-init low-level communication part
353 */
354lwespr_t
355lwesp_ll_deinit(lwesp_ll_t* ll) {
356 if (usart_ll_mbox_id != NULL) {
357 osMessageQueueId_t tmp = usart_ll_mbox_id;
358 usart_ll_mbox_id = NULL;
359 osMessageQueueDelete(tmp);
360 }
361 if (usart_ll_thread_id != NULL) {
362 osThreadId_t tmp = usart_ll_thread_id;
363 usart_ll_thread_id = NULL;
364 osThreadTerminate(tmp);
365 }
366 initialized = 0;
367 LWESP_UNUSED(ll);
368 return lwespOK;
369}
370
371/**
372 * \brief UART global interrupt handler
373 */
374void
375LWESP_USART_IRQHANDLER(void) {
376 LL_USART_ClearFlag_IDLE(LWESP_USART);
377 LL_USART_ClearFlag_PE(LWESP_USART);
378 LL_USART_ClearFlag_FE(LWESP_USART);
379 LL_USART_ClearFlag_ORE(LWESP_USART);
380 LL_USART_ClearFlag_NE(LWESP_USART);
381
382 if (usart_ll_mbox_id != NULL) {
383 void* d = (void*)1;
384 osMessageQueuePut(usart_ll_mbox_id, &d, 0, 0);
385 }
386}
387
388/**
389 * \brief UART DMA stream/channel handler
390 */
391void
392LWESP_USART_DMA_RX_IRQHANDLER(void) {
393 LWESP_USART_DMA_RX_CLEAR_TC;
394 LWESP_USART_DMA_RX_CLEAR_HT;
395
396 if (usart_ll_mbox_id != NULL) {
397 void* d = (void*)1;
398 osMessageQueuePut(usart_ll_mbox_id, &d, 0, 0);
399 }
400}
401
402#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) 2020 Tilen MAJERLE
8 *
9 * Permission is hereby granted, free of charge, to any person
10 * obtaining a copy of this software and associated documentation
11 * files (the "Software"), to deal in the Software without restriction,
12 * including without limitation the rights to use, copy, modify, merge,
13 * publish, distribute, sublicense, and/or sell copies of the Software,
14 * and to permit persons to whom the Software is furnished to do so,
15 * subject to the following conditions:
16 *
17 * The above copyright notice and this permission notice shall be
18 * included in all copies or substantial portions of the Software.
19 *
20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
22 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
23 * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
24 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
25 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
27 * OTHER DEALINGS IN THE SOFTWARE.
28 *
29 * This file is part of LwESP - Lightweight ESP-AT parser library.
30 *
31 * Author: Tilen MAJERLE <tilen@majerle.eu>
32 * Version: v1.1.2-dev
33 */
34#ifndef LWESP_HDR_SYSTEM_PORT_H
35#define LWESP_HDR_SYSTEM_PORT_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_HDR_SYSTEM_PORT_H */
1/**
2 * \file lwesp_sys_win32.c
3 * \brief System dependant functions for WIN32
4 */
5
6/*
7 * Copyright (c) 2020 Tilen MAJERLE
8 *
9 * Permission is hereby granted, free of charge, to any person
10 * obtaining a copy of this software and associated documentation
11 * files (the "Software"), to deal in the Software without restriction,
12 * including without limitation the rights to use, copy, modify, merge,
13 * publish, distribute, sublicense, and/or sell copies of the Software,
14 * and to permit persons to whom the Software is furnished to do so,
15 * subject to the following conditions:
16 *
17 * The above copyright notice and this permission notice shall be
18 * included in all copies or substantial portions of the Software.
19 *
20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
22 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
23 * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
24 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
25 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
27 * OTHER DEALINGS IN THE SOFTWARE.
28 *
29 * This file is part of LwESP - Lightweight ESP-AT parser library.
30 *
31 * Author: Tilen MAJERLE <tilen@majerle.eu>
32 * Version: v1.1.2-dev
33 */
34#include <string.h>
35#include <stdlib.h>
36#include "system/lwesp_sys.h"
37#include "windows.h"
38
39#if !__DOXYGEN__
40
41/**
42 * \brief Custom message queue implementation for WIN32
43 */
44typedef struct {
45 lwesp_sys_sem_t sem_not_empty; /*!< Semaphore indicates not empty */
46 lwesp_sys_sem_t sem_not_full; /*!< Semaphore indicates not full */
47 lwesp_sys_sem_t sem; /*!< Semaphore to lock access */
48 size_t in, out, size;
49 void* entries[1];
50} win32_mbox_t;
51
52static LARGE_INTEGER freq, sys_start_time;
53static lwesp_sys_mutex_t sys_mutex; /* Mutex ID for main protection */
54
55/**
56 * \brief Check if message box is full
57 * \param[in] m: Message box handle
58 * \return 1 if full, 0 otherwise
59 */
60static uint8_t
61mbox_is_full(win32_mbox_t* m) {
62 size_t size = 0;
63 if (m->in > m->out) {
64 size = (m->in - m->out);
65 } else if (m->out > m->in) {
66 size = m->size - m->out + m->in;
67 }
68 return size == m->size - 1;
69}
70
71/**
72 * \brief Check if message box is empty
73 * \param[in] m: Message box handle
74 * \return 1 if empty, 0 otherwise
75 */
76static uint8_t
77mbox_is_empty(win32_mbox_t* m) {
78 return m->in == m->out;
79}
80
81/**
82 * \brief Get current kernel time in units of milliseconds
83 */
84static uint32_t
85osKernelSysTick(void) {
86 LONGLONG ret;
87 LARGE_INTEGER now;
88
89 QueryPerformanceFrequency(&freq); /* Get frequency */
90 QueryPerformanceCounter(&now); /* Get current time */
91 ret = now.QuadPart - sys_start_time.QuadPart;
92 return (uint32_t)(((ret) * 1000) / freq.QuadPart);
93}
94
95uint8_t
96lwesp_sys_init(void) {
97 QueryPerformanceFrequency(&freq);
98 QueryPerformanceCounter(&sys_start_time);
99
100 lwesp_sys_mutex_create(&sys_mutex);
101 return 1;
102}
103
104uint32_t
105lwesp_sys_now(void) {
106 return osKernelSysTick();
107}
108
109#if LWESP_CFG_OS
110uint8_t
111lwesp_sys_protect(void) {
112 lwesp_sys_mutex_lock(&sys_mutex);
113 return 1;
114}
115
116uint8_t
117lwesp_sys_unprotect(void) {
118 lwesp_sys_mutex_unlock(&sys_mutex);
119 return 1;
120}
121
122uint8_t
123lwesp_sys_mutex_create(lwesp_sys_mutex_t* p) {
124 *p = CreateMutex(NULL, FALSE, NULL);
125 return *p != NULL;
126}
127
128uint8_t
129lwesp_sys_mutex_delete(lwesp_sys_mutex_t* p) {
130 return CloseHandle(*p);
131}
132
133uint8_t
134lwesp_sys_mutex_lock(lwesp_sys_mutex_t* p) {
135 DWORD ret;
136 ret = WaitForSingleObject(*p, INFINITE);
137 if (ret != WAIT_OBJECT_0) {
138 return 0;
139 }
140 return 1;
141}
142
143uint8_t
144lwesp_sys_mutex_unlock(lwesp_sys_mutex_t* p) {
145 return ReleaseMutex(*p);
146}
147
148uint8_t
149lwesp_sys_mutex_isvalid(lwesp_sys_mutex_t* p) {
150 return p != NULL && *p != NULL;
151}
152
153uint8_t
154lwesp_sys_mutex_invalid(lwesp_sys_mutex_t* p) {
155 *p = LWESP_SYS_MUTEX_NULL;
156 return 1;
157}
158
159uint8_t
160lwesp_sys_sem_create(lwesp_sys_sem_t* p, uint8_t cnt) {
161 HANDLE h;
162 h = CreateSemaphore(NULL, !!cnt, 1, NULL);
163 *p = h;
164 return *p != NULL;
165}
166
167uint8_t
168lwesp_sys_sem_delete(lwesp_sys_sem_t* p) {
169 return CloseHandle(*p);
170}
171
172uint32_t
173lwesp_sys_sem_wait(lwesp_sys_sem_t* p, uint32_t timeout) {
174 DWORD ret;
175 uint32_t tick = osKernelSysTick();
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, size_t stack_size, lwesp_sys_thread_prio_t prio) {
340 HANDLE h;
341 DWORD id;
342 h = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)thread_func, arg, 0, &id);
343 if (t != NULL) {
344 *t = h;
345 }
346 return h != NULL;
347}
348
349uint8_t
350lwesp_sys_thread_terminate(lwesp_sys_thread_t* t) {
351 if (t == NULL) { /* Shall we terminate ourself? */
352 ExitThread(0);
353 } else {
354 /* We have known thread, find handle by looking at ID */
355 TerminateThread(*t, 0);
356 }
357 return 1;
358}
359
360uint8_t
361lwesp_sys_thread_yield(void) {
362 /* Not implemented */
363 return 1;
364}
365
366#endif /* LWESP_CFG_OS */
367#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) 2020 Tilen MAJERLE
8 *
9 * Permission is hereby granted, free of charge, to any person
10 * obtaining a copy of this software and associated documentation
11 * files (the "Software"), to deal in the Software without restriction,
12 * including without limitation the rights to use, copy, modify, merge,
13 * publish, distribute, sublicense, and/or sell copies of the Software,
14 * and to permit persons to whom the Software is furnished to do so,
15 * subject to the following conditions:
16 *
17 * The above copyright notice and this permission notice shall be
18 * included in all copies or substantial portions of the Software.
19 *
20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
22 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
23 * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
24 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
25 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
27 * OTHER DEALINGS IN THE SOFTWARE.
28 *
29 * This file is part of LwESP - Lightweight ESP-AT parser library.
30 *
31 * Author: Tilen MAJERLE <tilen@majerle.eu>
32 * Version: v1.1.2-dev
33 */
34#ifndef LWESP_HDR_SYSTEM_PORT_H
35#define LWESP_HDR_SYSTEM_PORT_H
36
37#include <stdint.h>
38#include <stdlib.h>
39#include "lwesp/lwesp_opt.h"
40#include "cmsis_os.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_HDR_SYSTEM_PORT_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) 2020 Tilen MAJERLE
8 *
9 * Permission is hereby granted, free of charge, to any person
10 * obtaining a copy of this software and associated documentation
11 * files (the "Software"), to deal in the Software without restriction,
12 * including without limitation the rights to use, copy, modify, merge,
13 * publish, distribute, sublicense, and/or sell copies of the Software,
14 * and to permit persons to whom the Software is furnished to do so,
15 * subject to the following conditions:
16 *
17 * The above copyright notice and this permission notice shall be
18 * included in all copies or substantial portions of the Software.
19 *
20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
22 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
23 * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
24 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
25 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
27 * OTHER DEALINGS IN THE SOFTWARE.
28 *
29 * This file is part of LwESP - Lightweight ESP-AT parser library.
30 *
31 * Author: Tilen MAJERLE <tilen@majerle.eu>
32 * Version: v1.1.2-dev
33 */
34#include "system/lwesp_sys.h"
35#include "cmsis_os.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 osKernelSysTick();
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) : LWESP_SYS_TIMEOUT;
116}
117
118uint8_t
119lwesp_sys_sem_release(lwesp_sys_sem_t* p) {
120 return osSemaphoreRelease(*p) == osOK;
121}
122
123uint8_t
124lwesp_sys_sem_isvalid(lwesp_sys_sem_t* p) {
125 return p != NULL && *p != NULL;
126}
127
128uint8_t
129lwesp_sys_sem_invalid(lwesp_sys_sem_t* p) {
130 *p = LWESP_SYS_SEM_NULL;
131 return 1;
132}
133
134uint8_t
135lwesp_sys_mbox_create(lwesp_sys_mbox_t* b, size_t size) {
136 const osMessageQueueAttr_t attr = {
137 .name = "lwesp_mbox",
138 };
139 return (*b = osMessageQueueNew(size, sizeof(void*), &attr)) != NULL;
140}
141
142uint8_t
143lwesp_sys_mbox_delete(lwesp_sys_mbox_t* b) {
144 if (osMessageQueueGetCount(*b) > 0) {
145 return 0;
146 }
147 return osMessageQueueDelete(*b) == osOK;
148}
149
150uint32_t
151lwesp_sys_mbox_put(lwesp_sys_mbox_t* b, void* m) {
152 uint32_t tick = osKernelSysTick();
153 return osMessageQueuePut(*b, &m, 0, osWaitForever) == osOK ? (osKernelSysTick() - tick) : LWESP_SYS_TIMEOUT;
154}
155
156uint32_t
157lwesp_sys_mbox_get(lwesp_sys_mbox_t* b, void** m, uint32_t timeout) {
158 uint32_t tick = osKernelSysTick();
159 return (osMessageQueueGet(*b, m, NULL, timeout == 0 ? osWaitForever : timeout) == osOK) ? (osKernelSysTick() - tick) : LWESP_SYS_TIMEOUT;
160}
161
162uint8_t
163lwesp_sys_mbox_putnow(lwesp_sys_mbox_t* b, void* m) {
164 return osMessageQueuePut(*b, &m, 0, 0) == osOK;
165}
166
167uint8_t
168lwesp_sys_mbox_getnow(lwesp_sys_mbox_t* b, void** m) {
169 return osMessageQueueGet(*b, m, NULL, 0) == osOK;
170}
171
172uint8_t
173lwesp_sys_mbox_isvalid(lwesp_sys_mbox_t* b) {
174 return b != NULL && *b != NULL;
175}
176
177uint8_t
178lwesp_sys_mbox_invalid(lwesp_sys_mbox_t* b) {
179 *b = LWESP_SYS_MBOX_NULL;
180 return 1;
181}
182
183uint8_t
184lwesp_sys_thread_create(lwesp_sys_thread_t* t, const char* name, lwesp_sys_thread_fn thread_func, void* const arg, size_t stack_size, lwesp_sys_thread_prio_t prio) {
185 lwesp_sys_thread_t id;
186 const osThreadAttr_t thread_attr = {
187 .name = (char*)name,
188 .priority = (osPriority)prio,
189 .stack_size = stack_size > 0 ? stack_size : LWESP_SYS_THREAD_SS
190 };
191
192 id = osThreadNew(thread_func, arg, &thread_attr);
193 if (t != NULL) {
194 *t = id;
195 }
196 return id != NULL;
197}
198
199uint8_t
200lwesp_sys_thread_terminate(lwesp_sys_thread_t* t) {
201 if (t != NULL) {
202 osThreadTerminate(*t);
203 } else {
204 osThreadExit();
205 }
206 return 1;
207}
208
209uint8_t
210lwesp_sys_thread_yield(void) {
211 osThreadYield();
212 return 1;
213}
214
215#endif /* !__DOXYGEN__ */