From f537808a9a5cf508837fc6aa1b26c530cb1281ef Mon Sep 17 00:00:00 2001 From: Dong Heng Date: Tue, 25 Sep 2018 17:59:23 +0800 Subject: [PATCH] feat(esp8266): Add esp_timer function This function is based on FreeRTOS timer not real time hard timer. Adding this just for some user passing compiling. --- components/esp8266/include/esp_timer.h | 249 +++++++++++++++-------- components/esp8266/include/rom/ets_sys.h | 74 +++++++ components/esp8266/source/esp_timer.c | 200 ++++++++++++++++++ 3 files changed, 443 insertions(+), 80 deletions(-) create mode 100644 components/esp8266/source/esp_timer.c diff --git a/components/esp8266/include/esp_timer.h b/components/esp8266/include/esp_timer.h index ccef9e0a..c6b606c0 100644 --- a/components/esp8266/include/esp_timer.h +++ b/components/esp8266/include/esp_timer.h @@ -1,104 +1,193 @@ -/* - * ESPRSSIF MIT License +// Copyright 2017 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +/** + * @file esp_timer.h + * @brief microsecond-precision 64-bit timer API, replacement for ets_timer * - * Copyright (c) 2015 + * esp_timer APIs allow components to receive callbacks when a hardware timer + * reaches certain value. The timer provides microsecond accuracy and + * up to 64 bit range. Note that while the timer itself provides microsecond + * accuracy, callbacks are dispatched from an auxiliary task. Some time is + * needed to notify this task from timer ISR, and then to invoke the callback. + * If more than one callback needs to be dispatched at any particular time, + * each subsequent callback will be dispatched only when the previous callback + * returns. Therefore, callbacks should not do much work; instead, they should + * use RTOS notification mechanisms (queues, semaphores, event groups, etc.) to + * pass information to other tasks. * - * Permission is hereby granted for use on ESPRESSIF SYSTEMS ESP8266 only, in which case, - * it is free of charge, to any person obtaining a copy of this software and associated - * documentation files (the "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the Software is furnished - * to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * To be implemented: it should be possible to request the callback to be called + * directly from the ISR. This reduces the latency, but has potential impact on + * all other callbacks which need to be dispatched. This option should only be + * used for simple callback functions, which do not take longer than a few + * microseconds to run. * + * Implementation note: on the ESP32, esp_timer APIs use the "legacy" FRC2 + * timer. Timer callbacks are called from a task running on the PRO CPU. */ -#ifndef __ESP_TIMER_H__ -#define __ESP_TIMER_H__ - -#include #include +#include +#include "esp_err.h" #ifdef __cplusplus extern "C" { #endif -/* timer related */ -typedef void os_timer_func_t(void *timer_arg); - -typedef struct _os_timer_t { - struct _os_timer_t *timer_next; - void *timer_handle; - uint32_t timer_expire; - uint32_t timer_period; - os_timer_func_t *timer_func; - bool timer_repeat_flag; - void *timer_arg; -} os_timer_t; - -/** \defgroup Timer_APIs Software timer APIs - * @brief Software timer APIs - * - * Timers of the following interfaces are software timers. Functions of the timers are executed during the tasks. - * Since a task can be stopped, or be delayed because there are other tasks with higher priorities, the following os_timer interfaces cannot guarantee the precise execution of the timers. - * - For the same timer, os_timer_arm (or os_timer_arm_us) cannot be invoked repeatedly. os_timer_disarm should be invoked first. - * - os_timer_setfn can only be invoked when the timer is not enabled, i.e., after os_timer_disarm or before os_timer_arm (or os_timer_arm_us). - * - */ - -/** @addtogroup Timer_APIs - * @{ - */ +/** + * Important: This function is based on FreeRTOS timer not real time hard timer. + * Adding this just for some user passing compiling now. + */ /** - * @brief Set the timer callback function. - * - * @attention 1. The callback function must be set in order to enable the timer. - * @attention 2. Operating system scheduling is disabled in timer callback. - * - * @param os_timer_t *ptimer : Timer structure - * @param os_timer_func_t *pfunction : timer callback function - * @param void *parg : callback function parameter - * - * @return null - */ -void os_timer_setfn(os_timer_t *ptimer, os_timer_func_t *pfunction, void *parg); + * @brief Opaque type representing a single esp_timer + */ +typedef struct esp_timer* esp_timer_handle_t; /** - * @brief Enable the millisecond timer. - * - * @param os_timer_t *ptimer : timer structure - * @param uint32_t milliseconds : Timing, unit: millisecond, range: 5 ~ 0x68DB8 - * @param bool repeat_flag : Whether the timer will be invoked repeatedly or not - * - * @return null - */ -void os_timer_arm(os_timer_t *ptimer, uint32_t msec, bool repeat_flag); + * @brief Timer callback function type + * @param arg pointer to opaque user-specific data + */ +typedef void (*esp_timer_cb_t)(void* arg); + /** - * @brief Disarm the timer - * - * @param os_timer_t *ptimer : Timer structure - * - * @return null - */ -void os_timer_disarm(os_timer_t *ptimer); + * @brief Method for dispatching timer callback + */ +typedef enum { + ESP_TIMER_TASK, //!< Callback is called from timer task + + /* Not supported for now, provision to allow callbacks to run directly + * from an ISR: + + ESP_TIMER_ISR, //!< Callback is called from timer ISR + + */ +} esp_timer_dispatch_t; /** - * @} - */ + * @brief Timer configuration passed to esp_timer_create + */ +typedef struct { + esp_timer_cb_t callback; //!< Function to call when timer expires + void* arg; //!< Argument to pass to the callback + esp_timer_dispatch_t dispatch_method; //!< Call the callback from task or from ISR + const char* name; //!< Timer name, used in esp_timer_dump function +} esp_timer_create_args_t; + +/** + * @brief Initialize esp_timer library + * + * @note This function is called from startup code. Applications do not need + * to call this function before using other esp_timer APIs. + * + * @return + * - ESP_OK on success + * - ESP_ERR_NO_MEM if allocation has failed + * - ESP_ERR_INVALID_STATE if already initialized + * - other errors from interrupt allocator + */ +esp_err_t esp_timer_init(); + +/** + * @brief De-initialize esp_timer library + * + * @note Normally this function should not be called from applications + * + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_STATE if not yet initialized + */ +esp_err_t esp_timer_deinit(); + +/** + * @brief Create an esp_timer instance + * + * @note When done using the timer, delete it with esp_timer_delete function. + * + * @param create_args Pointer to a structure with timer creation arguments. + * Not saved by the library, can be allocated on the stack. + * @param[out] out_handle Output, pointer to esp_timer_handle_t variable which + * will hold the created timer handle. + * + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_ARG if some of the create_args are not valid + * - ESP_ERR_INVALID_STATE if esp_timer library is not initialized yet + * - ESP_ERR_NO_MEM if memory allocation fails + */ +esp_err_t esp_timer_create(const esp_timer_create_args_t* create_args, + esp_timer_handle_t* out_handle); + +/** + * @brief Start one-shot timer + * + * Timer should not be running when this function is called. + * + * @param timer timer handle created using esp_timer_create + * @param timeout_us timer timeout, in microseconds relative to the current moment + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_ARG if the handle is invalid + * - ESP_ERR_INVALID_STATE if the timer is already running + */ +esp_err_t esp_timer_start_once(esp_timer_handle_t timer, uint64_t timeout_us); + +/** + * @brief Start a periodic timer + * + * Timer should not be running when this function is called. This function will + * start the timer which will trigger every 'period' microseconds. + * + * @param timer timer handle created using esp_timer_create + * @param period timer period, in microseconds + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_ARG if the handle is invalid + * - ESP_ERR_INVALID_STATE if the timer is already running + */ +esp_err_t esp_timer_start_periodic(esp_timer_handle_t timer, uint64_t period); + +/** + * @brief Stop the timer + * + * This function stops the timer previously started using esp_timer_start_once + * or esp_timer_start_periodic. + * + * @param timer timer handle created using esp_timer_create + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_STATE if the timer is not running + */ +esp_err_t esp_timer_stop(esp_timer_handle_t timer); + +/** + * @brief Delete an esp_timer instance + * + * The timer must be stopped before deleting. A one-shot timer which has expired + * does not need to be stopped. + * + * @param timer timer handle allocated using esp_timer_create + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_STATE if the timer is not running + */ +esp_err_t esp_timer_delete(esp_timer_handle_t timer); #ifdef __cplusplus } #endif -#endif diff --git a/components/esp8266/include/rom/ets_sys.h b/components/esp8266/include/rom/ets_sys.h index 52d5d2e4..4d176d92 100644 --- a/components/esp8266/include/rom/ets_sys.h +++ b/components/esp8266/include/rom/ets_sys.h @@ -26,9 +26,14 @@ #define __ETS_SYS_H__ #include +#include #include "esp8266/eagle_soc.h" +#ifdef __cplusplus +extern "C" { +#endif + /* interrupt related */ #define ETS_SPI_INUM 2 #define ETS_GPIO_INUM 4 @@ -114,4 +119,73 @@ void os_install_putc1(void (*p)(char c)); */ void os_putc(char c); +/* timer related */ +typedef void os_timer_func_t(void *timer_arg); + +typedef struct _os_timer_t { + struct _os_timer_t *timer_next; + void *timer_handle; + uint32_t timer_expire; + uint32_t timer_period; + os_timer_func_t *timer_func; + bool timer_repeat_flag; + void *timer_arg; +} os_timer_t; + +/** \defgroup Timer_APIs Software timer APIs + * @brief Software timer APIs + * + * Timers of the following interfaces are software timers. Functions of the timers are executed during the tasks. + * Since a task can be stopped, or be delayed because there are other tasks with higher priorities, the following os_timer interfaces cannot guarantee the precise execution of the timers. + * - For the same timer, os_timer_arm (or os_timer_arm_us) cannot be invoked repeatedly. os_timer_disarm should be invoked first. + * - os_timer_setfn can only be invoked when the timer is not enabled, i.e., after os_timer_disarm or before os_timer_arm (or os_timer_arm_us). + * + */ + +/** @addtogroup Timer_APIs + * @{ + */ + +/** + * @brief Set the timer callback function. + * + * @attention 1. The callback function must be set in order to enable the timer. + * @attention 2. Operating system scheduling is disabled in timer callback. + * + * @param os_timer_t *ptimer : Timer structure + * @param os_timer_func_t *pfunction : timer callback function + * @param void *parg : callback function parameter + * + * @return null + */ +void os_timer_setfn(os_timer_t *ptimer, os_timer_func_t *pfunction, void *parg); + +/** + * @brief Enable the millisecond timer. + * + * @param os_timer_t *ptimer : timer structure + * @param uint32_t milliseconds : Timing, unit: millisecond, range: 5 ~ 0x68DB8 + * @param bool repeat_flag : Whether the timer will be invoked repeatedly or not + * + * @return null + */ +void os_timer_arm(os_timer_t *ptimer, uint32_t msec, bool repeat_flag); + +/** + * @brief Disarm the timer + * + * @param os_timer_t *ptimer : Timer structure + * + * @return null + */ +void os_timer_disarm(os_timer_t *ptimer); + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + #endif /* _ETS_SYS_H */ diff --git a/components/esp8266/source/esp_timer.c b/components/esp8266/source/esp_timer.c new file mode 100644 index 00000000..a2e1ccc2 --- /dev/null +++ b/components/esp8266/source/esp_timer.c @@ -0,0 +1,200 @@ +// Copyright 2018-2019 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "esp_timer.h" +#include "esp_heap_caps.h" +#include "esp_log.h" +#include "FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/timers.h" + +#define ESP_TIMER_HZ CONFIG_FREERTOS_HZ + +struct esp_timer { + TimerHandle_t os_timer; + + esp_timer_cb_t cb; + + void *arg; + + TickType_t period_ticks; +}; + +static const char *TAG = "esp_timer"; + +/** + * @brief FreeRTOS callback function + */ +static void esp_timer_callback(TimerHandle_t xTimer) +{ + BaseType_t os_ret; + struct esp_timer *timer = (struct esp_timer *)pvTimerGetTimerID(xTimer); + + timer->cb(timer->arg); + + if (!timer->period_ticks) { + os_ret = xTimerStop(timer->os_timer, 0); + if (os_ret != pdPASS) { + ESP_LOGE(TAG, "Set timer from periodic to once error"); + } + } +} + +/** + * @brief Initialize esp_timer library + */ +esp_err_t esp_timer_init(void) +{ + return ESP_OK; +} + +/** + * @brief De-initialize esp_timer library + */ +esp_err_t esp_timer_deinit(void) +{ + return ESP_OK; +} + +/** + * @brief Create an esp_timer instance + */ +esp_err_t esp_timer_create(const esp_timer_create_args_t* create_args, + esp_timer_handle_t* out_handle) +{ + assert(create_args); + assert(out_handle); + + TimerHandle_t os_timer; + esp_timer_handle_t esp_timer; + + esp_timer = heap_caps_malloc(sizeof(struct esp_timer), MALLOC_CAP_32BIT); + if (!esp_timer) + return ESP_ERR_NO_MEM; + + esp_timer->cb = create_args->callback; + esp_timer->arg = create_args->arg; + esp_timer->period_ticks = 0; + + os_timer = xTimerCreate(create_args->name, + portMAX_DELAY, + pdTRUE, + esp_timer, + esp_timer_callback); + if (os_timer) { + esp_timer->os_timer = os_timer; + *out_handle = (esp_timer_handle_t)esp_timer; + } else { + heap_caps_free(esp_timer); + return ESP_ERR_NO_MEM; + } + + return ESP_OK; +} + +/** + * @brief Start one-shot timer + */ +esp_err_t esp_timer_start_once(esp_timer_handle_t timer, uint64_t timeout_us) +{ + assert(timer); + + TimerHandle_t os_timer = timer->os_timer; + BaseType_t os_ret; + uint64_t last_us = timeout_us % (1000 * (1000 / ESP_TIMER_HZ)); + uint32_t ticks = timeout_us / (1000 * (1000 / ESP_TIMER_HZ)); + + if (last_us || !ticks) + return ESP_ERR_INVALID_ARG; + + os_ret = xTimerChangePeriod(os_timer, ticks, portMAX_DELAY); + if (os_ret == pdPASS) { + TickType_t period_ticks = timer->period_ticks; + + timer->period_ticks = 0; + os_ret = xTimerStart(os_timer, portMAX_DELAY); + if (os_ret != pdPASS) { + timer->period_ticks = period_ticks; + return ESP_ERR_INVALID_STATE; + } + } else { + return ESP_ERR_INVALID_STATE; + } + + return ESP_OK; +} + +/** + * @brief Start a periodic timer + */ +esp_err_t esp_timer_start_periodic(esp_timer_handle_t timer, uint64_t period) +{ + assert(timer); + + TimerHandle_t os_timer = timer->os_timer; + BaseType_t os_ret; + uint64_t last_us = period % (1000 * (1000 / ESP_TIMER_HZ)); + uint32_t ticks = period / (1000 * (1000 / ESP_TIMER_HZ)); + + if (last_us || !ticks) + return ESP_ERR_INVALID_ARG; + + os_ret = xTimerChangePeriod(os_timer, ticks, portMAX_DELAY); + if (os_ret == pdPASS) { + TickType_t period_ticks = timer->period_ticks; + + timer->period_ticks = ticks; + os_ret = xTimerStart(os_timer, portMAX_DELAY); + if (os_ret != pdPASS) { + timer->period_ticks = period_ticks; + return ESP_ERR_INVALID_STATE; + } + } else { + return ESP_ERR_INVALID_STATE; + } + + return ESP_OK; +} + +/** + * @brief Stop the timer + */ +esp_err_t esp_timer_stop(esp_timer_handle_t timer) +{ + assert(timer); + + TimerHandle_t os_timer = timer->os_timer; + BaseType_t os_ret; + + os_ret = xTimerStop(os_timer, portMAX_DELAY); + + return os_ret == pdPASS ? ESP_OK : ESP_ERR_INVALID_STATE; +} + +/** + * @brief Delete an esp_timer instance + */ +esp_err_t esp_timer_delete(esp_timer_handle_t timer) +{ + assert(timer); + + TimerHandle_t os_timer = timer->os_timer; + BaseType_t os_ret; + + os_ret = xTimerDelete(os_timer, portMAX_DELAY); + if (os_ret == pdPASS) + heap_caps_free(timer); + + return os_ret == pdPASS ? ESP_OK : ESP_ERR_INVALID_STATE; +}