Merge branch 'master' into 'bugfix/btdm_modify_code_format_and_comments'

# Conflicts:
#   examples/bluetooth/gatt_server_service_table/main/gatts_table_creat_demo.c
This commit is contained in:
Zhi Wei Jian 2017-12-13 15:42:41 +08:00
commit 833ecfdec9
85 changed files with 8222 additions and 4198 deletions

View file

@ -751,6 +751,13 @@ UT_003_08:
- UT_T1_1 - UT_T1_1
- UT_single_core - UT_single_core
UT_003_09:
<<: *unit_test_template
tags:
- ESP32_IDF
- UT_T1_1
- UT_single_core
UT_004_01: UT_004_01:
<<: *unit_test_template <<: *unit_test_template
tags: tags:

View file

@ -16,7 +16,7 @@
#include "esp_bt_main.h" #include "esp_bt_main.h"
#include "btc_task.h" #include "btc_task.h"
#include "btc_main.h" #include "btc_main.h"
#include "bt.h" #include "esp_bt.h"
#include "future.h" #include "future.h"
#include "allocator.h" #include "allocator.h"

View file

@ -25,7 +25,7 @@
#include "hci_internals.h" #include "hci_internals.h"
#include "hci_layer.h" #include "hci_layer.h"
#include "thread.h" #include "thread.h"
#include "bt.h" #include "esp_bt.h"
#define HCI_HAL_SERIAL_BUFFER_SIZE 1026 #define HCI_HAL_SERIAL_BUFFER_SIZE 1026
#define HCI_BLE_EVENT 0x3e #define HCI_BLE_EVENT 0x3e

View file

@ -16,7 +16,7 @@
* *
******************************************************************************/ ******************************************************************************/
#include <string.h> #include <string.h>
#include "bt.h" #include "esp_bt.h"
#include "bt_defs.h" #include "bt_defs.h"
#include "bt_trace.h" #include "bt_trace.h"
#include "hcidefs.h" #include "hcidefs.h"

View file

@ -31,7 +31,7 @@
#include "esp_intr.h" #include "esp_intr.h"
#include "esp_attr.h" #include "esp_attr.h"
#include "esp_phy_init.h" #include "esp_phy_init.h"
#include "bt.h" #include "esp_bt.h"
#include "esp_err.h" #include "esp_err.h"
#include "esp_log.h" #include "esp_log.h"
#include "esp_pm.h" #include "esp_pm.h"

View file

@ -1,244 +1,3 @@
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD #pragma once
// #warning "This header is deprecated, please use functions defined in esp_bt.h instead."
// Licensed under the Apache License, Version 2.0 (the "License"); #include "esp_bt.h"
// 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.
#ifndef __BT_H__
#define __BT_H__
#include <stdint.h>
#include <stdbool.h>
#include "esp_err.h"
#include "sdkconfig.h"
#include "esp_task.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Controller config options, depend on config mask.
* Config mask indicate which functions enabled, this means
* some options or parameters of some functions enabled by config mask.
*/
typedef struct {
uint16_t controller_task_stack_size; /*!< Bluetooth controller task stack size */
uint8_t controller_task_prio; /*!< Bluetooth controller task priority */
uint8_t hci_uart_no; /*!< If use UART1/2 as HCI IO interface, indicate UART number */
uint32_t hci_uart_baudrate; /*!< If use UART1/2 as HCI IO interface, indicate UART baudrate */
} esp_bt_controller_config_t;
#ifdef CONFIG_BT_ENABLED
#ifdef CONFIG_BT_HCI_UART_NO
#define BT_HCI_UART_NO_DEFAULT CONFIG_BT_HCI_UART_NO
#else
#define BT_HCI_UART_NO_DEFAULT 1
#endif /* BT_HCI_UART_NO_DEFAULT */
#ifdef CONFIG_BT_HCI_UART_BAUDRATE
#define BT_HCI_UART_BAUDRATE_DEFAULT CONFIG_BT_HCI_UART_BAUDRATE
#else
#define BT_HCI_UART_BAUDRATE_DEFAULT 921600
#endif /* BT_HCI_UART_BAUDRATE_DEFAULT */
#define BT_CONTROLLER_INIT_CONFIG_DEFAULT() { \
.controller_task_stack_size = ESP_TASK_BT_CONTROLLER_STACK, \
.controller_task_prio = ESP_TASK_BT_CONTROLLER_PRIO, \
.hci_uart_no = BT_HCI_UART_NO_DEFAULT, \
.hci_uart_baudrate = BT_HCI_UART_BAUDRATE_DEFAULT, \
};
#else
#define BT_CONTROLLER_INIT_CONFIG_DEFAULT() {0}; _Static_assert(0, "please enable bluetooth in menuconfig to use bt.h");
#endif
/**
* @brief Bluetooth mode for controller enable/disable
*/
typedef enum {
ESP_BT_MODE_IDLE = 0x00, /*!< Bluetooth is not running */
ESP_BT_MODE_BLE = 0x01, /*!< Run BLE mode */
ESP_BT_MODE_CLASSIC_BT = 0x02, /*!< Run Classic BT mode */
ESP_BT_MODE_BTDM = 0x03, /*!< Run dual mode */
} esp_bt_mode_t;
/**
* @brief Bluetooth controller enable/disable/initialised/de-initialised status
*/
typedef enum {
ESP_BT_CONTROLLER_STATUS_IDLE = 0,
ESP_BT_CONTROLLER_STATUS_INITED,
ESP_BT_CONTROLLER_STATUS_ENABLED,
ESP_BT_CONTROLLER_STATUS_NUM,
} esp_bt_controller_status_t;
/**
* @brief BLE tx power type
* ESP_BLE_PWR_TYPE_CONN_HDL0-8: for each connection, and only be set after connetion completed.
* when disconnect, the correspond TX power is not effected.
* ESP_BLE_PWR_TYPE_ADV : for advertising/scan response.
* ESP_BLE_PWR_TYPE_SCAN : for scan.
* ESP_BLE_PWR_TYPE_DEFAULT : if each connection's TX power is not set, it will use this default value.
* if neither in scan mode nor in adv mode, it will use this default value.
* If none of power type is set, system will use ESP_PWR_LVL_P1 as default for ADV/SCAN/CONN0-9.
*/
typedef enum {
ESP_BLE_PWR_TYPE_CONN_HDL0 = 0, /*!< For connection handle 0 */
ESP_BLE_PWR_TYPE_CONN_HDL1 = 1, /*!< For connection handle 1 */
ESP_BLE_PWR_TYPE_CONN_HDL2 = 2, /*!< For connection handle 2 */
ESP_BLE_PWR_TYPE_CONN_HDL3 = 3, /*!< For connection handle 3 */
ESP_BLE_PWR_TYPE_CONN_HDL4 = 4, /*!< For connection handle 4 */
ESP_BLE_PWR_TYPE_CONN_HDL5 = 5, /*!< For connection handle 5 */
ESP_BLE_PWR_TYPE_CONN_HDL6 = 6, /*!< For connection handle 6 */
ESP_BLE_PWR_TYPE_CONN_HDL7 = 7, /*!< For connection handle 7 */
ESP_BLE_PWR_TYPE_CONN_HDL8 = 8, /*!< For connection handle 8 */
ESP_BLE_PWR_TYPE_ADV = 9, /*!< For advertising */
ESP_BLE_PWR_TYPE_SCAN = 10, /*!< For scan */
ESP_BLE_PWR_TYPE_DEFAULT = 11, /*!< For default, if not set other, it will use default value */
ESP_BLE_PWR_TYPE_NUM = 12, /*!< TYPE numbers */
} esp_ble_power_type_t;
/**
* @brief Bluetooth TX power level(index), it's just a index corresponding to power(dbm).
*/
typedef enum {
ESP_PWR_LVL_N14 = 0, /*!< Corresponding to -14dbm */
ESP_PWR_LVL_N11 = 1, /*!< Corresponding to -11dbm */
ESP_PWR_LVL_N8 = 2, /*!< Corresponding to -8dbm */
ESP_PWR_LVL_N5 = 3, /*!< Corresponding to -5dbm */
ESP_PWR_LVL_N2 = 4, /*!< Corresponding to -2dbm */
ESP_PWR_LVL_P1 = 5, /*!< Corresponding to 1dbm */
ESP_PWR_LVL_P4 = 6, /*!< Corresponding to 4dbm */
ESP_PWR_LVL_P7 = 7, /*!< Corresponding to 7dbm */
} esp_power_level_t;
/**
* @brief Set BLE TX power
* Connection Tx power should only be set after connection created.
* @param power_type : The type of which tx power, could set Advertising/Connection/Default and etc
* @param power_level: Power level(index) corresponding to absolute value(dbm)
* @return ESP_OK - success, other - failed
*/
esp_err_t esp_ble_tx_power_set(esp_ble_power_type_t power_type, esp_power_level_t power_level);
/**
* @brief Get BLE TX power
* Connection Tx power should only be get after connection created.
* @param power_type : The type of which tx power, could set Advertising/Connection/Default and etc
* @return >= 0 - Power level, < 0 - Invalid
*/
esp_power_level_t esp_ble_tx_power_get(esp_ble_power_type_t power_type);
/**
* @brief Initialize BT controller to allocate task and other resource.
* @param cfg: Initial configuration of BT controller.
* This function should be called only once, before any other BT functions are called.
* @return ESP_OK - success, other - failed
*/
esp_err_t esp_bt_controller_init(esp_bt_controller_config_t *cfg);
/**
* @brief De-initialize BT controller to free resource and delete task.
*
* This function should be called only once, after any other BT functions are called.
* This function is not whole completed, esp_bt_controller_init cannot called after this function.
* @return ESP_OK - success, other - failed
*/
esp_err_t esp_bt_controller_deinit(void);
/**
* @brief Enable BT controller.
* Due to a known issue, you cannot call esp_bt_controller_enable() a second time
* to change the controller mode dynamically. To change controller mode, call
* esp_bt_controller_disable() and then call esp_bt_controller_enable() with the new mode.
* @param mode : the mode(BLE/BT/BTDM) to enable.
* @return ESP_OK - success, other - failed
*/
esp_err_t esp_bt_controller_enable(esp_bt_mode_t mode);
/**
* @brief Disable BT controller
* @return ESP_OK - success, other - failed
*/
esp_err_t esp_bt_controller_disable(void);
/**
* @brief Get BT controller is initialised/de-initialised/enabled/disabled
* @return status value
*/
esp_bt_controller_status_t esp_bt_controller_get_status(void);
/** @brief esp_vhci_host_callback
* used for vhci call host function to notify what host need to do
*/
typedef struct esp_vhci_host_callback {
void (*notify_host_send_available)(void); /*!< callback used to notify that the host can send packet to controller */
int (*notify_host_recv)(uint8_t *data, uint16_t len); /*!< callback used to notify that the controller has a packet to send to the host*/
} esp_vhci_host_callback_t;
/** @brief esp_vhci_host_check_send_available
* used for check actively if the host can send packet to controller or not.
* @return true for ready to send, false means cannot send packet
*/
bool esp_vhci_host_check_send_available(void);
/** @brief esp_vhci_host_send_packet
* host send packet to controller
* @param data the packet point
*,@param len the packet length
*/
void esp_vhci_host_send_packet(uint8_t *data, uint16_t len);
/** @brief esp_vhci_host_register_callback
* register the vhci referece callback, the call back
* struct defined by vhci_host_callback structure.
* @param callback esp_vhci_host_callback type variable
*/
void esp_vhci_host_register_callback(const esp_vhci_host_callback_t *callback);
/** @brief esp_bt_controller_mem_release
* release the memory by mode, if never use the bluetooth mode
* it can release the .bbs, .data and other section to heap.
* The total size is about 70k bytes.
*
* If esp_bt_controller_enable(mode) has already been called, calling
* esp_bt_controller_mem_release(ESP_BT_MODE_BTDM) will automatically
* release all memory which is not needed for the currently enabled
* Bluetooth controller mode.
*
* For example, calling esp_bt_controller_enable(ESP_BT_MODE_BLE) then
* esp_bt_controller_mem_release(ESP_BT_MODE_BTDM) will enable BLE modes
* and release memory only used by BT Classic. Also, call esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT)
* is the same.
*
* Note that once BT controller memory is released, the process cannot be reversed.
* If your firmware will later upgrade the Bluetooth controller mode (BLE -> BT Classic or disabled -> enabled)
* then do not call this function.
*
* If user never use bluetooth controller, could call esp_bt_controller_mem_release(ESP_BT_MODE_BTDM)
* before esp_bt_controller_init or after esp_bt_controller_deinit.
*
* For example, user only use bluetooth to config SSID and PASSWORD of WIFI, after config, will never use bluetooth.
* Then, could call esp_bt_controller_mem_release(ESP_BT_MODE_BTDM) after esp_bt_controller_deinit.
*
* @param mode : the mode want to release memory
* @return ESP_OK - success, other - failed
*/
esp_err_t esp_bt_controller_mem_release(esp_bt_mode_t mode);
#ifdef __cplusplus
}
#endif
#endif /* __BT_H__ */

View file

@ -0,0 +1,244 @@
// Copyright 2015-2016 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.
#ifndef __ESP_BT_H__
#define __ESP_BT_H__
#include <stdint.h>
#include <stdbool.h>
#include "esp_err.h"
#include "sdkconfig.h"
#include "esp_task.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Controller config options, depend on config mask.
* Config mask indicate which functions enabled, this means
* some options or parameters of some functions enabled by config mask.
*/
typedef struct {
uint16_t controller_task_stack_size; /*!< Bluetooth controller task stack size */
uint8_t controller_task_prio; /*!< Bluetooth controller task priority */
uint8_t hci_uart_no; /*!< If use UART1/2 as HCI IO interface, indicate UART number */
uint32_t hci_uart_baudrate; /*!< If use UART1/2 as HCI IO interface, indicate UART baudrate */
} esp_bt_controller_config_t;
#ifdef CONFIG_BT_ENABLED
#ifdef CONFIG_BT_HCI_UART_NO
#define BT_HCI_UART_NO_DEFAULT CONFIG_BT_HCI_UART_NO
#else
#define BT_HCI_UART_NO_DEFAULT 1
#endif /* BT_HCI_UART_NO_DEFAULT */
#ifdef CONFIG_BT_HCI_UART_BAUDRATE
#define BT_HCI_UART_BAUDRATE_DEFAULT CONFIG_BT_HCI_UART_BAUDRATE
#else
#define BT_HCI_UART_BAUDRATE_DEFAULT 921600
#endif /* BT_HCI_UART_BAUDRATE_DEFAULT */
#define BT_CONTROLLER_INIT_CONFIG_DEFAULT() { \
.controller_task_stack_size = ESP_TASK_BT_CONTROLLER_STACK, \
.controller_task_prio = ESP_TASK_BT_CONTROLLER_PRIO, \
.hci_uart_no = BT_HCI_UART_NO_DEFAULT, \
.hci_uart_baudrate = BT_HCI_UART_BAUDRATE_DEFAULT, \
};
#else
#define BT_CONTROLLER_INIT_CONFIG_DEFAULT() {0}; _Static_assert(0, "please enable bluetooth in menuconfig to use bt.h");
#endif
/**
* @brief Bluetooth mode for controller enable/disable
*/
typedef enum {
ESP_BT_MODE_IDLE = 0x00, /*!< Bluetooth is not running */
ESP_BT_MODE_BLE = 0x01, /*!< Run BLE mode */
ESP_BT_MODE_CLASSIC_BT = 0x02, /*!< Run Classic BT mode */
ESP_BT_MODE_BTDM = 0x03, /*!< Run dual mode */
} esp_bt_mode_t;
/**
* @brief Bluetooth controller enable/disable/initialised/de-initialised status
*/
typedef enum {
ESP_BT_CONTROLLER_STATUS_IDLE = 0,
ESP_BT_CONTROLLER_STATUS_INITED,
ESP_BT_CONTROLLER_STATUS_ENABLED,
ESP_BT_CONTROLLER_STATUS_NUM,
} esp_bt_controller_status_t;
/**
* @brief BLE tx power type
* ESP_BLE_PWR_TYPE_CONN_HDL0-8: for each connection, and only be set after connetion completed.
* when disconnect, the correspond TX power is not effected.
* ESP_BLE_PWR_TYPE_ADV : for advertising/scan response.
* ESP_BLE_PWR_TYPE_SCAN : for scan.
* ESP_BLE_PWR_TYPE_DEFAULT : if each connection's TX power is not set, it will use this default value.
* if neither in scan mode nor in adv mode, it will use this default value.
* If none of power type is set, system will use ESP_PWR_LVL_P1 as default for ADV/SCAN/CONN0-9.
*/
typedef enum {
ESP_BLE_PWR_TYPE_CONN_HDL0 = 0, /*!< For connection handle 0 */
ESP_BLE_PWR_TYPE_CONN_HDL1 = 1, /*!< For connection handle 1 */
ESP_BLE_PWR_TYPE_CONN_HDL2 = 2, /*!< For connection handle 2 */
ESP_BLE_PWR_TYPE_CONN_HDL3 = 3, /*!< For connection handle 3 */
ESP_BLE_PWR_TYPE_CONN_HDL4 = 4, /*!< For connection handle 4 */
ESP_BLE_PWR_TYPE_CONN_HDL5 = 5, /*!< For connection handle 5 */
ESP_BLE_PWR_TYPE_CONN_HDL6 = 6, /*!< For connection handle 6 */
ESP_BLE_PWR_TYPE_CONN_HDL7 = 7, /*!< For connection handle 7 */
ESP_BLE_PWR_TYPE_CONN_HDL8 = 8, /*!< For connection handle 8 */
ESP_BLE_PWR_TYPE_ADV = 9, /*!< For advertising */
ESP_BLE_PWR_TYPE_SCAN = 10, /*!< For scan */
ESP_BLE_PWR_TYPE_DEFAULT = 11, /*!< For default, if not set other, it will use default value */
ESP_BLE_PWR_TYPE_NUM = 12, /*!< TYPE numbers */
} esp_ble_power_type_t;
/**
* @brief Bluetooth TX power level(index), it's just a index corresponding to power(dbm).
*/
typedef enum {
ESP_PWR_LVL_N14 = 0, /*!< Corresponding to -14dbm */
ESP_PWR_LVL_N11 = 1, /*!< Corresponding to -11dbm */
ESP_PWR_LVL_N8 = 2, /*!< Corresponding to -8dbm */
ESP_PWR_LVL_N5 = 3, /*!< Corresponding to -5dbm */
ESP_PWR_LVL_N2 = 4, /*!< Corresponding to -2dbm */
ESP_PWR_LVL_P1 = 5, /*!< Corresponding to 1dbm */
ESP_PWR_LVL_P4 = 6, /*!< Corresponding to 4dbm */
ESP_PWR_LVL_P7 = 7, /*!< Corresponding to 7dbm */
} esp_power_level_t;
/**
* @brief Set BLE TX power
* Connection Tx power should only be set after connection created.
* @param power_type : The type of which tx power, could set Advertising/Connection/Default and etc
* @param power_level: Power level(index) corresponding to absolute value(dbm)
* @return ESP_OK - success, other - failed
*/
esp_err_t esp_ble_tx_power_set(esp_ble_power_type_t power_type, esp_power_level_t power_level);
/**
* @brief Get BLE TX power
* Connection Tx power should only be get after connection created.
* @param power_type : The type of which tx power, could set Advertising/Connection/Default and etc
* @return >= 0 - Power level, < 0 - Invalid
*/
esp_power_level_t esp_ble_tx_power_get(esp_ble_power_type_t power_type);
/**
* @brief Initialize BT controller to allocate task and other resource.
* @param cfg: Initial configuration of BT controller.
* This function should be called only once, before any other BT functions are called.
* @return ESP_OK - success, other - failed
*/
esp_err_t esp_bt_controller_init(esp_bt_controller_config_t *cfg);
/**
* @brief De-initialize BT controller to free resource and delete task.
*
* This function should be called only once, after any other BT functions are called.
* This function is not whole completed, esp_bt_controller_init cannot called after this function.
* @return ESP_OK - success, other - failed
*/
esp_err_t esp_bt_controller_deinit(void);
/**
* @brief Enable BT controller.
* Due to a known issue, you cannot call esp_bt_controller_enable() a second time
* to change the controller mode dynamically. To change controller mode, call
* esp_bt_controller_disable() and then call esp_bt_controller_enable() with the new mode.
* @param mode : the mode(BLE/BT/BTDM) to enable.
* @return ESP_OK - success, other - failed
*/
esp_err_t esp_bt_controller_enable(esp_bt_mode_t mode);
/**
* @brief Disable BT controller
* @return ESP_OK - success, other - failed
*/
esp_err_t esp_bt_controller_disable(void);
/**
* @brief Get BT controller is initialised/de-initialised/enabled/disabled
* @return status value
*/
esp_bt_controller_status_t esp_bt_controller_get_status(void);
/** @brief esp_vhci_host_callback
* used for vhci call host function to notify what host need to do
*/
typedef struct esp_vhci_host_callback {
void (*notify_host_send_available)(void); /*!< callback used to notify that the host can send packet to controller */
int (*notify_host_recv)(uint8_t *data, uint16_t len); /*!< callback used to notify that the controller has a packet to send to the host*/
} esp_vhci_host_callback_t;
/** @brief esp_vhci_host_check_send_available
* used for check actively if the host can send packet to controller or not.
* @return true for ready to send, false means cannot send packet
*/
bool esp_vhci_host_check_send_available(void);
/** @brief esp_vhci_host_send_packet
* host send packet to controller
* @param data the packet point
*,@param len the packet length
*/
void esp_vhci_host_send_packet(uint8_t *data, uint16_t len);
/** @brief esp_vhci_host_register_callback
* register the vhci referece callback, the call back
* struct defined by vhci_host_callback structure.
* @param callback esp_vhci_host_callback type variable
*/
void esp_vhci_host_register_callback(const esp_vhci_host_callback_t *callback);
/** @brief esp_bt_controller_mem_release
* release the memory by mode, if never use the bluetooth mode
* it can release the .bbs, .data and other section to heap.
* The total size is about 70k bytes.
*
* If esp_bt_controller_enable(mode) has already been called, calling
* esp_bt_controller_mem_release(ESP_BT_MODE_BTDM) will automatically
* release all memory which is not needed for the currently enabled
* Bluetooth controller mode.
*
* For example, calling esp_bt_controller_enable(ESP_BT_MODE_BLE) then
* esp_bt_controller_mem_release(ESP_BT_MODE_BTDM) will enable BLE modes
* and release memory only used by BT Classic. Also, call esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT)
* is the same.
*
* Note that once BT controller memory is released, the process cannot be reversed.
* If your firmware will later upgrade the Bluetooth controller mode (BLE -> BT Classic or disabled -> enabled)
* then do not call this function.
*
* If user never use bluetooth controller, could call esp_bt_controller_mem_release(ESP_BT_MODE_BTDM)
* before esp_bt_controller_init or after esp_bt_controller_deinit.
*
* For example, user only use bluetooth to config SSID and PASSWORD of WIFI, after config, will never use bluetooth.
* Then, could call esp_bt_controller_mem_release(ESP_BT_MODE_BTDM) after esp_bt_controller_deinit.
*
* @param mode : the mode want to release memory
* @return ESP_OK - success, other - failed
*/
esp_err_t esp_bt_controller_mem_release(esp_bt_mode_t mode);
#ifdef __cplusplus
}
#endif
#endif /* __ESP_BT_H__ */

View file

@ -270,7 +270,7 @@ extern char *suboptarg; /* getsubopt(3) external variable */
*/ */
#ifndef lint #ifndef lint
static const char rcsid[]="$Id: getopt_long.c,v 1.1 2009/10/16 19:50:28 rodney Exp rodney $"; //static const char rcsid[]="$Id: getopt_long.c,v 1.1 2009/10/16 19:50:28 rodney Exp rodney $";
#endif /* lint */ #endif /* lint */
/*- /*-
* Copyright (c) 2000 The NetBSD Foundation, Inc. * Copyright (c) 2000 The NetBSD Foundation, Inc.

View file

@ -100,7 +100,10 @@ typedef struct {
*/ */
typedef struct { typedef struct {
ledc_mode_t speed_mode; /*!< LEDC speed speed_mode, high-speed mode or low-speed mode */ ledc_mode_t speed_mode; /*!< LEDC speed speed_mode, high-speed mode or low-speed mode */
union {
ledc_timer_bit_t duty_resolution; /*!< LEDC channel duty resolution */ ledc_timer_bit_t duty_resolution; /*!< LEDC channel duty resolution */
ledc_timer_bit_t bit_num __attribute__((deprecated)); /*!< Deprecated in ESP-IDF 3.0. This is an alias to 'duty_resolution' for backward compatibility with ESP-IDF 2.1 */
};
ledc_timer_t timer_num; /*!< The timer source of channel (0 - 3) */ ledc_timer_t timer_num; /*!< The timer source of channel (0 - 3) */
uint32_t freq_hz; /*!< LEDC timer frequency (Hz) */ uint32_t freq_hz; /*!< LEDC timer frequency (Hz) */
} ledc_timer_config_t; } ledc_timer_config_t;

View file

@ -631,6 +631,10 @@ config BROWNOUT_DET_LVL
default 7 if BROWNOUT_DET_LVL_SEL_7 default 7 if BROWNOUT_DET_LVL_SEL_7
# Note about the use of "FRC1" name: currently FRC1 timer is not used for
# high resolution timekeeping anymore. Instead the esp_timer API, implemented
# using FRC2 timer, is used.
# FRC1 name in the option name is kept for compatibility.
choice ESP32_TIME_SYSCALL choice ESP32_TIME_SYSCALL
prompt "Timers used for gettimeofday function" prompt "Timers used for gettimeofday function"
default ESP32_TIME_SYSCALL_USE_RTC_FRC1 default ESP32_TIME_SYSCALL_USE_RTC_FRC1
@ -638,12 +642,12 @@ choice ESP32_TIME_SYSCALL
This setting defines which hardware timers are used to This setting defines which hardware timers are used to
implement 'gettimeofday' and 'time' functions in C library. implement 'gettimeofday' and 'time' functions in C library.
- If only FRC1 timer is used, gettimeofday will provide time at - If both high-resolution and RTC timers are used, timekeeping will
microsecond resolution. Time will not be preserved when going
into deep sleep mode.
- If both FRC1 and RTC timers are used, timekeeping will
continue in deep sleep. Time will be reported at 1 microsecond continue in deep sleep. Time will be reported at 1 microsecond
resolution. resolution. This is the default, and the recommended option.
- If only high-resolution timer is used, gettimeofday will
provide time at microsecond resolution.
Time will not be preserved when going into deep sleep mode.
- If only RTC timer is used, timekeeping will continue in - If only RTC timer is used, timekeeping will continue in
deep sleep, but time will be measured at 6.(6) microsecond deep sleep, but time will be measured at 6.(6) microsecond
resolution. Also the gettimeofday function itself may take resolution. Also the gettimeofday function itself may take
@ -653,12 +657,12 @@ choice ESP32_TIME_SYSCALL
- When RTC is used for timekeeping, two RTC_STORE registers are - When RTC is used for timekeeping, two RTC_STORE registers are
used to keep time in deep sleep mode. used to keep time in deep sleep mode.
config ESP32_TIME_SYSCALL_USE_RTC_FRC1
bool "RTC and high-resolution timer"
config ESP32_TIME_SYSCALL_USE_RTC config ESP32_TIME_SYSCALL_USE_RTC
bool "RTC" bool "RTC"
config ESP32_TIME_SYSCALL_USE_RTC_FRC1
bool "RTC and FRC1"
config ESP32_TIME_SYSCALL_USE_FRC1 config ESP32_TIME_SYSCALL_USE_FRC1
bool "FRC1" bool "High-resolution timer"
config ESP32_TIME_SYSCALL_USE_NONE config ESP32_TIME_SYSCALL_USE_NONE
bool "None" bool "None"
endchoice endchoice

View file

@ -37,13 +37,13 @@ extern "C" {
*/ */
//Keep the LEVELx values as they are here; they match up with (1<<level) //Keep the LEVELx values as they are here; they match up with (1<<level)
#define ESP_INTR_FLAG_LEVEL1 (1<<1) ///< Accept a Level 1 interrupt vector #define ESP_INTR_FLAG_LEVEL1 (1<<1) ///< Accept a Level 1 interrupt vector (lowest priority)
#define ESP_INTR_FLAG_LEVEL2 (1<<2) ///< Accept a Level 2 interrupt vector #define ESP_INTR_FLAG_LEVEL2 (1<<2) ///< Accept a Level 2 interrupt vector
#define ESP_INTR_FLAG_LEVEL3 (1<<3) ///< Accept a Level 3 interrupt vector #define ESP_INTR_FLAG_LEVEL3 (1<<3) ///< Accept a Level 3 interrupt vector
#define ESP_INTR_FLAG_LEVEL4 (1<<4) ///< Accept a Level 4 interrupt vector #define ESP_INTR_FLAG_LEVEL4 (1<<4) ///< Accept a Level 4 interrupt vector
#define ESP_INTR_FLAG_LEVEL5 (1<<5) ///< Accept a Level 5 interrupt vector #define ESP_INTR_FLAG_LEVEL5 (1<<5) ///< Accept a Level 5 interrupt vector
#define ESP_INTR_FLAG_LEVEL6 (1<<6) ///< Accept a Level 6 interrupt vector #define ESP_INTR_FLAG_LEVEL6 (1<<6) ///< Accept a Level 6 interrupt vector
#define ESP_INTR_FLAG_NMI (1<<7) ///< Accept a Level 7 interrupt vector #define ESP_INTR_FLAG_NMI (1<<7) ///< Accept a Level 7 interrupt vector (highest priority)
#define ESP_INTR_FLAG_SHARED (1<<8) ///< Interrupt can be shared between ISRs #define ESP_INTR_FLAG_SHARED (1<<8) ///< Interrupt can be shared between ISRs
#define ESP_INTR_FLAG_EDGE (1<<9) ///< Edge-triggered interrupt #define ESP_INTR_FLAG_EDGE (1<<9) ///< Edge-triggered interrupt
#define ESP_INTR_FLAG_IRAM (1<<10) ///< ISR can be called if cache is disabled #define ESP_INTR_FLAG_IRAM (1<<10) ///< ISR can be called if cache is disabled

View file

@ -43,6 +43,25 @@ typedef struct {
struct dirent cur_dirent; struct dirent cur_dirent;
} vfs_fat_dir_t; } vfs_fat_dir_t;
/* Date and time storage formats in FAT */
typedef union {
struct {
uint16_t mday : 5; /* Day of month, 1 - 31 */
uint16_t mon : 4; /* Month, 1 - 12 */
uint16_t year : 7; /* Year, counting from 1980. E.g. 37 for 2017 */
};
uint16_t as_int;
} fat_date_t;
typedef union {
struct {
uint16_t sec : 5; /* Seconds divided by 2. E.g. 21 for 42 seconds */
uint16_t min : 6; /* Minutes, 0 - 59 */
uint16_t hour : 5; /* Hour, 0 - 23 */
};
uint16_t as_int;
} fat_time_t;
static const char* TAG = "vfs_fat"; static const char* TAG = "vfs_fat";
static ssize_t vfs_fat_write(void* p, int fd, const void * data, size_t size); static ssize_t vfs_fat_write(void* p, int fd, const void * data, size_t size);
@ -389,11 +408,29 @@ static int vfs_fat_fstat(void* ctx, int fd, struct stat * st)
FIL* file = &fat_ctx->files[fd]; FIL* file = &fat_ctx->files[fd];
st->st_size = f_size(file); st->st_size = f_size(file);
st->st_mode = S_IRWXU | S_IRWXG | S_IRWXO | S_IFREG; st->st_mode = S_IRWXU | S_IRWXG | S_IRWXO | S_IFREG;
st->st_mtime = 0;
st->st_atime = 0;
st->st_ctime = 0;
return 0; return 0;
} }
static inline mode_t get_stat_mode(bool is_dir)
{
return S_IRWXU | S_IRWXG | S_IRWXO |
((is_dir) ? S_IFDIR : S_IFREG);
}
static int vfs_fat_stat(void* ctx, const char * path, struct stat * st) static int vfs_fat_stat(void* ctx, const char * path, struct stat * st)
{ {
if (strcmp(path, "/") == 0) {
/* FatFS f_stat function does not work for the drive root.
* Just pretend that this is a directory.
*/
memset(st, 0, sizeof(*st));
st->st_mode = get_stat_mode(true);
return 0;
}
vfs_fat_ctx_t* fat_ctx = (vfs_fat_ctx_t*) ctx; vfs_fat_ctx_t* fat_ctx = (vfs_fat_ctx_t*) ctx;
_lock_acquire(&fat_ctx->lock); _lock_acquire(&fat_ctx->lock);
prepend_drive_to_path(fat_ctx, &path, NULL); prepend_drive_to_path(fat_ctx, &path, NULL);
@ -405,23 +442,23 @@ static int vfs_fat_stat(void* ctx, const char * path, struct stat * st)
errno = fresult_to_errno(res); errno = fresult_to_errno(res);
return -1; return -1;
} }
memset(st, 0, sizeof(*st));
st->st_size = info.fsize; st->st_size = info.fsize;
st->st_mode = S_IRWXU | S_IRWXG | S_IRWXO | st->st_mode = get_stat_mode((info.fattrib & AM_DIR) != 0);
((info.fattrib & AM_DIR) ? S_IFDIR : S_IFREG); fat_date_t fdate = { .as_int = info.fdate };
struct tm tm; fat_time_t ftime = { .as_int = info.ftime };
uint16_t fdate = info.fdate; struct tm tm = {
tm.tm_mday = fdate & 0x1f; .tm_mday = fdate.mday,
fdate >>= 5; .tm_mon = fdate.mon - 1, /* unlike tm_mday, tm_mon is zero-based */
tm.tm_mon = (fdate & 0xf) - 1; .tm_year = fdate.year + 80,
fdate >>=4; .tm_sec = ftime.sec * 2,
tm.tm_year = fdate + 80; .tm_min = ftime.min,
uint16_t ftime = info.ftime; .tm_hour = ftime.hour
tm.tm_sec = (ftime & 0x1f) * 2; };
ftime >>= 5;
tm.tm_min = (ftime & 0x3f);
ftime >>= 6;
tm.tm_hour = (ftime & 0x1f);
st->st_mtime = mktime(&tm); st->st_mtime = mktime(&tm);
st->st_atime = 0;
st->st_ctime = 0;
return 0; return 0;
} }

View file

