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() 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) 2022 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 * \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[] = {
 89        "\\\\.\\COM16",
 90        "\\\\.\\COM4",
 91        "\\\\.\\COM9",
 92        "\\\\.\\COM10"
 93    };
 94
 95    /* Try to open one of listed COM ports */
 96    if (!initialized) {
 97        printf("Initializing COM port first time\r\n");
 98        for (i = 0; i < LWESP_ARRAYSIZE(com_port_names); ++i) {
 99            printf("Trying to open 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            if (GetCommState(com_port, &dcb)) {
102                printf("Successfully received info for COM port %s. Using this one..\r\n", com_port_names[i]);
103                break;
104            } else {
105                printf("Could not get info for COM port %s\r\n", com_port_names[i]);
106            }
107        }
108        if (i == LWESP_ARRAYSIZE(com_port_names)) {
109            printf("Could not get info for any COM port. Entering while loop\r\n");
110            while (1) {
111                Sleep(1000);
112            }
113        }
114    }
115
116    /* Configure COM port parameters */
117    if (GetCommState(com_port, &dcb)) {
118        COMMTIMEOUTS timeouts;
119
120        /* Set port config */
121        dcb.BaudRate = baudrate;
122        dcb.ByteSize = 8;
123        dcb.Parity = NOPARITY;
124        dcb.StopBits = ONESTOPBIT;
125        if (SetCommState(com_port, &dcb)) {
126            /* Set timeouts config */
127            if (GetCommTimeouts(com_port, &timeouts)) {
128                /* Set timeout to return immediately from ReadFile function */
129                timeouts.ReadIntervalTimeout = MAXDWORD;
130                timeouts.ReadTotalTimeoutConstant = 0;
131                timeouts.ReadTotalTimeoutMultiplier = 0;
132                if (SetCommTimeouts(com_port, &timeouts)) {
133                    GetCommTimeouts(com_port, &timeouts);
134                } else {
135                    printf("[LWESP LL] Could not set port timeout config\r\n");
136                }
137            } else {
138                printf("[LWESP LL] Could not get port timeout config\r\n");
139            }
140        } else {
141            printf("[LWESP LL] Could not set port config\r\n");
142        }
143    } else {
144        printf("[LWESP LL] Could not get port info\r\n");
145    }
146
147    /* On first function call, create a thread to read data from COM port */
148    if (!initialized) {
149        lwesp_sys_thread_create(&thread_handle, "lwesp_ll_thread", uart_thread, NULL, 0, 0);
150    }
151    return 1;
152}
153
154/**
155 * \brief           UART thread
156 */
157static void
158uart_thread(void* param) {
159    DWORD bytes_read;
160    lwesp_sys_sem_t sem;
161    FILE* file = NULL;
162
163    lwesp_sys_sem_create(&sem, 0);              /* Create semaphore for delay functions */
164    while (com_port == NULL) {
165        lwesp_sys_sem_wait(&sem, 1);            /* Add some delay with yield */
166    }
167
168    fopen_s(&file, "log_file.txt", "w+");       /* Open debug file in write mode */
169    while (1) {
170        while (com_port == NULL) {
171            lwesp_sys_sem_wait(&sem, 1);
172        }
173
174        /*
175         * Try to read data from COM port
176         * and send it to upper layer for processing
177         */
178        do {
179            ReadFile(com_port, data_buffer, sizeof(data_buffer), &bytes_read, NULL);
180            if (bytes_read > 0) {
181                HANDLE hConsole;
182                hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
183                SetConsoleTextAttribute(hConsole, FOREGROUND_GREEN);
184                for (DWORD i = 0; i < bytes_read; ++i) {
185                    printf("%c", data_buffer[i]);
186                }
187                SetConsoleTextAttribute(hConsole, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
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    return 0;                                   /* Hardware reset was not successful */
220}
221
222/**
223 * \brief           Callback function called from initialization process
224 */
225lwespr_t
226lwesp_ll_init(lwesp_ll_t* ll) {
227#if !LWESP_CFG_MEM_CUSTOM
228    /* Step 1: Configure memory for dynamic allocations */
229    static uint8_t memory[0x10000];             /* Create memory for dynamic allocations with specific size */
230
231    /*
232     * Create memory region(s) of memory.
233     * If device has internal/external memory available,
234     * multiple memories may be used
235     */
236    lwesp_mem_region_t mem_regions[] = {
237        { memory, sizeof(memory) }
238    };
239    if (!initialized) {
240        lwesp_mem_assignmemory(mem_regions, 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    if (thread_handle != NULL) {
264        lwesp_sys_thread_terminate(&thread_handle);
265        thread_handle = NULL;
266    }
267    initialized = 0;                            /* Clear initialized flag */
268    return lwespOK;
269}
270
271#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) 2022 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        }
112    }
113}
114
115/**
116 * \brief           Configure UART using DMA for receive in double buffer mode and IDLE line detection
117 */
118static void
119prv_configure_uart(uint32_t baudrate) {
120    static LL_USART_InitTypeDef usart_init;
121    static LL_DMA_InitTypeDef dma_init;
122    LL_GPIO_InitTypeDef gpio_init;
123
124    if (!initialized) {
125        /* Enable peripheral clocks */
126        LWESP_USART_CLK;
127        LWESP_USART_DMA_CLK;
128        LWESP_USART_TX_PORT_CLK;
129        LWESP_USART_RX_PORT_CLK;
130
131#if defined(LWESP_RESET_PIN)
132        LWESP_RESET_PORT_CLK;
133#endif /* defined(LWESP_RESET_PIN) */
134
135#if defined(LWESP_GPIO0_PIN)
136        LWESP_GPIO0_PORT_CLK;
137#endif /* defined(LWESP_GPIO0_PIN) */
138
139#if defined(LWESP_GPIO2_PIN)
140        LWESP_GPIO2_PORT_CLK;
141#endif /* defined(LWESP_GPIO2_PIN) */
142
143#if defined(LWESP_CH_PD_PIN)
144        LWESP_CH_PD_PORT_CLK;
145#endif /* defined(LWESP_CH_PD_PIN) */
146
147        /* Global pin configuration */
148        LL_GPIO_StructInit(&gpio_init);
149        gpio_init.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
150        gpio_init.Pull = LL_GPIO_PULL_UP;
151        gpio_init.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH;
152        gpio_init.Mode = LL_GPIO_MODE_OUTPUT;
153
154#if defined(LWESP_RESET_PIN)
155        /* Configure RESET pin */
156        gpio_init.Pin = LWESP_RESET_PIN;
157        LL_GPIO_Init(LWESP_RESET_PORT, &gpio_init);
158#endif /* defined(LWESP_RESET_PIN) */
159
160#if defined(LWESP_GPIO0_PIN)
161        /* Configure GPIO0 pin */
162        gpio_init.Pin = LWESP_GPIO0_PIN;
163        LL_GPIO_Init(LWESP_GPIO0_PORT, &gpio_init);
164        LL_GPIO_SetOutputPin(LWESP_GPIO0_PORT, LWESP_GPIO0_PIN);
165#endif /* defined(LWESP_GPIO0_PIN) */
166
167#if defined(LWESP_GPIO2_PIN)
168        /* Configure GPIO2 pin */
169        gpio_init.Pin = LWESP_GPIO2_PIN;
170        LL_GPIO_Init(LWESP_GPIO2_PORT, &gpio_init);
171        LL_GPIO_SetOutputPin(LWESP_GPIO2_PORT, LWESP_GPIO2_PIN);
172#endif /* defined(LWESP_GPIO2_PIN) */
173
174#if defined(LWESP_CH_PD_PIN)
175        /* Configure CH_PD pin */
176        gpio_init.Pin = LWESP_CH_PD_PIN;
177        LL_GPIO_Init(LWESP_CH_PD_PORT, &gpio_init);
178        LL_GPIO_SetOutputPin(LWESP_CH_PD_PORT, LWESP_CH_PD_PIN);
179#endif /* defined(LWESP_CH_PD_PIN) */
180
181        /* Configure USART pins */
182        gpio_init.Mode = LL_GPIO_MODE_ALTERNATE;
183
184        /* TX PIN */
185        gpio_init.Alternate = LWESP_USART_TX_PIN_AF;
186        gpio_init.Pin = LWESP_USART_TX_PIN;
187        LL_GPIO_Init(LWESP_USART_TX_PORT, &gpio_init);
188
189        /* RX PIN */
190        gpio_init.Alternate = LWESP_USART_RX_PIN_AF;
191        gpio_init.Pin = LWESP_USART_RX_PIN;
192        LL_GPIO_Init(LWESP_USART_RX_PORT, &gpio_init);
193
194        /* Configure UART */
195        LL_USART_DeInit(LWESP_USART);
196        LL_USART_StructInit(&usart_init);
197        usart_init.BaudRate = baudrate;
198        usart_init.DataWidth = LL_USART_DATAWIDTH_8B;
199        usart_init.HardwareFlowControl = LL_USART_HWCONTROL_NONE;
200        usart_init.OverSampling = LL_USART_OVERSAMPLING_16;
201        usart_init.Parity = LL_USART_PARITY_NONE;
202        usart_init.StopBits = LL_USART_STOPBITS_1;
203        usart_init.TransferDirection = LL_USART_DIRECTION_TX_RX;
204        LL_USART_Init(LWESP_USART, &usart_init);
205
206        /* Enable USART interrupts and DMA request */
207        LL_USART_EnableIT_IDLE(LWESP_USART);
208        LL_USART_EnableIT_PE(LWESP_USART);
209        LL_USART_EnableIT_ERROR(LWESP_USART);
210        LL_USART_EnableDMAReq_RX(LWESP_USART);
211
212        /* Enable USART interrupts */
213        NVIC_SetPriority(LWESP_USART_IRQ, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 0x07, 0x00));
214        NVIC_EnableIRQ(LWESP_USART_IRQ);
215
216        /* Configure DMA */
217        is_running = 0;
218#if defined(LWESP_USART_DMA_RX_STREAM)
219        LL_DMA_DeInit(LWESP_USART_DMA, LWESP_USART_DMA_RX_STREAM);
220        dma_init.Channel = LWESP_USART_DMA_RX_CH;
221#else
222        LL_DMA_DeInit(LWESP_USART_DMA, LWESP_USART_DMA_RX_CH);
223        dma_init.PeriphRequest = LWESP_USART_DMA_RX_REQ_NUM;
224#endif /* defined(LWESP_USART_DMA_RX_STREAM) */
225        dma_init.PeriphOrM2MSrcAddress = (uint32_t)&LWESP_USART->LWESP_USART_RDR_NAME;
226        dma_init.MemoryOrM2MDstAddress = (uint32_t)usart_mem;
227        dma_init.Direction = LL_DMA_DIRECTION_PERIPH_TO_MEMORY;
228        dma_init.Mode = LL_DMA_MODE_CIRCULAR;
229        dma_init.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT;
230        dma_init.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT;
231        dma_init.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_BYTE;
232        dma_init.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_BYTE;
233        dma_init.NbData = sizeof(usart_mem);
234        dma_init.Priority = LL_DMA_PRIORITY_MEDIUM;
235#if defined(LWESP_USART_DMA_RX_STREAM)
236        LL_DMA_Init(LWESP_USART_DMA, LWESP_USART_DMA_RX_STREAM, &dma_init);
237#else
238        LL_DMA_Init(LWESP_USART_DMA, LWESP_USART_DMA_RX_CH, &dma_init);
239#endif /* defined(LWESP_USART_DMA_RX_STREAM) */
240
241        /* Enable DMA interrupts */
242#if defined(LWESP_USART_DMA_RX_STREAM)
243        LL_DMA_EnableIT_HT(LWESP_USART_DMA, LWESP_USART_DMA_RX_STREAM);
244        LL_DMA_EnableIT_TC(LWESP_USART_DMA, LWESP_USART_DMA_RX_STREAM);
245        LL_DMA_EnableIT_TE(LWESP_USART_DMA, LWESP_USART_DMA_RX_STREAM);
246        LL_DMA_EnableIT_FE(LWESP_USART_DMA, LWESP_USART_DMA_RX_STREAM);
247        LL_DMA_EnableIT_DME(LWESP_USART_DMA, LWESP_USART_DMA_RX_STREAM);
248#else
249        LL_DMA_EnableIT_HT(LWESP_USART_DMA, LWESP_USART_DMA_RX_CH);
250        LL_DMA_EnableIT_TC(LWESP_USART_DMA, LWESP_USART_DMA_RX_CH);
251        LL_DMA_EnableIT_TE(LWESP_USART_DMA, LWESP_USART_DMA_RX_CH);
252#endif /* defined(LWESP_USART_DMA_RX_STREAM) */
253
254        /* Enable DMA interrupts */
255        NVIC_SetPriority(LWESP_USART_DMA_RX_IRQ, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 0x07, 0x00));
256        NVIC_EnableIRQ(LWESP_USART_DMA_RX_IRQ);
257
258        old_pos = 0;
259        is_running = 1;
260
261        /* Start DMA and USART */
262#if defined(LWESP_USART_DMA_RX_STREAM)
263        LL_DMA_EnableStream(LWESP_USART_DMA, LWESP_USART_DMA_RX_STREAM);
264#else
265        LL_DMA_EnableChannel(LWESP_USART_DMA, LWESP_USART_DMA_RX_CH);
266#endif /* defined(LWESP_USART_DMA_RX_STREAM) */
267        LL_USART_Enable(LWESP_USART);
268    } else {
269        osDelay(10);
270        LL_USART_Disable(LWESP_USART);
271        usart_init.BaudRate = baudrate;
272        LL_USART_Init(LWESP_USART, &usart_init);
273        LL_USART_Enable(LWESP_USART);
274    }
275
276    /* Create mbox and start thread */
277    if (usart_ll_mbox_id == NULL) {
278        usart_ll_mbox_id = osMessageQueueNew(10, sizeof(void*), NULL);
279    }
280    if (usart_ll_thread_id == NULL) {
281        const osThreadAttr_t attr = {
282            .stack_size = 1024
283        };
284        usart_ll_thread_id = osThreadNew(usart_ll_thread, usart_ll_mbox_id, &attr);
285    }
286}
287
288#if defined(LWESP_RESET_PIN)
289/**
290 * \brief           Hardware reset callback
291 */
292static uint8_t
293prv_reset_device(uint8_t state) {
294    if (state) {                                /* Activate reset line */
295        LL_GPIO_ResetOutputPin(LWESP_RESET_PORT, LWESP_RESET_PIN);
296    } else {
297        LL_GPIO_SetOutputPin(LWESP_RESET_PORT, LWESP_RESET_PIN);
298    }
299    return 1;
300}
301#endif /* defined(LWESP_RESET_PIN) */
302
303/**
304 * \brief           Send data to ESP device
305 * \param[in]       data: Pointer to data to send
306 * \param[in]       len: Number of bytes to send
307 * \return          Number of bytes sent
308 */
309static size_t
310prv_send_data(const void* data, size_t len) {
311    const uint8_t* d = data;
312
313    for (size_t i = 0; i < len; ++i, ++d) {
314        LL_USART_TransmitData8(LWESP_USART, *d);
315        while (!LL_USART_IsActiveFlag_TXE(LWESP_USART)) {}
316    }
317    return len;
318}
319
320/**
321 * \brief           Callback function called from initialization process
322 */
323lwespr_t
324lwesp_ll_init(lwesp_ll_t* ll) {
325#if !LWESP_CFG_MEM_CUSTOM
326    static uint8_t memory[LWESP_MEM_SIZE];
327    const lwesp_mem_region_t mem_regions[] = {
328        { memory, sizeof(memory) },
329        { NULL, 0 }
330    };
331
332    if (!initialized) {
333        lwesp_mem_assignmemory(mem_regions, LWESP_ARRAYSIZE(mem_regions));  /* Assign memory for allocations */
334    }
335#endif /* !LWESP_CFG_MEM_CUSTOM */
336
337    if (!initialized) {
338        ll->send_fn = prv_send_data;            /* Set callback function to send data */
339#if defined(LWESP_RESET_PIN)
340        ll->reset_fn = prv_reset_device;        /* Set callback for hardware reset */
341#endif /* defined(LWESP_RESET_PIN) */
342    }
343
344    prv_configure_uart(ll->uart.baudrate);      /* Initialize UART for communication */
345    initialized = 1;
346    return lwespOK;
347}
348
349/**
350 * \brief           Callback function to de-init low-level communication part
351 */
352lwespr_t
353lwesp_ll_deinit(lwesp_ll_t* ll) {
354    if (usart_ll_mbox_id != NULL) {
355        osMessageQueueId_t tmp = usart_ll_mbox_id;
356        usart_ll_mbox_id = NULL;
357        osMessageQueueDelete(tmp);
358    }
359    if (usart_ll_thread_id != NULL) {
360        osThreadId_t tmp = usart_ll_thread_id;
361        usart_ll_thread_id = NULL;
362        osThreadTerminate(tmp);
363    }
364    initialized = 0;
365    LWESP_UNUSED(ll);
366    return lwespOK;
367}
368
369/**
370 * \brief           UART global interrupt handler
371 */
372void
373LWESP_USART_IRQHANDLER(void) {
374    LL_USART_ClearFlag_IDLE(LWESP_USART);
375    LL_USART_ClearFlag_PE(LWESP_USART);
376    LL_USART_ClearFlag_FE(LWESP_USART);
377    LL_USART_ClearFlag_ORE(LWESP_USART);
378    LL_USART_ClearFlag_NE(LWESP_USART);
379
380    if (usart_ll_mbox_id != NULL) {
381        void* d = (void*)1;
382        osMessageQueuePut(usart_ll_mbox_id, &d, 0, 0);
383    }
384}
385
386/**
387 * \brief           UART DMA stream/channel handler
388 */
389void
390LWESP_USART_DMA_RX_IRQHANDLER(void) {
391    LWESP_USART_DMA_RX_CLEAR_TC;
392    LWESP_USART_DMA_RX_CLEAR_HT;
393
394    if (usart_ll_mbox_id != NULL) {
395        void* d = (void*)1;
396        osMessageQueuePut(usart_ll_mbox_id, &d, 0, 0);
397    }
398}
399
400#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) 2022 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 */
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) 2022 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

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) 2022 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 */
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) 2022 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 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) : 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__ */