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

Example: System functions for WIN32

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) 2020 Tilen MAJERLE
 8 *
 9 * Permission is hereby granted, free of charge, to any person
10 * obtaining a copy of this software and associated documentation
11 * files (the "Software"), to deal in the Software without restriction,
12 * including without limitation the rights to use, copy, modify, merge,
13 * publish, distribute, sublicense, and/or sell copies of the Software,
14 * and to permit persons to whom the Software is furnished to do so,
15 * subject to the following conditions:
16 *
17 * The above copyright notice and this permission notice shall be
18 * included in all copies or substantial portions of the Software.
19 *
20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
22 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
23 * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
24 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
25 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
27 * OTHER DEALINGS IN THE SOFTWARE.
28 *
29 * This file is part of LwESP - Lightweight ESP-AT parser library.
30 *
31 * Author:          Tilen MAJERLE <tilen@majerle.eu>
32 * Version:         v1.1.0-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) 2020 Tilen MAJERLE
  8 *
  9 * Permission is hereby granted, free of charge, to any person
 10 * obtaining a copy of this software and associated documentation
 11 * files (the "Software"), to deal in the Software without restriction,
 12 * including without limitation the rights to use, copy, modify, merge,
 13 * publish, distribute, sublicense, and/or sell copies of the Software,
 14 * and to permit persons to whom the Software is furnished to do so,
 15 * subject to the following conditions:
 16 *
 17 * The above copyright notice and this permission notice shall be
 18 * included in all copies or substantial portions of the Software.
 19 *
 20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 21 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 22 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
 23 * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 24 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 25 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 26 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 27 * OTHER DEALINGS IN THE SOFTWARE.
 28 *
 29 * This file is part of LwESP - Lightweight ESP-AT parser library.
 30 *
 31 * Author:          Tilen MAJERLE <tilen@majerle.eu>
 32 * Version:         v1.1.0-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    HANDLE h = NULL;
352
353    if (t == NULL) {                            /* Shall we terminate ourself? */
354        h = GetCurrentThread();                 /* Get current thread handle */
355    } else {                                    /* We have known thread, find handle by looking at ID */
356        h = *t;
357    }
358    TerminateThread(h, 0);
359    return 1;
360}
361
362uint8_t
363lwesp_sys_thread_yield(void) {
364    /* Not implemented */
365    return 1;
366}
367
368#endif /* LWESP_CFG_OS */
369#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) 2020 Tilen MAJERLE
 8 *
 9 * Permission is hereby granted, free of charge, to any person
10 * obtaining a copy of this software and associated documentation
11 * files (the "Software"), to deal in the Software without restriction,
12 * including without limitation the rights to use, copy, modify, merge,
13 * publish, distribute, sublicense, and/or sell copies of the Software,
14 * and to permit persons to whom the Software is furnished to do so,
15 * subject to the following conditions:
16 *
17 * The above copyright notice and this permission notice shall be
18 * included in all copies or substantial portions of the Software.
19 *
20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
22 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
23 * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
24 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
25 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
27 * OTHER DEALINGS IN THE SOFTWARE.
28 *
29 * This file is part of LwESP - Lightweight ESP-AT parser library.
30 *
31 * Author:          Tilen MAJERLE <tilen@majerle.eu>
32 * Version:         v1.1.0-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) 2020 Tilen MAJERLE
  8 *
  9 * Permission is hereby granted, free of charge, to any person
 10 * obtaining a copy of this software and associated documentation
 11 * files (the "Software"), to deal in the Software without restriction,
 12 * including without limitation the rights to use, copy, modify, merge,
 13 * publish, distribute, sublicense, and/or sell copies of the Software,
 14 * and to permit persons to whom the Software is furnished to do so,
 15 * subject to the following conditions:
 16 *
 17 * The above copyright notice and this permission notice shall be
 18 * included in all copies or substantial portions of the Software.
 19 *
 20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 21 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 22 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
 23 * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 24 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 25 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 26 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 27 * OTHER DEALINGS IN THE SOFTWARE.
 28 *
 29 * This file is part of LwESP - Lightweight ESP-AT parser library.
 30 *
 31 * Author:          Tilen MAJERLE <tilen@majerle.eu>
 32 * Version:         v1.1.0-dev
 33 */
 34#include "system/lwesp_sys.h"
 35#include "cmsis_os.h"
 36
 37#if !__DOXYGEN__
 38
 39static osMutexId_t sys_mutex;
 40
 41uint8_t
 42lwesp_sys_init(void) {
 43    lwesp_sys_mutex_create(&sys_mutex);
 44    return 1;
 45}
 46
 47uint32_t
 48lwesp_sys_now(void) {
 49    return osKernelSysTick();
 50}
 51
 52uint8_t
 53lwesp_sys_protect(void) {
 54    lwesp_sys_mutex_lock(&sys_mutex);
 55    return 1;
 56}
 57
 58uint8_t
 59lwesp_sys_unprotect(void) {
 60    lwesp_sys_mutex_unlock(&sys_mutex);
 61    return 1;
 62}
 63
 64uint8_t
 65lwesp_sys_mutex_create(lwesp_sys_mutex_t* p) {
 66    const osMutexAttr_t attr = {
 67        .attr_bits = osMutexRecursive,
 68        .name = "lwesp_mutex",
 69    };
 70    return (*p = osMutexNew(&attr)) != NULL;
 71}
 72
 73uint8_t
 74lwesp_sys_mutex_delete(lwesp_sys_mutex_t* p) {
 75    return osMutexDelete(*p) == osOK;
 76}
 77
 78uint8_t
 79lwesp_sys_mutex_lock(lwesp_sys_mutex_t* p) {
 80    return osMutexAcquire(*p, osWaitForever) == osOK;
 81}
 82
 83uint8_t
 84lwesp_sys_mutex_unlock(lwesp_sys_mutex_t* p) {
 85    return osMutexRelease(*p) == osOK;
 86}
 87
 88uint8_t
 89lwesp_sys_mutex_isvalid(lwesp_sys_mutex_t* p) {
 90    return p != NULL && *p != NULL;
 91}
 92
 93uint8_t
 94lwesp_sys_mutex_invalid(lwesp_sys_mutex_t* p) {
 95    *p = LWESP_SYS_MUTEX_NULL;
 96    return 1;
 97}
 98
 99uint8_t