@ -125,15 +125,15 @@ void test_fatfs_lseek(const char* filename)
TEST_ASSERT_EQUAL(0, fclose(f)); TEST_ASSERT_EQUAL(0, fclose(f));
} }
void test_fatfs_stat(const char* filename) void test_fatfs_stat(const char* filename, const char* root_dir)
{ {
struct tm tm; struct tm tm;
tm.tm_year = 2016 - 1900; tm.tm_year = 2017 - 1900;
tm.tm_mon = 0; tm.tm_mon = 11;
tm.tm_mday = 10; tm.tm_mday = 8;
tm.tm_hour = 16; tm.tm_hour = 19;
tm.tm_min = 30; tm.tm_min = 51;
tm.tm_sec = 0; tm.tm_sec = 10;
time_t t = mktime(&tm); time_t t = mktime(&tm);
printf("Setting time: %s", asctime(&tm)); printf("Setting time: %s", asctime(&tm));
struct timeval now = { .tv_sec = t }; struct timeval now = { .tv_sec = t };
@ -151,6 +151,11 @@ void test_fatfs_stat(const char* filename)
TEST_ASSERT(st.st_mode & S_IFREG); TEST_ASSERT(st.st_mode & S_IFREG);
TEST_ASSERT_FALSE(st.st_mode & S_IFDIR); TEST_ASSERT_FALSE(st.st_mode & S_IFDIR);
memset(&st, 0, sizeof(st));
TEST_ASSERT_EQUAL(0, stat(root_dir, &st));
TEST_ASSERT(st.st_mode & S_IFDIR);
TEST_ASSERT_FALSE(st.st_mode & S_IFREG);
} }
void test_fatfs_unlink(const char* filename) void test_fatfs_unlink(const char* filename)

View file

@ -43,7 +43,7 @@ void test_fatfs_open_max_files(const char* filename_prefix, size_t files_count);
void test_fatfs_lseek(const char* filename); void test_fatfs_lseek(const char* filename);
void test_fatfs_stat(const char* filename); void test_fatfs_stat(const char* filename, const char* root_dir);
void test_fatfs_unlink(const char* filename); void test_fatfs_unlink(const char* filename);

View file

@ -105,7 +105,7 @@ TEST_CASE("(SD) can lseek", "[fatfs][sdcard][ignore]")
TEST_CASE("(SD) stat returns correct values", "[fatfs][ignore]") TEST_CASE("(SD) stat returns correct values", "[fatfs][ignore]")
{ {
test_setup(); test_setup();
test_fatfs_stat("/sdcard/stat.txt"); test_fatfs_stat("/sdcard/stat.txt", "/sdcard");
test_teardown(); test_teardown();
} }

View file

@ -100,7 +100,7 @@ TEST_CASE("(WL) can lseek", "[fatfs][wear_levelling]")
TEST_CASE("(WL) stat returns correct values", "[fatfs][wear_levelling]") TEST_CASE("(WL) stat returns correct values", "[fatfs][wear_levelling]")
{ {
test_setup(); test_setup();
test_fatfs_stat("/spiflash/stat.txt"); test_fatfs_stat("/spiflash/stat.txt", "/spiflash");
test_teardown(); test_teardown();
} }

View file

