Porting guide

High level of ESP-AT library is platform independent, written in C (C11), 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() and lwesp_ll_deinit() callback functions

  • Implement 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 target

  • Add 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() or lwesp_input() functions, based on application configuration of LWESP_CFG_INPUT_USE_PROCESS parameter.

    • When LWESP_CFG_INPUT_USE_PROCESS is disabled, dedicated receive buffer is created by ESP-AT library and lwesp_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 of LWESP_MEM_SIZE size

  • It sets send and reset callback functions for ESP-AT library

Actual implementation of low-level driver for WIN32
  1/**
  2 * \file            lwesp_ll_win32.c
  3 * \brief           Low-level communication with ESP device for WIN32
  4 */
  5
  6/*
  7 * Copyright (c) 2024 Tilen MAJERLE
  8 *
  9 * Permission is hereby granted, free of charge, to any person
 10 * obtaining a copy of this software and associated documentation
 11 * files (the "Software"), to deal in the Software without restriction,
 12 * including without limitation the rights to use, copy, modify, merge,
 13 * publish, distribute, sublicense, and/or sell copies of the Software,
 14 * and to permit persons to whom the Software is furnished to do so,
 15 * subject to the following conditions:
 16 *
 17 * The above copyright notice and this permission notice shall be
 18 * included in all copies or substantial portions of the Software.
 19 *
 20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 21 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 22 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
 23 * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 24 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 25 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 26 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 27 * OTHER DEALINGS IN THE SOFTWARE.
 28 *
 29 * This file is part of 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 && 0
 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[] = {"\\\\.\\COM7", "\\\\.\\COM60", "\\\\.\\COM4", "\\\\.\\COM8",
 89                                           "\\\\.\\COM9", "\\\\.\\COM10", "\\\\.\\COM17"};
 90
 91    /* Try to open one of listed COM ports */
 92    if (!initialized) {
 93        printf("Initializing COM port first time\r\n");
 94        for (i = 0; i < LWESP_ARRAYSIZE(com_port_names); ++i) {
 95            printf("Trying to open COM port %s\r\n", com_port_names[i]);
 96            com_port = CreateFileA(com_port_names[i], GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, NULL);
 97            if (GetCommState(com_port, &dcb)) {
 98                printf("Successfully received info for COM port %s. Using this one..\r\n", com_port_names[i]);
 99                break;
100            } else {
101                printf("Could not get info for COM port %s\r\n", com_port_names[i]);
102            }
103        }
104        if (i == LWESP_ARRAYSIZE(com_port_names)) {
105            printf("Could not get info for any COM port. Entering while loop\r\n");
106            while (1) {
107                Sleep(1000);
108            }
109        }
110    }
111
112    /* Configure COM port parameters */
113    if (GetCommState(com_port, &dcb)) {
114        COMMTIMEOUTS timeouts;
115
116        /* Set port config */
117        dcb.BaudRate = baudrate;
118        dcb.ByteSize = 8;
119        dcb.Parity = NOPARITY;
120        dcb.StopBits = ONESTOPBIT;
121        if (SetCommState(com_port, &dcb)) {
122            /* Set timeouts config */
123            if (GetCommTimeouts(com_port, &timeouts)) {
124                /* Set timeout to return immediately from ReadFile function */
125                timeouts.ReadIntervalTimeout = MAXDWORD;
126                timeouts.ReadTotalTimeoutConstant = 0;
127                timeouts.ReadTotalTimeoutMultiplier = 0;
128                if (SetCommTimeouts(com_port, &timeouts)) {
129                    GetCommTimeouts(com_port, &timeouts);
130                } else {
131                    printf("[LWESP LL] Could not set port timeout config\r\n");
132                }
133            } else {
134                printf("[LWESP LL] Could not get port timeout config\r\n");
135            }
136        } else {
137            printf("[LWESP LL] Could not set port config\r\n");
138        }
139    } else {
140        printf("[LWESP LL] Could not get port info\r\n");
141    }
142
143    /* On first function call, create a thread to read data from COM port */
144    if (!initialized) {
145        lwesp_sys_thread_create(&thread_handle, "lwesp_ll_thread", uart_thread, NULL, 0, 0);
146    }
147    return 1;
148}
149
150/**
151 * \brief           UART thread
152 */
153static void
154uart_thread(void* param) {
155    DWORD bytes_read;
156    lwesp_sys_sem_t sem;
157    FILE* file = NULL;
158
159    LWESP_UNUSED(param);
160
161    lwesp_sys_sem_create(&sem, 0); /* Create semaphore for delay functions */
162    while (com_port == NULL) {
163        lwesp_sys_sem_wait(&sem, 1); /* Add some delay with yield */
164    }
165
166    fopen_s(&file, "log_file.txt", "w+"); /* Open debug file in write mode */
167    while (1) {
168        while (com_port == NULL) {
169            lwesp_sys_sem_wait(&sem, 1);
170        }
171
172        /*
173         * Try to read data from COM port
174         * and send it to upper layer for processing
175         */
176        do {
177            ReadFile(com_port, data_buffer, sizeof(data_buffer), &bytes_read, NULL);
178            if (bytes_read > 0) {
179                HANDLE hConsole;
180                hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
181#if 0
182                SetConsoleTextAttribute(hConsole, FOREGROUND_GREEN);
183                for (DWORD i = 0; i < bytes_read; ++i) {
184                    printf("%c", data_buffer[i]);
185                }
186                SetConsoleTextAttribute(hConsole, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
187#endif
188
189                if (lwesp_ll_win32_driver_ignore_data) {
190                    printf("IGNORING..\r\n");
191                    continue;
192                }
193
194                /* Send received data to input processing module */
195#if LWESP_CFG_INPUT_USE_PROCESS
196                lwesp_input_process(data_buffer, (size_t)bytes_read);
197#else  /* LWESP_CFG_INPUT_USE_PROCESS */
198                lwesp_input(data_buffer, (size_t)bytes_read);
199#endif /* !LWESP_CFG_INPUT_USE_PROCESS */
200
201                /* Write received data to output debug file */
202                if (file != NULL) {
203                    fwrite(data_buffer, 1, bytes_read, file);
204                    fflush(file);
205                }
206            }
207        } while (bytes_read == (DWORD)sizeof(data_buffer));
208
209        /* Implement delay to allow other tasks processing */
210        lwesp_sys_sem_wait(&sem, 1);
211    }
212}
213
214/**
215 * \brief           Reset device GPIO management
216 */
217static uint8_t
218reset_device(uint8_t state) {
219    LWESP_UNUSED(state);
220    return 0; /* Hardware reset was not successful */
221}
222
223/**
224 * \brief           Callback function called from initialization process
225 */
226lwespr_t
227lwesp_ll_init(lwesp_ll_t* ll) {
228#if !LWESP_CFG_MEM_CUSTOM
229    /* Step 1: Configure memory for dynamic allocations */
230    static uint8_t memory[0x10000]; /* Create memory for dynamic allocations with specific size */
231
232    /*
233     * Create memory region(s) of memory.
234     * If device has internal/external memory available,
235     * multiple memories may be used
236     */
237    lwesp_mem_region_t mem_regions[] = {{memory, sizeof(memory)}};
238    if (!initialized) {
239        lwesp_mem_assignmemory(mem_regions,
240                               LWESP_ARRAYSIZE(mem_regions)); /* Assign memory for allocations to ESP library */
241    }
242#endif /* !LWESP_CFG_MEM_CUSTOM */
243
244    /* Step 2: Set AT port send function to use when we have data to transmit */
245    if (!initialized) {
246        ll->send_fn = send_data; /* Set callback function to send data */
247        ll->reset_fn = reset_device;
248    }
249
250    /* Step 3: Configure AT port to be able to send/receive data to/from ESP device */
251    if (!configure_uart(ll->uart.baudrate)) { /* Initialize UART for communication */
252        return lwespERR;
253    }
254    initialized = 1;
255    return lwespOK;
256}
257
258/**
259 * \brief           Callback function to de-init low-level communication part
260 */
261lwespr_t
262lwesp_ll_deinit(lwesp_ll_t* ll) {
263    LWESP_UNUSED(ll);
264    if (thread_handle != NULL) {
265        lwesp_sys_thread_terminate(&thread_handle);
266        thread_handle = NULL;
267    }
268    initialized = 0; /* Clear initialized flag */
269    return lwespOK;
270}
271
272#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 buffer

  • Memory manager has been assigned to 1 region of LWESP_MEM_SIZE size

  • It sets send and reset callback functions for ESP-AT library

Actual implementation of low-level driver for STM32
  1/**
  2 * \file            lwesp_ll_stm32.c
  3 * \brief           Generic STM32 driver, included in various STM32 driver variants
  4 */
  5
  6/*
  7 * Copyright (c) 2024 Tilen MAJERLE
  8 *
  9 * Permission is hereby granted, free of charge, to any person
 10 * obtaining a copy of this software and associated documentation
 11 * files (the "Software"), to deal in the Software without restriction,
 12 * including without limitation the rights to use, copy, modify, merge,
 13 * publish, distribute, sublicense, and/or sell copies of the Software,
 14 * and to permit persons to whom the Software is furnished to do so,
 15 * subject to the following conditions:
 16 *
 17 * The above copyright notice and this permission notice shall be
 18 * included in all copies or substantial portions of the Software.
 19 *
 20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 21 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 22 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
 23 * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 24 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 25 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 26 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 27 * OTHER DEALINGS IN THE SOFTWARE.
 28 *
 29 * This file is part of 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 0x4000
 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#if defined(LWESP_USART_DMA_RX_CH)
221        dma_init.Channel = LWESP_USART_DMA_RX_CH;
222#else
223        dma_init.PeriphRequest = LWESP_USART_DMA_RX_REQ_NUM;
224#endif /* !defined(STM32F4xx) && !defined(STM32F7xx) && !defined(STM32F2xx) */
225#else
226        LL_DMA_DeInit(LWESP_USART_DMA, LWESP_USART_DMA_RX_CH);
227        dma_init.PeriphRequest = LWESP_USART_DMA_RX_REQ_NUM;
228#endif /* defined(LWESP_USART_DMA_RX_STREAM) */
229        dma_init.PeriphOrM2MSrcAddress = (uint32_t)&LWESP_USART->LWESP_USART_RDR_NAME;
230        dma_init.MemoryOrM2MDstAddress = (uint32_t)usart_mem;
231        dma_init.Direction = LL_DMA_DIRECTION_PERIPH_TO_MEMORY;
232        dma_init.Mode = LL_DMA_MODE_CIRCULAR;
233        dma_init.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT;
234        dma_init.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT;
235        dma_init.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_BYTE;
236        dma_init.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_BYTE;
237        dma_init.NbData = sizeof(usart_mem);
238        dma_init.Priority = LL_DMA_PRIORITY_MEDIUM;
239#if defined(LWESP_USART_DMA_RX_STREAM)
240        LL_DMA_Init(LWESP_USART_DMA, LWESP_USART_DMA_RX_STREAM, &dma_init);
241#else
242        LL_DMA_Init(LWESP_USART_DMA, LWESP_USART_DMA_RX_CH, &dma_init);
243#endif /* defined(LWESP_USART_DMA_RX_STREAM) */
244
245        /* Enable DMA interrupts */
246#if defined(LWESP_USART_DMA_RX_STREAM)
247        LL_DMA_EnableIT_HT(LWESP_USART_DMA, LWESP_USART_DMA_RX_STREAM);
248        LL_DMA_EnableIT_TC(LWESP_USART_DMA, LWESP_USART_DMA_RX_STREAM);
249        LL_DMA_EnableIT_TE(LWESP_USART_DMA, LWESP_USART_DMA_RX_STREAM);
250        LL_DMA_EnableIT_FE(LWESP_USART_DMA, LWESP_USART_DMA_RX_STREAM);
251        LL_DMA_EnableIT_DME(LWESP_USART_DMA, LWESP_USART_DMA_RX_STREAM);
252#else
253        LL_DMA_EnableIT_HT(LWESP_USART_DMA, LWESP_USART_DMA_RX_CH);
254        LL_DMA_EnableIT_TC(LWESP_USART_DMA, LWESP_USART_DMA_RX_CH);
255        LL_DMA_EnableIT_TE(LWESP_USART_DMA, LWESP_USART_DMA_RX_CH);
256#endif /* defined(LWESP_USART_DMA_RX_STREAM) */
257
258        /* Enable DMA interrupts */
259        NVIC_SetPriority(LWESP_USART_DMA_RX_IRQ, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 0x07, 0x00));
260        NVIC_EnableIRQ(LWESP_USART_DMA_RX_IRQ);
261
262        old_pos = 0;
263        is_running = 1;
264
265        /* Start DMA and USART */
266#if defined(LWESP_USART_DMA_RX_STREAM)
267        LL_DMA_EnableStream(LWESP_USART_DMA, LWESP_USART_DMA_RX_STREAM);
268#else
269        LL_DMA_EnableChannel(LWESP_USART_DMA, LWESP_USART_DMA_RX_CH);
270#endif /* defined(LWESP_USART_DMA_RX_STREAM) */
271        LL_USART_Enable(LWESP_USART);
272    } else {
273        osDelay(10);
274        LL_USART_Disable(LWESP_USART);
275        usart_init.BaudRate = baudrate;
276        LL_USART_Init(LWESP_USART, &usart_init);
277        LL_USART_Enable(LWESP_USART);
278    }
279
280    /* Create mbox and start thread */
281    if (usart_ll_mbox_id == NULL) {
282        usart_ll_mbox_id = osMessageQueueNew(10, sizeof(void*), NULL);
283    }
284    if (usart_ll_thread_id == NULL) {
285        const osThreadAttr_t attr = {.stack_size = 1536};
286        usart_ll_thread_id = osThreadNew(usart_ll_thread, usart_ll_mbox_id, &attr);
287    }
288}
289
290#if defined(LWESP_RESET_PIN)
291/**
292 * \brief           Hardware reset callback
293 */
294static uint8_t
295prv_reset_device(uint8_t state) {
296    if (state) { /* Activate reset line */
297        LL_GPIO_ResetOutputPin(LWESP_RESET_PORT, LWESP_RESET_PIN);
298    } else {
299        LL_GPIO_SetOutputPin(LWESP_RESET_PORT, LWESP_RESET_PIN);
300    }
301    return 1;
302}
303#endif /* defined(LWESP_RESET_PIN) */
304
305/**
306 * \brief           Send data to ESP device
307 * \param[in]       data: Pointer to data to send
308 * \param[in]       len: Number of bytes to send
309 * \return          Number of bytes sent
310 */
311static size_t
312prv_send_data(const void* data, size_t len) {
313    const uint8_t* d = data;
314
315    for (size_t i = 0; i < len; ++i, ++d) {
316        LL_USART_TransmitData8(LWESP_USART, *d);
317        while (!LL_USART_IsActiveFlag_TXE(LWESP_USART)) {}
318    }
319    return len;
320}
321
322/**
323 * \brief           Callback function called from initialization process
324 */
325lwespr_t
326lwesp_ll_init(lwesp_ll_t* ll) {
327#if !LWESP_CFG_MEM_CUSTOM
328    static uint8_t memory[LWESP_MEM_SIZE];
329    const lwesp_mem_region_t mem_regions[] = {{memory, sizeof(memory)}};
330
331    if (!initialized) {
332        lwesp_mem_assignmemory(mem_regions, LWESP_ARRAYSIZE(mem_regions)); /* Assign memory for allocations */
333    }
334#endif /* !LWESP_CFG_MEM_CUSTOM */
335
336    if (!initialized) {
337        ll->send_fn = prv_send_data; /* Set callback function to send data */
338#if defined(LWESP_RESET_PIN)
339        ll->reset_fn = prv_reset_device; /* Set callback for hardware reset */
340#endif                                   /* defined(LWESP_RESET_PIN) */
341    }
342
343    prv_configure_uart(ll->uart.baudrate); /* Initialize UART for communication */
344    initialized = 1;
345    return lwespOK;
346}
347
348/**
349 * \brief           Callback function to de-init low-level communication part
350 */
351lwespr_t
352lwesp_ll_deinit(lwesp_ll_t* ll) {
353    if (usart_ll_mbox_id != NULL) {
354        osMessageQueueId_t tmp = usart_ll_mbox_id;
355        usart_ll_mbox_id = NULL;
356        osMessageQueueDelete(tmp);
357    }
358    if (usart_ll_thread_id != NULL) {
359        osThreadId_t tmp = usart_ll_thread_id;
360        usart_ll_thread_id = NULL;
361        osThreadTerminate(tmp);
362    }
363    initialized = 0;
364    LWESP_UNUSED(ll);
365    return lwespOK;
366}
367
368/**
369 * \brief           UART global interrupt handler
370 */
371void
372LWESP_USART_IRQHANDLER(void) {
373    LL_USART_ClearFlag_IDLE(LWESP_USART);
374    LL_USART_ClearFlag_PE(LWESP_USART);
375    LL_USART_ClearFlag_FE(LWESP_USART);
376    LL_USART_ClearFlag_ORE(LWESP_USART);
377    LL_USART_ClearFlag_NE(LWESP_USART);
378
379    if (usart_ll_mbox_id != NULL) {
380        void* d = (void*)1;
381        osMessageQueuePut(usart_ll_mbox_id, &d, 0, 0);
382    }
383}
384
385/**
386 * \brief           UART DMA stream/channel handler
387 */
388void
389LWESP_USART_DMA_RX_IRQHANDLER(void) {
390    LWESP_USART_DMA_RX_CLEAR_TC;
391    LWESP_USART_DMA_RX_CLEAR_HT;
392
393    if (usart_ll_mbox_id != NULL) {
394        void* d = (void*)1;
395        osMessageQueuePut(usart_ll_mbox_id, &d, 0, 0);
396    }
397}
398
399#endif /* !__DOXYGEN__ */