100lwesp_sys_sem_create(lwesp_sys_sem_t* p, uint8_t cnt) {
101    const osSemaphoreAttr_t attr = {
102        .name = "lwesp_sem",
103    };
104    return (*p = osSemaphoreNew(1, cnt > 0 ? 1 : 0, &attr)) != NULL;
105}
106
107uint8_t
108lwesp_sys_sem_delete(lwesp_sys_sem_t* p) {
109    return osSemaphoreDelete(*p) == osOK;
110}
111
112uint32_t
113lwesp_sys_sem_wait(lwesp_sys_sem_t* p, uint32_t timeout) {
114    uint32_t tick = osKernelSysTick();
115    return (osSemaphoreAcquire(*p, timeout == 0 ? osWaitForever : timeout) == osOK) ? (osKernelSysTick() - tick) : LWESP_SYS_TIMEOUT;
116}
117
118uint8_t
119lwesp_sys_sem_release(lwesp_sys_sem_t* p) {
120    return osSemaphoreRelease(*p) == osOK;
121}
122
123uint8_t
124lwesp_sys_sem_isvalid(lwesp_sys_sem_t* p) {
125    return p != NULL && *p != NULL;
126}
127
128uint8_t
129lwesp_sys_sem_invalid(lwesp_sys_sem_t* p) {
130    *p = LWESP_SYS_SEM_NULL;
131    return 1;
132}
133
134uint8_t
135lwesp_sys_mbox_create(lwesp_sys_mbox_t* b, size_t size) {
136    const osMessageQueueAttr_t attr = {
137        .name = "lwesp_mbox",
138    };
139    return (*b = osMessageQueueNew(size, sizeof(void*), &attr)) != NULL;
140}
141
142uint8_t
143lwesp_sys_mbox_delete(lwesp_sys_mbox_t* b) {
144    if (osMessageQueueGetCount(*b) > 0) {
145        return 0;
146    }
147    return osMessageQueueDelete(*b) == osOK;
148}
149
150uint32_t
151lwesp_sys_mbox_put(lwesp_sys_mbox_t* b, void* m) {
152    uint32_t tick = osKernelSysTick();
153    return osMessageQueuePut(*b, &m, 0, osWaitForever) == osOK ? (osKernelSysTick() - tick) : LWESP_SYS_TIMEOUT;
154}
155
156uint32_t
157lwesp_sys_mbox_get(lwesp_sys_mbox_t* b, void** m, uint32_t timeout) {
158    uint32_t tick = osKernelSysTick();
159    return (osMessageQueueGet(*b, m, NULL, timeout == 0 ? osWaitForever : timeout) == osOK) ? (osKernelSysTick() - tick) : LWESP_SYS_TIMEOUT;
160}
161
162uint8_t
163lwesp_sys_mbox_putnow(lwesp_sys_mbox_t* b, void* m) {
164    return osMessageQueuePut(*b, &m, 0, 0) == osOK;
165}
166
167uint8_t
168lwesp_sys_mbox_getnow(lwesp_sys_mbox_t* b, void** m) {
169    return osMessageQueueGet(*b, m, NULL, 0) == osOK;
170}
171
172uint8_t
173lwesp_sys_mbox_isvalid(lwesp_sys_mbox_t* b) {
174    return b != NULL && *b != NULL;
175}
176
177uint8_t
178lwesp_sys_mbox_invalid(lwesp_sys_mbox_t* b) {
179    *b = LWESP_SYS_MBOX_NULL;
180    return 1;
181}
182
183uint8_t
184lwesp_sys_thread_create(lwesp_sys_thread_t* t, const char* name, lwesp_sys_thread_fn thread_func, void* const arg, size_t stack_size, lwesp_sys_thread_prio_t prio) {
185    lwesp_sys_thread_t id;
186    const osThreadAttr_t thread_attr = {
187        .name = (char*)name,
188        .priority = (osPriority)prio,
189        .stack_size = stack_size > 0 ? stack_size : LWESP_SYS_THREAD_SS
190    };
191
192    id = osThreadNew(thread_func, arg, &thread_attr);
193    if (t != NULL) {
194        *t = id;
195    }
196    return id != NULL;
197}
198
199uint8_t
200lwesp_sys_thread_terminate(lwesp_sys_thread_t* t) {
201    if (t != NULL) {
202        osThreadTerminate(*t);
203    } else {
204        osThreadExit();
205    }
206    return 1;
207}
208
209uint8_t
210lwesp_sys_thread_yield(void) {
211    osThreadYield();
212    return 1;
213}
214
215#endif /* !__DOXYGEN__ */