@ -104,7 +104,6 @@ extern "C" {
* used to create a synchronisation point between multiple tasks (a * used to create a synchronisation point between multiple tasks (a
* 'rendezvous'). * 'rendezvous').
* *
* \defgroup EventGroup
*/ */
@ -116,7 +115,6 @@ extern "C" {
* xEventGroupCreate() returns an EventGroupHandle_t variable that can then * xEventGroupCreate() returns an EventGroupHandle_t variable that can then
* be used as a parameter to other event group functions. * be used as a parameter to other event group functions.
* *
* \defgroup EventGroupHandle_t EventGroupHandle_t
* \ingroup EventGroup * \ingroup EventGroup
*/ */
typedef void * EventGroupHandle_t; typedef void * EventGroupHandle_t;
@ -126,17 +124,11 @@ typedef void * EventGroupHandle_t;
* number of bits it holds is set by configUSE_16_BIT_TICKS (16 bits if set to 1, * number of bits it holds is set by configUSE_16_BIT_TICKS (16 bits if set to 1,
* 32 bits if set to 0. * 32 bits if set to 0.
* *
* \defgroup EventBits_t EventBits_t
* \ingroup EventGroup * \ingroup EventGroup
*/ */
typedef TickType_t EventBits_t; typedef TickType_t EventBits_t;
/** /**
* event_groups.h
*<pre>
EventGroupHandle_t xEventGroupCreate( void );
</pre>
*
* Create a new event group. * Create a new event group.
* *
* Internally, within the FreeRTOS implementation, event groups use a [small] * Internally, within the FreeRTOS implementation, event groups use a [small]
@ -162,25 +154,24 @@ typedef TickType_t EventBits_t;
* event group then NULL is returned. See http://www.freertos.org/a00111.html * event group then NULL is returned. See http://www.freertos.org/a00111.html
* *
* Example usage: * Example usage:
<pre> * @code{c}
// Declare a variable to hold the created event group. * // Declare a variable to hold the created event group.
EventGroupHandle_t xCreatedEventGroup; * EventGroupHandle_t xCreatedEventGroup;
*
// Attempt to create the event group. * // Attempt to create the event group.
xCreatedEventGroup = xEventGroupCreate(); * xCreatedEventGroup = xEventGroupCreate();
*
// Was the event group created successfully? * // Was the event group created successfully?
if( xCreatedEventGroup == NULL ) * if( xCreatedEventGroup == NULL )
{ * {
// The event group was not created because there was insufficient * // The event group was not created because there was insufficient
// FreeRTOS heap available. * // FreeRTOS heap available.
} * }
else * else
{ * {
// The event group was created. * // The event group was created.
} * }
</pre> * @endcode
* \defgroup xEventGroupCreate xEventGroupCreate
* \ingroup EventGroup * \ingroup EventGroup
*/ */
#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) #if( configSUPPORT_DYNAMIC_ALLOCATION == 1 )
@ -188,11 +179,6 @@ typedef TickType_t EventBits_t;
#endif #endif
/** /**
* event_groups.h
*<pre>
EventGroupHandle_t xEventGroupCreateStatic( EventGroupHandle_t * pxEventGroupBuffer );
</pre>
*
* Create a new event group. * Create a new event group.
* *
* Internally, within the FreeRTOS implementation, event groups use a [small] * Internally, within the FreeRTOS implementation, event groups use a [small]
@ -221,35 +207,26 @@ typedef TickType_t EventBits_t;
* returned. If pxEventGroupBuffer was NULL then NULL is returned. * returned. If pxEventGroupBuffer was NULL then NULL is returned.
* *
* Example usage: * Example usage:
<pre> * @code{c}
// StaticEventGroup_t is a publicly accessible structure that has the same * // StaticEventGroup_t is a publicly accessible structure that has the same
// size and alignment requirements as the real event group structure. It is * // size and alignment requirements as the real event group structure. It is
// provided as a mechanism for applications to know the size of the event * // provided as a mechanism for applications to know the size of the event
// group (which is dependent on the architecture and configuration file * // group (which is dependent on the architecture and configuration file
// settings) without breaking the strict data hiding policy by exposing the * // settings) without breaking the strict data hiding policy by exposing the
// real event group internals. This StaticEventGroup_t variable is passed * // real event group internals. This StaticEventGroup_t variable is passed
// into the xSemaphoreCreateEventGroupStatic() function and is used to store * // into the xSemaphoreCreateEventGroupStatic() function and is used to store
// the event group's data structures * // the event group's data structures
StaticEventGroup_t xEventGroupBuffer; * StaticEventGroup_t xEventGroupBuffer;
*
// Create the event group without dynamically allocating any memory. * // Create the event group without dynamically allocating any memory.
xEventGroup = xEventGroupCreateStatic( &xEventGroupBuffer ); * xEventGroup = xEventGroupCreateStatic( &xEventGroupBuffer );
</pre> * @endcode
*/ */
#if( configSUPPORT_STATIC_ALLOCATION == 1 ) #if( configSUPPORT_STATIC_ALLOCATION == 1 )
EventGroupHandle_t xEventGroupCreateStatic( StaticEventGroup_t *pxEventGroupBuffer ) PRIVILEGED_FUNCTION; EventGroupHandle_t xEventGroupCreateStatic( StaticEventGroup_t *pxEventGroupBuffer ) PRIVILEGED_FUNCTION;
#endif #endif
/** /**
* event_groups.h
*<pre>
EventBits_t xEventGroupWaitBits( EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToWaitFor,
const BaseType_t xClearOnExit,
const BaseType_t xWaitForAllBits,
const TickType_t xTicksToWait );
</pre>
*
* [Potentially] block to wait for one or more bits to be set within a * [Potentially] block to wait for one or more bits to be set within a
* previously created event group. * previously created event group.
* *
@ -292,54 +269,48 @@ typedef TickType_t EventBits_t;
* pdTRUE. * pdTRUE.
* *
* Example usage: * Example usage:
<pre> * @code{c}
#define BIT_0 ( 1 << 0 ) * #define BIT_0 ( 1 << 0 )
#define BIT_4 ( 1 << 4 ) * #define BIT_4 ( 1 << 4 )
*
void aFunction( EventGroupHandle_t xEventGroup ) * void aFunction( EventGroupHandle_t xEventGroup )
{ * {
EventBits_t uxBits; * EventBits_t uxBits;
const TickType_t xTicksToWait = 100 / portTICK_PERIOD_MS; * const TickType_t xTicksToWait = 100 / portTICK_PERIOD_MS;
*
// Wait a maximum of 100ms for either bit 0 or bit 4 to be set within * // Wait a maximum of 100ms for either bit 0 or bit 4 to be set within
// the event group. Clear the bits before exiting. * // the event group. Clear the bits before exiting.
uxBits = xEventGroupWaitBits( * uxBits = xEventGroupWaitBits(
xEventGroup, // The event group being tested. * xEventGroup, // The event group being tested.
BIT_0 | BIT_4, // The bits within the event group to wait for. * BIT_0 | BIT_4, // The bits within the event group to wait for.
pdTRUE, // BIT_0 and BIT_4 should be cleared before returning. * pdTRUE, // BIT_0 and BIT_4 should be cleared before returning.
pdFALSE, // Don't wait for both bits, either bit will do. * pdFALSE, // Don't wait for both bits, either bit will do.
xTicksToWait ); // Wait a maximum of 100ms for either bit to be set. * xTicksToWait ); // Wait a maximum of 100ms for either bit to be set.
*
if( ( uxBits & ( BIT_0 | BIT_4 ) ) == ( BIT_0 | BIT_4 ) ) * if( ( uxBits & ( BIT_0 | BIT_4 ) ) == ( BIT_0 | BIT_4 ) )
{ * {
// xEventGroupWaitBits() returned because both bits were set. * // xEventGroupWaitBits() returned because both bits were set.
} * }
else if( ( uxBits & BIT_0 ) != 0 ) * else if( ( uxBits & BIT_0 ) != 0 )
{ * {
// xEventGroupWaitBits() returned because just BIT_0 was set. * // xEventGroupWaitBits() returned because just BIT_0 was set.
} * }
else if( ( uxBits & BIT_4 ) != 0 ) * else if( ( uxBits & BIT_4 ) != 0 )
{ * {
// xEventGroupWaitBits() returned because just BIT_4 was set. * // xEventGroupWaitBits() returned because just BIT_4 was set.
} * }
else * else
{ * {
// xEventGroupWaitBits() returned because xTicksToWait ticks passed * // xEventGroupWaitBits() returned because xTicksToWait ticks passed
// without either BIT_0 or BIT_4 becoming set. * // without either BIT_0 or BIT_4 becoming set.
} * }
} * }
</pre> * @endcode{c}
* \defgroup xEventGroupWaitBits xEventGroupWaitBits
* \ingroup EventGroup * \ingroup EventGroup
*/ */
EventBits_t xEventGroupWaitBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToWaitFor, const BaseType_t xClearOnExit, const BaseType_t xWaitForAllBits, TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; EventBits_t xEventGroupWaitBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToWaitFor, const BaseType_t xClearOnExit, const BaseType_t xWaitForAllBits, TickType_t xTicksToWait ) PRIVILEGED_FUNCTION;
/** /**
* event_groups.h
*<pre>
EventBits_t xEventGroupClearBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToClear );
</pre>
*
* Clear bits within an event group. This function cannot be called from an * Clear bits within an event group. This function cannot be called from an
* interrupt. * interrupt.
* *
@ -352,51 +323,45 @@ EventBits_t xEventGroupWaitBits( EventGroupHandle_t xEventGroup, const EventBits
* @return The value of the event group before the specified bits were cleared. * @return The value of the event group before the specified bits were cleared.
* *
* Example usage: * Example usage:
<pre> * @code{c}
#define BIT_0 ( 1 << 0 ) * #define BIT_0 ( 1 << 0 )
#define BIT_4 ( 1 << 4 ) * #define BIT_4 ( 1 << 4 )
*
void aFunction( EventGroupHandle_t xEventGroup ) * void aFunction( EventGroupHandle_t xEventGroup )
{ * {
EventBits_t uxBits; * EventBits_t uxBits;
*
// Clear bit 0 and bit 4 in xEventGroup. * // Clear bit 0 and bit 4 in xEventGroup.
uxBits = xEventGroupClearBits( * uxBits = xEventGroupClearBits(
xEventGroup, // The event group being updated. * xEventGroup, // The event group being updated.
BIT_0 | BIT_4 );// The bits being cleared. * BIT_0 | BIT_4 );// The bits being cleared.
*
if( ( uxBits & ( BIT_0 | BIT_4 ) ) == ( BIT_0 | BIT_4 ) ) * if( ( uxBits & ( BIT_0 | BIT_4 ) ) == ( BIT_0 | BIT_4 ) )
{ * {
// Both bit 0 and bit 4 were set before xEventGroupClearBits() was * // Both bit 0 and bit 4 were set before xEventGroupClearBits() was
// called. Both will now be clear (not set). * // called. Both will now be clear (not set).
} * }
else if( ( uxBits & BIT_0 ) != 0 ) * else if( ( uxBits & BIT_0 ) != 0 )
{ * {
// Bit 0 was set before xEventGroupClearBits() was called. It will * // Bit 0 was set before xEventGroupClearBits() was called. It will
// now be clear. * // now be clear.
} * }
else if( ( uxBits & BIT_4 ) != 0 ) * else if( ( uxBits & BIT_4 ) != 0 )
{ * {
// Bit 4 was set before xEventGroupClearBits() was called. It will * // Bit 4 was set before xEventGroupClearBits() was called. It will
// now be clear. * // now be clear.
} * }
else * else
{ * {
// Neither bit 0 nor bit 4 were set in the first place. * // Neither bit 0 nor bit 4 were set in the first place.
} * }
} * }
</pre> * @endcode
* \defgroup xEventGroupClearBits xEventGroupClearBits
* \ingroup EventGroup * \ingroup EventGroup
*/ */
EventBits_t xEventGroupClearBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToClear ) PRIVILEGED_FUNCTION; EventBits_t xEventGroupClearBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToClear ) PRIVILEGED_FUNCTION;
/** /**
* event_groups.h
*<pre>
BaseType_t xEventGroupClearBitsFromISR( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet );
</pre>
*
* A version of xEventGroupClearBits() that can be called from an interrupt. * A version of xEventGroupClearBits() that can be called from an interrupt.
* *
* Setting bits in an event group is not a deterministic operation because there * Setting bits in an event group is not a deterministic operation because there
@ -420,28 +385,27 @@ EventBits_t xEventGroupClearBits( EventGroupHandle_t xEventGroup, const EventBit
* if the timer service queue was full. * if the timer service queue was full.
* *
* Example usage: * Example usage:
<pre> * @code{c}
#define BIT_0 ( 1 << 0 ) * #define BIT_0 ( 1 << 0 )
#define BIT_4 ( 1 << 4 ) * #define BIT_4 ( 1 << 4 )
*
// An event group which it is assumed has already been created by a call to * // An event group which it is assumed has already been created by a call to
// xEventGroupCreate(). * // xEventGroupCreate().
EventGroupHandle_t xEventGroup; * EventGroupHandle_t xEventGroup;
*
void anInterruptHandler( void ) * void anInterruptHandler( void )
{ * {
// Clear bit 0 and bit 4 in xEventGroup. * // Clear bit 0 and bit 4 in xEventGroup.
xResult = xEventGroupClearBitsFromISR( * xResult = xEventGroupClearBitsFromISR(
xEventGroup, // The event group being updated. * xEventGroup, // The event group being updated.
BIT_0 | BIT_4 ); // The bits being set. * BIT_0 | BIT_4 ); // The bits being set.
*
if( xResult == pdPASS ) * if( xResult == pdPASS )
{ * {
// The message was posted successfully. * // The message was posted successfully.
} * }
} * }
</pre> * @endcode
* \defgroup xEventGroupSetBitsFromISR xEventGroupSetBitsFromISR
* \ingroup EventGroup * \ingroup EventGroup
*/ */
#if( configUSE_TRACE_FACILITY == 1 ) #if( configUSE_TRACE_FACILITY == 1 )
@ -451,11 +415,6 @@ EventBits_t xEventGroupClearBits( EventGroupHandle_t xEventGroup, const EventBit
#endif #endif
/** /**
* event_groups.h
*<pre>
EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet );
</pre>
*
* Set bits within an event group. * Set bits within an event group.
* This function cannot be called from an interrupt. xEventGroupSetBitsFromISR() * This function cannot be called from an interrupt. xEventGroupSetBitsFromISR()
* is a version that can be called from an interrupt. * is a version that can be called from an interrupt.
@ -480,56 +439,50 @@ EventBits_t xEventGroupClearBits( EventGroupHandle_t xEventGroup, const EventBit
* event group value before the call to xEventGroupSetBits() returns. * event group value before the call to xEventGroupSetBits() returns.
* *
* Example usage: * Example usage:
<pre> * @code{c}
#define BIT_0 ( 1 << 0 ) * #define BIT_0 ( 1 << 0 )
#define BIT_4 ( 1 << 4 ) * #define BIT_4 ( 1 << 4 )
*
void aFunction( EventGroupHandle_t xEventGroup ) * void aFunction( EventGroupHandle_t xEventGroup )
{ * {
EventBits_t uxBits; * EventBits_t uxBits;
*
// Set bit 0 and bit 4 in xEventGroup. * // Set bit 0 and bit 4 in xEventGroup.
uxBits = xEventGroupSetBits( * uxBits = xEventGroupSetBits(
xEventGroup, // The event group being updated. * xEventGroup, // The event group being updated.
BIT_0 | BIT_4 );// The bits being set. * BIT_0 | BIT_4 );// The bits being set.
*
if( ( uxBits & ( BIT_0 | BIT_4 ) ) == ( BIT_0 | BIT_4 ) ) * if( ( uxBits & ( BIT_0 | BIT_4 ) ) == ( BIT_0 | BIT_4 ) )
{ * {
// Both bit 0 and bit 4 remained set when the function returned. * // Both bit 0 and bit 4 remained set when the function returned.
} * }
else if( ( uxBits & BIT_0 ) != 0 ) * else if( ( uxBits & BIT_0 ) != 0 )
{ * {
// Bit 0 remained set when the function returned, but bit 4 was * // Bit 0 remained set when the function returned, but bit 4 was
// cleared. It might be that bit 4 was cleared automatically as a * // cleared. It might be that bit 4 was cleared automatically as a
// task that was waiting for bit 4 was removed from the Blocked * // task that was waiting for bit 4 was removed from the Blocked
// state. * // state.
} * }
else if( ( uxBits & BIT_4 ) != 0 ) * else if( ( uxBits & BIT_4 ) != 0 )
{ * {
// Bit 4 remained set when the function returned, but bit 0 was * // Bit 4 remained set when the function returned, but bit 0 was
// cleared. It might be that bit 0 was cleared automatically as a * // cleared. It might be that bit 0 was cleared automatically as a
// task that was waiting for bit 0 was removed from the Blocked * // task that was waiting for bit 0 was removed from the Blocked
// state. * // state.
} * }
else * else
{ * {
// Neither bit 0 nor bit 4 remained set. It might be that a task * // Neither bit 0 nor bit 4 remained set. It might be that a task
// was waiting for both of the bits to be set, and the bits were * // was waiting for both of the bits to be set, and the bits were
// cleared as the task left the Blocked state. * // cleared as the task left the Blocked state.
} * }
} * }
</pre> * @endcode{c}
* \defgroup xEventGroupSetBits xEventGroupSetBits
* \ingroup EventGroup * \ingroup EventGroup
*/ */
EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet ) PRIVILEGED_FUNCTION; EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet ) PRIVILEGED_FUNCTION;
/** /**
* event_groups.h
*<pre>
BaseType_t xEventGroupSetBitsFromISR( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet, BaseType_t *pxHigherPriorityTaskWoken );
</pre>
*
* A version of xEventGroupSetBits() that can be called from an interrupt. * A version of xEventGroupSetBits() that can be called from an interrupt.
* *
* Setting bits in an event group is not a deterministic operation because there * Setting bits in an event group is not a deterministic operation because there
@ -561,39 +514,38 @@ EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup, const EventBits_
* if the timer service queue was full. * if the timer service queue was full.
* *
* Example usage: * Example usage:
<pre> * @code{c}
#define BIT_0 ( 1 << 0 ) * #define BIT_0 ( 1 << 0 )
#define BIT_4 ( 1 << 4 ) * #define BIT_4 ( 1 << 4 )
*
// An event group which it is assumed has already been created by a call to * // An event group which it is assumed has already been created by a call to
// xEventGroupCreate(). * // xEventGroupCreate().
EventGroupHandle_t xEventGroup; * EventGroupHandle_t xEventGroup;
*
void anInterruptHandler( void ) * void anInterruptHandler( void )
{ * {
BaseType_t xHigherPriorityTaskWoken, xResult; * BaseType_t xHigherPriorityTaskWoken, xResult;
*
// xHigherPriorityTaskWoken must be initialised to pdFALSE. * // xHigherPriorityTaskWoken must be initialised to pdFALSE.
xHigherPriorityTaskWoken = pdFALSE; * xHigherPriorityTaskWoken = pdFALSE;
*
// Set bit 0 and bit 4 in xEventGroup. * // Set bit 0 and bit 4 in xEventGroup.
xResult = xEventGroupSetBitsFromISR( * xResult = xEventGroupSetBitsFromISR(
xEventGroup, // The event group being updated. * xEventGroup, // The event group being updated.
BIT_0 | BIT_4 // The bits being set. * BIT_0 | BIT_4 // The bits being set.
&xHigherPriorityTaskWoken ); * &xHigherPriorityTaskWoken );
*
// Was the message posted successfully? * // Was the message posted successfully?
if( xResult == pdPASS ) * if( xResult == pdPASS )
{ * {
// If xHigherPriorityTaskWoken is now set to pdTRUE then a context * // If xHigherPriorityTaskWoken is now set to pdTRUE then a context
// switch should be requested. The macro used is port specific and * // switch should be requested. The macro used is port specific and
// will be either portYIELD_FROM_ISR() or portEND_SWITCHING_ISR() - * // will be either portYIELD_FROM_ISR() or portEND_SWITCHING_ISR() -
// refer to the documentation page for the port being used. * // refer to the documentation page for the port being used.
portYIELD_FROM_ISR( xHigherPriorityTaskWoken ); * portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
} * }
} * }
</pre> * @endcode
* \defgroup xEventGroupSetBitsFromISR xEventGroupSetBitsFromISR
* \ingroup EventGroup * \ingroup EventGroup
*/ */
#if( configUSE_TRACE_FACILITY == 1 ) #if( configUSE_TRACE_FACILITY == 1 )
@ -603,14 +555,6 @@ EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup, const EventBits_
#endif #endif
/** /**
* event_groups.h
*<pre>
EventBits_t xEventGroupSync( EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToSet,
const EventBits_t uxBitsToWaitFor,
TickType_t xTicksToWait );
</pre>
*
* Atomically set bits within an event group, then wait for a combination of * Atomically set bits within an event group, then wait for a combination of
* bits to be set within the same event group. This functionality is typically * bits to be set within the same event group. This functionality is typically
* used to synchronise multiple tasks, where each task has to wait for the other * used to synchronise multiple tasks, where each task has to wait for the other
@ -648,93 +592,87 @@ EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup, const EventBits_
* automatically cleared. * automatically cleared.
* *
* Example usage: * Example usage:
<pre> * @code{c}
// Bits used by the three tasks. * // Bits used by the three tasks.
#define TASK_0_BIT ( 1 << 0 ) * #define TASK_0_BIT ( 1 << 0 )
#define TASK_1_BIT ( 1 << 1 ) * #define TASK_1_BIT ( 1 << 1 )
#define TASK_2_BIT ( 1 << 2 ) * #define TASK_2_BIT ( 1 << 2 )
*
#define ALL_SYNC_BITS ( TASK_0_BIT | TASK_1_BIT | TASK_2_BIT ) * #define ALL_SYNC_BITS ( TASK_0_BIT | TASK_1_BIT | TASK_2_BIT )
*
// Use an event group to synchronise three tasks. It is assumed this event * // Use an event group to synchronise three tasks. It is assumed this event
// group has already been created elsewhere. * // group has already been created elsewhere.
EventGroupHandle_t xEventBits; * EventGroupHandle_t xEventBits;
*
void vTask0( void *pvParameters ) * void vTask0( void *pvParameters )
{ * {
EventBits_t uxReturn; * EventBits_t uxReturn;
TickType_t xTicksToWait = 100 / portTICK_PERIOD_MS; * TickType_t xTicksToWait = 100 / portTICK_PERIOD_MS;
*
for( ;; ) * for( ;; )
{ * {
// Perform task functionality here. * // Perform task functionality here.
*
// Set bit 0 in the event flag to note this task has reached the * // Set bit 0 in the event flag to note this task has reached the
// sync point. The other two tasks will set the other two bits defined * // sync point. The other two tasks will set the other two bits defined
// by ALL_SYNC_BITS. All three tasks have reached the synchronisation * // by ALL_SYNC_BITS. All three tasks have reached the synchronisation
// point when all the ALL_SYNC_BITS are set. Wait a maximum of 100ms * // point when all the ALL_SYNC_BITS are set. Wait a maximum of 100ms
// for this to happen. * // for this to happen.
uxReturn = xEventGroupSync( xEventBits, TASK_0_BIT, ALL_SYNC_BITS, xTicksToWait ); * uxReturn = xEventGroupSync( xEventBits, TASK_0_BIT, ALL_SYNC_BITS, xTicksToWait );
*
if( ( uxReturn & ALL_SYNC_BITS ) == ALL_SYNC_BITS ) * if( ( uxReturn & ALL_SYNC_BITS ) == ALL_SYNC_BITS )
{ * {
// All three tasks reached the synchronisation point before the call * // All three tasks reached the synchronisation point before the call
// to xEventGroupSync() timed out. * // to xEventGroupSync() timed out.
} * }
} * }
} * }
*
void vTask1( void *pvParameters ) * void vTask1( void *pvParameters )
{ * {
for( ;; ) * for( ;; )
{ * {
// Perform task functionality here. * // Perform task functionality here.
*
// Set bit 1 in the event flag to note this task has reached the * // Set bit 1 in the event flag to note this task has reached the
// synchronisation point. The other two tasks will set the other two * // synchronisation point. The other two tasks will set the other two
// bits defined by ALL_SYNC_BITS. All three tasks have reached the * // bits defined by ALL_SYNC_BITS. All three tasks have reached the
// synchronisation point when all the ALL_SYNC_BITS are set. Wait * // synchronisation point when all the ALL_SYNC_BITS are set. Wait
// indefinitely for this to happen. * // indefinitely for this to happen.
xEventGroupSync( xEventBits, TASK_1_BIT, ALL_SYNC_BITS, portMAX_DELAY ); * xEventGroupSync( xEventBits, TASK_1_BIT, ALL_SYNC_BITS, portMAX_DELAY );
*
// xEventGroupSync() was called with an indefinite block time, so * // xEventGroupSync() was called with an indefinite block time, so
// this task will only reach here if the syncrhonisation was made by all * // this task will only reach here if the syncrhonisation was made by all
// three tasks, so there is no need to test the return value. * // three tasks, so there is no need to test the return value.
} * }
} * }
*
void vTask2( void *pvParameters ) * void vTask2( void *pvParameters )
{ * {
for( ;; ) * for( ;; )
{ * {
// Perform task functionality here. * // Perform task functionality here.
*
// Set bit 2 in the event flag to note this task has reached the * // Set bit 2 in the event flag to note this task has reached the
// synchronisation point. The other two tasks will set the other two * // synchronisation point. The other two tasks will set the other two
// bits defined by ALL_SYNC_BITS. All three tasks have reached the * // bits defined by ALL_SYNC_BITS. All three tasks have reached the
// synchronisation point when all the ALL_SYNC_BITS are set. Wait * // synchronisation point when all the ALL_SYNC_BITS are set. Wait
// indefinitely for this to happen. * // indefinitely for this to happen.
xEventGroupSync( xEventBits, TASK_2_BIT, ALL_SYNC_BITS, portMAX_DELAY ); * xEventGroupSync( xEventBits, TASK_2_BIT, ALL_SYNC_BITS, portMAX_DELAY );
*
// xEventGroupSync() was called with an indefinite block time, so * // xEventGroupSync() was called with an indefinite block time, so
// this task will only reach here if the syncrhonisation was made by all * // this task will only reach here if the syncrhonisation was made by all
// three tasks, so there is no need to test the return value. * // three tasks, so there is no need to test the return value.
} * }
} * }
*
</pre> * @endcode
* \defgroup xEventGroupSync xEventGroupSync
* \ingroup EventGroup * \ingroup EventGroup
*/ */
EventBits_t xEventGroupSync( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet, const EventBits_t uxBitsToWaitFor, TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; EventBits_t xEventGroupSync( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet, const EventBits_t uxBitsToWaitFor, TickType_t xTicksToWait ) PRIVILEGED_FUNCTION;
/** /**
* event_groups.h
*<pre>
EventBits_t xEventGroupGetBits( EventGroupHandle_t xEventGroup );
</pre>
*
* Returns the current value of the bits in an event group. This function * Returns the current value of the bits in an event group. This function
* cannot be used from an interrupt. * cannot be used from an interrupt.
* *
@ -742,33 +680,22 @@ EventBits_t xEventGroupSync( EventGroupHandle_t xEventGroup, const EventBits_t u
* *
* @return The event group bits at the time xEventGroupGetBits() was called. * @return The event group bits at the time xEventGroupGetBits() was called.
* *
* \defgroup xEventGroupGetBits xEventGroupGetBits
* \ingroup EventGroup * \ingroup EventGroup
*/ */
#define xEventGroupGetBits( xEventGroup ) xEventGroupClearBits( xEventGroup, 0 ) #define xEventGroupGetBits( xEventGroup ) xEventGroupClearBits( xEventGroup, 0 )
/** /**
* event_groups.h
*<pre>
EventBits_t xEventGroupGetBitsFromISR( EventGroupHandle_t xEventGroup );
</pre>
*
* A version of xEventGroupGetBits() that can be called from an ISR. * A version of xEventGroupGetBits() that can be called from an ISR.
* *
* @param xEventGroup The event group being queried. * @param xEventGroup The event group being queried.
* *
* @return The event group bits at the time xEventGroupGetBitsFromISR() was called. * @return The event group bits at the time xEventGroupGetBitsFromISR() was called.
* *
* \defgroup xEventGroupGetBitsFromISR xEventGroupGetBitsFromISR
* \ingroup EventGroup * \ingroup EventGroup
*/ */
EventBits_t xEventGroupGetBitsFromISR( EventGroupHandle_t xEventGroup ); EventBits_t xEventGroupGetBitsFromISR( EventGroupHandle_t xEventGroup );
/** /**
* event_groups.h
*<pre>
void xEventGroupDelete( EventGroupHandle_t xEventGroup );
</pre>
* *
* Delete an event group that was previously created by a call to * Delete an event group that was previously created by a call to
* xEventGroupCreate(). Tasks that are blocked on the event group will be * xEventGroupCreate(). Tasks that are blocked on the event group will be
@ -778,6 +705,8 @@ EventBits_t xEventGroupGetBitsFromISR( EventGroupHandle_t xEventGroup );
*/ */
void vEventGroupDelete( EventGroupHandle_t xEventGroup ); void vEventGroupDelete( EventGroupHandle_t xEventGroup );
/** @cond */
/* For internal use only. */ /* For internal use only. */
void vEventGroupSetBitsCallback( void *pvEventGroup, const uint32_t ulBitsToSet ); void vEventGroupSetBitsCallback( void *pvEventGroup, const uint32_t ulBitsToSet );
void vEventGroupClearBitsCallback( void *pvEventGroup, const uint32_t ulBitsToClear ); void vEventGroupClearBitsCallback( void *pvEventGroup, const uint32_t ulBitsToClear );
@ -786,6 +715,8 @@ void vEventGroupClearBitsCallback( void *pvEventGroup, const uint32_t ulBitsToCl
UBaseType_t uxEventGroupGetNumber( void* xEventGroup ); UBaseType_t uxEventGroupGetNumber( void* xEventGroup );
#endif #endif
/** @endcond */
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

File diff suppressed because it is too large Load diff

View file

@ -9,50 +9,58 @@
extern "C" { extern "C" {
#endif #endif
/*
Header definitions for a FreeRTOS ringbuffer object
A ringbuffer instantiated by these functions essentially acts like a FreeRTOS queue, with the
difference that it's strictly FIFO and with the main advantage that you can put in randomly-sized
items. The capacity, accordingly, isn't measured in the amount of items, but the amount of memory
that is used for storing the items. Dependent on the size of the items, more or less of them will
fit in the ring buffer.
This ringbuffer tries to be efficient with memory: when inserting an item, the item data will
be copied to the ringbuffer memory. When retrieving an item, however, a reference to ringbuffer
memory will be returned. The returned memory is guaranteed to be 32-bit aligned and contiguous.
The application can use this memory, but as long as it does, ringbuffer writes that would write
to this bit of memory will block.
The requirement for items to be contiguous is slightly problematic when the only way to place
the next item would involve a wraparound from the end to the beginning of the ringbuffer. This can
be solved (or not) in a few ways:
- type = RINGBUF_TYPE_ALLOWSPLIT: The insertion code will split the item in two items; one which fits
in the space left at the end of the ringbuffer, one that contains the remaining data which is placed
in the beginning. Two xRingbufferReceive calls will be needed to retrieve the data.
- type = RINGBUF_TYPE_NOSPLIT: The insertion code will leave the room at the end of the ringbuffer
unused and instead will put the entire item at the start of the ringbuffer, as soon as there is
enough free space.
- type = RINGBUF_TYPE_BYTEBUF: This is your conventional byte-based ringbuffer. It does have no
overhead, but it has no item contiguousness either: a read will just give you the entire written
buffer space, or the space up to the end of the buffer, and writes can be broken up in any way
possible. Note that this type cannot do a 2nd read before returning the memory of the 1st.
The maximum size of an item will be affected by this decision. When split items are allowed, it's
acceptable to push items of (buffer_size)-16 bytes into the buffer. When it's not allowed, the
maximum size is (buffer_size/2)-8 bytes. The bytebuf can fill the entire buffer with data, it has
no overhead.
*/
#include <freertos/queue.h> #include <freertos/queue.h>
//An opaque handle for a ringbuff object. //An opaque handle for a ringbuff object.
typedef void * RingbufHandle_t; typedef void * RingbufHandle_t;
//The various types of buffer /**
* @brief The various types of buffer
*
* A ringbuffer instantiated by these functions essentially acts like a
* FreeRTOS queue, with the difference that it's strictly FIFO and with
* the main advantage that you can put in randomly-sized items. The capacity,
* accordingly, isn't measured in the amount of items, but the amount of
* memory that is used for storing the items. Dependent on the size of
* the items, more or less of them will fit in the ring buffer.
*
* This ringbuffer tries to be efficient with memory: when inserting an item,
* the item data will be copied to the ringbuffer memory. When retrieving
* an item, however, a reference to ringbuffer memory will be returned.
* The returned memory is guaranteed to be 32-bit aligned and contiguous.
* The application can use this memory, but as long as it does, ringbuffer
* writes that would write to this bit of memory will block.
*
* The requirement for items to be contiguous is slightly problematic when
* the only way to place the next item would involve a wraparound from the end
* to the beginning of the ringbuffer. This can be solved (or not) in a few ways,
* see descriptions of possible ringbuf_type_t types below.
*
* The maximum size of an item will be affected by ringbuffer type.
* When split items are allowed, it is acceptable to push items of
* (buffer_size)-16 bytes into the buffer.
* When it's not allowed, the maximum size is (buffer_size/2)-8 bytes.
* The bytebuf can fill the entire buffer with data, it has no overhead.
*/
typedef enum { typedef enum {
/** The insertion code will leave the room at the end of the ringbuffer
* unused and instead will put the entire item at the start of the ringbuffer,
* as soon as there is enough free space.
*/
RINGBUF_TYPE_NOSPLIT = 0, RINGBUF_TYPE_NOSPLIT = 0,
/** The insertion code will split the item in two items; one which fits
* in the space left at the end of the ringbuffer, one that contains
* the remaining data which is placed in the beginning.
* Two xRingbufferReceive calls will be needed to retrieve the data.
*/
RINGBUF_TYPE_ALLOWSPLIT, RINGBUF_TYPE_ALLOWSPLIT,
/** This is your conventional byte-based ringbuffer. It does have no
* overhead, but it has no item contiguousness either: a read will just
* give you the entire written buffer space, or the space up to the end
* of the buffer, and writes can be broken up in any way possible.
* Note that this type cannot do a 2nd read before returning the memory
* of the 1st.
*/
RINGBUF_TYPE_BYTEBUF RINGBUF_TYPE_BYTEBUF
} ringbuf_type_t; } ringbuf_type_t;
@ -60,22 +68,19 @@ typedef enum {
/** /**
* @brief Create a ring buffer * @brief Create a ring buffer
* *
* @param buf_length : Length of circular buffer, in bytes. Each entry will take up its own length, plus a header * @param buf_length Length of circular buffer, in bytes. Each entry will
* that at the moment is equal to sizeof(size_t). * take up its own length, plus a header that at the moment
* @param allow_split_items : pdTRUE if it is acceptable that item data is inserted as two * is equal to sizeof(size_t).
* items instead of one. * @param type Type of ring buffer, see ringbuf_type_t.
* *
* @return A RingbufHandle_t handle to the created ringbuffer, or NULL in case of error. * @return A RingbufHandle_t handle to the created ringbuffer, or NULL in case of error.
*/ */
RingbufHandle_t xRingbufferCreate(size_t buf_length, ringbuf_type_t type); RingbufHandle_t xRingbufferCreate(size_t buf_length, ringbuf_type_t type);
/** /**
* @brief Delete a ring buffer * @brief Delete a ring buffer
* *
* @param ringbuf - Ring buffer to delete * @param ringbuf Ring buffer to delete
*
* @return void
*/ */
void vRingbufferDelete(RingbufHandle_t ringbuf); void vRingbufferDelete(RingbufHandle_t ringbuf);
@ -83,7 +88,7 @@ void vRingbufferDelete(RingbufHandle_t ringbuf);
/** /**
* @brief Get maximum size of an item that can be placed in the ring buffer * @brief Get maximum size of an item that can be placed in the ring buffer
* *
* @param ringbuf - Ring buffer to query * @param ringbuf Ring buffer to query
* *
* @return Maximum size, in bytes, of an item that can be placed in a ring buffer. * @return Maximum size, in bytes, of an item that can be placed in a ring buffer.
*/ */
@ -93,13 +98,15 @@ size_t xRingbufferGetMaxItemSize(RingbufHandle_t ringbuf);
/** /**
* @brief Insert an item into the ring buffer * @brief Insert an item into the ring buffer
* *
* @param ringbuf - Ring buffer to insert the item into * @param ringbuf Ring buffer to insert the item into
* @param data - Pointer to data to insert. NULL is allowed if data_size is 0. * @param data Pointer to data to insert. NULL is allowed if data_size is 0.
* @param data_size - Size of data to insert. A value of 0 is allowed. * @param data_size Size of data to insert. A value of 0 is allowed.
* @param xTicksToWait - Ticks to wait for room in the ringbuffer. * @param ticks_to_wait Ticks to wait for room in the ringbuffer.
* *
* @return pdTRUE if succeeded, pdFALSE on time-out or when the buffer is larger * @return
* than indicated by xRingbufferGetMaxItemSize(ringbuf). * - pdTRUE if succeeded
* - pdFALSE on time-out or when the buffer is larger than indicated
* by xRingbufferGetMaxItemSize(ringbuf).
*/ */
BaseType_t xRingbufferSend(RingbufHandle_t ringbuf, void *data, size_t data_size, TickType_t ticks_to_wait); BaseType_t xRingbufferSend(RingbufHandle_t ringbuf, void *data, size_t data_size, TickType_t ticks_to_wait);
@ -107,11 +114,11 @@ BaseType_t xRingbufferSend(RingbufHandle_t ringbuf, void *data, size_t data_size
/** /**
* @brief Insert an item into the ring buffer from an ISR * @brief Insert an item into the ring buffer from an ISR
* *
* @param ringbuf - Ring buffer to insert the item into * @param ringbuf Ring buffer to insert the item into
* @param data - Pointer to data to insert. NULL is allowed if data_size is 0. * @param data Pointer to data to insert. NULL is allowed if data_size is 0.
* @param data_size - Size of data to insert. A value of 0 is allowed. * @param data_size Size of data to insert. A value of 0 is allowed.
* @param higher_prio_task_awoken - Value pointed to will be set to pdTRUE if the push woke up a higher * @param[out] higher_prio_task_awoken Value pointed to will be set to pdTRUE
* priority task. * if the push woke up a higher priority task.
* *
* @return pdTRUE if succeeded, pdFALSE when the ring buffer does not have space. * @return pdTRUE if succeeded, pdFALSE when the ring buffer does not have space.
*/ */
@ -120,14 +127,18 @@ BaseType_t xRingbufferSendFromISR(RingbufHandle_t ringbuf, void *data, size_t da
/** /**
* @brief Retrieve an item from the ring buffer * @brief Retrieve an item from the ring buffer
* *
* @note A call to vRingbufferReturnItem() is required after this to free up the data received. * @note A call to vRingbufferReturnItem() is required after this to free up
* the data received.
* *
* @param ringbuf - Ring buffer to retrieve the item from * @param ringbuf Ring buffer to retrieve the item from
* @param item_size - Pointer to a variable to which the size of the retrieved item will be written. * @param[out] item_size Pointer to a variable to which the size of the
* @param xTicksToWait - Ticks to wait for items in the ringbuffer. * retrieved item will be written.
* @param ticks_to_wait Ticks to wait for items in the ringbuffer.
* *
* @return Pointer to the retrieved item on success; *item_size filled with the length of the * @return
* item. NULL on timeout, *item_size is untouched in that case. * - pointer to the retrieved item on success; *item_size filled with
* the length of the item.
* - NULL on timeout, *item_size is untouched in that case.
*/ */
void *xRingbufferReceive(RingbufHandle_t ringbuf, size_t *item_size, TickType_t ticks_to_wait); void *xRingbufferReceive(RingbufHandle_t ringbuf, size_t *item_size, TickType_t ticks_to_wait);
@ -135,44 +146,58 @@ void *xRingbufferReceive(RingbufHandle_t ringbuf, size_t *item_size, TickType_t
/** /**
* @brief Retrieve an item from the ring buffer from an ISR * @brief Retrieve an item from the ring buffer from an ISR
* *
* @note A call to vRingbufferReturnItemFromISR() is required after this to free up the data received * @note A call to vRingbufferReturnItemFromISR() is required after this to
* free up the data received
* *
* @param ringbuf - Ring buffer to retrieve the item from * @param ringbuf Ring buffer to retrieve the item from
* @param item_size - Pointer to a variable to which the size of the retrieved item will be written. * @param[out] item_size Pointer to a variable to which the size of the
* retrieved item will be written.
* *
* @return Pointer to the retrieved item on success; *item_size filled with the length of the * @return
* item. NULL when the ringbuffer is empty, *item_size is untouched in that case. * - Pointer to the retrieved item on success; *item_size filled with
* the length of the item.
* - NULL when the ringbuffer is empty, *item_size is untouched in that case.
*/ */
void *xRingbufferReceiveFromISR(RingbufHandle_t ringbuf, size_t *item_size); void *xRingbufferReceiveFromISR(RingbufHandle_t ringbuf, size_t *item_size);
/** /**
* @brief Retrieve bytes from a ByteBuf type of ring buffer, specifying the maximum amount of bytes * @brief Retrieve bytes from a ByteBuf type of ring buffer,
* to return * specifying the maximum amount of bytes to return
* @note A call to vRingbufferReturnItem() is required after this to free up the data received.
* *
* @param ringbuf - Ring buffer to retrieve the item from * @note A call to vRingbufferReturnItem() is required after this to free up
* @param item_size - Pointer to a variable to which the size of the retrieved item will be written. * the data received.
* @param xTicksToWait - Ticks to wait for items in the ringbuffer.
* *
* @return Pointer to the retrieved item on success; *item_size filled with the length of the * @param ringbuf Ring buffer to retrieve the item from
* item. NULL on timeout, *item_size is untouched in that case. * @param[out] item_size Pointer to a variable to which the size
* of the retrieved item will be written.
* @param ticks_to_wait Ticks to wait for items in the ringbuffer.
* @param wanted_size Maximum number of bytes to return.
*
* @return
* - Pointer to the retrieved item on success; *item_size filled with
* the length of the item.
* - NULL on timeout, *item_size is untouched in that case.
*/ */
void *xRingbufferReceiveUpTo(RingbufHandle_t ringbuf, size_t *item_size, TickType_t ticks_to_wait, size_t wanted_size); void *xRingbufferReceiveUpTo(RingbufHandle_t ringbuf, size_t *item_size, TickType_t ticks_to_wait, size_t wanted_size);
/** /**
* @brief Retrieve bytes from a ByteBuf type of ring buffer, specifying the maximum amount of bytes * @brief Retrieve bytes from a ByteBuf type of ring buffer,
* to return. Call this from an ISR. * specifying the maximum amount of bytes to return. Call this from an ISR.
* *
* @note A call to vRingbufferReturnItemFromISR() is required after this to free up the data received * @note A call to vRingbufferReturnItemFromISR() is required after this
* to free up the data received.
* *
* @param ringbuf - Ring buffer to retrieve the item from * @param ringbuf Ring buffer to retrieve the item from
* @param item_size - Pointer to a variable to which the size of the retrieved item will be written. * @param[out] item_size Pointer to a variable to which the size of the
* retrieved item will be written.
* @param wanted_size Maximum number of bytes to return.
* *
* @return Pointer to the retrieved item on success; *item_size filled with the length of the * @return
* item. NULL when the ringbuffer is empty, *item_size is untouched in that case. * - Pointer to the retrieved item on success; *item_size filled with
* the length of the item.
* - NULL when the ringbuffer is empty, *item_size is untouched in that case.
*/ */
void *xRingbufferReceiveUpToFromISR(RingbufHandle_t ringbuf, size_t *item_size, size_t wanted_size); void *xRingbufferReceiveUpToFromISR(RingbufHandle_t ringbuf, size_t *item_size, size_t wanted_size);
@ -181,10 +206,8 @@ void *xRingbufferReceiveUpToFromISR(RingbufHandle_t ringbuf, size_t *item_size,
/** /**
* @brief Return a previously-retrieved item to the ringbuffer * @brief Return a previously-retrieved item to the ringbuffer
* *
* @param ringbuf - Ring buffer the item was retrieved from * @param ringbuf Ring buffer the item was retrieved from
* @param item - Item that was received earlier * @param item Item that was received earlier
*
* @return void
*/ */
void vRingbufferReturnItem(RingbufHandle_t ringbuf, void *item); void vRingbufferReturnItem(RingbufHandle_t ringbuf, void *item);
@ -193,34 +216,37 @@ void vRingbufferReturnItem(RingbufHandle_t ringbuf, void *item);
/** /**
* @brief Return a previously-retrieved item to the ringbuffer from an ISR * @brief Return a previously-retrieved item to the ringbuffer from an ISR
* *
* @param ringbuf - Ring buffer the item was retrieved from * @param ringbuf Ring buffer the item was retrieved from
* @param item - Item that was received earlier * @param item Item that was received earlier
* @param higher_prio_task_awoken - Value pointed to will be set to pdTRUE if the push woke up a higher * @param[out] higher_prio_task_awoken Value pointed to will be set to pdTRUE
* priority task. * if the push woke up a higher priority task.
*
* @return void
*/ */
void vRingbufferReturnItemFromISR(RingbufHandle_t ringbuf, void *item, BaseType_t *higher_prio_task_awoken); void vRingbufferReturnItemFromISR(RingbufHandle_t ringbuf, void *item, BaseType_t *higher_prio_task_awoken);
/** /**
* @brief Add the ringbuffer to a queue set. This specifically adds the semaphore that indicates * @brief Add the ringbuffer to a queue set.
* more space has become available in the ringbuffer.
* *
* @param ringbuf - Ring buffer to add to the queue set * This specifically adds the semaphore that indicates more space
* @param xQueueSet - Queue set to add the ringbuffer to * has become available in the ringbuffer.
* *
* @return pdTRUE on success, pdFALSE otherwise * @param ringbuf Ring buffer to add to the queue set
* @param xQueueSet Queue set to add the ringbuffer to
*
* @return
* - pdTRUE on success, pdFALSE otherwise
*/ */
BaseType_t xRingbufferAddToQueueSetRead(RingbufHandle_t ringbuf, QueueSetHandle_t xQueueSet); BaseType_t xRingbufferAddToQueueSetRead(RingbufHandle_t ringbuf, QueueSetHandle_t xQueueSet);
/** /**
* @brief Add the ringbuffer to a queue set. This specifically adds the semaphore that indicates * @brief Add the ringbuffer to a queue set.
* something has been written into the ringbuffer.
* *
* @param ringbuf - Ring buffer to add to the queue set * This specifically adds the semaphore that indicates something has been
* @param xQueueSet - Queue set to add the ringbuffer to * written into the ringbuffer.
*
* @param ringbuf Ring buffer to add to the queue set
* @param xQueueSet Queue set to add the ringbuffer to
* *
* @return pdTRUE on success, pdFALSE otherwise * @return pdTRUE on success, pdFALSE otherwise
*/ */
@ -228,11 +254,13 @@ BaseType_t xRingbufferAddToQueueSetWrite(RingbufHandle_t ringbuf, QueueSetHandle
/** /**
* @brief Remove the ringbuffer from a queue set. This specifically removes the semaphore that indicates * @brief Remove the ringbuffer from a queue set.
* more space has become available in the ringbuffer.
* *
* @param ringbuf - Ring buffer to remove from the queue set * This specifically removes the semaphore that indicates more space
* @param xQueueSet - Queue set to remove the ringbuffer from * has become available in the ringbuffer.
*
* @param ringbuf Ring buffer to remove from the queue set
* @param xQueueSet Queue set to remove the ringbuffer from
* *
* @return pdTRUE on success, pdFALSE otherwise * @return pdTRUE on success, pdFALSE otherwise
*/ */
@ -240,11 +268,13 @@ BaseType_t xRingbufferRemoveFromQueueSetRead(RingbufHandle_t ringbuf, QueueSetHa
/** /**
* @brief Remove the ringbuffer from a queue set. This specifically removes the semaphore that indicates * @brief Remove the ringbuffer from a queue set.
* something has been written to the ringbuffer.
* *
* @param ringbuf - Ring buffer to remove from the queue set * This specifically removes the semaphore that indicates something
* @param xQueueSet - Queue set to remove the ringbuffer from * has been written to the ringbuffer.
*
* @param ringbuf Ring buffer to remove from the queue set
* @param xQueueSet Queue set to remove the ringbuffer from
* *
* @return pdTRUE on success, pdFALSE otherwise * @return pdTRUE on success, pdFALSE otherwise
*/ */
@ -254,9 +284,7 @@ BaseType_t xRingbufferRemoveFromQueueSetWrite(RingbufHandle_t ringbuf, QueueSetH
/** /**
* @brief Debugging function to print the internal pointers in the ring buffer * @brief Debugging function to print the internal pointers in the ring buffer
* *
* @param ringbuf - Ring buffer to show * @param ringbuf Ring buffer to show
*
* @return void
*/ */
void xRingbufferPrintInfo(RingbufHandle_t ringbuf); void xRingbufferPrintInfo(RingbufHandle_t ringbuf);

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -117,24 +117,18 @@ or interrupt version of the queue send function should be used. */
*/ */
typedef void * TimerHandle_t; typedef void * TimerHandle_t;
/* /**
* Defines the prototype to which timer callback functions must conform. * Defines the prototype to which timer callback functions must conform.
*/ */
typedef void (*TimerCallbackFunction_t)( TimerHandle_t xTimer ); typedef void (*TimerCallbackFunction_t)( TimerHandle_t xTimer );
/* /**
* Defines the prototype to which functions used with the * Defines the prototype to which functions used with the
* xTimerPendFunctionCallFromISR() function must conform. * xTimerPendFunctionCallFromISR() function must conform.
*/ */
typedef void (*PendedFunction_t)( void *, uint32_t ); typedef void (*PendedFunction_t)( void *, uint32_t );
/** /**
* TimerHandle_t xTimerCreate( const char * const pcTimerName,
* TickType_t xTimerPeriodInTicks,
* UBaseType_t uxAutoReload,
* void * pvTimerID,
* TimerCallbackFunction_t pxCallbackFunction );
*
* Creates a new software timer instance, and returns a handle by which the * Creates a new software timer instance, and returns a handle by which the
* created software timer can be referenced. * created software timer can be referenced.
* *
@ -184,7 +178,7 @@ typedef void (*PendedFunction_t)( void *, uint32_t );
* structures, or the timer period was set to 0) then NULL is returned. * structures, or the timer period was set to 0) then NULL is returned.
* *
* Example usage: * Example usage:
* @verbatim * @code{c}
* #define NUM_TIMERS 5 * #define NUM_TIMERS 5
* *
* // An array to hold handles to the created timers. * // An array to hold handles to the created timers.
@ -263,7 +257,7 @@ typedef void (*PendedFunction_t)( void *, uint32_t );
* // Should not reach here. * // Should not reach here.
* for( ;; ); * for( ;; );
* } * }
* @endverbatim * @endcode
*/ */
#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) #if( configSUPPORT_DYNAMIC_ALLOCATION == 1 )
TimerHandle_t xTimerCreate( const char * const pcTimerName, TimerHandle_t xTimerCreate( const char * const pcTimerName,
@ -274,13 +268,6 @@ typedef void (*PendedFunction_t)( void *, uint32_t );
#endif #endif
/** /**
* TimerHandle_t xTimerCreateStatic(const char * const pcTimerName,
* TickType_t xTimerPeriodInTicks,
* UBaseType_t uxAutoReload,
* void * pvTimerID,
* TimerCallbackFunction_t pxCallbackFunction,
* StaticTimer_t *pxTimerBuffer );
*
* Creates a new software timer instance, and returns a handle by which the * Creates a new software timer instance, and returns a handle by which the
* created software timer can be referenced. * created software timer can be referenced.
* *
@ -332,7 +319,7 @@ typedef void (*PendedFunction_t)( void *, uint32_t );
* returned. If pxTimerBuffer was NULL then NULL is returned. * returned. If pxTimerBuffer was NULL then NULL is returned.
* *
* Example usage: * Example usage:
* @verbatim * @code{c}
* *
* // The buffer used to hold the software timer's data structure. * // The buffer used to hold the software timer's data structure.
* static StaticTimer_t xTimerBuffer; * static StaticTimer_t xTimerBuffer;
@ -393,7 +380,7 @@ typedef void (*PendedFunction_t)( void *, uint32_t );
* // Should not reach here. * // Should not reach here.
* for( ;; ); * for( ;; );
* } * }
* @endverbatim * @endcode
*/ */
#if( configSUPPORT_STATIC_ALLOCATION == 1 ) #if( configSUPPORT_STATIC_ALLOCATION == 1 )
TimerHandle_t xTimerCreateStatic( const char * const pcTimerName, TimerHandle_t xTimerCreateStatic( const char * const pcTimerName,
@ -405,8 +392,6 @@ typedef void (*PendedFunction_t)( void *, uint32_t );
#endif /* configSUPPORT_STATIC_ALLOCATION */ #endif /* configSUPPORT_STATIC_ALLOCATION */
/** /**
* void *pvTimerGetTimerID( TimerHandle_t xTimer );
*
* Returns the ID assigned to the timer. * Returns the ID assigned to the timer.
* *
* IDs are assigned to timers using the pvTimerID parameter of the call to * IDs are assigned to timers using the pvTimerID parameter of the call to
@ -427,8 +412,6 @@ typedef void (*PendedFunction_t)( void *, uint32_t );
void *pvTimerGetTimerID( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION; void *pvTimerGetTimerID( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION;
/** /**
* void vTimerSetTimerID( TimerHandle_t xTimer, void *pvNewID );
*
* Sets the ID assigned to the timer. * Sets the ID assigned to the timer.
* *
* IDs are assigned to timers using the pvTimerID parameter of the call to * IDs are assigned to timers using the pvTimerID parameter of the call to
@ -448,12 +431,12 @@ void *pvTimerGetTimerID( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION;
void vTimerSetTimerID( TimerHandle_t xTimer, void *pvNewID ) PRIVILEGED_FUNCTION; void vTimerSetTimerID( TimerHandle_t xTimer, void *pvNewID ) PRIVILEGED_FUNCTION;
/** /**
* BaseType_t xTimerIsTimerActive( TimerHandle_t xTimer );
*
* Queries a timer to see if it is active or dormant. * Queries a timer to see if it is active or dormant.
* *
* A timer will be dormant if: * A timer will be dormant if:
*
* 1) It has been created but not started, or * 1) It has been created but not started, or
*
* 2) It is an expired one-shot timer that has not been restarted. * 2) It is an expired one-shot timer that has not been restarted.
* *
* Timers are created in the dormant state. The xTimerStart(), xTimerReset(), * Timers are created in the dormant state. The xTimerStart(), xTimerReset(),
@ -467,7 +450,7 @@ void vTimerSetTimerID( TimerHandle_t xTimer, void *pvNewID ) PRIVILEGED_FUNCTION
* pdFALSE will be returned if the timer is active. * pdFALSE will be returned if the timer is active.
* *
* Example usage: * Example usage:
* @verbatim * @code{c}
* // This function assumes xTimer has already been created. * // This function assumes xTimer has already been created.
* void vAFunction( TimerHandle_t xTimer ) * void vAFunction( TimerHandle_t xTimer )
* { * {
@ -480,13 +463,11 @@ void vTimerSetTimerID( TimerHandle_t xTimer, void *pvNewID ) PRIVILEGED_FUNCTION
* // xTimer is not active, do something else. * // xTimer is not active, do something else.
* } * }
* } * }
* @endverbatim * @endcode
*/ */
BaseType_t xTimerIsTimerActive( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION; BaseType_t xTimerIsTimerActive( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION;
/** /**
* TaskHandle_t xTimerGetTimerDaemonTaskHandle( void );
*
* xTimerGetTimerDaemonTaskHandle() is only available if * xTimerGetTimerDaemonTaskHandle() is only available if
* INCLUDE_xTimerGetTimerDaemonTaskHandle is set to 1 in FreeRTOSConfig.h. * INCLUDE_xTimerGetTimerDaemonTaskHandle is set to 1 in FreeRTOSConfig.h.
* *
@ -496,8 +477,6 @@ BaseType_t xTimerIsTimerActive( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION;
TaskHandle_t xTimerGetTimerDaemonTaskHandle( void ); TaskHandle_t xTimerGetTimerDaemonTaskHandle( void );
/** /**
* TickType_t xTimerGetPeriod( TimerHandle_t xTimer );
*
* Returns the period of a timer. * Returns the period of a timer.
* *
* @param xTimer The handle of the timer being queried. * @param xTimer The handle of the timer being queried.
@ -507,8 +486,6 @@ TaskHandle_t xTimerGetTimerDaemonTaskHandle( void );
TickType_t xTimerGetPeriod( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION; TickType_t xTimerGetPeriod( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION;
/** /**
* TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer );
*
* Returns the time in ticks at which the timer will expire. If this is less * Returns the time in ticks at which the timer will expire. If this is less
* than the current tick count then the expiry time has overflowed from the * than the current tick count then the expiry time has overflowed from the
* current time. * current time.
@ -522,8 +499,6 @@ TickType_t xTimerGetPeriod( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION;
TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION; TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION;
/** /**
* BaseType_t xTimerStart( TimerHandle_t xTimer, TickType_t xTicksToWait );
*
* Timer functionality is provided by a timer service/daemon task. Many of the * Timer functionality is provided by a timer service/daemon task. Many of the
* public FreeRTOS timer API functions send commands to the timer service task * public FreeRTOS timer API functions send commands to the timer service task
* through a queue called the timer command queue. The timer command queue is * through a queue called the timer command queue. The timer command queue is
@ -574,8 +549,6 @@ TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION;
#define xTimerStart( xTimer, xTicksToWait ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_START, ( xTaskGetTickCount() ), NULL, ( xTicksToWait ) ) #define xTimerStart( xTimer, xTicksToWait ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_START, ( xTaskGetTickCount() ), NULL, ( xTicksToWait ) )
/** /**
* BaseType_t xTimerStop( TimerHandle_t xTimer, TickType_t xTicksToWait );
*
* Timer functionality is provided by a timer service/daemon task. Many of the * Timer functionality is provided by a timer service/daemon task. Many of the
* public FreeRTOS timer API functions send commands to the timer service task * public FreeRTOS timer API functions send commands to the timer service task
* through a queue called the timer command queue. The timer command queue is * through a queue called the timer command queue. The timer command queue is
@ -616,10 +589,6 @@ TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION;
#define xTimerStop( xTimer, xTicksToWait ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_STOP, 0U, NULL, ( xTicksToWait ) ) #define xTimerStop( xTimer, xTicksToWait ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_STOP, 0U, NULL, ( xTicksToWait ) )
/** /**
* BaseType_t xTimerChangePeriod( TimerHandle_t xTimer,
* TickType_t xNewPeriod,
* TickType_t xTicksToWait );
*
* Timer functionality is provided by a timer service/daemon task. Many of the * Timer functionality is provided by a timer service/daemon task. Many of the
* public FreeRTOS timer API functions send commands to the timer service task * public FreeRTOS timer API functions send commands to the timer service task
* through a queue called the timer command queue. The timer command queue is * through a queue called the timer command queue. The timer command queue is
@ -661,7 +630,7 @@ TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION;
* configTIMER_TASK_PRIORITY configuration constant. * configTIMER_TASK_PRIORITY configuration constant.
* *
* Example usage: * Example usage:
* @verbatim * @code{c}
* // This function assumes xTimer has already been created. If the timer * // This function assumes xTimer has already been created. If the timer
* // referenced by xTimer is already active when it is called, then the timer * // referenced by xTimer is already active when it is called, then the timer
* // is deleted. If the timer referenced by xTimer is not active when it is * // is deleted. If the timer referenced by xTimer is not active when it is
@ -691,13 +660,11 @@ TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION;
* } * }
* } * }
* } * }
* @endverbatim * @endcode
*/ */
#define xTimerChangePeriod( xTimer, xNewPeriod, xTicksToWait ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_CHANGE_PERIOD, ( xNewPeriod ), NULL, ( xTicksToWait ) ) #define xTimerChangePeriod( xTimer, xNewPeriod, xTicksToWait ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_CHANGE_PERIOD, ( xNewPeriod ), NULL, ( xTicksToWait ) )
/** /**
* BaseType_t xTimerDelete( TimerHandle_t xTimer, TickType_t xTicksToWait );
*
* Timer functionality is provided by a timer service/daemon task. Many of the * Timer functionality is provided by a timer service/daemon task. Many of the
* public FreeRTOS timer API functions send commands to the timer service task * public FreeRTOS timer API functions send commands to the timer service task
* through a queue called the timer command queue. The timer command queue is * through a queue called the timer command queue. The timer command queue is
@ -734,8 +701,6 @@ TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION;
#define xTimerDelete( xTimer, xTicksToWait ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_DELETE, 0U, NULL, ( xTicksToWait ) ) #define xTimerDelete( xTimer, xTicksToWait ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_DELETE, 0U, NULL, ( xTicksToWait ) )
/** /**
* BaseType_t xTimerReset( TimerHandle_t xTimer, TickType_t xTicksToWait );
*
* Timer functionality is provided by a timer service/daemon task. Many of the * Timer functionality is provided by a timer service/daemon task. Many of the
* public FreeRTOS timer API functions send commands to the timer service task * public FreeRTOS timer API functions send commands to the timer service task
* through a queue called the timer command queue. The timer command queue is * through a queue called the timer command queue. The timer command queue is
@ -781,7 +746,7 @@ TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION;
* configuration constant. * configuration constant.
* *
* Example usage: * Example usage:
* @verbatim * @code{c}
* // When a key is pressed, an LCD back-light is switched on. If 5 seconds pass * // When a key is pressed, an LCD back-light is switched on. If 5 seconds pass
* // without a key being pressed, then the LCD back-light is switched off. In * // without a key being pressed, then the LCD back-light is switched off. In
* // this case, the timer is a one-shot timer. * // this case, the timer is a one-shot timer.
@ -853,14 +818,11 @@ TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION;
* // Should not reach here. * // Should not reach here.
* for( ;; ); * for( ;; );
* } * }
* @endverbatim * @endcode
*/ */
#define xTimerReset( xTimer, xTicksToWait ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_RESET, ( xTaskGetTickCount() ), NULL, ( xTicksToWait ) ) #define xTimerReset( xTimer, xTicksToWait ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_RESET, ( xTaskGetTickCount() ), NULL, ( xTicksToWait ) )
/** /**
* BaseType_t xTimerStartFromISR( TimerHandle_t xTimer,
* BaseType_t *pxHigherPriorityTaskWoken );
*
* A version of xTimerStart() that can be called from an interrupt service * A version of xTimerStart() that can be called from an interrupt service
* routine. * routine.
* *
@ -888,7 +850,7 @@ TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION;
* configuration constant. * configuration constant.
* *
* Example usage: * Example usage:
* @verbatim * @code{c}
* // This scenario assumes xBacklightTimer has already been created. When a * // This scenario assumes xBacklightTimer has already been created. When a
* // key is pressed, an LCD back-light is switched on. If 5 seconds pass * // key is pressed, an LCD back-light is switched on. If 5 seconds pass
* // without a key being pressed, then the LCD back-light is switched off. In * // without a key being pressed, then the LCD back-light is switched off. In
@ -939,14 +901,11 @@ TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION;
* // depends on the FreeRTOS port being used). * // depends on the FreeRTOS port being used).
* } * }
* } * }
* @endverbatim * @endcode
*/ */
#define xTimerStartFromISR( xTimer, pxHigherPriorityTaskWoken ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_START_FROM_ISR, ( xTaskGetTickCountFromISR() ), ( pxHigherPriorityTaskWoken ), 0U ) #define xTimerStartFromISR( xTimer, pxHigherPriorityTaskWoken ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_START_FROM_ISR, ( xTaskGetTickCountFromISR() ), ( pxHigherPriorityTaskWoken ), 0U )
/** /**
* BaseType_t xTimerStopFromISR( TimerHandle_t xTimer,
* BaseType_t *pxHigherPriorityTaskWoken );
*
* A version of xTimerStop() that can be called from an interrupt service * A version of xTimerStop() that can be called from an interrupt service
* routine. * routine.
* *
@ -972,7 +931,7 @@ TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION;
* priority is set by the configTIMER_TASK_PRIORITY configuration constant. * priority is set by the configTIMER_TASK_PRIORITY configuration constant.
* *
* Example usage: * Example usage:
* @verbatim * @code{c}
* // This scenario assumes xTimer has already been created and started. When * // This scenario assumes xTimer has already been created and started. When
* // an interrupt occurs, the timer should be simply stopped. * // an interrupt occurs, the timer should be simply stopped.
* *
@ -1002,15 +961,11 @@ TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION;
* // depends on the FreeRTOS port being used). * // depends on the FreeRTOS port being used).
* } * }
* } * }
* @endverbatim * @endcode
*/ */
#define xTimerStopFromISR( xTimer, pxHigherPriorityTaskWoken ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_STOP_FROM_ISR, 0, ( pxHigherPriorityTaskWoken ), 0U ) #define xTimerStopFromISR( xTimer, pxHigherPriorityTaskWoken ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_STOP_FROM_ISR, 0, ( pxHigherPriorityTaskWoken ), 0U )
/** /**
* BaseType_t xTimerChangePeriodFromISR( TimerHandle_t xTimer,
* TickType_t xNewPeriod,
* BaseType_t *pxHigherPriorityTaskWoken );
*
* A version of xTimerChangePeriod() that can be called from an interrupt * A version of xTimerChangePeriod() that can be called from an interrupt
* service routine. * service routine.
* *
@ -1045,7 +1000,7 @@ TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION;
* priority is set by the configTIMER_TASK_PRIORITY configuration constant. * priority is set by the configTIMER_TASK_PRIORITY configuration constant.
* *
* Example usage: * Example usage:
* @verbatim * @code{c}
* // This scenario assumes xTimer has already been created and started. When * // This scenario assumes xTimer has already been created and started. When
* // an interrupt occurs, the period of xTimer should be changed to 500ms. * // an interrupt occurs, the period of xTimer should be changed to 500ms.
* *
@ -1075,14 +1030,11 @@ TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION;
* // depends on the FreeRTOS port being used). * // depends on the FreeRTOS port being used).
* } * }
* } * }
* @endverbatim * @endcode
*/ */
#define xTimerChangePeriodFromISR( xTimer, xNewPeriod, pxHigherPriorityTaskWoken ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_CHANGE_PERIOD_FROM_ISR, ( xNewPeriod ), ( pxHigherPriorityTaskWoken ), 0U ) #define xTimerChangePeriodFromISR( xTimer, xNewPeriod, pxHigherPriorityTaskWoken ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_CHANGE_PERIOD_FROM_ISR, ( xNewPeriod ), ( pxHigherPriorityTaskWoken ), 0U )
/** /**
* BaseType_t xTimerResetFromISR( TimerHandle_t xTimer,
* BaseType_t *pxHigherPriorityTaskWoken );
*
* A version of xTimerReset() that can be called from an interrupt service * A version of xTimerReset() that can be called from an interrupt service
* routine. * routine.
* *
@ -1110,7 +1062,7 @@ TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION;
* task priority is set by the configTIMER_TASK_PRIORITY configuration constant. * task priority is set by the configTIMER_TASK_PRIORITY configuration constant.
* *
* Example usage: * Example usage:
* @verbatim * @code{c}
* // This scenario assumes xBacklightTimer has already been created. When a * // This scenario assumes xBacklightTimer has already been created. When a
* // key is pressed, an LCD back-light is switched on. If 5 seconds pass * // key is pressed, an LCD back-light is switched on. If 5 seconds pass
* // without a key being pressed, then the LCD back-light is switched off. In * // without a key being pressed, then the LCD back-light is switched off. In
@ -1161,18 +1113,12 @@ TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION;
* // depends on the FreeRTOS port being used). * // depends on the FreeRTOS port being used).
* } * }
* } * }
* @endverbatim * @endcode
*/ */
#define xTimerResetFromISR( xTimer, pxHigherPriorityTaskWoken ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_RESET_FROM_ISR, ( xTaskGetTickCountFromISR() ), ( pxHigherPriorityTaskWoken ), 0U ) #define xTimerResetFromISR( xTimer, pxHigherPriorityTaskWoken ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_RESET_FROM_ISR, ( xTaskGetTickCountFromISR() ), ( pxHigherPriorityTaskWoken ), 0U )
/** /**
* BaseType_t xTimerPendFunctionCallFromISR( PendedFunction_t xFunctionToPend,
* void *pvParameter1,
* uint32_t ulParameter2,
* BaseType_t *pxHigherPriorityTaskWoken );
*
*
* Used from application interrupt service routines to defer the execution of a * Used from application interrupt service routines to defer the execution of a
* function to the RTOS daemon task (the timer service task, hence this function * function to the RTOS daemon task (the timer service task, hence this function
* is implemented in timers.c and is prefixed with 'Timer'). * is implemented in timers.c and is prefixed with 'Timer').
@ -1214,7 +1160,7 @@ TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION;
* timer daemon task, otherwise pdFALSE is returned. * timer daemon task, otherwise pdFALSE is returned.
* *
* Example usage: * Example usage:
* @verbatim * @code{c}
* *
* // The callback function that will execute in the context of the daemon task. * // The callback function that will execute in the context of the daemon task.
* // Note callback functions must all use this same prototype. * // Note callback functions must all use this same prototype.
@ -1252,17 +1198,11 @@ TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION;
* portYIELD_FROM_ISR( xHigherPriorityTaskWoken ); * portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
* *
* } * }
* @endverbatim * @endcode
*/ */
BaseType_t xTimerPendFunctionCallFromISR( PendedFunction_t xFunctionToPend, void *pvParameter1, uint32_t ulParameter2, BaseType_t *pxHigherPriorityTaskWoken ); BaseType_t xTimerPendFunctionCallFromISR( PendedFunction_t xFunctionToPend, void *pvParameter1, uint32_t ulParameter2, BaseType_t *pxHigherPriorityTaskWoken );
/** /**
* BaseType_t xTimerPendFunctionCall( PendedFunction_t xFunctionToPend,
* void *pvParameter1,
* uint32_t ulParameter2,
* TickType_t xTicksToWait );
*
*
* Used to defer the execution of a function to the RTOS daemon task (the timer * Used to defer the execution of a function to the RTOS daemon task (the timer
* service task, hence this function is implemented in timers.c and is prefixed * service task, hence this function is implemented in timers.c and is prefixed
* with 'Timer'). * with 'Timer').
@ -1291,8 +1231,6 @@ BaseType_t xTimerPendFunctionCallFromISR( PendedFunction_t xFunctionToPend, void
BaseType_t xTimerPendFunctionCall( PendedFunction_t xFunctionToPend, void *pvParameter1, uint32_t ulParameter2, TickType_t xTicksToWait ); BaseType_t xTimerPendFunctionCall( PendedFunction_t xFunctionToPend, void *pvParameter1, uint32_t ulParameter2, TickType_t xTicksToWait );
/** /**
* const char * const pcTimerGetTimerName( TimerHandle_t xTimer );
*
* Returns the name that was assigned to a timer when the timer was created. * Returns the name that was assigned to a timer when the timer was created.
* *
* @param xTimer The handle of the timer being queried. * @param xTimer The handle of the timer being queried.
@ -1301,6 +1239,7 @@ BaseType_t xTimerPendFunctionCall( PendedFunction_t xFunctionToPend, void *pvPar
*/ */
const char * pcTimerGetTimerName( TimerHandle_t xTimer ); /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ const char * pcTimerGetTimerName( TimerHandle_t xTimer ); /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
/** @cond */
/* /*
* Functions beyond this part are not part of the public API and are intended * Functions beyond this part are not part of the public API and are intended
* for use by the kernel only. * for use by the kernel only.
@ -1308,6 +1247,8 @@ const char * pcTimerGetTimerName( TimerHandle_t xTimer ); /*lint !e971 Unqualifi
BaseType_t xTimerCreateTimerTask( void ) PRIVILEGED_FUNCTION; BaseType_t xTimerCreateTimerTask( void ) PRIVILEGED_FUNCTION;
BaseType_t xTimerGenericCommand( TimerHandle_t xTimer, const BaseType_t xCommandID, const TickType_t xOptionalValue, BaseType_t * const pxHigherPriorityTaskWoken, const TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; BaseType_t xTimerGenericCommand( TimerHandle_t xTimer, const BaseType_t xCommandID, const TickType_t xOptionalValue, BaseType_t * const pxHigherPriorityTaskWoken, const TickType_t xTicksToWait ) PRIVILEGED_FUNCTION;
/** @endcond */
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View file

@ -541,6 +541,12 @@ static portTASK_FUNCTION_PROTO( prvIdleTask, pvParameters );
#endif #endif
//Function to call the Thread Local Storage Pointer Deletion Callbacks. Will be
//called during task deletion before prvDeleteTCB is called.
#if ( configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 ) && ( configTHREAD_LOCAL_STORAGE_DELETE_CALLBACKS )
static void prvDeleteTLS( TCB_t *pxTCB );
#endif
/* /*
* Used only by the idle task. This checks to see if anything has been placed * Used only by the idle task. This checks to see if anything has been placed
* in the list of tasks waiting to be deleted. If so the task is cleaned up * in the list of tasks waiting to be deleted. If so the task is cleaned up
@ -1201,19 +1207,25 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB, TaskFunction_t pxTaskCode
/*-----------------------------------------------------------*/ /*-----------------------------------------------------------*/
#if ( INCLUDE_vTaskDelete == 1 ) #if ( INCLUDE_vTaskDelete == 1 )
void vTaskDelete( TaskHandle_t xTaskToDelete ) void vTaskDelete( TaskHandle_t xTaskToDelete )
{ {
//The following vTaskDelete() is backported from FreeRTOS v9.0.0 and modified for SMP.
//v9.0.0 vTaskDelete() will immediately free task memory if the task being deleted is
//NOT currently running and not pinned to the other core. Otherwise, freeing of task memory
//will still be delegated to the Idle Task.
TCB_t *pxTCB; TCB_t *pxTCB;
int core = xPortGetCoreID(); //Current core
UBaseType_t free_now; //Flag to indicate if task memory can be freed immediately
taskENTER_CRITICAL(&xTaskQueueMutex); taskENTER_CRITICAL(&xTaskQueueMutex);
{ {
/* If null is passed in here then it is the calling task that is /* If null is passed in here then it is the calling task that is
being deleted. */ being deleted. */
pxTCB = prvGetTCBFromHandle( xTaskToDelete ); pxTCB = prvGetTCBFromHandle( xTaskToDelete );
/* Remove task from the ready list and place in the termination list. /* Remove task from the ready list. */
This will stop the task from be scheduled. The idle task will check
the termination list and free up any memory allocated by the
scheduler for the TCB and stack. */
if( uxListRemove( &( pxTCB->xGenericListItem ) ) == ( UBaseType_t ) 0 ) if( uxListRemove( &( pxTCB->xGenericListItem ) ) == ( UBaseType_t ) 0 )
{ {
taskRESET_READY_PRIORITY( pxTCB->uxPriority ); taskRESET_READY_PRIORITY( pxTCB->uxPriority );
@ -1233,6 +1245,22 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB, TaskFunction_t pxTaskCode
mtCOVERAGE_TEST_MARKER(); mtCOVERAGE_TEST_MARKER();
} }
/* Increment the uxTaskNumber also so kernel aware debuggers can
detect that the task lists need re-generating. This is done before
portPRE_TASK_DELETE_HOOK() as in the Windows port that macro will
not return. */
uxTaskNumber++;
//If task to be deleted is currently running on either core or is pinned to the other core. Let Idle free memory
if( pxTCB == pxCurrentTCB[ core ] ||
(portNUM_PROCESSORS > 1 && pxTCB == pxCurrentTCB[ !core ]) ||
(portNUM_PROCESSORS > 1 && pxTCB->xCoreID == (!core)) )
{
/* Deleting a currently running task. This cannot complete
within the task itself, as a context switch to another task is
required. Place the task in the termination list. The idle task
will check the termination list and free up any memory allocated
by the scheduler for the TCB and stack of the deleted task. */
vListInsertEnd( &xTasksWaitingTermination, &( pxTCB->xGenericListItem ) ); vListInsertEnd( &xTasksWaitingTermination, &( pxTCB->xGenericListItem ) );
/* Increment the ucTasksDeleted variable so the idle task knows /* Increment the ucTasksDeleted variable so the idle task knows
@ -1240,22 +1268,44 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB, TaskFunction_t pxTaskCode
check the xTasksWaitingTermination list. */ check the xTasksWaitingTermination list. */
++uxTasksDeleted; ++uxTasksDeleted;
/* Increment the uxTaskNumberVariable also so kernel aware debuggers /* The pre-delete hook is primarily for the Windows simulator,
can detect that the task lists need re-generating. */ in which Windows specific clean up operations are performed,
uxTaskNumber++; after which it is not possible to yield away from this task -
hence xYieldPending is used to latch that a context switch is
required. */
portPRE_TASK_DELETE_HOOK( pxTCB, &xYieldPending );
free_now = pdFALSE; //Let Idle Task free task memory
}
else //Task is not currently running and not pinned to the other core
{
--uxCurrentNumberOfTasks;
/* Reset the next expected unblock time in case it referred to
the task that has just been deleted. */
prvResetNextTaskUnblockTime();
free_now = pdTRUE; //Set flag to free task memory immediately
}
traceTASK_DELETE( pxTCB ); traceTASK_DELETE( pxTCB );
} }
taskEXIT_CRITICAL(&xTaskQueueMutex); taskEXIT_CRITICAL(&xTaskQueueMutex);
if(free_now == pdTRUE){ //Free task memory. Outside critical section due to deletion callbacks
#if ( configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 ) && ( configTHREAD_LOCAL_STORAGE_DELETE_CALLBACKS )
prvDeleteTLS( pxTCB ); //Run deletion callbacks before deleting TCB
#endif
prvDeleteTCB( pxTCB ); //Must only be called after del cb
}
/* Force a reschedule if it is the currently running task that has just /* Force a reschedule if it is the currently running task that has just
been deleted. */ been deleted. */
if( xSchedulerRunning != pdFALSE ) if( xSchedulerRunning != pdFALSE )
{ {
//No mux; no harm done if this misfires. The deleted task won't get scheduled anyway. //No mux; no harm done if this misfires. The deleted task won't get scheduled anyway.
if( pxTCB == pxCurrentTCB[ xPortGetCoreID() ] ) if( pxTCB == pxCurrentTCB[ core ] ) //If task was currently running on this core
{ {
configASSERT( uxSchedulerSuspended[ xPortGetCoreID() ] == 0 ); configASSERT( uxSchedulerSuspended[ core ] == 0 );
/* The pre-delete hook is primarily for the Windows simulator, /* The pre-delete hook is primarily for the Windows simulator,
in which Windows specific clean up operations are performed, in which Windows specific clean up operations are performed,
@ -1265,20 +1315,14 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB, TaskFunction_t pxTaskCode
portPRE_TASK_DELETE_HOOK( pxTCB, &xYieldPending[xPortGetCoreID()] ); portPRE_TASK_DELETE_HOOK( pxTCB, &xYieldPending[xPortGetCoreID()] );
portYIELD_WITHIN_API(); portYIELD_WITHIN_API();
} }
else if ( portNUM_PROCESSORS > 1 && pxTCB == pxCurrentTCB[ !xPortGetCoreID() ] ) else if ( portNUM_PROCESSORS > 1 && pxTCB == pxCurrentTCB[ !core] ) //If task was currently running on the other core
{ {
/* if task is running on the other CPU, force a yield on that CPU to take it off */ /* if task is running on the other CPU, force a yield on that CPU to take it off */
vPortYieldOtherCore( !xPortGetCoreID() ); vPortYieldOtherCore( !core );
} }
else else
{ {
/* Reset the next expected unblock time in case it referred to mtCOVERAGE_TEST_MARKER();
the task that has just been deleted. */
taskENTER_CRITICAL(&xTaskQueueMutex);
{
prvResetNextTaskUnblockTime();
}
taskEXIT_CRITICAL(&xTaskQueueMutex);
} }
} }
} }
@ -3583,52 +3627,48 @@ static void prvCheckTasksWaitingTermination( void )
#if ( INCLUDE_vTaskDelete == 1 ) #if ( INCLUDE_vTaskDelete == 1 )
{ {
BaseType_t xListIsEmpty; BaseType_t xListIsEmpty;
int core = xPortGetCoreID();
/* ucTasksDeleted is used to prevent vTaskSuspendAll() being called /* ucTasksDeleted is used to prevent vTaskSuspendAll() being called
too often in the idle task. */ too often in the idle task. */
while(uxTasksDeleted > ( UBaseType_t ) 0U ) while(uxTasksDeleted > ( UBaseType_t ) 0U )
{ {
TCB_t *pxTCB = NULL; TCB_t *pxTCB = NULL;
taskENTER_CRITICAL(&xTaskQueueMutex); taskENTER_CRITICAL(&xTaskQueueMutex);
{ {
xListIsEmpty = listLIST_IS_EMPTY( &xTasksWaitingTermination ); xListIsEmpty = listLIST_IS_EMPTY( &xTasksWaitingTermination );
}
if( xListIsEmpty == pdFALSE ) if( xListIsEmpty == pdFALSE )
{ {
{
pxTCB = ( TCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( ( &xTasksWaitingTermination ) );
/* We only want to kill tasks that ran on this core because e.g. _xt_coproc_release needs to /* We only want to kill tasks that ran on this core because e.g. _xt_coproc_release needs to
be called on the core the process is pinned on, if any */ be called on the core the process is pinned on, if any */
if( pxTCB->xCoreID == tskNO_AFFINITY || pxTCB->xCoreID == xPortGetCoreID()) { ListItem_t *target = listGET_HEAD_ENTRY(&xTasksWaitingTermination);
( void ) uxListRemove( &( pxTCB->xGenericListItem ) ); for( ; target != listGET_END_MARKER(&xTasksWaitingTermination); target = listGET_NEXT(target) ){
--uxCurrentNumberOfTasks; int coreid = (( TCB_t * )listGET_LIST_ITEM_OWNER(target))->xCoreID;
--uxTasksDeleted; if(coreid == core || coreid == tskNO_AFFINITY){ //Find first item not pinned to other core
} else { pxTCB = ( TCB_t * )listGET_LIST_ITEM_OWNER(target);
/* Need to wait until the idle task on the other processor kills that task first. */
taskEXIT_CRITICAL(&xTaskQueueMutex);
break; break;
} }
} }
if(pxTCB != NULL){
( void ) uxListRemove( target ); //Remove list item from list
--uxCurrentNumberOfTasks;
--uxTasksDeleted;
} }
taskEXIT_CRITICAL(&xTaskQueueMutex); }
}
taskEXIT_CRITICAL(&xTaskQueueMutex); //Need to call deletion callbacks outside critical section
if (pxTCB != NULL) { if (pxTCB != NULL) { //Call deletion callbacks and free TCB memory
#if ( configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 ) && ( configTHREAD_LOCAL_STORAGE_DELETE_CALLBACKS ) #if ( configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 ) && ( configTHREAD_LOCAL_STORAGE_DELETE_CALLBACKS )
int x; prvDeleteTLS( pxTCB );
for( x = 0; x < ( UBaseType_t ) configNUM_THREAD_LOCAL_STORAGE_POINTERS; x++ )
{
if (pxTCB->pvThreadLocalStoragePointersDelCallback[ x ] != NULL)
{
pxTCB->pvThreadLocalStoragePointersDelCallback[ x ](x, pxTCB->pvThreadLocalStoragePointers[ x ]);
}
}
#endif #endif
prvDeleteTCB( pxTCB ); prvDeleteTCB( pxTCB );
} }
else else
{ {
mtCOVERAGE_TEST_MARKER(); mtCOVERAGE_TEST_MARKER();
break; //No TCB found that could be freed by this core, break out of loop
} }
} }
} }
@ -3831,7 +3871,6 @@ BaseType_t xTaskGetAffinity( TaskHandle_t xTask )
#if ( INCLUDE_vTaskDelete == 1 ) #if ( INCLUDE_vTaskDelete == 1 )
static void prvDeleteTCB( TCB_t *pxTCB ) static void prvDeleteTCB( TCB_t *pxTCB )
{ {
/* Free up the memory allocated by the scheduler for the task. It is up /* Free up the memory allocated by the scheduler for the task. It is up
@ -3886,6 +3925,23 @@ BaseType_t xTaskGetAffinity( TaskHandle_t xTask )
#endif /* INCLUDE_vTaskDelete */ #endif /* INCLUDE_vTaskDelete */
/*-----------------------------------------------------------*/ /*-----------------------------------------------------------*/
#if ( configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 ) && ( configTHREAD_LOCAL_STORAGE_DELETE_CALLBACKS )
static void prvDeleteTLS( TCB_t *pxTCB )
{
configASSERT( pxTCB );
for( int x = 0; x < ( UBaseType_t ) configNUM_THREAD_LOCAL_STORAGE_POINTERS; x++ )
{
if (pxTCB->pvThreadLocalStoragePointersDelCallback[ x ] != NULL) //If del cb is set
{
pxTCB->pvThreadLocalStoragePointersDelCallback[ x ](x, pxTCB->pvThreadLocalStoragePointers[ x ]); //Call del cb
}
}
}
#endif /* ( configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 ) && ( configTHREAD_LOCAL_STORAGE_DELETE_CALLBACKS ) */
/*-----------------------------------------------------------*/
static void prvResetNextTaskUnblockTime( void ) static void prvResetNextTaskUnblockTime( void )
{ {
TCB_t *pxTCB; TCB_t *pxTCB;

View file

@ -1,26 +1,66 @@
/*
* Test backported deletion behavior by creating tasks of various affinities and
* check if the task memory is freed immediately under the correct conditions.
*
* The behavior of vTaskDelete() has been backported form FreeRTOS v9.0.0. This
* results in the immediate freeing of task memory and the immediate execution
* of deletion callbacks under the following conditions...
* - When deleting a task that is not currently running on either core
* - When deleting a task that is pinned to the same core (with respect to
* the core that calls vTaskDelete()
*
* If the two conditions are not met, freeing of task memory and execution of
* deletion callbacks will still be carried out by the Idle Task.
*/
#include <stdio.h> #include <stdio.h>
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
#include "freertos/task.h" #include "freertos/task.h"
#include "freertos/semphr.h" #include "esp_heap_caps.h"
#include "freertos/queue.h"
#include "freertos/event_groups.h"
#include "unity.h" #include "unity.h"
static void task_delete_self(void *param) #define NO_OF_TSKS 3
#define DELAY_TICKS 2
#define HEAP_CAPS (MALLOC_CAP_INTERNAL|MALLOC_CAP_DEFAULT)
static void tsk_self_del(void *param)
{ {
printf("Task %p running on core %d. Deleting shortly...\n", xTaskGetCurrentTaskHandle(), xPortGetCoreID()); vTaskDelete(NULL); //Deleting self means deleting currently running task
vTaskDelay(5); }
vTaskDelete(NULL);
static void tsk_extern_del(void *param)
{
vTaskDelay(portMAX_DELAY); //Await external deletion
} }
TEST_CASE("FreeRTOS Delete Tasks", "[freertos]") TEST_CASE("FreeRTOS Delete Tasks", "[freertos]")
{ {
/* -------------- Test vTaskDelete() on currently running tasks ----------------*/
uint32_t before_count = uxTaskGetNumberOfTasks(); uint32_t before_count = uxTaskGetNumberOfTasks();
uint32_t before_heap = heap_caps_get_free_size(HEAP_CAPS);
xTaskCreatePinnedToCore(task_delete_self, "tsk_self_a", 4096, NULL, configMAX_PRIORITIES - 1, NULL, 0); for(int i = 0; i < portNUM_PROCESSORS; i++){
xTaskCreatePinnedToCore(task_delete_self, "tsk_self_a", 4096, NULL, configMAX_PRIORITIES - 1, NULL, 0); for(int j = 0; j < NO_OF_TSKS; j++){
TEST_ASSERT_EQUAL(before_count + 2, uxTaskGetNumberOfTasks()); TEST_ASSERT_EQUAL(pdTRUE, xTaskCreatePinnedToCore(tsk_self_del, "tsk_self", 1024, NULL, configMAX_PRIORITIES - 1, NULL, i));
vTaskDelay(200 / portTICK_PERIOD_MS); }
}
vTaskDelay(DELAY_TICKS); //Minimal delay to see if Idle task cleans up all tasks awaiting deletion in a single tick
TEST_ASSERT_EQUAL(before_count, uxTaskGetNumberOfTasks()); TEST_ASSERT_EQUAL(before_count, uxTaskGetNumberOfTasks());
TEST_ASSERT_EQUAL(before_heap, heap_caps_get_free_size(HEAP_CAPS));
/* ------------- Test vTaskDelete() on not currently running tasks ------------ */
TaskHandle_t handles[NO_OF_TSKS];
before_heap = heap_caps_get_free_size(HEAP_CAPS);
//Create task pinned to the same core that will not run during task deletion
for(int j = 0 ; j < NO_OF_TSKS; j++){
TEST_ASSERT_EQUAL(pdTRUE, xTaskCreatePinnedToCore(tsk_extern_del, "tsk_extern", 4096, NULL, configMAX_PRIORITIES - 1, &handles[j], xPortGetCoreID()));
}
TEST_ASSERT_NOT_EQUAL(before_heap, heap_caps_get_free_size(HEAP_CAPS)); //Check tasks have been created
//Delete the tasks, memory should be freed immediately
for(int j = 0; j < NO_OF_TSKS; j++){
vTaskDelete(handles[j]);
}
TEST_ASSERT_EQUAL(before_heap, heap_caps_get_free_size(HEAP_CAPS));
} }

View file

@ -373,11 +373,11 @@ static IRAM_ATTR __attribute__((noinline)) void *trace_realloc(void *p, size_t s
record_free(p, callers); record_free(p, callers);
} }
heap_trace_record_t rec = { heap_trace_record_t rec = {
.address = p, .address = r,
.ccount = ccount, .ccount = ccount,
.size = size, .size = size,
}; };
memcpy(rec.alloced_by, callers, sizeof(heap_trace_record_t) * STACK_DEPTH); memcpy(rec.alloced_by, callers, sizeof(void *) * STACK_DEPTH);
record_allocation(&rec); record_allocation(&rec);
} }
return r; return r;

View file

@ -1,5 +1,5 @@
/* /*
Copyright (c) 2009 Dave Gamble Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal
@ -28,108 +28,211 @@ extern "C"
{ {
#endif #endif
/* project version */
#define CJSON_VERSION_MAJOR 1
#define CJSON_VERSION_MINOR 6
#define CJSON_VERSION_PATCH 0
#include <stddef.h>
/* cJSON Types: */ /* cJSON Types: */
#define cJSON_False 0 #define cJSON_Invalid (0)
#define cJSON_True 1 #define cJSON_False (1 << 0)
#define cJSON_NULL 2 #define cJSON_True (1 << 1)
#define cJSON_Number 3 #define cJSON_NULL (1 << 2)
#define cJSON_String 4 #define cJSON_Number (1 << 3)
#define cJSON_Array 5 #define cJSON_String (1 << 4)
#define cJSON_Object 6 #define cJSON_Array (1 << 5)
#define cJSON_Object (1 << 6)
#define cJSON_Raw (1 << 7) /* raw json */
#define cJSON_IsReference 256 #define cJSON_IsReference 256
#define cJSON_StringIsConst 512 #define cJSON_StringIsConst 512
/* The cJSON structure: */ /* The cJSON structure: */
typedef struct cJSON { typedef struct cJSON
struct cJSON *next,*prev; /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */ {
struct cJSON *child; /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */ /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */
struct cJSON *next;
struct cJSON *prev;
/* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */
struct cJSON *child;
int type; /* The type of the item, as above. */ /* The type of the item, as above. */
int type;
char *valuestring; /* The item's string, if type==cJSON_String */ /* The item's string, if type==cJSON_String and type == cJSON_Raw */
int valueint; /* The item's number, if type==cJSON_Number */ char *valuestring;
double valuedouble; /* The item's number, if type==cJSON_Number */ /* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */
int valueint;
/* The item's number, if type==cJSON_Number */
double valuedouble;
char *string; /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */ /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */
char *string;
} cJSON; } cJSON;
typedef struct cJSON_Hooks { typedef struct cJSON_Hooks
{
void *(*malloc_fn)(size_t sz); void *(*malloc_fn)(size_t sz);
void (*free_fn)(void *ptr); void (*free_fn)(void *ptr);
} cJSON_Hooks; } cJSON_Hooks;
typedef int cJSON_bool;
#if !defined(__WINDOWS__) && (defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32))
#define __WINDOWS__
#endif
#ifdef __WINDOWS__
/* When compiling for windows, we specify a specific calling convention to avoid issues where we are being called from a project with a different default calling convention. For windows you have 2 define options:
CJSON_HIDE_SYMBOLS - Define this in the case where you don't want to ever dllexport symbols
CJSON_EXPORT_SYMBOLS - Define this on library build when you want to dllexport symbols (default)
CJSON_IMPORT_SYMBOLS - Define this if you want to dllimport symbol
For *nix builds that support visibility attribute, you can define similar behavior by
setting default visibility to hidden by adding
-fvisibility=hidden (for gcc)
or
-xldscope=hidden (for sun cc)
to CFLAGS
then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJSON_EXPORT_SYMBOLS does
*/
/* export symbols by default, this is necessary for copy pasting the C and header file */
#if !defined(CJSON_HIDE_SYMBOLS) && !defined(CJSON_IMPORT_SYMBOLS) && !defined(CJSON_EXPORT_SYMBOLS)
#define CJSON_EXPORT_SYMBOLS
#endif
#if defined(CJSON_HIDE_SYMBOLS)
#define CJSON_PUBLIC(type) type __stdcall
#elif defined(CJSON_EXPORT_SYMBOLS)
#define CJSON_PUBLIC(type) __declspec(dllexport) type __stdcall
#elif defined(CJSON_IMPORT_SYMBOLS)
#define CJSON_PUBLIC(type) __declspec(dllimport) type __stdcall
#endif
#else /* !WIN32 */
#if (defined(__GNUC__) || defined(__SUNPRO_CC) || defined (__SUNPRO_C)) && defined(CJSON_API_VISIBILITY)
#define CJSON_PUBLIC(type) __attribute__((visibility("default"))) type
#else
#define CJSON_PUBLIC(type) type
#endif
#endif
/* Limits how deeply nested arrays/objects can be before cJSON rejects to parse them.
* This is to prevent stack overflows. */
#ifndef CJSON_NESTING_LIMIT
#define CJSON_NESTING_LIMIT 1000
#endif
/* returns the version of cJSON as a string */
CJSON_PUBLIC(const char*) cJSON_Version(void);
/* Supply malloc, realloc and free functions to cJSON */ /* Supply malloc, realloc and free functions to cJSON */
extern void cJSON_InitHooks(cJSON_Hooks* hooks); CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks);
/* Memory Management: the caller is always responsible to free the results from all variants of cJSON_Parse (with cJSON_Delete) and cJSON_Print (with stdlib free, cJSON_Hooks.free_fn, or cJSON_free as appropriate). The exception is cJSON_PrintPreallocated, where the caller has full responsibility of the buffer. */
/* Supply a block of JSON, and this returns a cJSON object you can interrogate. */
CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value);
/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */
/* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error so will match cJSON_GetErrorPtr(). */
CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated);
/* Supply a block of JSON, and this returns a cJSON object you can interrogate. Call cJSON_Delete when finished. */ /* Render a cJSON entity to text for transfer/storage. */
extern cJSON *cJSON_Parse(const char *value); CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item);
/* Render a cJSON entity to text for transfer/storage. Free the char* when finished. */ /* Render a cJSON entity to text for transfer/storage without any formatting. */
extern char *cJSON_Print(cJSON *item); CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item);
/* Render a cJSON entity to text for transfer/storage without any formatting. Free the char* when finished. */
extern char *cJSON_PrintUnformatted(cJSON *item);
/* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */ /* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */
extern char *cJSON_PrintBuffered(cJSON *item,int prebuffer,int fmt); CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt);
/* Render a cJSON entity to text using a buffer already allocated in memory with given length. Returns 1 on success and 0 on failure. */
/* NOTE: cJSON is not always 100% accurate in estimating how much memory it will use, so to be safe allocate 5 bytes more than you actually need */
CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format);
/* Delete a cJSON entity and all subentities. */ /* Delete a cJSON entity and all subentities. */
extern void cJSON_Delete(cJSON *c); CJSON_PUBLIC(void) cJSON_Delete(cJSON *c);
/* Returns the number of items in an array (or object). */ /* Returns the number of items in an array (or object). */
extern int cJSON_GetArraySize(cJSON *array); CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array);
/* Retrieve item number "item" from array "array". Returns NULL if unsuccessful. */ /* Retrieve item number "item" from array "array". Returns NULL if unsuccessful. */
extern cJSON *cJSON_GetArrayItem(cJSON *array,int item); CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index);
/* Get item "string" from object. Case insensitive. */ /* Get item "string" from object. Case insensitive. */
extern cJSON *cJSON_GetObjectItem(cJSON *object,const char *string); CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string);
CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string);
CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string);
/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */ /* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */
extern const char *cJSON_GetErrorPtr(void); CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void);
/* These functions check the type of an item */
CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item);
/* These calls create a cJSON item of the appropriate type. */ /* These calls create a cJSON item of the appropriate type. */
extern cJSON *cJSON_CreateNull(void); CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void);
extern cJSON *cJSON_CreateTrue(void); CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void);
extern cJSON *cJSON_CreateFalse(void); CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void);
extern cJSON *cJSON_CreateBool(int b); CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean);
extern cJSON *cJSON_CreateNumber(double num); CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num);
extern cJSON *cJSON_CreateDouble(double num,int i_num); CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string);
extern cJSON *cJSON_CreateString(const char *string); /* raw json */
extern cJSON *cJSON_CreateArray(void); CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw);
extern cJSON *cJSON_CreateObject(void); CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void);
CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void);
/* These utilities create an Array of count items. */ /* These utilities create an Array of count items. */
extern cJSON *cJSON_CreateIntArray(const int *numbers,int count); CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count);
extern cJSON *cJSON_CreateFloatArray(const float *numbers,int count); CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count);
extern cJSON *cJSON_CreateDoubleArray(const double *numbers,int count); CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count);
extern cJSON *cJSON_CreateStringArray(const char **strings,int count); CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char **strings, int count);
/* Append item to the specified array/object. */ /* Append item to the specified array/object. */
extern void cJSON_AddItemToArray(cJSON *array, cJSON *item); CJSON_PUBLIC(void) cJSON_AddItemToArray(cJSON *array, cJSON *item);
extern void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item); CJSON_PUBLIC(void) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item);
extern void cJSON_AddItemToObjectCS(cJSON *object,const char *string,cJSON *item); /* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object */ /* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object.
* WARNING: When this function was used, make sure to always check that (item->type & cJSON_StringIsConst) is zero before
* writing to `item->string` */
CJSON_PUBLIC(void) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item);
/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */ /* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */
extern void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item); CJSON_PUBLIC(void) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item);
extern void cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item); CJSON_PUBLIC(void) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item);
/* Remove/Detatch items from Arrays/Objects. */ /* Remove/Detatch items from Arrays/Objects. */
extern cJSON *cJSON_DetachItemFromArray(cJSON *array,int which); CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item);
extern void cJSON_DeleteItemFromArray(cJSON *array,int which); CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which);
extern cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string); CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which);
extern void cJSON_DeleteItemFromObject(cJSON *object,const char *string); CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string);
CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string);
CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string);
CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string);
/* Update array items. */ /* Update array items. */
extern void cJSON_InsertItemInArray(cJSON *array,int which,cJSON *newitem); /* Shifts pre-existing items to the right. */ CJSON_PUBLIC(void) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */
extern void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem); CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement);
extern void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem); CJSON_PUBLIC(void) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem);
CJSON_PUBLIC(void) cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem);
CJSON_PUBLIC(void) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object,const char *string,cJSON *newitem);
/* Duplicate a cJSON item */ /* Duplicate a cJSON item */
extern cJSON *cJSON_Duplicate(cJSON *item,int recurse); CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse);
/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will /* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will
need to be released. With recurse!=0, it will duplicate any children connected to the item. need to be released. With recurse!=0, it will duplicate any children connected to the item.
The item->next and ->prev pointers are always zero on return from Duplicate. */ The item->next and ->prev pointers are always zero on return from Duplicate. */
/* Recursively compare two cJSON items for equality. If either a or b is NULL or invalid, they will be considered unequal.
* case_sensitive determines if object keys are treated case sensitive (1) or case insensitive (0) */
CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive);
/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */
extern cJSON *cJSON_ParseWithOpts(const char *value,const char **return_parse_end,int require_null_terminated);
extern void cJSON_Minify(char *json); CJSON_PUBLIC(void) cJSON_Minify(char *json);
/* Macros for creating things quickly. */ /* Macros for creating things quickly. */
#define cJSON_AddNullToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateNull()) #define cJSON_AddNullToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateNull())
@ -138,10 +241,20 @@ extern void cJSON_Minify(char *json);
#define cJSON_AddBoolToObject(object,name,b) cJSON_AddItemToObject(object, name, cJSON_CreateBool(b)) #define cJSON_AddBoolToObject(object,name,b) cJSON_AddItemToObject(object, name, cJSON_CreateBool(b))
#define cJSON_AddNumberToObject(object,name,n) cJSON_AddItemToObject(object, name, cJSON_CreateNumber(n)) #define cJSON_AddNumberToObject(object,name,n) cJSON_AddItemToObject(object, name, cJSON_CreateNumber(n))
#define cJSON_AddStringToObject(object,name,s) cJSON_AddItemToObject(object, name, cJSON_CreateString(s)) #define cJSON_AddStringToObject(object,name,s) cJSON_AddItemToObject(object, name, cJSON_CreateString(s))
#define cJSON_AddRawToObject(object,name,s) cJSON_AddItemToObject(object, name, cJSON_CreateRaw(s))
/* When assigning an integer value, it needs to be propagated to valuedouble too. */ /* When assigning an integer value, it needs to be propagated to valuedouble too. */
#define cJSON_SetIntValue(object,val) ((object)?(object)->valueint=(object)->valuedouble=(val):(val)) #define cJSON_SetIntValue(object, number) ((object) ? (object)->valueint = (object)->valuedouble = (number) : (number))
#define cJSON_SetNumberValue(object,val) ((object)?(object)->valueint=(object)->valuedouble=(val):(val)) /* helper for the cJSON_SetNumberValue macro */
CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number);
#define cJSON_SetNumberValue(object, number) ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (number))
/* Macro for iterating over an array or object */
#define cJSON_ArrayForEach(element, array) for(element = (array != NULL) ? (array)->child : NULL; element != NULL; element = element->next)
/* malloc/free objects using the malloc/free functions that have been set with cJSON_InitHooks */
CJSON_PUBLIC(void *) cJSON_malloc(size_t size);
CJSON_PUBLIC(void) cJSON_free(void *object);
#ifdef __cplusplus #ifdef __cplusplus
} }

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,30 +1,74 @@
/*
Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
Permission is hereby granted, 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.
*/
#include "cJSON.h" #include "cJSON.h"
/* Implement RFC6901 (https://tools.ietf.org/html/rfc6901) JSON Pointer spec. */ /* Implement RFC6901 (https://tools.ietf.org/html/rfc6901) JSON Pointer spec. */
cJSON *cJSONUtils_GetPointer(cJSON *object,const char *pointer); CJSON_PUBLIC(cJSON *) cJSONUtils_GetPointer(cJSON * const object, const char *pointer);
CJSON_PUBLIC(cJSON *) cJSONUtils_GetPointerCaseSensitive(cJSON * const object, const char *pointer);
/* Implement RFC6902 (https://tools.ietf.org/html/rfc6902) JSON Patch spec. */ /* Implement RFC6902 (https://tools.ietf.org/html/rfc6902) JSON Patch spec. */
cJSON* cJSONUtils_GeneratePatches(cJSON *from,cJSON *to); /* NOTE: This modifies objects in 'from' and 'to' by sorting the elements by their key */
void cJSONUtils_AddPatchToArray(cJSON *array,const char *op,const char *path,cJSON *val); /* Utility for generating patch array entries. */ CJSON_PUBLIC(cJSON *) cJSONUtils_GeneratePatches(cJSON * const from, cJSON * const to);
int cJSONUtils_ApplyPatches(cJSON *object,cJSON *patches); /* Returns 0 for success. */ CJSON_PUBLIC(cJSON *) cJSONUtils_GeneratePatchesCaseSensitive(cJSON * const from, cJSON * const to);
/* Utility for generating patch array entries. */
CJSON_PUBLIC(void) cJSONUtils_AddPatchToArray(cJSON * const array, const char * const operation, const char * const path, const cJSON * const value);
/* Returns 0 for success. */
CJSON_PUBLIC(int) cJSONUtils_ApplyPatches(cJSON * const object, const cJSON * const patches);
CJSON_PUBLIC(int) cJSONUtils_ApplyPatchesCaseSensitive(cJSON * const object, const cJSON * const patches);
/* /*
// Note that ApplyPatches is NOT atomic on failure. To implement an atomic ApplyPatches, use: // Note that ApplyPatches is NOT atomic on failure. To implement an atomic ApplyPatches, use:
//int cJSONUtils_AtomicApplyPatches(cJSON **object, cJSON *patches) //int cJSONUtils_AtomicApplyPatches(cJSON **object, cJSON *patches)
//{ //{
// cJSON *modme=cJSON_Duplicate(*object,1); // cJSON *modme = cJSON_Duplicate(*object, 1);
// int error=cJSONUtils_ApplyPatches(modme,patches); // int error = cJSONUtils_ApplyPatches(modme, patches);
// if (!error) {cJSON_Delete(*object);*object=modme;} // if (!error)
// else cJSON_Delete(modme); // {
// cJSON_Delete(*object);
// *object = modme;
// }
// else
// {
// cJSON_Delete(modme);
// }
//
// return error; // return error;
//} //}
// Code not added to library since this strategy is a LOT slower. // Code not added to library since this strategy is a LOT slower.
*/ */
/* Implement RFC7386 (https://tools.ietf.org/html/rfc7396) JSON Merge Patch spec. */ /* Implement RFC7386 (https://tools.ietf.org/html/rfc7396) JSON Merge Patch spec. */
cJSON* cJSONUtils_MergePatch(cJSON *target, cJSON *patch); /* target will be modified by patch. return value is new ptr for target. */ /* target will be modified by patch. return value is new ptr for target. */
cJSON *cJSONUtils_GenerateMergePatch(cJSON *from,cJSON *to); /* generates a patch to move from -> to */ CJSON_PUBLIC(cJSON *) cJSONUtils_MergePatch(cJSON *target, const cJSON * const patch);
CJSON_PUBLIC(cJSON *) cJSONUtils_MergePatchCaseSensitive(cJSON *target, const cJSON * const patch);
/* generates a patch to move from -> to */
/* NOTE: This modifies objects in 'from' and 'to' by sorting the elements by their key */
CJSON_PUBLIC(cJSON *) cJSONUtils_GenerateMergePatch(cJSON * const from, cJSON * const to);
CJSON_PUBLIC(cJSON *) cJSONUtils_GenerateMergePatchCaseSensitive(cJSON * const from, cJSON * const to);
char *cJSONUtils_FindPointerFromObjectTo(cJSON *object,cJSON *target); /* Given a root object and a target object, construct a pointer from one to the other. */ /* Given a root object and a target object, construct a pointer from one to the other. */
CJSON_PUBLIC(char *) cJSONUtils_FindPointerFromObjectTo(const cJSON * const object, const cJSON * const target);
void cJSONUtils_SortObject(cJSON *object); /* Sorts the members of the object into alphabetical order. */ /* Sorts the members of the object into alphabetical order. */
CJSON_PUBLIC(void) cJSONUtils_SortObject(cJSON * const object);
CJSON_PUBLIC(void) cJSONUtils_SortObjectCaseSensitive(cJSON * const object);