Example: System functions for WIN32

Actual header implementation of system functions for WIN32
 1/**
 2 * \file            lwesp_sys_port.h
 3 * \brief           WIN32 based system file implementation
 4 */
 5
 6/*
 7 * Copyright (c) 2024 Tilen MAJERLE
 8 *
 9 * Permission is hereby granted, free of charge, to any person
10 * obtaining a copy of this software and associated documentation
11 * files (the "Software"), to deal in the Software without restriction,
12 * including without limitation the rights to use, copy, modify, merge,
13 * publish, distribute, sublicense, and/or sell copies of the Software,
14 * and to permit persons to whom the Software is furnished to do so,
15 * subject to the following conditions:
16 *
17 * The above copyright notice and this permission notice shall be
18 * included in all copies or substantial portions of the Software.
19 *
20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
22 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
23 * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
24 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
25 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
27 * OTHER DEALINGS IN THE SOFTWARE.
28 *
29 * This file is part of 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 */
Actual implementation of system functions for WIN32
  1/**
  2 * \file            lwesp_sys_win32.c
  3 * \brief           System dependant functions for WIN32
  4 */
  5
  6/*
  7 * Copyright (c) 2024 Tilen MAJERLE
  8 *
  9 * Permission is hereby granted, free of charge, to any person
 10 * obtaining a copy of this software and associated documentation
 11 * files (the "Software"), to deal in the Software without restriction,
 12 * including without limitation the rights to use, copy, modify, merge,
 13 * publish, distribute, sublicense, and/or sell copies of the Software,
 14 * and to permit persons to whom the Software is furnished to do so,
 15 * subject to the following conditions:
 16 *
 17 * The above copyright notice and this permission notice shall be
 18 * included in all copies or substantial portions of the Software.
 19 *
 20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 21 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 22 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
 23 * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 24 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 25 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 26 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 27 * OTHER DEALINGS IN THE SOFTWARE.
 28 *
 29 * This file is part of 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

Actual header implementation of system functions for CMSIS-OS based operating systems
 1/**
 2 * \file            lwesp_sys_port.h
 3 * \brief           CMSIS-OS based system file
 4 */
 5
 6/*
 7 * Copyright (c) 2024 Tilen MAJERLE
 8 *
 9 * Permission is hereby granted, free of charge, to any person
10 * obtaining a copy of this software and associated documentation
11 * files (the "Software"), to deal in the Software without restriction,
12 * including without limitation the rights to use, copy, modify, merge,
13 * publish, distribute, sublicense, and/or sell copies of the Software,
14 * and to permit persons to whom the Software is furnished to do so,
15 * subject to the following conditions:
16 *
17 * The above copyright notice and this permission notice shall be
18 * included in all copies or substantial portions of the Software.
19 *
20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
22 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
23 * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
24 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
25 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
27 * OTHER DEALINGS IN THE SOFTWARE.
28 *
29 * This file is part of 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   (1536)
60
61#endif /* LWESP_CFG_OS && !__DOXYGEN__ */
62
63#ifdef __cplusplus
64}
65#endif /* __cplusplus */
66
67#endif /* LWESP_SYSTEM_PORT_HDR_H */
Actual implementation of system functions for CMSIS-OS based operating systems
  1/**
  2 * \file            lwesp_sys_cmsis_os.c
  3 * \brief           System dependent functions for CMSIS based operating system
  4 */
  5
  6/*
  7 * Copyright (c) 2024 Tilen MAJERLE
  8 *
  9 * Permission is hereby granted, free of charge, to any person
 10 * obtaining a copy of this software and associated documentation
 11 * files (the "Software"), to deal in the Software without restriction,
 12 * including without limitation the rights to use, copy, modify, merge,
 13 * publish, distribute, sublicense, and/or sell copies of the Software,
 14 * and to permit persons to whom the Software is furnished to do so,
 15 * subject to the following conditions:
 16 *
 17 * The above copyright notice and this permission notice shall be
 18 * included in all copies or substantial portions of the Software.
 19 *
 20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 21 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 22 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
 23 * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 24 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 25 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 26 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 27 * OTHER DEALINGS IN THE SOFTWARE.
 28 *
 29 * This file is part of 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__ */