View file

@ -136,6 +136,15 @@ netconn_new_with_proto_and_callback(enum netconn_type t, u8_t proto, netconn_cal
return conn; return conn;
} }
static inline bool is_created_by_socket(struct netconn *conn)
{
#if LWIP_SOCKET
if (conn && (conn->socket != -1)) {
return true;
}
#endif
return false;
}
/** /**
* Close a netconn 'connection' and free its resources. * Close a netconn 'connection' and free its resources.
* UDP and RAW connection are completely closed, TCP pcbs might still be in a waitstate * UDP and RAW connection are completely closed, TCP pcbs might still be in a waitstate
@ -174,7 +183,12 @@ netconn_delete(struct netconn *conn)
return err; return err;
} }
#if !ESP_THREAD_SAFE #if ESP_THREAD_SAFE
if (is_created_by_socket(conn) == false) {
LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("netconn_delete - free conn\n"));
netconn_free(conn);
}
#else
LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("netconn_delete - free conn\n")); LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("netconn_delete - free conn\n"));
netconn_free(conn); netconn_free(conn);
#endif #endif

View file

@ -11,6 +11,12 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
#ifndef _ESP_DEBUG_H_
#define _ESP_DEBUG_H_
#ifdef __cplusplus
extern "C" {
#endif
#include "sdkconfig.h" #include "sdkconfig.h"
#ifdef CONFIG_MBEDTLS_DEBUG #ifdef CONFIG_MBEDTLS_DEBUG
@ -43,3 +49,9 @@ void mbedtls_esp_disable_debug_log(mbedtls_ssl_config *conf);
#endif #endif
#ifdef __cplusplus
}
#endif
#endif /* __ESP_DEBUG_H__ */

View file

@ -569,7 +569,8 @@ static const uint8_t * _mdns_read_fqdn(const uint8_t * packet, const uint8_t * s
&& (strcmp(buf, MDNS_DEFAULT_DOMAIN) != 0) && (strcmp(buf, MDNS_DEFAULT_DOMAIN) != 0)
&& (strcmp(buf, "ip6") != 0) && (strcmp(buf, "ip6") != 0)
&& (strcmp(buf, "in-addr") != 0)) { && (strcmp(buf, "in-addr") != 0)) {
snprintf((char*)name, MDNS_NAME_BUF_LEN, "%s.%s", name->host, buf); strlcat(name->host, ".", sizeof(name->host));
strlcat(name->host, buf, sizeof(name->host));
} else if (strcmp(buf, MDNS_SUB_STR) == 0) { } else if (strcmp(buf, MDNS_SUB_STR) == 0) {
name->sub = 1; name->sub = 1;
} else { } else {

View file

@ -42,7 +42,7 @@
#endif #endif
#if defined( CONFIG_ESP32_TIME_SYSCALL_USE_FRC1 ) || defined( CONFIG_ESP32_TIME_SYSCALL_USE_RTC_FRC1 ) #if defined( CONFIG_ESP32_TIME_SYSCALL_USE_FRC1 ) || defined( CONFIG_ESP32_TIME_SYSCALL_USE_RTC_FRC1 )
#define WITH_FRC1 1 #define WITH_FRC 1
#endif #endif
#ifdef WITH_RTC #ifdef WITH_RTC
@ -72,19 +72,21 @@ static uint64_t get_rtc_time_us()
// s_boot_time: time from Epoch to the first boot time // s_boot_time: time from Epoch to the first boot time
#ifdef WITH_RTC #ifdef WITH_RTC
// when RTC is used to persist time, two RTC_STORE registers are used to store boot time // when RTC is used to persist time, two RTC_STORE registers are used to store boot time
#elif defined(WITH_FRC1) #elif defined(WITH_FRC)
static uint64_t s_boot_time; static uint64_t s_boot_time;
#endif #endif // WITH_RTC
#if defined(WITH_RTC) || defined(WITH_FRC1) #if defined(WITH_RTC) || defined(WITH_FRC)
static _lock_t s_boot_time_lock; static _lock_t s_boot_time_lock;
#endif #endif
#ifdef WITH_RTC // Offset between FRC timer and the RTC.
// Initialized after reset or light sleep.
#if defined(WITH_RTC) && defined(WITH_FRC)
uint64_t s_microseconds_offset; uint64_t s_microseconds_offset;
#endif #endif
#if defined(WITH_RTC) || defined(WITH_FRC1) #if defined(WITH_RTC) || defined(WITH_FRC)
static void set_boot_time(uint64_t time_us) static void set_boot_time(uint64_t time_us)
{ {
_lock_acquire(&s_boot_time_lock); _lock_acquire(&s_boot_time_lock);
@ -109,7 +111,7 @@ static uint64_t get_boot_time()
_lock_release(&s_boot_time_lock); _lock_release(&s_boot_time_lock);
return result; return result;
} }
#endif //defined(WITH_RTC) || defined(WITH_FRC1) #endif //defined(WITH_RTC) || defined(WITH_FRC)
void esp_clk_slowclk_cal_set(uint32_t new_cal) void esp_clk_slowclk_cal_set(uint32_t new_cal)
@ -139,10 +141,10 @@ uint32_t esp_clk_slowclk_cal_get()
void esp_set_time_from_rtc() void esp_set_time_from_rtc()
{ {
#if defined( WITH_FRC1 ) && defined( WITH_RTC ) #if defined( WITH_FRC ) && defined( WITH_RTC )
// initialize time from RTC clock // initialize time from RTC clock
s_microseconds_offset = get_rtc_time_us() - esp_timer_get_time(); s_microseconds_offset = get_rtc_time_us() - esp_timer_get_time();
#endif // WITH_FRC1 && WITH_RTC #endif // WITH_FRC && WITH_RTC
} }
uint64_t esp_clk_rtc_time(void) uint64_t esp_clk_rtc_time(void)
@ -166,23 +168,27 @@ clock_t IRAM_ATTR _times_r(struct _reent *r, struct tms *ptms)
return (clock_t) tv.tv_sec; return (clock_t) tv.tv_sec;
} }
#if defined( WITH_FRC1 ) || defined( WITH_RTC ) #if defined( WITH_FRC ) || defined( WITH_RTC )
static uint64_t get_time_since_boot() static uint64_t get_time_since_boot()
{ {
uint64_t microseconds = 0; uint64_t microseconds = 0;
#ifdef WITH_FRC1 #ifdef WITH_FRC
#ifdef WITH_RTC
microseconds = s_microseconds_offset + esp_timer_get_time(); microseconds = s_microseconds_offset + esp_timer_get_time();
#else
microseconds = esp_timer_get_time();
#endif // WITH_RTC
#elif defined(WITH_RTC) #elif defined(WITH_RTC)
microseconds = get_rtc_time_us(); microseconds = get_rtc_time_us();
#endif #endif // WITH_FRC
return microseconds; return microseconds;
} }
#endif // defined( WITH_FRC1 ) || defined( WITH_RTC ) #endif // defined( WITH_FRC ) || defined( WITH_RTC )
int IRAM_ATTR _gettimeofday_r(struct _reent *r, struct timeval *tv, void *tz) int IRAM_ATTR _gettimeofday_r(struct _reent *r, struct timeval *tv, void *tz)
{ {
(void) tz; (void) tz;
#if defined( WITH_FRC1 ) || defined( WITH_RTC ) #if defined( WITH_FRC ) || defined( WITH_RTC )
if (tv) { if (tv) {
uint64_t microseconds = get_boot_time() + get_time_since_boot(); uint64_t microseconds = get_boot_time() + get_time_since_boot();
tv->tv_sec = microseconds / 1000000; tv->tv_sec = microseconds / 1000000;
@ -192,13 +198,13 @@ int IRAM_ATTR _gettimeofday_r(struct _reent *r, struct timeval *tv, void *tz)
#else #else
__errno_r(r) = ENOSYS; __errno_r(r) = ENOSYS;
return -1; return -1;
#endif // defined( WITH_FRC1 ) || defined( WITH_RTC ) #endif // defined( WITH_FRC ) || defined( WITH_RTC )
} }
int settimeofday(const struct timeval *tv, const struct timezone *tz) int settimeofday(const struct timeval *tv, const struct timezone *tz)
{ {
(void) tz; (void) tz;
#if defined( WITH_FRC1 ) || defined( WITH_RTC ) #if defined( WITH_FRC ) || defined( WITH_RTC )
if (tv) { if (tv) {
uint64_t now = ((uint64_t) tv->tv_sec) * 1000000LL + tv->tv_usec; uint64_t now = ((uint64_t) tv->tv_sec) * 1000000LL + tv->tv_usec;
uint64_t since_boot = get_time_since_boot(); uint64_t since_boot = get_time_since_boot();
@ -233,7 +239,7 @@ unsigned int sleep(unsigned int seconds)
uint32_t system_get_time(void) uint32_t system_get_time(void)
{ {
#if defined( WITH_FRC1 ) || defined( WITH_RTC ) #if defined( WITH_FRC ) || defined( WITH_RTC )
return get_time_since_boot(); return get_time_since_boot();
#else #else
return 0; return 0;
@ -244,7 +250,7 @@ uint32_t system_get_current_time(void) __attribute__((alias("system_get_time")))
uint32_t system_relative_time(uint32_t current_time) uint32_t system_relative_time(uint32_t current_time)
{ {
#if defined( WITH_FRC1 ) || defined( WITH_RTC ) #if defined( WITH_FRC ) || defined( WITH_RTC )
return get_time_since_boot() - current_time; return get_time_since_boot() - current_time;
#else #else
return 0; return 0;

View file

@ -62,7 +62,7 @@ void HashList::insert(const Item& item, size_t index)
void HashList::erase(size_t index) void HashList::erase(size_t index)
{ {
for (auto it = std::begin(mBlockList); it != std::end(mBlockList);) { for (auto it = mBlockList.begin(); it != mBlockList.end();) {
bool haveEntries = false; bool haveEntries = false;
for (size_t i = 0; i < it->mCount; ++i) { for (size_t i = 0; i < it->mCount; ++i) {
if (it->mNodes[i].mIndex == index) { if (it->mNodes[i].mIndex == index) {
@ -88,7 +88,7 @@ void HashList::erase(size_t index)
size_t HashList::find(size_t start, const Item& item) size_t HashList::find(size_t start, const Item& item)
{ {
const uint32_t hash_24 = item.calculateCrc32WithoutValue() & 0xffffff; const uint32_t hash_24 = item.calculateCrc32WithoutValue() & 0xffffff;
for (auto it = std::begin(mBlockList); it != std::end(mBlockList); ++it) { for (auto it = mBlockList.begin(); it != mBlockList.end(); ++it) {
for (size_t index = 0; index < it->mCount; ++index) { for (size_t index = 0; index < it->mCount; ++index) {
HashListNode& e = it->mNodes[index]; HashListNode& e = it->mNodes[index];
if (e.mIndex >= start && if (e.mIndex >= start &&

View file

@ -154,7 +154,11 @@ int ssl_pm_new(SSL *ssl)
} }
if (ssl->ctx->ssl_alpn.alpn_status == ALPN_ENABLE) { if (ssl->ctx->ssl_alpn.alpn_status == ALPN_ENABLE) {
#ifdef MBEDTLS_SSL_ALPN
mbedtls_ssl_conf_alpn_protocols( &ssl_pm->conf, ssl->ctx->ssl_alpn.alpn_list ); mbedtls_ssl_conf_alpn_protocols( &ssl_pm->conf, ssl->ctx->ssl_alpn.alpn_list );
#else
SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "CONFIG_MBEDTLS_SSL_ALPN must be enabled to use ALPN", -1);
#endif // MBEDTLS_SSL_ALPN
} }
mbedtls_ssl_conf_rng(&ssl_pm->conf, mbedtls_ctr_drbg_random, &ssl_pm->ctr_drbg); mbedtls_ssl_conf_rng(&ssl_pm->conf, mbedtls_ctr_drbg_random, &ssl_pm->ctr_drbg);

View file

@ -69,7 +69,7 @@ typedef volatile struct {
struct { struct {
union { union {
struct { struct {
uint32_t duty_resolution: 5; /*This register controls resolution of PWN duty by defining the bith width of timer's counter. The max bit width of the counter is 20.*/ uint32_t duty_resolution: 5; /*This register controls resolution of PWN duty by defining the bit width of timer's counter. The max bit width of the counter is 20.*/
uint32_t clock_divider: 18; /*This register is used to configure the divider of clock at the entry of timer. The least significant eight bits represent the decimal part.*/ uint32_t clock_divider: 18; /*This register is used to configure the divider of clock at the entry of timer. The least significant eight bits represent the decimal part.*/
uint32_t pause: 1; /*This bit is used to pause the counter in high speed timer*/ uint32_t pause: 1; /*This bit is used to pause the counter in high speed timer*/
uint32_t rst: 1; /*This bit is used to reset high speed timer the counter will be 0 after reset.*/ uint32_t rst: 1; /*This bit is used to reset high speed timer the counter will be 0 after reset.*/
@ -77,6 +77,11 @@ typedef volatile struct {
uint32_t low_speed_update: 1; /*This bit is only useful for low speed timer channels, reserved for high speed timers*/ uint32_t low_speed_update: 1; /*This bit is only useful for low speed timer channels, reserved for high speed timers*/
uint32_t reserved26: 5; uint32_t reserved26: 5;
}; };
struct {
uint32_t bit_num: 5 __attribute__((deprecated)); /*Deprecated in ESP-IDF 3.0. This is an alias to 'duty_resolution' for backward compatibility with ESP-IDF 2.1.*/
uint32_t div_num: 18 __attribute__((deprecated)); /*Deprecated in ESP-IDF 3.0. This is an alias to 'clock_divider' for backward compatibility with ESP-IDF 2.1.*/
uint32_t place_holder: 9 __attribute__((deprecated)); /*A place holder to accommodate deprecated members*/
};
uint32_t val; uint32_t val;
} conf; } conf;
union { union {

View file

@ -1830,7 +1830,16 @@
#define RTC_CNTL_SCRATCH7_V 0xFFFFFFFF #define RTC_CNTL_SCRATCH7_V 0xFFFFFFFF
#define RTC_CNTL_SCRATCH7_S 0 #define RTC_CNTL_SCRATCH7_S 0
#define RTC_CNTL_DIAG0_REG (DR_REG_RTCCNTL_BASE + 0xc0) #define RTC_CNTL_LOW_POWER_ST_REG (DR_REG_RTCCNTL_BASE + 0xc0)
/* RTC_CNTL_RDY_FOR_WAKEUP : R/0; bitpos:[19]; default: 0 */
/*description: 1 if RTC controller is ready to execute WAKE instruction, 0 otherwise */
#define RTC_CNTL_RDY_FOR_WAKEUP (BIT(19))
#define RTC_CNTL_RDY_FOR_WAKEUP_M (BIT(19))
#define RTC_CNTL_RDY_FOR_WAKEUP_V 0x1
#define RTC_CNTL_RDY_FOR_WAKEUP_S 19
/* Compatibility definition */
#define RTC_CNTL_DIAG0_REG RTC_CNTL_LOW_POWER_ST_REG
/* RTC_CNTL_LOW_POWER_DIAG0 : RO ;bitpos:[31:0] ;default: 0 ; */ /* RTC_CNTL_LOW_POWER_DIAG0 : RO ;bitpos:[31:0] ;default: 0 ; */
/*description: */ /*description: */
#define RTC_CNTL_LOW_POWER_DIAG0 0xFFFFFFFF #define RTC_CNTL_LOW_POWER_DIAG0 0xFFFFFFFF

View file

@ -112,6 +112,15 @@ static void test_clock_switching(void (*switch_func)(rtc_cpu_freq_t))
ref_clock_deinit(); ref_clock_deinit();
} }
TEST_CASE("Calculate 8M clock frequency", "[rtc_clk]")
{
// calibrate 8M/256 clock against XTAL, get 8M/256 clock period
uint32_t rtc_8md256_period = rtc_clk_cal(RTC_CAL_8MD256, 100);
uint32_t rtc_fast_freq_hz = 1000000ULL * (1 << RTC_CLK_CAL_FRACT) * 256 / rtc_8md256_period;
printf("RTC_FAST_CLK=%d Hz\n", rtc_fast_freq_hz);
TEST_ASSERT_INT32_WITHIN(500000, RTC_FAST_CLK_FREQ_APPROX, rtc_fast_freq_hz);
}
TEST_CASE("Test switching between PLL and XTAL", "[rtc_clk]") TEST_CASE("Test switching between PLL and XTAL", "[rtc_clk]")
{ {
test_clock_switching(rtc_clk_cpu_freq_set); test_clock_switching(rtc_clk_cpu_freq_set);

View file

@ -1,5 +1,36 @@
menu "SPI Flash driver" menu "SPI Flash driver"
config SPI_FLASH_VERIFY_WRITE
bool "Verify SPI flash writes"
default n
help
If this option is enabled, any time SPI flash is written then the data will be read
back and verified. This can catch hardware problems with SPI flash, or flash which
was not erased before verification.
config SPI_FLASH_LOG_FAILED_WRITE
bool "Log errors if verification fails"
depends on SPI_FLASH_VERIFY_WRITE
default n
help
If this option is enabled, if SPI flash write verification fails then a log error line
will be written with the address, expected & actual values. This can be useful when
debugging hardware SPI flash problems.
config SPI_FLASH_WARN_SETTING_ZERO_TO_ONE
bool "Log warning if writing zero bits to ones"
depends on SPI_FLASH_VERIFY_WRITE
default n
help
If this option is enabled, any SPI flash write which tries to set zero bits in the flash to
ones will log a warning. Such writes will not result in the requested data appearing identically
in flash once written, as SPI NOR flash can only set bits to one when an entire sector is erased.
After erasing, individual bits can only be written from one to zero.
Note that some software (such as SPIFFS) which is aware of SPI NOR flash may write one bits as an
optimisation, relying on the data in flash becoming a bitwise AND of the new data and any existing data.
Such software will log spurious warnings if this option is enabled.
config SPI_FLASH_ENABLE_COUNTERS config SPI_FLASH_ENABLE_COUNTERS
bool "Enable operation counters" bool "Enable operation counters"
default 0 default 0

View file

@ -44,8 +44,9 @@
#define MAX_WRITE_CHUNK 8192 #define MAX_WRITE_CHUNK 8192
#define MAX_READ_CHUNK 16384 #define MAX_READ_CHUNK 16384
static const char *TAG __attribute__((unused)) = "spi_flash";
#if CONFIG_SPI_FLASH_ENABLE_COUNTERS #if CONFIG_SPI_FLASH_ENABLE_COUNTERS
static const char *TAG = "spi_flash";
static spi_flash_counters_t s_flash_stats; static spi_flash_counters_t s_flash_stats;
#define COUNTER_START() uint32_t ts_begin = xthal_get_ccount() #define COUNTER_START() uint32_t ts_begin = xthal_get_ccount()
@ -233,6 +234,81 @@ esp_err_t IRAM_ATTR spi_flash_erase_range(uint32_t start_addr, uint32_t size)
return spi_flash_translate_rc(rc); return spi_flash_translate_rc(rc);
} }
/* Wrapper around esp_rom_spiflash_write() that verifies data as written if CONFIG_SPI_FLASH_VERIFY_WRITE is set.
If CONFIG_SPI_FLASH_VERIFY_WRITE is not set, this is esp_rom_spiflash_write().
*/
static IRAM_ATTR esp_rom_spiflash_result_t spi_flash_write_inner(uint32_t target, const uint32_t *src_addr, int32_t len)
{
#ifndef CONFIG_SPI_FLASH_VERIFY_WRITE
return esp_rom_spiflash_write(target, src_addr, len);
#else // CONFIG_SPI_FLASH_VERIFY_WRITE
esp_rom_spiflash_result_t res = ESP_ROM_SPIFLASH_RESULT_OK;
assert(len % sizeof(uint32_t) == 0);
uint32_t before_buf[ESP_ROM_SPIFLASH_BUFF_BYTE_READ_NUM / sizeof(uint32_t)];
uint32_t after_buf[ESP_ROM_SPIFLASH_BUFF_BYTE_READ_NUM / sizeof(uint32_t)];
int32_t remaining = len;
for(int i = 0; i < len; i += sizeof(before_buf)) {
int i_w = i / sizeof(uint32_t); // index in words (i is an index in bytes)
int32_t read_len = MIN(sizeof(before_buf), remaining);
// Read "before" contents from flash
res = esp_rom_spiflash_read(target + i, before_buf, read_len);
if (res != ESP_ROM_SPIFLASH_RESULT_OK) {
break;
}
#ifdef CONFIG_SPI_FLASH_WARN_SETTING_ZERO_TO_ONE
for (int r = 0; r < read_len; r += sizeof(uint32_t)) {
int r_w = r / sizeof(uint32_t); // index in words (r is index in bytes)
uint32_t write = src_addr[i_w + r_w];
uint32_t before = before_buf[r_w];
if ((before & write) != write) {
spi_flash_guard_end();
ESP_LOGW(TAG, "Write at offset 0x%x requests 0x%08x but will write 0x%08x -> 0x%08x",
target + i + r, write, before, before & write);
spi_flash_guard_start();
}
}
#endif
res = esp_rom_spiflash_write(target + i, &src_addr[i_w], read_len);
if (res != ESP_ROM_SPIFLASH_RESULT_OK) {
break;
}
res = esp_rom_spiflash_read(target + i, after_buf, read_len);
if (res != ESP_ROM_SPIFLASH_RESULT_OK) {
break;
}
for (int r = 0; r < read_len; r += sizeof(uint32_t)) {
int r_w = r / sizeof(uint32_t); // index in words (r is index in bytes)
uint32_t expected = src_addr[i_w + r_w] & before_buf[r_w];
uint32_t actual = after_buf[r_w];
if (expected != actual) {
#ifdef CONFIG_SPI_FLASH_LOG_FAILED_WRITE
spi_flash_guard_end();
ESP_LOGE(TAG, "Bad write at offset 0x%x expected 0x%08x readback 0x%08x", target + i + r, expected, actual);
spi_flash_guard_start();
#endif
res = ESP_ROM_SPIFLASH_RESULT_ERR;
}
}
if (res != ESP_ROM_SPIFLASH_RESULT_OK) {
break;
}
remaining -= read_len;
}
return res;
#endif // CONFIG_SPI_FLASH_VERIFY_WRITE
}
esp_err_t IRAM_ATTR spi_flash_write(size_t dst, const void *srcv, size_t size) esp_err_t IRAM_ATTR spi_flash_write(size_t dst, const void *srcv, size_t size)
{ {
CHECK_WRITE_ADDRESS(dst, size); CHECK_WRITE_ADDRESS(dst, size);
@ -269,7 +345,7 @@ esp_err_t IRAM_ATTR spi_flash_write(size_t dst, const void *srcv, size_t size)
uint32_t t = 0xffffffff; uint32_t t = 0xffffffff;
memcpy(((uint8_t *) &t) + (dst - left_off), srcc, left_size); memcpy(((uint8_t *) &t) + (dst - left_off), srcc, left_size);
spi_flash_guard_start(); spi_flash_guard_start();
rc = esp_rom_spiflash_write(left_off, &t, 4); rc = spi_flash_write_inner(left_off, &t, 4);
spi_flash_guard_end(); spi_flash_guard_end();
if (rc != ESP_ROM_SPIFLASH_RESULT_OK) { if (rc != ESP_ROM_SPIFLASH_RESULT_OK) {
goto out; goto out;
@ -296,7 +372,7 @@ esp_err_t IRAM_ATTR spi_flash_write(size_t dst, const void *srcv, size_t size)
write_src = (const uint8_t *)write_buf; write_src = (const uint8_t *)write_buf;
} }
spi_flash_guard_start(); spi_flash_guard_start();
rc = esp_rom_spiflash_write(dst + mid_off, (const uint32_t *) write_src, write_size); rc = spi_flash_write_inner(dst + mid_off, (const uint32_t *) write_src, write_size);
spi_flash_guard_end(); spi_flash_guard_end();
COUNTER_ADD_BYTES(write, write_size); COUNTER_ADD_BYTES(write, write_size);
mid_size -= write_size; mid_size -= write_size;
@ -311,7 +387,7 @@ esp_err_t IRAM_ATTR spi_flash_write(size_t dst, const void *srcv, size_t size)
uint32_t t = 0xffffffff; uint32_t t = 0xffffffff;
memcpy(&t, srcc + right_off, right_size); memcpy(&t, srcc + right_off, right_size);
spi_flash_guard_start(); spi_flash_guard_start();
rc = esp_rom_spiflash_write(dst + right_off, &t, 4); rc = spi_flash_write_inner(dst + right_off, &t, 4);
spi_flash_guard_end(); spi_flash_guard_end();
if (rc != ESP_ROM_SPIFLASH_RESULT_OK) { if (rc != ESP_ROM_SPIFLASH_RESULT_OK) {
goto out; goto out;

View file

@ -88,6 +88,25 @@ config SPIFFS_USE_MAGIC_LENGTH
configured and formatted for 4 megabytes will not be accepted configured and formatted for 4 megabytes will not be accepted
for mounting with a configuration defining the filesystem as 2 megabytes. for mounting with a configuration defining the filesystem as 2 megabytes.
config SPIFFS_META_LENGTH
int "Size of per-file metadata field"
default 4
help
This option sets the number of extra bytes stored in the file header.
These bytes can be used in an application-specific manner.
Set this to at least 4 bytes to enable support for saving file
modification time.
config SPIFFS_USE_MTIME
bool "Save file modification time"
default "y"
depends on SPIFFS_META_LENGTH >= 4
help
If enabled, then the first 4 bytes of per-file metadata will be used
to store file modification time (mtime), accessible through
stat/fstat functions.
Modification time is updated when the file is opened.
menu "Debug Configuration" menu "Debug Configuration"
config SPIFFS_DBG config SPIFFS_DBG

View file

@ -33,6 +33,10 @@
static const char * TAG = "SPIFFS"; static const char * TAG = "SPIFFS";
#ifdef CONFIG_SPIFFS_USE_MTIME
_Static_assert(CONFIG_SPIFFS_META_LENGTH >= sizeof(time_t),
"SPIFFS_META_LENGTH size should be >= sizeof(time_t)");
#endif //CONFIG_SPIFFS_USE_MTIME
/** /**
* @brief SPIFFS definition structure * @brief SPIFFS definition structure
*/ */
@ -80,6 +84,8 @@ static long vfs_spiffs_telldir(void* ctx, DIR* pdir);
static void vfs_spiffs_seekdir(void* ctx, DIR* pdir, long offset); static void vfs_spiffs_seekdir(void* ctx, DIR* pdir, long offset);
static int vfs_spiffs_mkdir(void* ctx, const char* name, mode_t mode); static int vfs_spiffs_mkdir(void* ctx, const char* name, mode_t mode);
static int vfs_spiffs_rmdir(void* ctx, const char* name); static int vfs_spiffs_rmdir(void* ctx, const char* name);
static void vfs_spiffs_update_mtime(spiffs *fs, spiffs_file f);
static time_t vfs_spiffs_get_mtime(const spiffs_stat* s);
static esp_spiffs_t * _efs[CONFIG_SPIFFS_MAX_PARTITIONS]; static esp_spiffs_t * _efs[CONFIG_SPIFFS_MAX_PARTITIONS];
@ -507,12 +513,16 @@ static int vfs_spiffs_open(void* ctx, const char * path, int flags, int mode)
{ {
assert(path); assert(path);
esp_spiffs_t * efs = (esp_spiffs_t *)ctx; esp_spiffs_t * efs = (esp_spiffs_t *)ctx;
int fd = SPIFFS_open(efs->fs, path, spiffs_mode_conv(flags), mode); int spiffs_flags = spiffs_mode_conv(flags);
int fd = SPIFFS_open(efs->fs, path, spiffs_flags, mode);
if (fd < 0) { if (fd < 0) {
errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs)); errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs));
SPIFFS_clearerr(efs->fs); SPIFFS_clearerr(efs->fs);
return -1; return -1;
} }
if (!(spiffs_flags & SPIFFS_RDONLY)) {
vfs_spiffs_update_mtime(efs->fs, fd);
}
return fd; return fd;
} }
@ -562,7 +572,6 @@ static off_t vfs_spiffs_lseek(void* ctx, int fd, off_t offset, int mode)
return -1; return -1;
} }
return res; return res;
} }
static int vfs_spiffs_fstat(void* ctx, int fd, struct stat * st) static int vfs_spiffs_fstat(void* ctx, int fd, struct stat * st)
@ -578,6 +587,9 @@ static int vfs_spiffs_fstat(void* ctx, int fd, struct stat * st)
} }
st->st_size = s.size; st->st_size = s.size;
st->st_mode = S_IRWXU | S_IRWXG | S_IRWXO | S_IFREG; st->st_mode = S_IRWXU | S_IRWXG | S_IRWXO | S_IFREG;
st->st_mtime = vfs_spiffs_get_mtime(&s);
st->st_atime = 0;
st->st_ctime = 0;
return res; return res;
} }
@ -597,6 +609,9 @@ static int vfs_spiffs_stat(void* ctx, const char * path, struct stat * st)
st->st_size = s.size; st->st_size = s.size;
st->st_mode = S_IRWXU | S_IRWXG | S_IRWXO; st->st_mode = S_IRWXU | S_IRWXG | S_IRWXO;
st->st_mode |= (s.type == SPIFFS_TYPE_DIR)?S_IFDIR:S_IFREG; st->st_mode |= (s.type == SPIFFS_TYPE_DIR)?S_IFDIR:S_IFREG;
st->st_mtime = vfs_spiffs_get_mtime(&s);
st->st_atime = 0;
st->st_ctime = 0;
return res; return res;
} }
@ -764,3 +779,31 @@ static int vfs_spiffs_link(void* ctx, const char* n1, const char* n2)
errno = ENOTSUP; errno = ENOTSUP;
return -1; return -1;
} }
static void vfs_spiffs_update_mtime(spiffs *fs, spiffs_file fd)
{
#ifdef CONFIG_SPIFFS_USE_MTIME
time_t t = time(NULL);
spiffs_stat s;
int ret = SPIFFS_OK;
if (CONFIG_SPIFFS_META_LENGTH > sizeof(t)) {
ret = SPIFFS_fstat(fs, fd, &s);
}
if (ret == SPIFFS_OK) {
memcpy(s.meta, &t, sizeof(t));
ret = SPIFFS_fupdate_meta(fs, fd, s.meta);
}
if (ret != SPIFFS_OK) {
ESP_LOGW(TAG, "Failed to update mtime (%d)", ret);
}
#endif //CONFIG_SPIFFS_USE_MTIME
}
static time_t vfs_spiffs_get_mtime(const spiffs_stat* s)
{
time_t t = 0;
#ifdef CONFIG_SPIFFS_USE_MTIME
memcpy(&t, s->meta, sizeof(t));
#endif
return t;
}

View file

@ -158,7 +158,7 @@ extern void spiffs_api_unlock(struct spiffs_t *fs);
// This is derived from following: // This is derived from following:
// logical_page_size - (SPIFFS_OBJ_NAME_LEN + sizeof(spiffs_page_header) + // logical_page_size - (SPIFFS_OBJ_NAME_LEN + sizeof(spiffs_page_header) +
// spiffs_object_ix_header fields + at least some LUT entries) // spiffs_object_ix_header fields + at least some LUT entries)
#define SPIFFS_OBJ_META_LEN (0) #define SPIFFS_OBJ_META_LEN (CONFIG_SPIFFS_META_LENGTH)
// Size of buffer allocated on stack used when copying data. // Size of buffer allocated on stack used when copying data.
// Lower value generates more read/writes. No meaning having it bigger // Lower value generates more read/writes. No meaning having it bigger

View file

@ -505,3 +505,45 @@ TEST_CASE("multiple tasks can use same volume", "[spiffs]")
test_spiffs_concurrent("/spiffs/f"); test_spiffs_concurrent("/spiffs/f");
test_teardown(); test_teardown();
} }
#ifdef CONFIG_SPIFFS_USE_MTIME
TEST_CASE("mtime is updated when file is opened", "[spiffs]")
{
/* Open a file, check that mtime is set correctly */
const char* filename = "/spiffs/time";
test_setup();
time_t t_before_create = time(NULL);
test_spiffs_create_file_with_text(filename, "\n");
time_t t_after_create = time(NULL);
struct stat st;
TEST_ASSERT_EQUAL(0, stat(filename, &st));
printf("mtime=%d\n", (int) st.st_mtime);
TEST_ASSERT(st.st_mtime >= t_before_create
&& st.st_mtime <= t_after_create);
/* Wait a bit, open again, check that mtime is updated */
vTaskDelay(2000 / portTICK_PERIOD_MS);
time_t t_before_open = time(NULL);
FILE *f = fopen(filename, "a");
time_t t_after_open = time(NULL);
TEST_ASSERT_EQUAL(0, fstat(fileno(f), &st));
printf("mtime=%d\n", (int) st.st_mtime);
TEST_ASSERT(st.st_mtime >= t_before_open
&& st.st_mtime <= t_after_open);
fclose(f);
/* Wait a bit, open for reading, check that mtime is not updated */
vTaskDelay(2000 / portTICK_PERIOD_MS);
time_t t_before_open_ro = time(NULL);
f = fopen(filename, "r");
TEST_ASSERT_EQUAL(0, fstat(fileno(f), &st));
printf("mtime=%d\n", (int) st.st_mtime);
TEST_ASSERT(t_before_open_ro > t_after_open
&& st.st_mtime >= t_before_open
&& st.st_mtime <= t_after_open);
fclose(f);
test_teardown();
}
#endif // CONFIG_SPIFFS_USE_MTIME

View file

@ -18,6 +18,10 @@ In addition to the existing binutils port for the ESP32 ULP coprocessor, it is p
The ``program`` array is an array of ``ulp_insn_t``, i.e. ULP coprocessor instructions. Each ``I_XXX`` preprocessor define translates into a single 32-bit instruction. Arguments of these preprocessor defines can be register numbers (``R0 — R3``) and literal constants. See `ULP coprocessor instruction defines`_ section for descriptions of instructions and arguments they take. The ``program`` array is an array of ``ulp_insn_t``, i.e. ULP coprocessor instructions. Each ``I_XXX`` preprocessor define translates into a single 32-bit instruction. Arguments of these preprocessor defines can be register numbers (``R0 — R3``) and literal constants. See `ULP coprocessor instruction defines`_ section for descriptions of instructions and arguments they take.
.. note::
Because some of the instruction macros expand to inline function calls, defining such array in global scope will cause the compiler to produce an "initializer element is not constant" error. To fix this error, move the definition of instructions array into local scope.
Load and store instructions use addresses expressed in 32-bit words. Address 0 corresponds to the first word of ``RTC_SLOW_MEM`` (which is address 0x50000000 as seen by the main CPUs). Load and store instructions use addresses expressed in 32-bit words. Address 0 corresponds to the first word of ``RTC_SLOW_MEM`` (which is address 0x50000000 as seen by the main CPUs).
To generate branch instructions, special ``M_`` preprocessor defines are used. ``M_LABEL`` define can be used to define a branch target. Label identifier is a 16-bit integer. ``M_Bxxx`` defines can be used to generate branch instructions with target set to a particular label. To generate branch instructions, special ``M_`` preprocessor defines are used. ``M_LABEL`` define can be used to define a branch target. Label identifier is a 16-bit integer. ``M_Bxxx`` defines can be used to generate branch instructions with target set to a particular label.

View file

@ -266,9 +266,9 @@ _Static_assert(sizeof(ulp_insn_t) == 4, "ULP coprocessor instruction size should
* Delay (nop) for a given number of cycles * Delay (nop) for a given number of cycles
*/ */
#define I_DELAY(cycles_) { .delay = {\ #define I_DELAY(cycles_) { .delay = {\
.opcode = OPCODE_DELAY, \ .cycles = cycles_, \
.unused = 0, \ .unused = 0, \
.cycles = cycles_ } } .opcode = OPCODE_DELAY } }
/** /**
* Halt the coprocessor. * Halt the coprocessor.

View file

@ -153,7 +153,17 @@ INPUT = \
../components/esp32/include/esp_pm.h \ ../components/esp32/include/esp_pm.h \
../components/esp32/include/esp32/pm.h \ ../components/esp32/include/esp32/pm.h \
### esp_timer, High Resolution Timer ### esp_timer, High Resolution Timer
../components/esp32/include/esp_timer.h ../components/esp32/include/esp_timer.h \
###
### FreeRTOS
###
../components/freertos/include/freertos/task.h \
../components/freertos/include/freertos/queue.h \
../components/freertos/include/freertos/semphr.h \
../components/freertos/include/freertos/timers.h \
../components/freertos/include/freertos/event_groups.h \
../components/freertos/include/freertos/ringbuf.h
## Get warnings for functions that have no documentation for their parameters or return value ## Get warnings for functions that have no documentation for their parameters or return value
@ -165,7 +175,16 @@ WARN_NO_PARAMDOC = YES
ENABLE_PREPROCESSING = YES ENABLE_PREPROCESSING = YES
MACRO_EXPANSION = YES MACRO_EXPANSION = YES
EXPAND_ONLY_PREDEF = YES EXPAND_ONLY_PREDEF = YES
PREDEFINED = __attribute__(x)= PREDEFINED = \
__attribute__(x)= \
IRAM_ATTR= \
configSUPPORT_DYNAMIC_ALLOCATION=1 \
configSUPPORT_STATIC_ALLOCATION=1 \
configQUEUE_REGISTRY_SIZE=1 \
configUSE_RECURSIVE_MUTEXES=1 \
configTHREAD_LOCAL_STORAGE_DELETE_CALLBACKS=1 \
configNUM_THREAD_LOCAL_STORAGE_POINTERS=1 \
configUSE_APPLICATION_TASK_TAG=1
## Do not complain about not having dot ## Do not complain about not having dot
## ##

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 77 KiB

After

Width:  |  Height:  |  Size: 81 KiB

View file

@ -156,7 +156,7 @@ Minimal Component Makefile
The minimal ``component.mk`` file is an empty file(!). If the file is empty, the default component behaviour is set: The minimal ``component.mk`` file is an empty file(!). If the file is empty, the default component behaviour is set:
- All source files in the same directory as the makefile (``*.c``, ``*.cpp``, ``*.S``) will be compiled into the component library - All source files in the same directory as the makefile (``*.c``, ``*.cpp``, ``*.cc``, ``*.S``) will be compiled into the component library
- A sub-directory "include" will be added to the global include search path for all other components. - A sub-directory "include" will be added to the global include search path for all other components.
- The component library will be linked into the project app. - The component library will be linked into the project app.

View file

@ -20,8 +20,8 @@ found via http://www.freertos.org/a00106.html
port of FreeRTOS v8.2.0, a number of FreeRTOS v9.0.0 features have been backported port of FreeRTOS v8.2.0, a number of FreeRTOS v9.0.0 features have been backported
to ESP-IDF. to ESP-IDF.
:ref:`tasks-and-task-creation`: Use ``xTaskCreatePinnedToCore()`` or :ref:`tasks-and-task-creation`: Use :cpp:func:`xTaskCreatePinnedToCore` or
``xTaskCreateStaticPinnedToCore()`` to create tasks in ESP-IDF FreeRTOS. The :cpp:func:`xTaskCreateStaticPinnedToCore` to create tasks in ESP-IDF FreeRTOS. The
last parameter of the two functions is ``xCoreID``. This parameter specifies last parameter of the two functions is ``xCoreID``. This parameter specifies
which core the task is pinned to. Acceptable values are ``0`` for **PRO_CPU**, which core the task is pinned to. Acceptable values are ``0`` for **PRO_CPU**,
``1`` for **APP_CPU**, or ``tskNO_AFFINITY`` which allows the task to run on ``1`` for **APP_CPU**, or ``tskNO_AFFINITY`` which allows the task to run on
@ -34,13 +34,13 @@ enter a blocked state, or are distributed across a wider range of priorities.
:ref:`scheduler-suspension`: Suspending the scheduler in ESP-IDF FreeRTOS will only :ref:`scheduler-suspension`: Suspending the scheduler in ESP-IDF FreeRTOS will only
affect the scheduler on the the calling core. In other words, calling affect the scheduler on the the calling core. In other words, calling
``vTaskSuspendAll()`` on **PRO_CPU** will not prevent **APP_CPU** from scheduling, and :cpp:func:`vTaskSuspendAll` on **PRO_CPU** will not prevent **APP_CPU** from scheduling, and
vice versa. Use critical sections or semaphores instead for simultaneous vice versa. Use critical sections or semaphores instead for simultaneous
access protection. access protection.
:ref:`tick-interrupt-synchronicity`: Tick interrupts of **PRO_CPU** and **APP_CPU** :ref:`tick-interrupt-synchronicity`: Tick interrupts of **PRO_CPU** and **APP_CPU**
are not synchronized. Do not expect to use ``vTaskDelay`` or are not synchronized. Do not expect to use :cpp:func:`vTaskDelay` or
``vTaskDelayUntil`` as an accurate method of synchronizing task execution :cpp:func:`vTaskDelayUntil` as an accurate method of synchronizing task execution
between the two cores. Use a counting semaphore instead as their context between the two cores. Use a counting semaphore instead as their context
switches are not tied to tick interrupts due to preemption. switches are not tied to tick interrupts due to preemption.
@ -50,12 +50,25 @@ scheduler and interrupts of the calling core. However the other core is left
unaffected. If the other core attemps to take same mutex, it will spin until unaffected. If the other core attemps to take same mutex, it will spin until
the calling core has released the mutex by exiting the critical section. the calling core has released the mutex by exiting the critical section.
:ref:`deletion-callbacks`: ESP-IDF FreeRTOS has :ref:`floating-points`: The ESP32 supports hardware acceleration of single
backported the Thread Local Storage Pointers feature. However they have the precision floating point arithmetic (``float``). However the use of hardware
extra feature of deletion callbacks. Deletion callbacks are used to acceleration leads to some behavioral restrictions in ESP-IDF FreeRTOS.
automatically free memory used by Thread Local Storage Pointers during the task Therefore, tasks that utilize ``float`` will automatically be pinned to a core if
deletion. Call ``vTaskSetThreadLocalStoragePointerAndDelCallback()`` not done so already. Furthermore, ``float`` cannot be used in interrupt service
to set Thread Local Storage Pointers and deletion callbacks. routines.
:ref:`task-deletion`: Task deletion behavior has been backported from FreeRTOS
v9.0.0 and modified to be SMP compatible. Task memory will be freed immediately
when :cpp:func:`vTaskDelete` is called to delete a task that is not currently running
and not pinned to the other core. Otherwise, freeing of task memory will still
be delegated to the Idle Task.
:ref:`deletion-callbacks`: ESP-IDF FreeRTOS has backported the Thread Local
Storage Pointers (TLSP) feature. However the extra feature of Deletion Callbacks has been
added. Deletion callbacks are called automatically during task deletion and are
used to free memory pointed to by TLSP. Call
:cpp:func:`vTaskSetThreadLocalStoragePointerAndDelCallback()` to set TLSP and Deletion
Callbacks.
:ref:`FreeRTOS Hooks<hooks_api_reference>`: Vanilla FreeRTOS Hooks were not designed for SMP. :ref:`FreeRTOS Hooks<hooks_api_reference>`: Vanilla FreeRTOS Hooks were not designed for SMP.
ESP-IDF provides its own Idle and Tick Hooks in addition to the Vanilla FreeRTOS ESP-IDF provides its own Idle and Tick Hooks in addition to the Vanilla FreeRTOS
@ -81,34 +94,34 @@ This feature has been backported from FreeRTOS v9.0.0 to ESP-IDF. The
in order for static allocation functions to be available. Once enabled, the in order for static allocation functions to be available. Once enabled, the
following functions can be called... following functions can be called...
- ``xTaskCreateStatic()`` See :ref:`backporting-notes` below - :cpp:func:`xTaskCreateStatic` (see :ref:`backporting-notes` below)
- ``xQueueCreateStatic()`` - :c:macro:`xQueueCreateStatic`
- ``xSemaphoreCreateBinaryStatic()`` - :c:macro:`xSemaphoreCreateBinaryStatic`
- ``xSemaphoreCreateCountingStatic()`` - :c:macro:`xSemaphoreCreateCountingStatic`
- ``xSemaphoreCreateMutexStatic()`` - :c:macro:`xSemaphoreCreateMutexStatic`
- ``xSemaphoreCreateRecursiveMutexStatic()`` - :c:macro:`xSemaphoreCreateRecursiveMutexStatic`
- ``xTimerCreateStatic()`` See :ref:`backporting-notes` below - :cpp:func:`xTimerCreateStatic` (see :ref:`backporting-notes` below)
- ``xEventGroupCreateStatic()`` - :cpp:func:`xEventGroupCreateStatic`
Other Features Other Features
^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^
- ``vTaskSetThreadLocalStoragePointer()`` See :ref:`backporting-notes` below - :cpp:func:`vTaskSetThreadLocalStoragePointer` (see :ref:`backporting-notes` below)
- ``pvTaskGetThreadLocalStoragePointer()`` See :ref:`backporting-notes` below - :cpp:func:`pvTaskGetThreadLocalStoragePointer` (see :ref:`backporting-notes` below)
- ``vTimerSetTimerID()`` - :cpp:func:`vTimerSetTimerID`
- ``xTimerGetPeriod()`` - :cpp:func:`xTimerGetPeriod`
- ``xTimerGetExpiryTime()`` - :cpp:func:`xTimerGetExpiryTime`
- ``pcQueueGetName()`` - :cpp:func:`pcQueueGetName`
- ``uxSemaphoreGetCount()`` - :c:macro:`uxSemaphoreGetCount`
.. _backporting-notes: .. _backporting-notes:
Backporting Notes Backporting Notes
^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^
**1)** ``xTaskCreateStatic`` has been made SMP compatible in a similar **1)** :cpp:func:`xTaskCreateStatic` has been made SMP compatible in a similar
fashion to ``xTaskCreate`` (see :ref:`tasks-and-task-creation`). Therefore fashion to :cpp:func:`xTaskCreate` (see :ref:`tasks-and-task-creation`). Therefore
``xTaskCreateStaticPinnedToCore()`` can also be called. :cpp:func:`xTaskCreateStaticPinnedToCore` can also be called.
**2)** Although vanilla FreeRTOS allows the Timer feature's daemon task to **2)** Although vanilla FreeRTOS allows the Timer feature's daemon task to
be statically allocated, the daemon task is always dynamically allocated in be statically allocated, the daemon task is always dynamically allocated in
@ -117,7 +130,7 @@ defined when using statically allocated timers in ESP-IDF FreeRTOS.
**3)** The Thread Local Storage Pointer feature has been modified in ESP-IDF **3)** The Thread Local Storage Pointer feature has been modified in ESP-IDF
FreeRTOS to include Deletion Callbacks (see :ref:`deletion-callbacks`). Therefore FreeRTOS to include Deletion Callbacks (see :ref:`deletion-callbacks`). Therefore
the function ``vTaskSetThreadLocalStoragePointerAndDelCallback()`` can also be the function :cpp:func:`vTaskSetThreadLocalStoragePointerAndDelCallback` can also be
called. called.
@ -129,9 +142,9 @@ Tasks and Task Creation
Tasks in ESP-IDF FreeRTOS are designed to run on a particular core, therefore Tasks in ESP-IDF FreeRTOS are designed to run on a particular core, therefore
two new task creation functions have been added to ESP-IDF FreeRTOS by two new task creation functions have been added to ESP-IDF FreeRTOS by
appending ``PinnedToCore`` to the names of the task creation functions in appending ``PinnedToCore`` to the names of the task creation functions in
vanilla FreeRTOS. The vanilla FreeRTOS functions of ``xTaskCreate()`` vanilla FreeRTOS. The vanilla FreeRTOS functions of :cpp:func:`xTaskCreate`
and ``xTaskCreateStatic()`` have led to the addition of and :cpp:func:`xTaskCreateStatic` have led to the addition of
``xTaskCreatePinnedToCore()`` and ``xTaskCreateStaticPinnedToCore()`` in :cpp:func:`xTaskCreatePinnedToCore` and :cpp:func:`xTaskCreateStaticPinnedToCore` in
ESP-IDF FreeRTOS (see :ref:`backported-features`). ESP-IDF FreeRTOS (see :ref:`backported-features`).
For more details see :component_file:`freertos/task.c` For more details see :component_file:`freertos/task.c`
@ -151,9 +164,9 @@ of 1000 bytes. It should be noted that the ``uxStackDepth`` parameter in
vanilla FreeRTOS specifies a tasks stack depth in terms of the number of vanilla FreeRTOS specifies a tasks stack depth in terms of the number of
words, whereas ESP-IDF FreeRTOS specifies the stack depth in terms of bytes. words, whereas ESP-IDF FreeRTOS specifies the stack depth in terms of bytes.
Note that the vanilla FreeRTOS functions ``xTaskCreate`` and Note that the vanilla FreeRTOS functions :cpp:func:`xTaskCreate` and
``xTaskCreateStatic`` have been macro defined in ESP-IDF FreeRTOS to call :cpp:func:`xTaskCreateStatic` have been defined in ESP-IDF FreeRTOS as inline functions which call
``xTaskCreatePinnedToCore()`` and ``xTaskCreateStaticPinnedToCore()`` :cpp:func:`xTaskCreatePinnedToCore` and :cpp:func:`xTaskCreateStaticPinnedToCore`
respectively with ``tskNO_AFFINITY`` as the ``xCoreID`` value. respectively with ``tskNO_AFFINITY`` as the ``xCoreID`` value.
Each Task Control Block (TCB) in ESP-IDF stores the ``xCoreID`` as a member. Each Task Control Block (TCB) in ESP-IDF stores the ``xCoreID`` as a member.
@ -270,18 +283,18 @@ different cores.
Scheduler Suspension Scheduler Suspension
^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^
In vanilla FreeRTOS, suspending the scheduler via ``vTaskSuspendAll()`` will In vanilla FreeRTOS, suspending the scheduler via :cpp:func:`vTaskSuspendAll` will
prevent calls of ``vTaskSwitchContext()`` from context switching until the prevent calls of ``vTaskSwitchContext`` from context switching until the
scheduler has been resumed with ``vTaskResumeAll()``. However servicing ISRs scheduler has been resumed with :cpp:func:`xTaskResumeAll`. However servicing ISRs
are still permitted. Therefore any changes in task states as a result from the are still permitted. Therefore any changes in task states as a result from the
current running task or ISRSs will not be executed until the scheduler is current running task or ISRSs will not be executed until the scheduler is
resumed. Scheduler suspension in vanilla FreeRTOS is a common protection method resumed. Scheduler suspension in vanilla FreeRTOS is a common protection method
against simultaneous access of data shared between tasks, whilst still allowing against simultaneous access of data shared between tasks, whilst still allowing
ISRs to be serviced. ISRs to be serviced.
In ESP-IDF FreeRTOS, ``vTaskSuspendAll()`` will only prevent calls of In ESP-IDF FreeRTOS, :cpp:func:`xTaskResumeAll` will only prevent calls of
``vTaskSwitchContext()`` from switching contexts on the core that called for the ``vTaskSwitchContext()`` from switching contexts on the core that called for the
suspension. Hence if **PRO_CPU** calls ``vTaskSuspendAll()``, **APP_CPU** will suspension. Hence if **PRO_CPU** calls :cpp:func:`vTaskSuspendAll`, **APP_CPU** will
still be able to switch contexts. If data is shared between tasks that are still be able to switch contexts. If data is shared between tasks that are
pinned to different cores, scheduler suspension is **NOT** a valid method of pinned to different cores, scheduler suspension is **NOT** a valid method of
protection against simultaneous access. Consider using critical sections protection against simultaneous access. Consider using critical sections
@ -289,7 +302,7 @@ protection against simultaneous access. Consider using critical sections
protecting shared resources in ESP-IDF FreeRTOS. protecting shared resources in ESP-IDF FreeRTOS.
In general, it's better to use other RTOS primitives like mutex semaphores to protect In general, it's better to use other RTOS primitives like mutex semaphores to protect
against data shared between tasks, rather than ``vTaskSuspendAll()``. against data shared between tasks, rather than :cpp:func:`vTaskSuspendAll`.
.. _tick-interrupt-synchronicity: .. _tick-interrupt-synchronicity:
@ -303,8 +316,8 @@ each core being independent, and the tick interrupts to each core being
unsynchronized. unsynchronized.
In vanilla FreeRTOS the tick interrupt triggers a call to In vanilla FreeRTOS the tick interrupt triggers a call to
``xTaskIncrementTick()`` which is responsible for incrementing the tick :cpp:func:`xTaskIncrementTick` which is responsible for incrementing the tick
counter, checking if tasks which have called ``vTaskDelay()`` have fulfilled counter, checking if tasks which have called :cpp:func:`vTaskDelay` have fulfilled
their delay period, and moving those tasks from the Delayed Task List to the their delay period, and moving those tasks from the Delayed Task List to the
Ready Task List. The tick interrupt will then call the scheduler if a context Ready Task List. The tick interrupt will then call the scheduler if a context
switch is necessary. switch is necessary.
@ -359,11 +372,11 @@ The ESP-IDF FreeRTOS critical section functions have been modified as follows…
- ``taskENTER_CRITICAL(mux)``, ``taskENTER_CRITICAL_ISR(mux)``, - ``taskENTER_CRITICAL(mux)``, ``taskENTER_CRITICAL_ISR(mux)``,
``portENTER_CRITICAL(mux)``, ``portENTER_CRITICAL_ISR(mux)`` are all macro ``portENTER_CRITICAL(mux)``, ``portENTER_CRITICAL_ISR(mux)`` are all macro
defined to call ``vTaskEnterCritical()`` defined to call :cpp:func:`vTaskEnterCritical`
- ``taskEXIT_CRITICAL(mux)``, ``taskEXIT_CRITICAL_ISR(mux)``, - ``taskEXIT_CRITICAL(mux)``, ``taskEXIT_CRITICAL_ISR(mux)``,
``portEXIT_CRITICAL(mux)``, ``portEXIT_CRITICAL_ISR(mux)`` are all macro ``portEXIT_CRITICAL(mux)``, ``portEXIT_CRITICAL_ISR(mux)`` are all macro
defined to call ``vTaskExitCritical()`` defined to call :cpp:func:`vTaskExitCritical`
For more details see :component_file:`freertos/include/freertos/portmacro.h` For more details see :component_file:`freertos/include/freertos/portmacro.h`
and :component_file:`freertos/task.c` and :component_file:`freertos/task.c`
@ -375,41 +388,85 @@ mutex is provided upon entering and exiting, the type of call should not
matter. matter.
.. _floating-points:
Floating Point Aritmetic
------------------------
The ESP32 supports hardware acceleration of single precision floating point
arithmetic (``float``) via Floating Point Units (FPU, also known as coprocessors)
attached to each core. The use of the FPUs imposes some behavioral restrictions
on ESP-IDF FreeRTOS.
ESP-IDF FreeRTOS implements Lazy Context Switching for FPUs. In other words,
the state of a core's FPU registers are not immediately saved when a context
switch occurs. Therefore, tasks that utilize ``float`` must be pinned to a
particular core upon creation. If not, ESP-IDF FreeRTOS will automatically pin
the task in question to whichever core the task was running on upon the task's
first use of ``float``. Likewise due to Lazy Context Switching, interrupt service
routines must also not use ``float``.
ESP32 does not support hardware acceleration for double precision floating point
arithmetic (``double``). Instead ``double`` is implemented via software hence the
behavioral restrictions with regards to ``float`` do not apply to ``double``. Note
that due to the lack of hardware acceleration, ``double`` operations may consume
significantly larger amount of CPU time in comparison to ``float``.
.. _task-deletion:
Task Deletion
-------------
FreeRTOS task deletion prior to v9.0.0 delegated the freeing of task memory
entirely to the Idle Task. Currently, the freeing of task memory will occur
immediately (within :cpp:func:`vTaskDelete`) if the task being deleted is not currently
running or is not pinned to the other core (with respect to the core
:cpp:func:`vTaskDelete` is called on). TLSP deletion callbacks will also run immediately
if the same conditions are met.
However, calling :cpp:func:`vTaskDelete` to delete a task that is either currently
running or pinned to the other core will still result in the freeing of memory
being delegated to the Idle Task.
.. _deletion-callbacks: .. _deletion-callbacks:
Thread Local Storage Pointers & Deletion Callbacks Thread Local Storage Pointers & Deletion Callbacks
-------------------------------------------------- --------------------------------------------------
Thread Local Storage Pointers are pointers stored directly in the TCB which Thread Local Storage Pointers (TLSP) are pointers stored directly in the TCB.
allows each task to have a pointer to a data structure containing that is TLSP allow each task to have its own unique set of pointers to data structures.
specific to that task. However vanilla FreeRTOS provides no functionality to However task deletion behavior in vanilla FreeRTOS does not automatically
free the memory pointed to by the Thread Local Storage Pointers. Therefore if free the memory pointed to by TLSP. Therefore if the memory pointed to by
the memory pointed to by the Thread Local Storage Pointers is not explicitly TLSP is not explicitly freed by the user before task deletion, memory leak will
freed by the user before a task is deleted, memory leak will occur. occur.
ESP-IDF FreeRTOS provides the added feature of deletion callbacks. These ESP-IDF FreeRTOS provides the added feature of Deletion Callbacks. Deletion
deletion callbacks are used to automatically free the memory pointed to by the Callbacks are called automatically during task deletion to free memory pointed
Thread Local Storage Pointers when a task is deleted. Each Thread Local Storage to by TLSP. Each TLSP can have its own Deletion Callback. Note that due to the
Pointer can have its own call back, and these call backs are called when the to :ref:`task-deletion` behavior, there can be instances where Deletion
Idle tasks cleans up a deleted tasks. Callbacks are called in the context of the Idle Tasks. Therefore Deletion
Callbacks **should never attempt to block** and critical sections should be kept
as short as possible to minimize priority inversion.
Vanilla FreeRTOS sets a Thread Local Storage Pointers using Deletion callbacks are of type
``vTaskSetThreadLocalStoragePointer()`` whereas ESP-IDF FreeRTOS sets a Thread ``void (*TlsDeleteCallbackFunction_t)( int, void * )`` where the first parameter
Local Storage Pointers and Deletion Callbacks using is the index number of the associated TLSP, and the second parameter is the
``vTaskSetThreadLocalStoragePointerAndDelCallback()`` which accepts a pointer TLSP itself.
to the deletion call back as an extra parameter of type
```TlsDeleteCallbackFunction_t``. Calling the vanilla FreeRTOS API
``vTaskSetThreadLocalStoragePointer()`` is still valid however it is internally
defined to call ``vTaskSetThreadLocalStoragePointerAndDelCallback()`` with a
``NULL`` pointer as the deletion call back. This results in the selected Thread
Local Storage Pointer to have no deletion call back.
In IDF the FreeRTOS thread local storage at index 0 is reserved and is used to implement Deletion callbacks are set alongside TLSP by calling
the pthreads API thread local storage (pthread_getspecific() & pthread_setspecific()). :cpp:func:`vTaskSetThreadLocalStoragePointerAndDelCallback`. Calling the vanilla
Other indexes can be used for any purpose, provided FreeRTOS function :cpp:func:`vTaskSetThreadLocalStoragePointer` will simply set the
:ref:`CONFIG_FREERTOS_THREAD_LOCAL_STORAGE_POINTERS` is set to a high enough value. TLSP's associated Deletion Callback to `NULL` meaning that no callback will be
called for that TLSP during task deletion. If a deletion callback is `NULL`,
users should manually free the memory pointed to by the associated TLSP before
task deletion in order to avoid memory leak.
For more details see :component_file:`freertos/include/freertos/task.h` :ref:`CONFIG_FREERTOS_THREAD_LOCAL_STORAGE_POINTERS` in menuconfig can be used
to configure the number TLSP and Deletion Callbacks a TCB will have.
For more details see :doc:`FreeRTOS API reference<../api-reference/system/freertos>`.
.. _esp-idf-freertos-configuration: .. _esp-idf-freertos-configuration:
@ -434,7 +491,7 @@ number of Thread Local Storage Pointers each task will have in ESP-IDF
FreeRTOS. FreeRTOS.
:ref:`CONFIG_SUPPORT_STATIC_ALLOCATION` will enable the backported :ref:`CONFIG_SUPPORT_STATIC_ALLOCATION` will enable the backported
functionality of ``xTaskCreateStaticPinnedToCore()`` in ESP-IDF FreeRTOS functionality of :cpp:func:`xTaskCreateStaticPinnedToCore` in ESP-IDF FreeRTOS
:ref:`CONFIG_FREERTOS_ASSERT_ON_UNTESTED_FUNCTION` will trigger a halt in :ref:`CONFIG_FREERTOS_ASSERT_ON_UNTESTED_FUNCTION` will trigger a halt in
particular functions in ESP-IDF FreeRTOS which have not been fully tested particular functions in ESP-IDF FreeRTOS which have not been fully tested

View file

@ -62,14 +62,32 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol
ST R2, R1, 0 // write value of R2 into the third array element, ST R2, R1, 0 // write value of R2 into the third array element,
// i.e. array[2] // i.e. array[2]
Note about instruction execution time
-------------------------------------
ULP coprocessor is clocked from RTC_FAST_CLK, which is normally derived from the internal 8MHz oscillator. Applications which need to know exact ULP clock frequency can calibrate it against the main XTAL clock::
#include "soc/rtc.h"
// calibrate 8M/256 clock against XTAL, get 8M/256 clock period
uint32_t rtc_8md256_period = rtc_clk_cal(RTC_CAL_8MD256, 100);
uint32_t rtc_fast_freq_hz = 1000000ULL * (1 << RTC_CLK_CAL_FRACT) * 256 / rtc_8md256_period;
ULP coprocessor needs 2 clock cycle to fetch each instuction (fetching is not pipelined), plus certain number of cycles to execute, depending on the instruction. See description of each instruction for details on the execution time.
Note that when accessing RTC memories and RTC registers, ULP coprocessor has lower priority than the main CPUs. This means that ULP coprocessor execution may be suspended while the main CPUs access same memory region as the ULP.
**NOP** - no operation **NOP** - no operation
---------------------- ----------------------
**Syntax:** **Syntax**
**NOP** **NOP**
**Operands:** **Operands**
None None
**Description:** **Cycles**
2 (fetch) + 1 (execute)
**Description**
No operation is performed. Only the PC is incremented. No operation is performed. Only the PC is incremented.
**Example**:: **Example**::
@ -80,20 +98,22 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol
**ADD** - Add to register **ADD** - Add to register
------------------------- -------------------------
**Syntax:** **Syntax**
**ADD** *Rdst, Rsrc1, Rsrc2* **ADD** *Rdst, Rsrc1, Rsrc2*
**ADD** *Rdst, Rsrc1, imm* **ADD** *Rdst, Rsrc1, imm*
**Operands:** **Operands**
- *Rdst* - Register R[0..3] - *Rdst* - Register R[0..3]
- *Rsrc1* - Register R[0..3] - *Rsrc1* - Register R[0..3]
- *Rsrc2* - Register R[0..3] - *Rsrc2* - Register R[0..3]
- *Imm* - 16-bit signed value - *Imm* - 16-bit signed value
**Cycles**
2 (fetch) + 2 (execute)
**Description:** **Description**
The instruction adds source register to another source register or to a 16-bit signed value and stores result to the destination register. The instruction adds source register to another source register or to a 16-bit signed value and stores result to the destination register.
**Examples**:: **Examples**::
@ -115,21 +135,24 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol
**SUB** - Subtract from register **SUB** - Subtract from register
-------------------------------- --------------------------------
**Syntax:** **Syntax**
**SUB** *Rdst, Rsrc1, Rsrc2* **SUB** *Rdst, Rsrc1, Rsrc2*
**SUB** *Rdst, Rsrc1, imm* **SUB** *Rdst, Rsrc1, imm*
**Operands:** **Operands**
- *Rdst* - Register R[0..3] - *Rdst* - Register R[0..3]
- *Rsrc1* - Register R[0..3] - *Rsrc1* - Register R[0..3]
- *Rsrc2* - Register R[0..3] - *Rsrc2* - Register R[0..3]
- *Imm* - 16-bit signed value - *Imm* - 16-bit signed value
**Description:** **Cycles**
2 (fetch) + 2 (execute)
**Description**
The instruction subtracts the source register from another source register or subtracts 16-bit signed value from a source register, and stores result to the destination register. The instruction subtracts the source register from another source register or subtracts 16-bit signed value from a source register, and stores result to the destination register.
**Examples:**:: **Examples**::
1: SUB R1, R2, R3 //R1 = R2 - R3 1: SUB R1, R2, R3 //R1 = R2 - R3
@ -146,21 +169,24 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol
**AND** - Logical AND of two operands **AND** - Logical AND of two operands
------------------------------------- -------------------------------------
**Syntax:** **Syntax**
**AND** *Rdst, Rsrc1, Rsrc2* **AND** *Rdst, Rsrc1, Rsrc2*
**AND** *Rdst, Rsrc1, imm* **AND** *Rdst, Rsrc1, imm*
**Operands:** **Operands**
- *Rdst* - Register R[0..3] - *Rdst* - Register R[0..3]
- *Rsrc1* - Register R[0..3] - *Rsrc1* - Register R[0..3]
- *Rsrc2* - Register R[0..3] - *Rsrc2* - Register R[0..3]
- *Imm* - 16-bit signed value - *Imm* - 16-bit signed value
**Description:** **Cycles**
2 (fetch) + 2 (execute)
**Description**
The instruction does logical AND of a source register and another source register or 16-bit signed value and stores result to the destination register. The instruction does logical AND of a source register and another source register or 16-bit signed value and stores result to the destination register.
**Example**:: **Examples**::
1: AND R1, R2, R3 //R1 = R2 & R3 1: AND R1, R2, R3 //R1 = R2 & R3
@ -183,13 +209,15 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol
**OR** *Rdst, Rsrc1, imm* **OR** *Rdst, Rsrc1, imm*
**Operands** **Operands**
- *Rdst* - Register R[0..3] - *Rdst* - Register R[0..3]
- *Rsrc1* - Register R[0..3] - *Rsrc1* - Register R[0..3]
- *Rsrc2* - Register R[0..3] - *Rsrc2* - Register R[0..3]
- *Imm* - 16-bit signed value - *Imm* - 16-bit signed value
**Cycles**
2 (fetch) + 2 (execute)
**Description** **Description**
The instruction does logical OR of a source register and another source register or 16-bit signed value and stores result to the destination register. The instruction does logical OR of a source register and another source register or 16-bit signed value and stores result to the destination register.
@ -223,6 +251,9 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol
- *Rsrc2* - Register R[0..3] - *Rsrc2* - Register R[0..3]
- *Imm* - 16-bit signed value - *Imm* - 16-bit signed value
**Cycles**
2 (fetch) + 2 (execute)
**Description** **Description**
The instruction does logical shift to left of source register to number of bits from another source register or 16-bit signed value and store result to the destination register. The instruction does logical shift to left of source register to number of bits from another source register or 16-bit signed value and store result to the destination register.
@ -255,6 +286,9 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol
*Rsrc2* - Register R[0..3] *Rsrc2* - Register R[0..3]
*Imm* - 16-bit signed value *Imm* - 16-bit signed value
**Cycles**
2 (fetch) + 2 (execute)
**Description** **Description**
The instruction does logical shift to right of source register to number of bits from another source register or 16-bit signed value and store result to the destination register. The instruction does logical shift to right of source register to number of bits from another source register or 16-bit signed value and store result to the destination register.
@ -286,6 +320,9 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol
- *Rsrc* Register R[0..3] - *Rsrc* Register R[0..3]
- *Imm* 16-bit signed value - *Imm* 16-bit signed value
**Cycles**
2 (fetch) + 2 (execute)
**Description** **Description**
The instruction move to destination register value from source register or 16-bit signed value. The instruction move to destination register value from source register or 16-bit signed value.
@ -318,6 +355,9 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol
- *Rdst* Register R[0..3], address of the destination, in 32-bit words - *Rdst* Register R[0..3], address of the destination, in 32-bit words
- *Offset* 10-bit signed value, offset in bytes - *Offset* 10-bit signed value, offset in bytes
**Cycles**
2 (fetch) + 4 (execute)
**Description** **Description**
The instruction stores the 16-bit value of Rsrc to the lower half-word of memory with address Rdst+offset. The upper half-word is written with the current program counter (PC), expressed in words, shifted left by 5 bits:: The instruction stores the 16-bit value of Rsrc to the lower half-word of memory with address Rdst+offset. The upper half-word is written with the current program counter (PC), expressed in words, shifted left by 5 bits::
@ -352,6 +392,9 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol
*Offset* 10-bit signed value, offset in bytes *Offset* 10-bit signed value, offset in bytes
**Cycles**
2 (fetch) + 4 (execute)
**Description** **Description**
The instruction loads lower 16-bit half-word from memory with address Rsrc+offset into the destination register Rdst:: The instruction loads lower 16-bit half-word from memory with address Rsrc+offset into the destination register Rdst::
@ -395,6 +438,8 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol
- EQ jump if last ALU operation result was zero - EQ jump if last ALU operation result was zero
- OV jump if last ALU has set overflow flag - OV jump if last ALU has set overflow flag
**Cycles**
2 (fetch) + 2 (execute)
**Description** **Description**
The instruction makes jump to the specified address. Jump can be either unconditional or based on an ALU flag. The instruction makes jump to the specified address. Jump can be either unconditional or based on an ALU flag.
@ -432,6 +477,9 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol
- *LT* (less than) jump if value in R0 < threshold - *LT* (less than) jump if value in R0 < threshold
**Cycles**
2 (fetch) + 2 (execute)
**Description** **Description**
The instruction makes a jump to a relative address if condition is true. Condition is the result of comparison of R0 register value and the threshold value. The instruction makes a jump to a relative address if condition is true. Condition is the result of comparison of R0 register value and the threshold value.
@ -461,6 +509,9 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol
- *LT* (less than) jump if value in stage_cnt < threshold - *LT* (less than) jump if value in stage_cnt < threshold
- *GT* (greater than) jump if value in stage_cnt > threshold - *GT* (greater than) jump if value in stage_cnt > threshold
**Cycles**
2 (fetch) + 2 (execute)
**Description** **Description**
The instruction makes a jump to a relative address if condition is true. Condition is the result of comparison of count register value and threshold value. The instruction makes a jump to a relative address if condition is true. Condition is the result of comparison of count register value and threshold value.
@ -487,6 +538,9 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol
**Description** **Description**
The instruction sets the stage count register to 0 The instruction sets the stage count register to 0
**Cycles**
2 (fetch) + 2 (execute)
**Examples**:: **Examples**::
1: STAGE_RST // Reset stage count register 1: STAGE_RST // Reset stage count register
@ -502,6 +556,9 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol
**Operands** **Operands**
- *Value* 8 bits value - *Value* 8 bits value
**Cycles**
2 (fetch) + 2 (execute)
**Description** **Description**
The instruction increments stage count register by given value. The instruction increments stage count register by given value.
@ -525,6 +582,9 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol
**Operands** **Operands**
- *Value* 8 bits value - *Value* 8 bits value
**Cycles**
2 (fetch) + 2 (execute)
**Description** **Description**
The instruction decrements stage count register by given value. The instruction decrements stage count register by given value.
@ -548,17 +608,21 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol
**Operands** **Operands**
No operands No operands
**Cycles**
2 (fetch) + 2 (execute)
**Description** **Description**
The instruction halt the processor to the power down mode The instruction halts the ULP coprocessor and restarts ULP wakeup timer, if it is enabled.
**Examples**:: **Examples**::
1: HALT // Move chip to powerdown 1: HALT // Halt the coprocessor
**WAKE** wakeup the chip **WAKE** Wake up the chip
-------------------------- ---------------------------
**Syntax** **Syntax**
**WAKE** **WAKE**
@ -566,6 +630,9 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol
**Operands** **Operands**
No operands No operands
**Cycles**
2 (fetch) + 2 (execute)
**Description** **Description**
The instruction sends an interrupt from ULP to RTC controller. The instruction sends an interrupt from ULP to RTC controller.
@ -573,9 +640,15 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol
- If the SoC is not in deep sleep mode, and ULP interrupt bit (RTC_CNTL_ULP_CP_INT_ENA) is set in RTC_CNTL_INT_ENA_REG register, RTC interrupt will be triggered. - If the SoC is not in deep sleep mode, and ULP interrupt bit (RTC_CNTL_ULP_CP_INT_ENA) is set in RTC_CNTL_INT_ENA_REG register, RTC interrupt will be triggered.
Note that before using WAKE instruction, ULP program may needs to wait until RTC controller is ready to wake up the main CPU. This is indicated using RTC_CNTL_RDY_FOR_WAKEUP bit of RTC_CNTL_LOW_POWER_ST_REG register. If WAKE instruction is executed while RTC_CNTL_RDY_FOR_WAKEUP is zero, it has no effect (wake up does not occur).
**Examples**:: **Examples**::
1: WAKE // Trigger wake up 1: is_rdy_for_wakeup: // Read RTC_CNTL_RDY_FOR_WAKEUP bit
READ_RTC_FIELD(RTC_CNTL_LOW_POWER_ST_REG, RTC_CNTL_RDY_FOR_WAKEUP)
AND r0, r0, 1
JUMP is_rdy_for_wakeup, eq // Retry until the bit is set
WAKE // Trigger wake up
REG_WR 0x006, 24, 24, 0 // Stop ULP timer (clear RTC_CNTL_ULP_CP_SLP_TIMER_EN) REG_WR 0x006, 24, 24, 0 // Stop ULP timer (clear RTC_CNTL_ULP_CP_SLP_TIMER_EN)
HALT // Stop the ULP program HALT // Stop the ULP program
// After these instructions, SoC will wake up, // After these instructions, SoC will wake up,
@ -592,6 +665,9 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol
**Operands** **Operands**
- *sleep_reg* 0..4, selects one of ``SENS_ULP_CP_SLEEP_CYCx_REG`` registers. - *sleep_reg* 0..4, selects one of ``SENS_ULP_CP_SLEEP_CYCx_REG`` registers.
**Cycles**
2 (fetch) + 2 (execute)
**Description** **Description**
The instruction selects which of the ``SENS_ULP_CP_SLEEP_CYCx_REG`` (x = 0..4) register values is to be used by the ULP wakeup timer as wakeup period. By default, the value from ``SENS_ULP_CP_SLEEP_CYC0_REG`` is used. The instruction selects which of the ``SENS_ULP_CP_SLEEP_CYCx_REG`` (x = 0..4) register values is to be used by the ULP wakeup timer as wakeup period. By default, the value from ``SENS_ULP_CP_SLEEP_CYC0_REG`` is used.
@ -612,6 +688,9 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol
**Operands** **Operands**
- *Cycles* number of cycles for wait - *Cycles* number of cycles for wait
**Cycles**
2 (fetch) + *Cycles* (execute)
**Description** **Description**
The instruction delays for given number of cycles. The instruction delays for given number of cycles.
@ -635,6 +714,8 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol
- *Rdst* Destination Register R[0..3], result will be stored to this register - *Rdst* Destination Register R[0..3], result will be stored to this register
- *Wait_Delay* number of cycles used to perform the measurement - *Wait_Delay* number of cycles used to perform the measurement
**Cycles**
2 (fetch) + *Wait_Delay* + 3 * TSENS_CLK
**Description** **Description**
The instruction performs measurement using TSENS and stores the result into a general purpose register. The instruction performs measurement using TSENS and stores the result into a general purpose register.
@ -660,6 +741,9 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol
- *Sar_sel* Select ADC: 0 = SARADC1, 1 = SARADC2 - *Sar_sel* Select ADC: 0 = SARADC1, 1 = SARADC2
- *Mux* - selected PAD, SARADC Pad[Mux+1] is enabled - *Mux* - selected PAD, SARADC Pad[Mux+1] is enabled
**Cycles**
2 (fetch) + 21 + max(1, SAR_AMP_WAIT1) + max(1, SAR_AMP_WAIT2) + max(1, SAR_AMP_WAIT3) + SARx_SAMPLE_CYCLE + SARx_SAMPLE_BIT
**Description** **Description**
The instruction makes measurements from ADC. The instruction makes measurements from ADC.
@ -667,6 +751,51 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol
1: ADC R1, 0, 1 // Measure value using ADC1 pad 2 and store result into R1 1: ADC R1, 0, 1 // Measure value using ADC1 pad 2 and store result into R1
**I2C_RD** - read single byte from I2C slave
----------------------------------------------
**Syntax**
- **I2C_RD** *Sub_addr, High, Low, Slave_sel*
**Operands**
- *Sub_addr* Address within the I2C slave to read.
- *High*, *Low* — Define range of bits to read. Bits outside of [High, Low] range are masked.
- *Slave_sel* - Index of I2C slave address to use.
**Cycles**
2 (fetch) + I2C communication time
**Description**
``I2C_RD`` instruction reads one byte from I2C slave with index ``Slave_sel``. Slave address (in 7-bit format) has to be set in advance into `SENS_I2C_SLAVE_ADDRx` register field, where ``x == Slave_sel``.
8 bits of read result is stored into `R0` register.
**Examples**::
1: I2C_RD 0x10, 7, 0, 0 // Read byte from sub-address 0x10 of slave with address set in SENS_I2C_SLAVE_ADDR0
**I2C_WR** - write single byte to I2C slave
----------------------------------------------
**Syntax**
- **I2C_WR** *Sub_addr, Value, High, Low, Slave_sel*
**Operands**
- *Sub_addr* Address within the I2C slave to write.
- *Value* 8-bit value to be written.
- *High*, *Low* — Define range of bits to write. Bits outside of [High, Low] range are masked.
- *Slave_sel* - Index of I2C slave address to use.
**Cycles**
2 (fetch) + I2C communication time
**Description**
``I2C_WR`` instruction writes one byte to I2C slave with index ``Slave_sel``. Slave address (in 7-bit format) has to be set in advance into `SENS_I2C_SLAVE_ADDRx` register field, where ``x == Slave_sel``.
**Examples**::
1: I2C_WR 0x20, 0x33, 7, 0, 1 // Write byte 0x33 to sub-address 0x20 of slave with address set in SENS_I2C_SLAVE_ADDR1.
**REG_RD** read from peripheral register **REG_RD** read from peripheral register
------------------------------------------ ------------------------------------------
@ -679,6 +808,9 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol
- *High* High part of R0 - *High* High part of R0
- *Low* Low part of R0 - *Low* Low part of R0
**Cycles**
2 (fetch) + 6 (execute)
**Description** **Description**
The instruction reads up to 16 bits from a peripheral register into a general purpose register: ``R0 = REG[Addr][High:Low]``. The instruction reads up to 16 bits from a peripheral register into a general purpose register: ``R0 = REG[Addr][High:Low]``.
@ -704,6 +836,9 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol
- *Low* Low part of R0 - *Low* Low part of R0
- *Data* value to write, 8 bits - *Data* value to write, 8 bits
**Cycles**
2 (fetch) + 10 (execute)
**Description** **Description**
The instruction writes up to 8 bits from a general purpose register into a peripheral register. ``REG[Addr][High:Low] = data`` The instruction writes up to 8 bits from a general purpose register into a peripheral register. ``REG[Addr][High:Low] = data``

View file

@ -3,8 +3,8 @@ Unit Testing in ESP32
ESP-IDF comes with a unit test app based on Unity - unit test framework. Unit tests are integrated in the ESP-IDF repository and are placed in ``test`` subdirectory of each component respectively. ESP-IDF comes with a unit test app based on Unity - unit test framework. Unit tests are integrated in the ESP-IDF repository and are placed in ``test`` subdirectory of each component respectively.
Adding unit tests Add normal test cases
----------------- ---------------------
Unit tests are added in the ``test`` subdirectory of the respective component. Unit tests are added in the ``test`` subdirectory of the respective component.
Tests are added in C files, a single C file can include multiple test cases. Tests are added in C files, a single C file can include multiple test cases.
@ -31,6 +31,59 @@ Each `test` subdirectory needs to include component.mk file with at least the fo
See http://www.throwtheswitch.org/unity for more information about writing tests in Unity. See http://www.throwtheswitch.org/unity for more information about writing tests in Unity.
Add multiple devices test cases
-------------------------------
The normal test cases will be executed on one DUT (Device Under Test). Components need to communicate with each other (like GPIO, SPI ...) can't be tested with normal test cases.
Multiple devices test cases support writing and running test with multiple DUTs.
Here's an example of multiple devices test case::
void gpio_master_test()
{
gpio_config_t slave_config = {
.pin_bit_mask = 1 << MASTER_GPIO_PIN,
.mode = GPIO_MODE_INPUT,
};
gpio_config(&slave_config);
unity_wait_for_signal("output high level");
TEST_ASSERT(gpio_get_level(MASTER_GPIO_PIN) == 1);
}
void gpio_slave_test()
{
gpio_config_t master_config = {
.pin_bit_mask = 1 << SLAVE_GPIO_PIN,
.mode = GPIO_MODE_OUTPUT,
};
gpio_config(&master_config);
gpio_set_level(SLAVE_GPIO_PIN, 1);
unity_send_signal("output high level");
}
TEST_CASE_MULTIPLE_DEVICES("gpio multiple devices test example", "[driver]", gpio_master_test, gpio_slave_test);
The macro ``TEST_CASE_MULTIPLE_DEVICES`` is used to declare multiple devices test cases.
First argument is test case name, second argument is test case description.
From the third argument, upto 5 test functions can be defined, each function will be the entry point of tests running on each DUT.
Running test cases from different DUTs could require synchronizing between DUTs. We provide ``unity_wait_for_signal`` and ``unity_send_signal`` to support synchronizing with UART.
As the secnario in the above example, slave should get GPIO level after master set level. DUT UART console will prompt and requires user interaction:
DUT1 (master) console::
Waiting for signal: [output high level]!
Please press "Enter" key to once any board send this signal.
DUT2 (slave) console::
Send signal: [output high level]!
Once the signal is set from DUT2, you need to press "Enter" on DUT1, then DUT1 unblocks from ``unity_wait_for_signal`` and starts to change GPIO level.
Building unit test app Building unit test app
---------------------- ----------------------
@ -55,7 +108,32 @@ Running unit tests
After flashing reset the ESP32 and it will boot the unit test app. After flashing reset the ESP32 and it will boot the unit test app.
Unit test app prints a test menu with all available tests. When unit test app is idle, press "Enter" will make it print test menu with all available tests::
Here's the test menu, pick your combo:
(1) "esp_ota_begin() verifies arguments" [ota]
(2) "esp_ota_get_next_update_partition logic" [ota]
(3) "Verify bootloader image in flash" [bootloader_support]
(4) "Verify unit test app image" [bootloader_support]
(5) "can use new and delete" [cxx]
(6) "can call virtual functions" [cxx]
(7) "can use static initializers for non-POD types" [cxx]
(8) "can use std::vector" [cxx]
(9) "static initialization guards work as expected" [cxx]
(10) "global initializers run in the correct order" [cxx]
(11) "before scheduler has started, static initializers work correctly" [cxx]
(12) "adc2 work with wifi" [adc]
(13) "gpio master/slave test example" [ignore][misc][test_env=UT_T2_1]
(1) "gpio_master_test"
(2) "gpio_slave_test"
(14) "SPI Master clockdiv calculation routines" [spi]
(15) "SPI Master test" [spi][ignore]
(16) "SPI Master test, interaction of multiple devs" [spi][ignore]
(17) "SPI Master no response when switch from host1 (HSPI) to host2 (VSPI)" [spi]
(18) "SPI Master DMA test, TX and RX in different regions" [spi]
(19) "SPI Master DMA test: length, start, not aligned" [spi]
Normal case will print the case name and description. Master slave cases will also print the sub-menu (the registered test function names).
Test cases can be run by inputting one of the following: Test cases can be run by inputting one of the following:
@ -66,3 +144,12 @@ Test cases can be run by inputting one of the following:
- Module name in square brackets to run all test cases for a specific module - Module name in square brackets to run all test cases for a specific module
- An asterisk to run all test cases - An asterisk to run all test cases
After you select multiple devices test case, it will print sub menu::
Running gpio master/slave test example...
gpio master/slave test example
(1) "gpio_master_test"
(2) "gpio_slave_test"
You need to input number to select the test running on the DUT.

View file

@ -34,11 +34,10 @@ Short example of I2S configuration:
.bits_per_sample = 16, .bits_per_sample = 16,
.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
.communication_format = I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB, .communication_format = I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB,
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, // high interrupt priority .intr_alloc_flags = 0, // default interrupt priority
.dma_buf_count = 8, .dma_buf_count = 8,
.dma_buf_len = 64, .dma_buf_len = 64,
.use_apll = 0, .use_apll = 0
.apll_param = I2S_APLL_NONE
}; };
static const i2s_pin_config_t pin_config = { static const i2s_pin_config_t pin_config = {
@ -71,9 +70,10 @@ Short example configuring I2S to use internal DAC for analog output::
.bits_per_sample = 16, /* the DAC module will only take the 8bits from MSB */ .bits_per_sample = 16, /* the DAC module will only take the 8bits from MSB */
.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
.communication_format = I2S_COMM_FORMAT_I2S_MSB, .communication_format = I2S_COMM_FORMAT_I2S_MSB,
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, // high interrupt priority .intr_alloc_flags = 0, // default interrupt priority
.dma_buf_count = 8, .dma_buf_count = 8,
.dma_buf_len = 64 .dma_buf_len = 64,
.use_apll = 0
}; };
... ...

View file

@ -0,0 +1,42 @@
FreeRTOS
========
Overview
--------
This section contains documentation of FreeRTOS types, functions, and macros. It is automatically generated from FreeRTOS header files.
For more information about FreeRTOS features specific to ESP-IDF, see :doc:`ESP-IDF FreeRTOS SMP Changes<../../api-guides/freertos-smp>`.
Task API
--------
.. include:: /_build/inc/task.inc
Queue API
---------
.. include:: /_build/inc/queue.inc
Semaphore API
-------------
.. include:: /_build/inc/semphr.inc
Timer API
---------
.. include:: /_build/inc/timers.inc
Event Group API
---------------
.. include:: /_build/inc/event_groups.inc
Ringbuffer API
--------------
.. include:: /_build/inc/ringbuf.inc

View file

@ -1,7 +1,7 @@
.. _hooks_api_reference: .. _hooks_api_reference:
ESP-IDF FreeRTOS Hooks FreeRTOS Hooks
====================== ==============
Overview Overview
-------- --------

View file

@ -4,19 +4,20 @@ System API
.. toctree:: .. toctree::
:maxdepth: 1 :maxdepth: 1
FreeRTOS <freertos>
FreeRTOS Hooks <hooks>
Heap Memory Allocation <mem_alloc> Heap Memory Allocation <mem_alloc>
Heap Memory Debugging <heap_debug> Heap Memory Debugging <heap_debug>
Interrupt Allocation <intr_alloc> Interrupt Allocation <intr_alloc>
Watchdogs <wdts> Watchdogs <wdts>
Hooks <hooks>
Inter-Processor Call <ipc> Inter-Processor Call <ipc>
High Resolution Timer <esp_timer> High Resolution Timer <esp_timer>
Over The Air Updates (OTA) <ota>
Sleep Modes <sleep_modes>
Power Management <power_management>
Logging <log> Logging <log>
Base MAC address <base_mac_address>
Application Level Tracing <app_trace> Application Level Tracing <app_trace>
Power Management <power_management>
Sleep Modes <sleep_modes>
Base MAC address <base_mac_address>
Over The Air Updates (OTA) <ota>
Example code for this API section is provided in :example:`system` directory of ESP-IDF examples. Example code for this API section is provided in :example:`system` directory of ESP-IDF examples.

View file

@ -11,15 +11,30 @@ What You Need
* 1 × USB A / Micro USB B cable * 1 × USB A / Micro USB B cable
* 1 × PC loaded with Windows, Linux or Mac OS * 1 × PC loaded with Windows, Linux or Mac OS
If you like to start using this board right now, go directly to section `Start Application Development`_.
Overview Overview
-------- --------
ESP32-PICO-KIT V4 is a mini development board based on the ESP32-PICO-D4 SIP module produced by `Espressif <https://espressif.com>`_. For easy interfacing, all the IO signals and system power on ESP32-PICO-D4 are led out through two 20 x 0.1" pitch header pads on both sides of the development board. To make the ESP32-PICO-KIT V4 fit into mini breadboards, the header pads are populated with two 17 pin headers. Remaining 2 x 3 pads grouped on one side of the board besides the antenna are not populated. If required, the additional 2 x 3 pin headers may be soldered later by the user. The development board integrates a USB-UART Bridge circuit, allowing the developers to connect the board to a PC's USB port for downloads and debugging. ESP32-PICO-KIT V4 is a mini development board produced by `Espressif <https://espressif.com>`_. At the core of this board is the ESP32-PICO-D4, a System-in-Package (SIP) module with complete Wi-Fi and Bluetooth functionalities. Comparing to other ESP32 chips, the ESP32-PICO-D4 integrates several peripheral components in one single package, that otherwise would need to be installed separately. This includes a 40 MHz crystal oscillator, 4 MB flash, filter capacitors and RF matching links in. This greatly reduces quantity and costs of additional components, subsequent assembly and testing cost, as well as overall product complexity.
The development board integrates a USB-UART Bridge circuit, allowing the developers to connect the board to a PC's USB port for downloads and debugging.
For easy interfacing, all the IO signals and system power on ESP32-PICO-D4 are led out through two rows of 20 x 0.1" pitch header pads on both sides of the development board. To make the ESP32-PICO-KIT V4 fit into mini breadboards, the header pads are populated with two rows of 17 pin headers. Remaining 2 x 3 pads grouped on each side of the board besides the antenna are not populated. The remaining 2 x 3 pin headers may be soldered later by the user.
.. note:: .. note::
The 2 x 3 pads not populated with pin headers are internally connected to the flash memory embedded in the ESP32-PICO-D4 SIP module. For more details see module's datasheet in :ref:`get-started-pico-kit-v4-related-documents`. The 2 x 3 pads not populated with pin headers are internally connected to the flash memory embedded in the ESP32-PICO-D4 SIP module. For more details see module's datasheet in `Related Documents`_.
The board dimensions are 52 x 20.3 x 10 mm (2.1" x 0.8" x 0.4"), see section `Board Dimensions`_. An overview functional block diagram is shown below.
.. figure:: ../_static/esp32-pico-kit-v4-functional-block-diagram.png
:align: center
:alt: ESP32-PICO-KIT V4 functional block diagram
:figclass: align-center
ESP32-PICO-KIT V4 functional block diagram
Functional Description Functional Description
@ -29,16 +44,20 @@ The following list and figure below describe key components, interfaces and cont
ESP32-PICO-D4 ESP32-PICO-D4
Standard ESP32-PICO-D4 module soldered to the ESP32-PICO-KIT V4 board. The complete system of the ESP32 chip has been integrated into the SIP module, requiring only external antenna with LC matching network, decoupling capacitors and pull-up resistors for EN signals to function properly. Standard ESP32-PICO-D4 module soldered to the ESP32-PICO-KIT V4 board. The complete system of the ESP32 chip has been integrated into the SIP module, requiring only external antenna with LC matching network, decoupling capacitors and pull-up resistors for EN signals to function properly.
LDO
5V-to-3.3V Low dropout voltage regulator (LDO).
USB-UART Bridge USB-UART Bridge
A single chip USB-UART bridge provides up to 1 Mbps transfers rates. A single chip USB-UART bridge provides up to 1 Mbps transfers rates.
I/O
All the pins on ESP32-PICO-D4 are broken out to the pin headers on the board. Users can program ESP32 to enable multiple functions such as PWM, ADC, DAC, I2C, I2S, SPI, etc.
Micro USB Port Micro USB Port
USB interface. It functions as the power supply for the board and the communication interface between PC and ESP32-PICO-KIT V4. USB interface. It functions as the power supply for the board and the communication interface between PC and ESP32-PICO-KIT V4.
EN Button 5V Power On LED
Reset button; pressing this button resets the system. This light emitting diode lits when the USB or an external 5V power supply is applied to the board. For details see schematic in `Related Documents`_.
I/O
All the pins on ESP32-PICO-D4 are broken out to the pin headers on the board. Users can program ESP32 to enable multiple functions such as PWM, ADC, DAC, I2C, I2S, SPI, etc. For details please see section `Pin Descriptions`_.
BOOT Button BOOT Button
Holding down the Boot button and pressing the EN button initiates the firmware download mode. Then user can download firmware through the serial port. Holding down the Boot button and pressing the EN button initiates the firmware download mode. Then user can download firmware through the serial port.
EN Button
Reset button; pressing this button resets the system.
.. _get-started-pico-kit-v4-board-front: .. _get-started-pico-kit-v4-board-front:
@ -65,7 +84,7 @@ There following options are available to provide power supply to the ESP32-PICO-
Start Application Development Start Application Development
------------------------------ -----------------------------
Before powering up the ESP32-PICO-KIT V4, please make sure that the board has been received in good condition with no obvious signs of damage. Before powering up the ESP32-PICO-KIT V4, please make sure that the board has been received in good condition with no obvious signs of damage.
@ -77,7 +96,106 @@ To start development of applications, proceed to section :doc:`index`, that will
* :ref:`get-started-build-monitor` instantly what the application is doing * :ref:`get-started-build-monitor` instantly what the application is doing
.. _get-started-pico-kit-v4-related-documents: Pin Descriptions
----------------
The two tables below provide the **Name** and **Function** of I/O headers on both sides of the board, see :ref:`get-started-pico-kit-v4-board-front`. The pin numbering and header names are the same as on a schematic in `Related Documents`_.
Header J2
"""""""""
====== ================= ====== ======================================================
No. Name Type Function
====== ================= ====== ======================================================
1 FLASH_SD1 (FSD1) I/O | GPIO8, SD_DATA1, SPID, HS1_DATA1 :ref:`(1) <get-started-pico-kit-v4-pin-notes>` , U2CTS
2 FLASH_SD3 (FSD3) I/O | GPIO7, SD_DATA0, SPIQ, HS1_DATA0 :ref:`(1) <get-started-pico-kit-v4-pin-notes>` , U2RTS
3 FLASH_CLK (FCLK) I/O | GPIO11, SD_CMD, SPICS0, HS1_CMD :ref:`(1) <get-started-pico-kit-v4-pin-notes>` , U1RTS
4 IO21 I/O | GPIO21, VSPIHD, EMAC_TX_EN
5 IO22 I/O | GPIO22, VSPIWP, U0RTS, EMAC_TXD1
6 IO19 I/O | GPIO19, VSPIQ, U0CTS, EMAC_TXD0
7 IO23 I/O | GPIO23, VSPID, HS1_STROBE
8 IO18 I/O | GPIO18, VSPICLK, HS1_DATA7
9 IO5 I/O | GPIO5, VSPICS0, HS1_DATA6, EMAC_RX_CLK
10 IO10 I/O | GPIO10, SD_DATA3, SPIWP, HS1_DATA3, U1TXD
11 IO9 I/O | GPIO9, SD_DATA2, SPIHD, HS1_DATA2, U1RXD
12 RXD0 I/O | GPIO3, U0RXD :ref:`(4) <get-started-pico-kit-v4-pin-notes>` , CLK_OUT2
13 TXD0 I/O | GPIO1, U0TXD :ref:`(4) <get-started-pico-kit-v4-pin-notes>` , CLK_OUT3, EMAC_RXD2
14 IO35 I | ADC1_CH7, RTC_GPIO5
15 IO34 I | ADC1_CH6, RTC_GPIO4
16 IO38 I | GPIO38, ADC1_CH2, ADC_PRE_AMP :ref:`(2b) <get-started-pico-kit-v4-pin-notes>` , RTC_GPIO2
17 IO37 I | GPIO37, ADC_PRE_AMP :ref:`(2a) <get-started-pico-kit-v4-pin-notes>` , ADC1_CH1, RTC_GPIO1
18 EN I | CHIP_PU
19 GND P | Ground
20 VDD33 (3V3) P | 3.3V power supply
====== ================= ====== ======================================================
Header J3
"""""""""
====== ================= ====== ======================================================
No. Name Type Function
====== ================= ====== ======================================================
1 FLASH_CS (FCS) I/O | GPIO16, HS1_DATA4 :ref:`(1) <get-started-pico-kit-v4-pin-notes>` , U2RXD, EMAC_CLK_OUT
2 FLASH_SD0 (FSD0) I/O | GPIO17, HS1_DATA5 :ref:`(1) <get-started-pico-kit-v4-pin-notes>` , U2TXD, EMAC_CLK_OUT_180
3 FLASH_SD2 (FSD2) I/O | GPIO6, SD_CLK, SPICLK, HS1_CLK :ref:`(1) <get-started-pico-kit-v4-pin-notes>` , U1CTS
4 SENSOR_VP (FSVP) I | GPIO36, ADC1_CH0, ADC_PRE_AMP :ref:`(2a) <get-started-pico-kit-v4-pin-notes>` , RTC_GPIO0
5 SENSOR_VN (FSVN) I | GPIO39, ADC1_CH3, ADC_PRE_AMP :ref:`(2b) <get-started-pico-kit-v4-pin-notes>` , RTC_GPIO3
6 IO25 I/O | GPIO25, DAC_1, ADC2_CH8, RTC_GPIO6, EMAC_RXD0
7 IO26 I/O | GPIO26, DAC_2, ADC2_CH9, RTC_GPIO7, EMAC_RXD1
8 IO32 I/O | 32K_XP :ref:`(3a) <get-started-pico-kit-v4-pin-notes>` , ADC1_CH4, TOUCH9, RTC_GPIO9
9 IO33 I/O | 32K_XN :ref:`(3b) <get-started-pico-kit-v4-pin-notes>` , ADC1_CH5, TOUCH8, RTC_GPIO8
10 IO27 I/O | GPIO27, ADC2_CH7, TOUCH7, RTC_GPIO17
| EMAC_RX_DV
11 IO14 I/O | ADC2_CH6, TOUCH6, RTC_GPIO16, MTMS, HSPICLK,
| HS2_CLK, SD_CLK, EMAC_TXD2
12 IO12 I/O | ADC2_CH5, TOUCH5, RTC_GPIO15, MTDI :ref:`(5) <get-started-pico-kit-v4-pin-notes>` , HSPIQ,
| HS2_DATA2, SD_DATA2, EMAC_TXD3
13 IO13 I/O | ADC2_CH4, TOUCH4, RTC_GPIO14, MTCK, HSPID,
| HS2_DATA3, SD_DATA3, EMAC_RX_ER
14 IO15 I/O | ADC2_CH3, TOUCH3, RTC_GPIO13, MTDO, HSPICS0
| HS2_CMD, SD_CMD, EMAC_RXD3
15 IO2 I/O | ADC2_CH2, TOUCH2, RTC_GPIO12, HSPIWP,
| HS2_DATA0, SD_DATA0
16 IO4 I/O | ADC2_CH0, TOUCH0, RTC_GPIO10, HSPIHD,
| HS2_DATA1, SD_DATA1, EMAC_TX_ER
17 IO0 I/O | ADC2_CH1, TOUCH1, RTC_GPIO11, CLK_OUT1
| EMAC_TX_CLK
18 VDD33 (3V3) P | 3.3V power supply
19 GND P | Ground
20 EXT_5V (5V) P | 5V power supply
====== ================= ====== ======================================================
.. _get-started-pico-kit-v4-pin-notes:
**Notes to** `Pin Descriptions`_
1. This pin is connected to the flash pin of ESP32-PICO-D4.
2. When used as ADC_PRE_AMP, connect 270 pF capacitors between: (a) SENSOR_VP and IO37, (b) SENSOR_VN and IO38.
3. 32.768 kHz crystal oscillator: (a) input, (b) output.
4. This pin is connected to the pin of the USB bridge chip on the board.
5. The operating voltage of ESP32-PICO-KITs embedded SPI flash is 3.3V. Therefore, the strapping pin MTDI should hold bit ”0” during the module power-on reset.
Board Dimensions
----------------
.. figure:: ../_static/esp32-pico-kit-v4-dimensions-back.jpg
:align: center
:alt: ESP32-PICO-KIT V4 dimensions - back
:figclass: align-center
ESP32-PICO-KIT V4 dimensions - back
.. figure:: ../_static/esp32-pico-kit-v4-dimensions-side.jpg
:align: center
:alt: ESP32-PICO-KIT V4 dimensions - side
:figclass: align-center
ESP32-PICO-KIT V4 dimensions - side
Related Documents Related Documents
----------------- -----------------
@ -86,7 +204,6 @@ Related Documents
* `ESP32-PICO-D4 Datasheet <http://espressif.com/sites/default/files/documentation/esp32-pico-d4_datasheet_en.pdf>`_ (PDF) * `ESP32-PICO-D4 Datasheet <http://espressif.com/sites/default/files/documentation/esp32-pico-d4_datasheet_en.pdf>`_ (PDF)
* :doc:`../hw-reference/index` * :doc:`../hw-reference/index`
.. * `ESP32-PICO-KIT Datasheet <http://espressif.com/sites/default/files/documentation/esp32-pico-kit_datasheet_en.pdf>`_ (PDF)
.. toctree:: .. toctree::
:hidden: :hidden:

View file

@ -127,10 +127,6 @@ ESP-IDF will be downloaded into ``~/esp/esp-idf``.
cd ~/esp/esp-idf cd ~/esp/esp-idf
git submodule update --init git submodule update --init
.. note::
While cloning submodules on **Windows** platform, the ``git clone`` command may print some output starting ``': not a valid identifier...``. This is a `known issue <https://github.com/espressif/esp-idf/issues/11>`_ but the git clone still succeeds without any problems.
.. _get-started-setup-path: .. _get-started-setup-path:
@ -206,9 +202,6 @@ Here are couple of tips on navigation and use of ``menuconfig``:
If you are **Arch Linux** user, navigate to ``SDK tool configuration`` and change the name of ``Python 2 interpreter`` from ``python`` to ``python2``. If you are **Arch Linux** user, navigate to ``SDK tool configuration`` and change the name of ``Python 2 interpreter`` from ``python`` to ``python2``.
.. note::
Most ESP32 development boards have a 40MHz crystal installed. However, some boards use a 26MHz crystal. If your board uses a 26MHz crystal, or you get garbage output from serial port after code upload, adjust the :ref:`CONFIG_ESP32_XTAL_FREQ_SEL` option in menuconfig.
.. _get-started-build-flash: .. _get-started-build-flash:
@ -283,7 +276,18 @@ Several lines below, after start up and diagnostic log, you should see "Hello wo
Restarting in 8 seconds... Restarting in 8 seconds...
Restarting in 7 seconds... Restarting in 7 seconds...
To exit monitor use shortcut ``Ctrl+]``. To execute ``make flash`` and ``make monitor`` in one shoot type ``make flash monitor``. Check section :doc:`IDF Monitor <idf-monitor>` for handy shortcuts and more details on using this application. To exit the monitor use shortcut ``Ctrl+]``.
.. note::
If instead of the messages above, you see a random garbage similar to::
e<><65><EFBFBD>)(Xn@<40>y.!<21><>(<28>PW+)<29><>Hn9a؅/9<>!<21>t5<74><35>P<EFBFBD>~<7E>k<EFBFBD><6B>e<EFBFBD>ea<65>5<EFBFBD>jA
~zY<7A><59>Y(1<>,1<15><> e<><65><EFBFBD>)(Xn@<40>y.!Dr<44>zY(<28>jpi<70>|<7C>+z5Ymvp
or monitor fails shortly after upload, your board is likely using 26MHz crystal, while the ESP-IDF assumes default of 40MHz. Exit the monitor, go back to the :ref:`menuconfig <get-started-configure>`, change :ref:`CONFIG_ESP32_XTAL_FREQ_SEL` to 26MHz, then :ref:`build and flash <get-started-build-flash>` the application again.
To execute ``make flash`` and ``make monitor`` in one go, type ``make flash monitor``. Check section :doc:`IDF Monitor <idf-monitor>` for handy shortcuts and more details on using this application.
That's all what you need to get started with ESP32! That's all what you need to get started with ESP32!

View file

@ -23,7 +23,7 @@
#include "esp_system.h" #include "esp_system.h"
#include "esp_log.h" #include "esp_log.h"
#include "bt.h" #include "esp_bt.h"
#include "bt_app_core.h" #include "bt_app_core.h"
#include "bt_app_av.h" #include "bt_app_av.h"
#include "esp_bt_main.h" #include "esp_bt_main.h"

View file

@ -16,7 +16,7 @@
#include <string.h> #include <string.h>
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
#include "freertos/task.h" #include "freertos/task.h"
#include "bt.h" #include "esp_bt.h"
#include "esp_log.h" #include "esp_log.h"
#include "nvs_flash.h" #include "nvs_flash.h"

View file

@ -23,7 +23,7 @@
#include <stdint.h> #include <stdint.h>
#include <string.h> #include <string.h>
#include "bt.h" #include "esp_bt.h"
#include "nvs_flash.h" #include "nvs_flash.h"
#include "esp_log.h" #include "esp_log.h"
#include "esp_bt_defs.h" #include "esp_bt_defs.h"

View file

@ -23,7 +23,7 @@
#include "controller.h" #include "controller.h"
#include "nvs_flash.h" #include "nvs_flash.h"
#include "bt.h" #include "esp_bt.h"
#include "esp_gap_ble_api.h" #include "esp_gap_ble_api.h"
#include "esp_gattc_api.h" #include "esp_gattc_api.h"
#include "esp_gatt_defs.h" #include "esp_gatt_defs.h"

View file

@ -27,7 +27,7 @@
#include "controller.h" #include "controller.h"
#include "driver/uart.h" #include "driver/uart.h"
#include "bt.h" #include "esp_bt.h"
#include "nvs_flash.h" #include "nvs_flash.h"
#include "esp_bt_device.h" #include "esp_bt_device.h"
#include "esp_gap_ble_api.h" #include "esp_gap_ble_api.h"

View file

@ -19,7 +19,7 @@
#include "esp_system.h" #include "esp_system.h"
#include "esp_log.h" #include "esp_log.h"
#include "nvs_flash.h" #include "nvs_flash.h"
#include "bt.h" #include "esp_bt.h"
#include "driver/uart.h" #include "driver/uart.h"
#include "string.h" #include "string.h"

View file

@ -23,7 +23,7 @@
#include "esp_event_loop.h" #include "esp_event_loop.h"
#include "esp_log.h" #include "esp_log.h"
#include "nvs_flash.h" #include "nvs_flash.h"
#include "bt.h" #include "esp_bt.h"
#include "esp_blufi_api.h" #include "esp_blufi_api.h"
#include "esp_bt_defs.h" #include "esp_bt_defs.h"

View file

@ -23,7 +23,7 @@
#include "esp_event_loop.h" #include "esp_event_loop.h"
#include "esp_log.h" #include "esp_log.h"
#include "nvs_flash.h" #include "nvs_flash.h"
#include "bt.h" #include "esp_bt.h"
#include "esp_blufi_api.h" #include "esp_blufi_api.h"
#include "esp_bt_defs.h" #include "esp_bt_defs.h"

View file

@ -15,7 +15,7 @@
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include "nvs_flash.h" #include "nvs_flash.h"
#include "bt.h" #include "esp_bt.h"
#include "driver/uart.h" #include "driver/uart.h"
#include "esp_log.h" #include "esp_log.h"

View file

@ -31,7 +31,7 @@
#include "nvs_flash.h" #include "nvs_flash.h"
#include "controller.h" #include "controller.h"
#include "bt.h" #include "esp_bt.h"
#include "esp_gap_ble_api.h" #include "esp_gap_ble_api.h"
#include "esp_gattc_api.h" #include "esp_gattc_api.h"
#include "esp_gatt_defs.h" #include "esp_gatt_defs.h"

View file

@ -30,7 +30,7 @@
#include "nvs_flash.h" #include "nvs_flash.h"
#include "controller.h" #include "controller.h"
#include "bt.h" #include "esp_bt.h"
#include "esp_gap_ble_api.h" #include "esp_gap_ble_api.h"
#include "esp_gattc_api.h" #include "esp_gattc_api.h"
#include "esp_gatt_defs.h" #include "esp_gatt_defs.h"

View file

@ -18,7 +18,7 @@
#include "esp_system.h" #include "esp_system.h"
#include "esp_log.h" #include "esp_log.h"
#include "nvs_flash.h" #include "nvs_flash.h"
#include "bt.h" #include "esp_bt.h"
#include "esp_gap_ble_api.h" #include "esp_gap_ble_api.h"
#include "esp_gatts_api.h" #include "esp_gatts_api.h"

View file

@ -31,7 +31,7 @@
#include "esp_system.h" #include "esp_system.h"
#include "esp_log.h" #include "esp_log.h"
#include "nvs_flash.h" #include "nvs_flash.h"
#include "bt.h" #include "esp_bt.h"
#include "esp_gap_ble_api.h" #include "esp_gap_ble_api.h"
#include "esp_gatts_api.h" #include "esp_gatts_api.h"

View file

@ -22,13 +22,13 @@
****************************************************************************/ ****************************************************************************/
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
#include "freertos/task.h" #include "freertos/task.h"
#include "freertos/event_groups.h" #include "freertos/event_groups.h"
#include "esp_system.h" #include "esp_system.h"
#include "esp_log.h" #include "esp_log.h"
#include "nvs_flash.h" #include "nvs_flash.h"
#include "bt.h" #include "esp_bt.h"
#include "esp_gap_ble_api.h" #include "esp_gap_ble_api.h"
#include "esp_gatts_api.h" #include "esp_gatts_api.h"
@ -572,4 +572,3 @@ void app_main()
ESP_LOGE(GATTS_TABLE_TAG, "set local MTU failed, error code = %x", local_mtu_ret); ESP_LOGE(GATTS_TABLE_TAG, "set local MTU failed, error code = %x", local_mtu_ret);
} }
} }

View file

@ -34,7 +34,7 @@
#include "nvs_flash.h" #include "nvs_flash.h"
#include "controller.h" #include "controller.h"
#include "bt.h" #include "esp_bt.h"
#include "esp_gap_ble_api.h" #include "esp_gap_ble_api.h"
#include "esp_gattc_api.h" #include "esp_gattc_api.h"
#include "esp_gatt_defs.h" #include "esp_gatt_defs.h"

View file

@ -33,10 +33,10 @@
#define GPIO_OUTPUT_IO_0 18 #define GPIO_OUTPUT_IO_0 18
#define GPIO_OUTPUT_IO_1 19 #define GPIO_OUTPUT_IO_1 19
#define GPIO_OUTPUT_PIN_SEL ((1<<GPIO_OUTPUT_IO_0) | (1<<GPIO_OUTPUT_IO_1)) #define GPIO_OUTPUT_PIN_SEL ((1ULL<<GPIO_OUTPUT_IO_0) | (1ULL<<GPIO_OUTPUT_IO_1))
#define GPIO_INPUT_IO_0 4 #define GPIO_INPUT_IO_0 4
#define GPIO_INPUT_IO_1 5 #define GPIO_INPUT_IO_1 5
#define GPIO_INPUT_PIN_SEL ((1<<GPIO_INPUT_IO_0) | (1<<GPIO_INPUT_IO_1)) #define GPIO_INPUT_PIN_SEL ((1ULL<<GPIO_INPUT_IO_0) | (1ULL<<GPIO_INPUT_IO_1))
#define ESP_INTR_FLAG_DEFAULT 0 #define ESP_INTR_FLAG_DEFAULT 0
static xQueueHandle gpio_evt_queue = NULL; static xQueueHandle gpio_evt_queue = NULL;

View file

@ -130,6 +130,11 @@ edge_detected:
.global wake_up .global wake_up
wake_up: wake_up:
/* Check if the system can be woken up */
READ_RTC_FIELD(RTC_CNTL_LOW_POWER_ST_REG, RTC_CNTL_RDY_FOR_WAKEUP)
and r0, r0, 1
jump wake_up, eq
/* Wake up the SoC, end program */ /* Wake up the SoC, end program */
wake wake
halt halt

View file

@ -105,7 +105,7 @@ exit:
.global wake_up .global wake_up
wake_up: wake_up:
/* Check if the system can be woken up */ /* Check if the system can be woken up */
READ_RTC_REG(RTC_CNTL_DIAG0_REG, 19, 1) READ_RTC_FIELD(RTC_CNTL_LOW_POWER_ST_REG, RTC_CNTL_RDY_FOR_WAKEUP)
and r0, r0, 1 and r0, r0, 1
jump exit, eq jump exit, eq

View file

@ -85,11 +85,12 @@ include $(COMPONENT_MAKEFILE)
ifndef COMPONENT_CONFIG_ONLY # Skip steps 3-5 if COMPONENT_CONFIG_ONLY is set ifndef COMPONENT_CONFIG_ONLY # Skip steps 3-5 if COMPONENT_CONFIG_ONLY is set
# Object files which need to be linked into the library # Object files which need to be linked into the library
# By default we take all .c, .cpp & .S files in COMPONENT_SRCDIRS. # By default we take all .c, .cpp, .cc & .S files in COMPONENT_SRCDIRS.
ifndef COMPONENT_OBJS ifndef COMPONENT_OBJS
# Find all source files in all COMPONENT_SRCDIRS # Find all source files in all COMPONENT_SRCDIRS
COMPONENT_OBJS := $(foreach compsrcdir,$(COMPONENT_SRCDIRS),$(patsubst %.c,%.o,$(wildcard $(COMPONENT_PATH)/$(compsrcdir)/*.c))) COMPONENT_OBJS := $(foreach compsrcdir,$(COMPONENT_SRCDIRS),$(patsubst %.c,%.o,$(wildcard $(COMPONENT_PATH)/$(compsrcdir)/*.c)))
COMPONENT_OBJS += $(foreach compsrcdir,$(COMPONENT_SRCDIRS),$(patsubst %.cpp,%.o,$(wildcard $(COMPONENT_PATH)/$(compsrcdir)/*.cpp))) COMPONENT_OBJS += $(foreach compsrcdir,$(COMPONENT_SRCDIRS),$(patsubst %.cpp,%.o,$(wildcard $(COMPONENT_PATH)/$(compsrcdir)/*.cpp)))
COMPONENT_OBJS += $(foreach compsrcdir,$(COMPONENT_SRCDIRS),$(patsubst %.cc,%.o,$(wildcard $(COMPONENT_PATH)/$(compsrcdir)/*.cc)))
COMPONENT_OBJS += $(foreach compsrcdir,$(COMPONENT_SRCDIRS),$(patsubst %.S,%.o,$(wildcard $(COMPONENT_PATH)/$(compsrcdir)/*.S))) COMPONENT_OBJS += $(foreach compsrcdir,$(COMPONENT_SRCDIRS),$(patsubst %.S,%.o,$(wildcard $(COMPONENT_PATH)/$(compsrcdir)/*.S)))
# Make relative by removing COMPONENT_PATH from all found object paths # Make relative by removing COMPONENT_PATH from all found object paths
COMPONENT_OBJS := $(patsubst $(COMPONENT_PATH)/%,%,$(COMPONENT_OBJS)) COMPONENT_OBJS := $(patsubst $(COMPONENT_PATH)/%,%,$(COMPONENT_OBJS))
@ -221,6 +222,11 @@ $(1)/%.o: $$(COMPONENT_PATH)/$(1)/%.cpp $(COMMON_MAKEFILES) $(COMPONENT_MAKEFILE
$$(CXX) $$(CXXFLAGS) $$(CPPFLAGS) $$(addprefix -I,$$(COMPONENT_INCLUDES)) $$(addprefix -I,$$(COMPONENT_EXTRA_INCLUDES)) -I$(1) -c $$< -o $$@ $$(CXX) $$(CXXFLAGS) $$(CPPFLAGS) $$(addprefix -I,$$(COMPONENT_INCLUDES)) $$(addprefix -I,$$(COMPONENT_EXTRA_INCLUDES)) -I$(1) -c $$< -o $$@
$(call AppendSourceToDependencies,$$<,$$@) $(call AppendSourceToDependencies,$$<,$$@)
$(1)/%.o: $$(COMPONENT_PATH)/$(1)/%.cc $(COMMON_MAKEFILES) $(COMPONENT_MAKEFILE) | $(COMPONENT_SRCDIRS)
$$(summary) CXX $$(patsubst $$(PWD)/%,%,$$(CURDIR))/$$@
$$(CXX) $$(CXXFLAGS) $$(CPPFLAGS) $$(addprefix -I,$$(COMPONENT_INCLUDES)) $$(addprefix -I,$$(COMPONENT_EXTRA_INCLUDES)) -I$(1) -c $$< -o $$@
$(call AppendSourceToDependencies,$$<,$$@)
$(1)/%.o: $$(COMPONENT_PATH)/$(1)/%.S $(COMMON_MAKEFILES) $(COMPONENT_MAKEFILE) | $(COMPONENT_SRCDIRS) $(1)/%.o: $$(COMPONENT_PATH)/$(1)/%.S $(COMMON_MAKEFILES) $(COMPONENT_MAKEFILE) | $(COMPONENT_SRCDIRS)
$$(summary) AS $$(patsubst $$(PWD)/%,%,$$(CURDIR))/$$@ $$(summary) AS $$(patsubst $$(PWD)/%,%,$$(CURDIR))/$$@
$$(CC) $$(CPPFLAGS) $$(DEBUG_FLAGS) $$(addprefix -I ,$$(COMPONENT_INCLUDES)) $$(addprefix -I ,$$(COMPONENT_EXTRA_INCLUDES)) -I$(1) -c $$< -o $$@ $$(CC) $$(CPPFLAGS) $$(DEBUG_FLAGS) $$(addprefix -I ,$$(COMPONENT_INCLUDES)) $$(addprefix -I ,$$(COMPONENT_EXTRA_INCLUDES)) -I$(1) -c $$< -o $$@

View file

@ -36,8 +36,48 @@ void ref_clock_init();
*/ */
void ref_clock_deinit(); void ref_clock_deinit();
/** /**
* @brief Get reference clock timestamp * @brief Get reference clock timestamp
* @return number of microseconds since the reference clock was initialized * @return number of microseconds since the reference clock was initialized
*/ */
uint64_t ref_clock_get(); uint64_t ref_clock_get();
/**
* @brief wait for signals.
*
* for multiple devices test cases, DUT might need to wait for other DUTs before continue testing.
* As all DUTs are independent, need user (or test script) interaction to make test synchronized.
*
* Here we provide signal functions for this.
* For example, we're testing GPIO, DUT1 has one pin connect to with DUT2.
* DUT2 will output high level and then DUT1 will read input.
* DUT1 should call `unity_wait_for_signal("output high level");` before it reads input.
* DUT2 should call `unity_send_signal("output high level");` after it finished setting output high level.
* According to the console logs:
*
* DUT1 console:
*
* ```
* Waiting for signal: [output high level]!
* Please press "Enter" key to once any board send this signal.
* ```
*
* DUT2 console:
*
* ```
* Send signal: [output high level]!
* ```
*
* Then we press Enter key on DUT1's console, DUT1 starts to read input and then test success.
*
* @param signal_name signal name which DUT expected to wait before proceed testing
*/
void unity_wait_for_signal(const char* signal_name);
/**
* @brief DUT send signal.
*
* @param signal_name signal name which DUT send once it finished preparing.
*/
void unity_send_signal(const char* signal_name);

View file

@ -20,20 +20,49 @@
#define UNITY_OUTPUT_FLUSH unity_flush #define UNITY_OUTPUT_FLUSH unity_flush
// Define helpers to register test cases from multiple files // Define helpers to register test cases from multiple files
#define UNITY_EXPAND2(a, b) a ## b #define UNITY_EXPAND2(a, b) a ## b
#define UNITY_EXPAND(a, b) UNITY_EXPAND2(a, b) #define UNITY_EXPAND(a, b) UNITY_EXPAND2(a, b)
#define UNITY_TEST_UID(what) UNITY_EXPAND(what, __LINE__) #define UNITY_TEST_UID(what) UNITY_EXPAND(what, __LINE__)
#define UNITY_TEST_REG_HELPER reg_helper ## UNITY_TEST_UID #define UNITY_TEST_REG_HELPER reg_helper ## UNITY_TEST_UID
#define UNITY_TEST_DESC_UID desc ## UNITY_TEST_UID #define UNITY_TEST_DESC_UID desc ## UNITY_TEST_UID
// get count of __VA_ARGS__
#define PP_NARG(...) \
PP_NARG_(__VA_ARGS__,PP_RSEQ_N())
#define PP_NARG_(...) \
PP_ARG_N(__VA_ARGS__)
#define PP_ARG_N( \
_1, _2, _3, _4, _5, _6, _7, _8, _9, N, ...) N
#define PP_RSEQ_N() 9,8,7,6,5,4,3,2,1,0
// support max 5 test func now
#define FN_NAME_SET_1(a) {#a}
#define FN_NAME_SET_2(a, b) {#a, #b}
#define FN_NAME_SET_3(a, b, c) {#a, #b, #c}
#define FN_NAME_SET_4(a, b, c, d) {#a, #b, #c, #d}
#define FN_NAME_SET_5(a, b, c, d, e) {#a, #b, #c, #d, #e}
#define FN_NAME_SET2(n) FN_NAME_SET_##n
#define FN_NAME_SET(n, ...) FN_NAME_SET2(n)(__VA_ARGS__)
#define UNITY_TEST_FN_SET(...) \
static test_func UNITY_TEST_UID(test_functions)[] = {__VA_ARGS__}; \
static char* UNITY_TEST_UID(test_fn_name)[] = FN_NAME_SET(PP_NARG(__VA_ARGS__), __VA_ARGS__)
typedef void (* test_func)(void);
struct test_desc_t struct test_desc_t
{ {
const char* name; const char* name;
const char* desc; const char* desc;
void (*fn)(void); test_func* fn;
const char* file; const char* file;
int line; int line;
uint8_t test_fn_count;
char ** test_fn_name;
struct test_desc_t* next; struct test_desc_t* next;
}; };
@ -56,21 +85,51 @@ void unity_run_all_tests();
// test goes here // test goes here
} }
*/ */
#define TEST_CASE(name_, desc_) \ #define TEST_CASE(name_, desc_) \
static void UNITY_TEST_UID(test_func_) (void); \ static void UNITY_TEST_UID(test_func_) (void); \
static void __attribute__((constructor)) UNITY_TEST_UID(test_reg_helper_) () \ static void __attribute__((constructor)) UNITY_TEST_UID(test_reg_helper_) () \
{ \ { \
static test_func test_fn_[] = {&UNITY_TEST_UID(test_func_)}; \
static struct test_desc_t UNITY_TEST_UID(test_desc_) = { \ static struct test_desc_t UNITY_TEST_UID(test_desc_) = { \
.name = name_, \ .name = name_, \
.desc = desc_, \ .desc = desc_, \
.fn = &UNITY_TEST_UID(test_func_), \ .fn = test_fn_, \
.file = __FILE__, \ .file = __FILE__, \
.line = __LINE__, \ .line = __LINE__, \
.test_fn_count = 1, \
.test_fn_name = NULL, \
.next = NULL \ .next = NULL \
}; \ }; \
unity_testcase_register( & UNITY_TEST_UID(test_desc_) ); \ unity_testcase_register( & UNITY_TEST_UID(test_desc_) ); \
}\ }\
static void UNITY_TEST_UID(test_func_) (void) static void UNITY_TEST_UID(test_func_) (void)
/*
* First argument is a free-form description,
* second argument is (by convention) a list of identifiers, each one in square brackets.
* subsequent arguments are names of test functions for different DUTs
* e.g:
* TEST_CASE_MULTIPLE_DEVICES("master and slave spi","[spi][test_env=UT_T2_1]", master_test, slave_test);
* */
#define TEST_CASE_MULTIPLE_DEVICES(name_, desc_, ...) \
UNITY_TEST_FN_SET(__VA_ARGS__); \
static void __attribute__((constructor)) UNITY_TEST_UID(test_reg_helper_) () \
{ \
static struct test_desc_t UNITY_TEST_UID(test_desc_) = { \
.name = name_, \
.desc = desc_, \
.fn = UNITY_TEST_UID(test_functions), \
.file = __FILE__, \
.line = __LINE__, \
.test_fn_count = PP_NARG(__VA_ARGS__), \
.test_fn_name = UNITY_TEST_UID(test_fn_name), \
.next = NULL \
}; \
unity_testcase_register( & UNITY_TEST_UID(test_desc_) ); \
}
/** /**
* Note: initialization of test_desc_t fields above has to be done exactly * Note: initialization of test_desc_t fields above has to be done exactly
* in the same order as the fields are declared in the structure. * in the same order as the fields are declared in the structure.

View file

@ -12,8 +12,11 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
#include <string.h>
#include "unity.h" #include "unity.h"
#include "test_utils.h" #include "test_utils.h"
#include "rom/ets_sys.h"
#include "rom/uart.h"
const esp_partition_t *get_test_data_partition() const esp_partition_t *get_test_data_partition()
{ {
@ -23,3 +26,31 @@ const esp_partition_t *get_test_data_partition()
TEST_ASSERT_NOT_NULL(result); /* means partition table set wrong */ TEST_ASSERT_NOT_NULL(result); /* means partition table set wrong */
return result; return result;
} }
// wait user to send "Enter" key
static void wait_user_control()
{
char sign[5] = {0};
while(strlen(sign) == 0)
{
/* Flush anything already in the RX buffer */
while(uart_rx_one_char((uint8_t *) sign) == OK) {
}
/* Read line */
UartRxString((uint8_t*) sign, sizeof(sign) - 1);
}
}
// signal functions, used for sync between unity DUTs for multiple devices cases
void unity_wait_for_signal(const char* signal_name)
{
printf("Waiting for signal: [%s]!\n"
"Please press \"Enter\" key to once any board send this signal.\n", signal_name);
wait_user_control();
}
void unity_send_signal(const char* signal_name)
{
printf("Send signal: [%s]!\n", signal_name);
}

View file

@ -145,12 +145,57 @@ void unity_testcase_register(struct test_desc_t* desc)
} }
} }
/* print the multiple devices case name and its sub-menu
* e.g:
* (1) spi master/slave case
* (1)master case
* (2)slave case
* */
static void print_multiple_devices_test_menu(const struct test_desc_t* test_ms)
{
unity_printf("%s\n", test_ms->name);
for (int i = 0; i < test_ms->test_fn_count; i++)
{
unity_printf("\t(%d)\t\"%s\"\n", i+1, test_ms->test_fn_name[i]);
}
}
void multiple_devices_option(const struct test_desc_t* test_ms)
{
int selection;
char cmdline[256] = {0};
print_multiple_devices_test_menu(test_ms);
while(strlen(cmdline) == 0)
{
/* Flush anything already in the RX buffer */
while(uart_rx_one_char((uint8_t *) cmdline) == OK) {
}
UartRxString((uint8_t*) cmdline, sizeof(cmdline) - 1);
if(strlen(cmdline) == 0) {
/* if input was newline, print a new menu */
print_multiple_devices_test_menu(test_ms);
}
}
selection = atoi((const char *) cmdline) - 1;
if(selection >= 0 && selection < test_ms->test_fn_count) {
UnityDefaultTestRun(test_ms->fn[selection], test_ms->name, test_ms->line);
} else {
printf("Invalid selection, your should input number 1-%d!", test_ms->test_fn_count);
}
}
static void unity_run_single_test(const struct test_desc_t* test) static void unity_run_single_test(const struct test_desc_t* test)
{ {
printf("Running %s...\n", test->name); printf("Running %s...\n", test->name);
Unity.TestFile = test->file; Unity.TestFile = test->file;
Unity.CurrentDetail1 = test->desc; Unity.CurrentDetail1 = test->desc;
UnityDefaultTestRun(test->fn, test->name, test->line); if(test->test_fn_count == 1) {
UnityDefaultTestRun(test->fn[0], test->name, test->line);
} else {
multiple_devices_option(test);
}
} }
static void unity_run_single_test_by_index(int index) static void unity_run_single_test_by_index(int index)
@ -158,6 +203,7 @@ static void unity_run_single_test_by_index(int index)
const struct test_desc_t* test; const struct test_desc_t* test;
for (test = s_unity_tests_first; test != NULL && index != 0; test = test->next, --index) for (test = s_unity_tests_first; test != NULL && index != 0; test = test->next, --index)
{ {
} }
if (test != NULL) if (test != NULL)
{ {
@ -253,6 +299,13 @@ static int print_test_menu(void)
test = test->next, ++test_counter) test = test->next, ++test_counter)
{ {
unity_printf("(%d)\t\"%s\" %s\n", test_counter + 1, test->name, test->desc); unity_printf("(%d)\t\"%s\" %s\n", test_counter + 1, test->name, test->desc);
if(test->test_fn_count > 1)
{
for (int i = 0; i < test->test_fn_count; i++)
{
unity_printf("\t(%d)\t\"%s\"\n", i+1, test->test_fn_name[i]);
}
}
} }
return test_counter; return test_counter;
} }

View file

@ -6,7 +6,7 @@
void unityTask(void *pvParameters) void unityTask(void *pvParameters)
{ {
vTaskDelay(30); /* Delay a bit to let the main task be deleted */ vTaskDelay(2); /* Delay a bit to let the main task be deleted */
unity_run_menu(); /* Doesn't return */ unity_run_menu(); /* Doesn't return */
} }