Merge branch 'cherry-pick-into-3.0' into 'release/v3.0'

Cherry pick into 3.0

See merge request !1714
This commit is contained in:
Jiang Jiang Jian
2017-12-28 17:58:35 +08:00
86 changed files with 8637 additions and 4196 deletions

34
.editorconfig Normal file
View File

@@ -0,0 +1,34 @@
# EditorConfig helps developers define and maintain consistent
# coding styles between different editors and IDEs
# http://editorconfig.org
root = true
[*]
indent_style = space
indent_size = 4
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[{*.md,*.rst}]
trim_trailing_whitespace = false
[{Makefile,*.mk,*.bat}]
indent_style = tab
indent_size = 2
[*/freertos/**]
indent_style = tab
indent_size = 4
[{*/freertos/**.S,**/FreeRTOSConfig.h}]
indent_style = space
indent_size = 4
[*.pem]
insert_final_newline = false
[*.py]
max_line_length = 119

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

@@ -48,6 +48,7 @@
#include "bootloader_flash.h" #include "bootloader_flash.h"
#include "bootloader_random.h" #include "bootloader_random.h"
#include "bootloader_config.h" #include "bootloader_config.h"
#include "bootloader_clock.h"
#include "flash_qio_mode.h" #include "flash_qio_mode.h"
@@ -75,7 +76,6 @@ static void set_cache_and_start_app(uint32_t drom_addr,
static void update_flash_config(const esp_image_header_t* pfhdr); static void update_flash_config(const esp_image_header_t* pfhdr);
static void vddsdio_configure(); static void vddsdio_configure();
static void flash_gpio_configure(); static void flash_gpio_configure();
static void clock_configure(void);
static void uart_console_configure(void); static void uart_console_configure(void);
static void wdt_reset_check(void); static void wdt_reset_check(void);
@@ -447,7 +447,7 @@ void bootloader_main()
{ {
vddsdio_configure(); vddsdio_configure();
flash_gpio_configure(); flash_gpio_configure();
clock_configure(); bootloader_clock_configure();
uart_console_configure(); uart_console_configure();
wdt_reset_check(); wdt_reset_check();
ESP_LOGI(TAG, "ESP-IDF %s 2nd stage bootloader", IDF_VER); ESP_LOGI(TAG, "ESP-IDF %s 2nd stage bootloader", IDF_VER);
@@ -796,24 +796,20 @@ static void IRAM_ATTR flash_gpio_configure()
if (pkg_ver == EFUSE_RD_CHIP_VER_PKG_ESP32D2WDQ5) { if (pkg_ver == EFUSE_RD_CHIP_VER_PKG_ESP32D2WDQ5) {
// For ESP32D2WD the SPI pins are already configured // For ESP32D2WD the SPI pins are already configured
ESP_LOGI(TAG, "Detected ESP32D2WD");
// flash clock signal should come from IO MUX. // flash clock signal should come from IO MUX.
PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_CLK_U, FUNC_SD_CLK_SPICLK); PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_CLK_U, FUNC_SD_CLK_SPICLK);
SET_PERI_REG_BITS(PERIPHS_IO_MUX_SD_CLK_U, FUN_DRV, drv, FUN_DRV_S); SET_PERI_REG_BITS(PERIPHS_IO_MUX_SD_CLK_U, FUN_DRV, drv, FUN_DRV_S);
} else if (pkg_ver == EFUSE_RD_CHIP_VER_PKG_ESP32PICOD2) { } else if (pkg_ver == EFUSE_RD_CHIP_VER_PKG_ESP32PICOD2) {
// For ESP32PICOD2 the SPI pins are already configured // For ESP32PICOD2 the SPI pins are already configured
ESP_LOGI(TAG, "Detected ESP32PICOD2");
// flash clock signal should come from IO MUX. // flash clock signal should come from IO MUX.
PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_CLK_U, FUNC_SD_CLK_SPICLK); PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_CLK_U, FUNC_SD_CLK_SPICLK);
SET_PERI_REG_BITS(PERIPHS_IO_MUX_SD_CLK_U, FUN_DRV, drv, FUN_DRV_S); SET_PERI_REG_BITS(PERIPHS_IO_MUX_SD_CLK_U, FUN_DRV, drv, FUN_DRV_S);
} else if (pkg_ver == EFUSE_RD_CHIP_VER_PKG_ESP32PICOD4) { } else if (pkg_ver == EFUSE_RD_CHIP_VER_PKG_ESP32PICOD4) {
// For ESP32PICOD4 the SPI pins are already configured // For ESP32PICOD4 the SPI pins are already configured
ESP_LOGI(TAG, "Detected ESP32PICOD4");
// flash clock signal should come from IO MUX. // flash clock signal should come from IO MUX.
PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_CLK_U, FUNC_SD_CLK_SPICLK); PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_CLK_U, FUNC_SD_CLK_SPICLK);
SET_PERI_REG_BITS(PERIPHS_IO_MUX_SD_CLK_U, FUN_DRV, drv, FUN_DRV_S); SET_PERI_REG_BITS(PERIPHS_IO_MUX_SD_CLK_U, FUN_DRV, drv, FUN_DRV_S);
} else { } else {
ESP_LOGI(TAG, "Detected ESP32");
const uint32_t spiconfig = ets_efuse_get_spiconfig(); const uint32_t spiconfig = ets_efuse_get_spiconfig();
if (spiconfig == EFUSE_SPICONFIG_SPI_DEFAULTS) { if (spiconfig == EFUSE_SPICONFIG_SPI_DEFAULTS) {
gpio_matrix_out(FLASH_CS_IO, SPICS0_OUT_IDX, 0, 0); gpio_matrix_out(FLASH_CS_IO, SPICS0_OUT_IDX, 0, 0);
@@ -839,46 +835,6 @@ static void IRAM_ATTR flash_gpio_configure()
} }
} }
static void clock_configure(void)
{
// ROM bootloader may have put a lot of text into UART0 FIFO.
// Wait for it to be printed.
// This is not needed on power on reset, when ROM bootloader is running at
// 40 MHz. But in case of TG WDT reset, CPU may still be running at >80 MHZ,
// and will be done with the bootloader much earlier than UART FIFO is empty.
uart_tx_wait_idle(0);
/* Set CPU to 80MHz. Keep other clocks unmodified. */
rtc_cpu_freq_t cpu_freq = RTC_CPU_FREQ_80M;
/* On ESP32 rev 0, switching to 80MHz if clock was previously set to
* 240 MHz may cause the chip to lock up (see section 3.5 of the errata
* document). For rev. 0, switch to 240 instead if it was chosen in
* menuconfig.
*/
uint32_t chip_ver_reg = REG_READ(EFUSE_BLK0_RDATA3_REG);
if ((chip_ver_reg & EFUSE_RD_CHIP_VER_REV1_M) == 0 &&
CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ == 240) {
cpu_freq = RTC_CPU_FREQ_240M;
}
rtc_clk_config_t clk_cfg = RTC_CLK_CONFIG_DEFAULT();
clk_cfg.xtal_freq = CONFIG_ESP32_XTAL_FREQ;
clk_cfg.cpu_freq = cpu_freq;
clk_cfg.slow_freq = rtc_clk_slow_freq_get();
clk_cfg.fast_freq = rtc_clk_fast_freq_get();
rtc_clk_init(clk_cfg);
/* As a slight optimization, if 32k XTAL was enabled in sdkconfig, we enable
* it here. Usually it needs some time to start up, so we amortize at least
* part of the start up time by enabling 32k XTAL early.
* App startup code will wait until the oscillator has started up.
*/
#ifdef CONFIG_ESP32_RTC_CLOCK_SOURCE_EXTERNAL_CRYSTAL
if (!rtc_clk_32k_enabled()) {
rtc_clk_32k_bootstrap();
}
#endif
}
static void uart_console_configure(void) static void uart_console_configure(void)
{ {

View File

@@ -0,0 +1,21 @@
// Copyright 2017 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
/** @brief Configure clocks for early boot
*
* Called by bootloader, or by the app if the bootloader version is old (pre v2.1).
*/
void bootloader_clock_configure(void);

View File

@@ -0,0 +1,61 @@
// Copyright 2017 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "rom/uart.h"
#include "rom/rtc.h"
#include "soc/soc.h"
#include "soc/rtc.h"
#include "soc/dport_reg.h"
#include "soc/efuse_reg.h"
#include "soc/rtc_cntl_reg.h"
void bootloader_clock_configure()
{
// ROM bootloader may have put a lot of text into UART0 FIFO.
// Wait for it to be printed.
// This is not needed on power on reset, when ROM bootloader is running at
// 40 MHz. But in case of TG WDT reset, CPU may still be running at >80 MHZ,
// and will be done with the bootloader much earlier than UART FIFO is empty.
uart_tx_wait_idle(0);
/* Set CPU to 80MHz. Keep other clocks unmodified. */
rtc_cpu_freq_t cpu_freq = RTC_CPU_FREQ_80M;
/* On ESP32 rev 0, switching to 80MHz if clock was previously set to
* 240 MHz may cause the chip to lock up (see section 3.5 of the errata
* document). For rev. 0, switch to 240 instead if it was chosen in
* menuconfig.
*/
uint32_t chip_ver_reg = REG_READ(EFUSE_BLK0_RDATA3_REG);
if ((chip_ver_reg & EFUSE_RD_CHIP_VER_REV1_M) == 0 &&
CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ == 240) {
cpu_freq = RTC_CPU_FREQ_240M;
}
rtc_clk_config_t clk_cfg = RTC_CLK_CONFIG_DEFAULT();
clk_cfg.xtal_freq = CONFIG_ESP32_XTAL_FREQ;
clk_cfg.cpu_freq = cpu_freq;
clk_cfg.slow_freq = rtc_clk_slow_freq_get();
clk_cfg.fast_freq = rtc_clk_fast_freq_get();
rtc_clk_init(clk_cfg);
/* As a slight optimization, if 32k XTAL was enabled in sdkconfig, we enable
* it here. Usually it needs some time to start up, so we amortize at least
* part of the start up time by enabling 32k XTAL early.
* App startup code will wait until the oscillator has started up.
*/
#ifdef CONFIG_ESP32_RTC_CLOCK_SOURCE_EXTERNAL_CRYSTAL
if (!rtc_clk_32k_enabled()) {
rtc_clk_32k_bootstrap();
}
#endif
}

View File

@@ -25,8 +25,6 @@
#include "esp_system.h" #include "esp_system.h"
#endif #endif
const char *TAG = "boot_rng";
void bootloader_fill_random(void *buffer, size_t length) void bootloader_fill_random(void *buffer, size_t length)
{ {
uint8_t *buffer_bytes = (uint8_t *)buffer; uint8_t *buffer_bytes = (uint8_t *)buffer;

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

@@ -23,7 +23,6 @@
#include "freertos/xtensa_api.h" #include "freertos/xtensa_api.h"
#include "freertos/task.h" #include "freertos/task.h"
#include "freertos/ringbuf.h" #include "freertos/ringbuf.h"
#include "freertos/event_groups.h"
#include "soc/dport_reg.h" #include "soc/dport_reg.h"
#include "soc/i2c_struct.h" #include "soc/i2c_struct.h"
#include "soc/i2c_reg.h" #include "soc/i2c_reg.h"
@@ -53,7 +52,7 @@ static DRAM_ATTR i2c_dev_t* const I2C[I2C_NUM_MAX] = { &I2C0, &I2C1 };
#define I2C_TIMEING_VAL_ERR_STR "i2c timing value error" #define I2C_TIMEING_VAL_ERR_STR "i2c timing value error"
#define I2C_ADDR_ERROR_STR "i2c null address error" #define I2C_ADDR_ERROR_STR "i2c null address error"
#define I2C_DRIVER_NOT_INSTALL_ERR_STR "i2c driver not installed" #define I2C_DRIVER_NOT_INSTALL_ERR_STR "i2c driver not installed"
#define I2C_SLAVE_BUFFER_LEN_ERR_STR "i2c buffer size too short for slave mode" #define I2C_SLAVE_BUFFER_LEN_ERR_STR "i2c buffer size too small for slave mode"
#define I2C_EVT_QUEUE_ERR_STR "i2c evt queue error" #define I2C_EVT_QUEUE_ERR_STR "i2c evt queue error"
#define I2C_SEM_ERR_STR "i2c semaphore error" #define I2C_SEM_ERR_STR "i2c semaphore error"
#define I2C_BUF_ERR_STR "i2c ringbuffer error" #define I2C_BUF_ERR_STR "i2c ringbuffer error"
@@ -65,13 +64,16 @@ static DRAM_ATTR i2c_dev_t* const I2C[I2C_NUM_MAX] = { &I2C0, &I2C1 };
#define I2C_SDA_IO_ERR_STR "sda gpio number error" #define I2C_SDA_IO_ERR_STR "sda gpio number error"
#define I2C_SCL_IO_ERR_STR "scl gpio number error" #define I2C_SCL_IO_ERR_STR "scl gpio number error"
#define I2C_CMD_LINK_INIT_ERR_STR "i2c command link error" #define I2C_CMD_LINK_INIT_ERR_STR "i2c command link error"
#define I2C_GPIO_PULLUP_ERR_STR "this i2c pin do not support internal pull-up" #define I2C_GPIO_PULLUP_ERR_STR "this i2c pin does not support internal pull-up"
#define I2C_ACK_TYPE_ERR_STR "i2c ack type error"
#define I2C_DATA_LEN_ERR_STR "i2c data read length error"
#define I2C_FIFO_FULL_THRESH_VAL (28) #define I2C_FIFO_FULL_THRESH_VAL (28)
#define I2C_FIFO_EMPTY_THRESH_VAL (5) #define I2C_FIFO_EMPTY_THRESH_VAL (5)
#define I2C_IO_INIT_LEVEL (1) #define I2C_IO_INIT_LEVEL (1)
#define I2C_CMD_ALIVE_INTERVAL_TICK (1000 / portTICK_PERIOD_MS) #define I2C_CMD_ALIVE_INTERVAL_TICK (1000 / portTICK_PERIOD_MS)
#define I2C_CMD_EVT_ALIVE (BIT0) #define I2C_CMD_EVT_ALIVE (0)
#define I2C_CMD_EVT_DONE (BIT1) #define I2C_CMD_EVT_DONE (1)
#define I2C_EVT_QUEUE_LEN (1)
#define I2C_SLAVE_TIMEOUT_DEFAULT (32000) /* I2C slave timeout value, APB clock cycle number */ #define I2C_SLAVE_TIMEOUT_DEFAULT (32000) /* I2C slave timeout value, APB clock cycle number */
#define I2C_SLAVE_SDA_SAMPLE_DEFAULT (10) /* I2C slave sample time after scl positive edge default value */ #define I2C_SLAVE_SDA_SAMPLE_DEFAULT (10) /* I2C slave sample time after scl positive edge default value */
#define I2C_SLAVE_SDA_HOLD_DEFAULT (10) /* I2C slave hold time after scl negative edge default value */ #define I2C_SLAVE_SDA_HOLD_DEFAULT (10) /* I2C slave hold time after scl negative edge default value */
@@ -107,6 +109,10 @@ typedef enum {
I2C_STATUS_TIMEOUT, /*!< I2C bus status error, and operation timeout */ I2C_STATUS_TIMEOUT, /*!< I2C bus status error, and operation timeout */
} i2c_status_t; } i2c_status_t;
typedef struct {
int type;
} i2c_cmd_evt_t;
typedef struct { typedef struct {
int i2c_num; /*!< I2C port number */ int i2c_num; /*!< I2C port number */
int mode; /*!< I2C mode, master or slave */ int mode; /*!< I2C mode, master or slave */
@@ -117,7 +123,7 @@ typedef struct {
uint8_t data_buf[I2C_FIFO_LEN]; /*!< a buffer to store i2c fifo data */ uint8_t data_buf[I2C_FIFO_LEN]; /*!< a buffer to store i2c fifo data */
i2c_cmd_desc_t cmd_link; /*!< I2C command link */ i2c_cmd_desc_t cmd_link; /*!< I2C command link */
EventGroupHandle_t cmd_evt; /*!< I2C command event bits */ QueueHandle_t cmd_evt_queue; /*!< I2C command event queue */
xSemaphoreHandle cmd_mux; /*!< semaphore to lock command process */ xSemaphoreHandle cmd_mux; /*!< semaphore to lock command process */
size_t tx_fifo_remain; /*!< tx fifo remain length, for master mode */ size_t tx_fifo_remain; /*!< tx fifo remain length, for master mode */
size_t rx_fifo_remain; /*!< rx fifo remain length, for master mode */ size_t rx_fifo_remain; /*!< rx fifo remain length, for master mode */
@@ -175,7 +181,7 @@ esp_err_t i2c_driver_install(i2c_port_t i2c_num, i2c_mode_t mode, size_t slv_rx_
} }
p_i2c->rx_buf_length = slv_rx_buf_len; p_i2c->rx_buf_length = slv_rx_buf_len;
} else { } else {
p_i2c->tx_ring_buf = NULL; p_i2c->rx_ring_buf = NULL;
p_i2c->rx_buf_length = 0; p_i2c->rx_buf_length = 0;
} }
if (slv_tx_buf_len > 0) { if (slv_tx_buf_len > 0) {
@@ -191,7 +197,7 @@ esp_err_t i2c_driver_install(i2c_port_t i2c_num, i2c_mode_t mode, size_t slv_rx_
} }
p_i2c->slv_rx_mux = xSemaphoreCreateMutex(); p_i2c->slv_rx_mux = xSemaphoreCreateMutex();
p_i2c->slv_tx_mux = xSemaphoreCreateMutex(); p_i2c->slv_tx_mux = xSemaphoreCreateMutex();
if (p_i2c->slv_rx_mux == NULL || p_i2c->slv_rx_mux == NULL) { if (p_i2c->slv_rx_mux == NULL || p_i2c->slv_tx_mux == NULL) {
ESP_LOGE(I2C_TAG, I2C_SEM_ERR_STR); ESP_LOGE(I2C_TAG, I2C_SEM_ERR_STR);
goto err; goto err;
} }
@@ -199,8 +205,8 @@ esp_err_t i2c_driver_install(i2c_port_t i2c_num, i2c_mode_t mode, size_t slv_rx_
} else { } else {
//semaphore to sync sending process, because we only have 32 bytes for hardware fifo. //semaphore to sync sending process, because we only have 32 bytes for hardware fifo.
p_i2c->cmd_mux = xSemaphoreCreateMutex(); p_i2c->cmd_mux = xSemaphoreCreateMutex();
p_i2c->cmd_evt = xEventGroupCreate(); p_i2c->cmd_evt_queue = xQueueCreate(I2C_EVT_QUEUE_LEN, sizeof(i2c_cmd_evt_t));
if (p_i2c->cmd_mux == NULL || p_i2c->cmd_evt == NULL) { if (p_i2c->cmd_mux == NULL || p_i2c->cmd_evt_queue == NULL) {
ESP_LOGE(I2C_TAG, I2C_SEM_ERR_STR); ESP_LOGE(I2C_TAG, I2C_SEM_ERR_STR);
goto err; goto err;
} }
@@ -243,9 +249,9 @@ esp_err_t i2c_driver_install(i2c_port_t i2c_num, i2c_mode_t mode, size_t slv_rx_
p_i2c_obj[i2c_num]->tx_ring_buf = NULL; p_i2c_obj[i2c_num]->tx_ring_buf = NULL;
p_i2c_obj[i2c_num]->tx_buf_length = 0; p_i2c_obj[i2c_num]->tx_buf_length = 0;
} }
if (p_i2c_obj[i2c_num]->cmd_evt) { if (p_i2c_obj[i2c_num]->cmd_evt_queue) {
vEventGroupDelete(p_i2c_obj[i2c_num]->cmd_evt); vQueueDelete(p_i2c_obj[i2c_num]->cmd_evt_queue);
p_i2c_obj[i2c_num]->cmd_evt = NULL; p_i2c_obj[i2c_num]->cmd_evt_queue = NULL;
} }
if (p_i2c_obj[i2c_num]->cmd_mux) { if (p_i2c_obj[i2c_num]->cmd_mux) {
vSemaphoreDelete(p_i2c_obj[i2c_num]->cmd_mux); vSemaphoreDelete(p_i2c_obj[i2c_num]->cmd_mux);
@@ -258,6 +264,7 @@ esp_err_t i2c_driver_install(i2c_port_t i2c_num, i2c_mode_t mode, size_t slv_rx_
} }
} }
free(p_i2c_obj[i2c_num]); free(p_i2c_obj[i2c_num]);
p_i2c_obj[i2c_num] = NULL;
return ESP_FAIL; return ESP_FAIL;
} }
@@ -296,9 +303,9 @@ esp_err_t i2c_driver_delete(i2c_port_t i2c_num)
xSemaphoreTake(p_i2c->cmd_mux, portMAX_DELAY); xSemaphoreTake(p_i2c->cmd_mux, portMAX_DELAY);
vSemaphoreDelete(p_i2c->cmd_mux); vSemaphoreDelete(p_i2c->cmd_mux);
} }
if (p_i2c_obj[i2c_num]->cmd_evt) { if (p_i2c_obj[i2c_num]->cmd_evt_queue) {
vEventGroupDelete(p_i2c_obj[i2c_num]->cmd_evt); vQueueDelete(p_i2c_obj[i2c_num]->cmd_evt_queue);
p_i2c_obj[i2c_num]->cmd_evt = NULL; p_i2c_obj[i2c_num]->cmd_evt_queue = NULL;
} }
if (p_i2c->slv_rx_mux) { if (p_i2c->slv_rx_mux) {
vSemaphoreDelete(p_i2c->slv_rx_mux); vSemaphoreDelete(p_i2c->slv_rx_mux);
@@ -437,7 +444,9 @@ static void IRAM_ATTR i2c_isr_handler_default(void* arg)
} }
} }
if (p_i2c->mode == I2C_MODE_MASTER) { if (p_i2c->mode == I2C_MODE_MASTER) {
xEventGroupSetBitsFromISR(p_i2c->cmd_evt, I2C_CMD_EVT_ALIVE, &HPTaskAwoken); i2c_cmd_evt_t evt;
evt.type = I2C_CMD_EVT_ALIVE;
xQueueSendFromISR(p_i2c->cmd_evt_queue, &evt, &HPTaskAwoken);
if (HPTaskAwoken == pdTRUE) { if (HPTaskAwoken == pdTRUE) {
portYIELD_FROM_ISR(); portYIELD_FROM_ISR();
} }
@@ -651,8 +660,10 @@ esp_err_t i2c_set_start_timing(i2c_port_t i2c_num, int setup_time, int hold_time
I2C_CHECK((hold_time <= I2C_SCL_START_HOLD_TIME_V) && (hold_time > 0), I2C_TIMEING_VAL_ERR_STR, ESP_ERR_INVALID_ARG); I2C_CHECK((hold_time <= I2C_SCL_START_HOLD_TIME_V) && (hold_time > 0), I2C_TIMEING_VAL_ERR_STR, ESP_ERR_INVALID_ARG);
I2C_CHECK((setup_time <= I2C_SCL_RSTART_SETUP_TIME_V) && (setup_time > 0), I2C_TIMEING_VAL_ERR_STR, ESP_ERR_INVALID_ARG); I2C_CHECK((setup_time <= I2C_SCL_RSTART_SETUP_TIME_V) && (setup_time > 0), I2C_TIMEING_VAL_ERR_STR, ESP_ERR_INVALID_ARG);
I2C_ENTER_CRITICAL(&i2c_spinlock[i2c_num]);
I2C[i2c_num]->scl_start_hold.time = hold_time; I2C[i2c_num]->scl_start_hold.time = hold_time;
I2C[i2c_num]->scl_rstart_setup.time = setup_time; I2C[i2c_num]->scl_rstart_setup.time = setup_time;
I2C_EXIT_CRITICAL(&i2c_spinlock[i2c_num]);
return ESP_OK; return ESP_OK;
} }
@@ -676,8 +687,10 @@ esp_err_t i2c_set_stop_timing(i2c_port_t i2c_num, int setup_time, int hold_time)
I2C_CHECK((setup_time <= I2C_SCL_STOP_SETUP_TIME_V) && (setup_time > 0), I2C_TIMEING_VAL_ERR_STR, ESP_ERR_INVALID_ARG); I2C_CHECK((setup_time <= I2C_SCL_STOP_SETUP_TIME_V) && (setup_time > 0), I2C_TIMEING_VAL_ERR_STR, ESP_ERR_INVALID_ARG);
I2C_CHECK((hold_time <= I2C_SCL_STOP_HOLD_TIME_V) && (hold_time > 0), I2C_TIMEING_VAL_ERR_STR, ESP_ERR_INVALID_ARG); I2C_CHECK((hold_time <= I2C_SCL_STOP_HOLD_TIME_V) && (hold_time > 0), I2C_TIMEING_VAL_ERR_STR, ESP_ERR_INVALID_ARG);
I2C_ENTER_CRITICAL(&i2c_spinlock[i2c_num]);
I2C[i2c_num]->scl_stop_hold.time = hold_time; I2C[i2c_num]->scl_stop_hold.time = hold_time;
I2C[i2c_num]->scl_stop_setup.time = setup_time; I2C[i2c_num]->scl_stop_setup.time = setup_time;
I2C_EXIT_CRITICAL(&i2c_spinlock[i2c_num]);
return ESP_OK; return ESP_OK;
} }
@@ -701,8 +714,10 @@ esp_err_t i2c_set_data_timing(i2c_port_t i2c_num, int sample_time, int hold_time
I2C_CHECK((sample_time <= I2C_SDA_SAMPLE_TIME_V) && (sample_time > 0), I2C_TIMEING_VAL_ERR_STR, ESP_ERR_INVALID_ARG); I2C_CHECK((sample_time <= I2C_SDA_SAMPLE_TIME_V) && (sample_time > 0), I2C_TIMEING_VAL_ERR_STR, ESP_ERR_INVALID_ARG);
I2C_CHECK((hold_time <= I2C_SDA_HOLD_TIME_V) && (hold_time > 0), I2C_TIMEING_VAL_ERR_STR, ESP_ERR_INVALID_ARG); I2C_CHECK((hold_time <= I2C_SDA_HOLD_TIME_V) && (hold_time > 0), I2C_TIMEING_VAL_ERR_STR, ESP_ERR_INVALID_ARG);
I2C_ENTER_CRITICAL(&i2c_spinlock[i2c_num]);
I2C[i2c_num]->sda_hold.time = hold_time; I2C[i2c_num]->sda_hold.time = hold_time;
I2C[i2c_num]->sda_sample.time = sample_time; I2C[i2c_num]->sda_sample.time = sample_time;
I2C_EXIT_CRITICAL(&i2c_spinlock[i2c_num]);
return ESP_OK; return ESP_OK;
} }
@@ -723,7 +738,7 @@ esp_err_t i2c_get_data_timing(i2c_port_t i2c_num, int* sample_time, int* hold_ti
esp_err_t i2c_set_timeout(i2c_port_t i2c_num, int timeout) esp_err_t i2c_set_timeout(i2c_port_t i2c_num, int timeout)
{ {
I2C_CHECK(i2c_num < I2C_NUM_MAX, I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); I2C_CHECK(i2c_num < I2C_NUM_MAX, I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG);
I2C_CHECK((timeout <= I2C_SDA_SAMPLE_TIME_V) && (timeout > 0), I2C_TIMEING_VAL_ERR_STR, ESP_ERR_INVALID_ARG); I2C_CHECK((timeout <= I2C_TIME_OUT_REG_V) && (timeout > 0), I2C_TIMEING_VAL_ERR_STR, ESP_ERR_INVALID_ARG);
I2C_ENTER_CRITICAL(&i2c_spinlock[i2c_num]); I2C_ENTER_CRITICAL(&i2c_spinlock[i2c_num]);
I2C[i2c_num]->timeout.tout = timeout; I2C[i2c_num]->timeout.tout = timeout;
@@ -945,11 +960,8 @@ esp_err_t i2c_master_write_byte(i2c_cmd_handle_t cmd_handle, uint8_t data, bool
return i2c_cmd_link_append(cmd_handle, &cmd); return i2c_cmd_link_append(cmd_handle, &cmd);
} }
esp_err_t i2c_master_read(i2c_cmd_handle_t cmd_handle, uint8_t* data, size_t data_len, int ack) static esp_err_t i2c_master_read_static(i2c_cmd_handle_t cmd_handle, uint8_t* data, size_t data_len, i2c_ack_type_t ack)
{ {
I2C_CHECK((data != NULL), I2C_ADDR_ERROR_STR, ESP_ERR_INVALID_ARG);
I2C_CHECK(cmd_handle != NULL, I2C_CMD_LINK_INIT_ERR_STR, ESP_ERR_INVALID_ARG);
int len_tmp; int len_tmp;
int data_offset = 0; int data_offset = 0;
esp_err_t ret; esp_err_t ret;
@@ -972,25 +984,49 @@ esp_err_t i2c_master_read(i2c_cmd_handle_t cmd_handle, uint8_t* data, size_t dat
return ESP_OK; return ESP_OK;
} }
esp_err_t i2c_master_read_byte(i2c_cmd_handle_t cmd_handle, uint8_t* data, int ack) esp_err_t i2c_master_read_byte(i2c_cmd_handle_t cmd_handle, uint8_t* data, i2c_ack_type_t ack)
{ {
I2C_CHECK((data != NULL), I2C_ADDR_ERROR_STR, ESP_ERR_INVALID_ARG); I2C_CHECK((data != NULL), I2C_ADDR_ERROR_STR, ESP_ERR_INVALID_ARG);
I2C_CHECK(cmd_handle != NULL, I2C_CMD_LINK_INIT_ERR_STR, ESP_ERR_INVALID_ARG); I2C_CHECK(cmd_handle != NULL, I2C_CMD_LINK_INIT_ERR_STR, ESP_ERR_INVALID_ARG);
I2C_CHECK(ack < I2C_MASTER_ACK_MAX, I2C_ACK_TYPE_ERR_STR, ESP_ERR_INVALID_ARG);
i2c_cmd_t cmd; i2c_cmd_t cmd;
cmd.ack_en = 0; cmd.ack_en = 0;
cmd.ack_exp = 0; cmd.ack_exp = 0;
cmd.ack_val = ack & 0x1; cmd.ack_val = ((ack == I2C_MASTER_LAST_NACK) ? I2C_MASTER_NACK : (ack & 0x1));
cmd.byte_num = 1; cmd.byte_num = 1;
cmd.op_code = I2C_CMD_READ; cmd.op_code = I2C_CMD_READ;
cmd.data = data; cmd.data = data;
return i2c_cmd_link_append(cmd_handle, &cmd); return i2c_cmd_link_append(cmd_handle, &cmd);
} }
esp_err_t i2c_master_read(i2c_cmd_handle_t cmd_handle, uint8_t* data, size_t data_len, i2c_ack_type_t ack)
{
I2C_CHECK((data != NULL), I2C_ADDR_ERROR_STR, ESP_ERR_INVALID_ARG);
I2C_CHECK(cmd_handle != NULL, I2C_CMD_LINK_INIT_ERR_STR, ESP_ERR_INVALID_ARG);
I2C_CHECK(ack < I2C_MASTER_ACK_MAX, I2C_ACK_TYPE_ERR_STR, ESP_ERR_INVALID_ARG);
I2C_CHECK(data_len < 1, I2C_DATA_LEN_ERR_STR, ESP_ERR_INVALID_ARG);
if(ack != I2C_MASTER_LAST_NACK) {
return i2c_master_read_static(cmd_handle, data, data_len, ack);
} else {
if(data_len == 1) {
return i2c_master_read_byte(cmd_handle, data, I2C_MASTER_NACK);
} else {
esp_err_t ret;
if((ret = i2c_master_read_static(cmd_handle, data, data_len - 1, I2C_MASTER_ACK)) != ESP_OK) {
return ret;
}
return i2c_master_read_byte(cmd_handle, data + data_len - 1, I2C_MASTER_NACK);
}
}
}
static void IRAM_ATTR i2c_master_cmd_begin_static(i2c_port_t i2c_num) static void IRAM_ATTR i2c_master_cmd_begin_static(i2c_port_t i2c_num)
{ {
i2c_obj_t* p_i2c = p_i2c_obj[i2c_num]; i2c_obj_t* p_i2c = p_i2c_obj[i2c_num];
portBASE_TYPE HPTaskAwoken = pdFALSE; portBASE_TYPE HPTaskAwoken = pdFALSE;
i2c_cmd_evt_t evt;
//This should never happen //This should never happen
if (p_i2c->mode == I2C_MODE_SLAVE) { if (p_i2c->mode == I2C_MODE_SLAVE) {
return; return;
@@ -1005,7 +1041,8 @@ static void IRAM_ATTR i2c_master_cmd_begin_static(i2c_port_t i2c_num)
I2C[i2c_num]->int_clr.time_out = 1; I2C[i2c_num]->int_clr.time_out = 1;
I2C[i2c_num]->int_ena.val = 0; I2C[i2c_num]->int_ena.val = 0;
} }
xEventGroupSetBitsFromISR(p_i2c->cmd_evt, I2C_CMD_EVT_DONE, &HPTaskAwoken); evt.type = I2C_CMD_EVT_DONE;
xQueueOverwriteFromISR(p_i2c->cmd_evt_queue, &evt, &HPTaskAwoken);
if (HPTaskAwoken == pdTRUE) { if (HPTaskAwoken == pdTRUE) {
portYIELD_FROM_ISR(); portYIELD_FROM_ISR();
} }
@@ -1024,7 +1061,8 @@ static void IRAM_ATTR i2c_master_cmd_begin_static(i2c_port_t i2c_num)
} }
if (p_i2c->cmd_link.head == NULL) { if (p_i2c->cmd_link.head == NULL) {
p_i2c->cmd_link.cur = NULL; p_i2c->cmd_link.cur = NULL;
xEventGroupSetBitsFromISR(p_i2c->cmd_evt, I2C_CMD_EVT_DONE, &HPTaskAwoken); evt.type = I2C_CMD_EVT_DONE;
xQueueOverwriteFromISR(p_i2c->cmd_evt_queue, &evt, &HPTaskAwoken);
if (HPTaskAwoken == pdTRUE) { if (HPTaskAwoken == pdTRUE) {
portYIELD_FROM_ISR(); portYIELD_FROM_ISR();
} }
@@ -1103,12 +1141,12 @@ esp_err_t i2c_master_cmd_begin(i2c_port_t i2c_num, i2c_cmd_handle_t cmd_handle,
esp_err_t ret = ESP_FAIL; esp_err_t ret = ESP_FAIL;
i2c_obj_t* p_i2c = p_i2c_obj[i2c_num]; i2c_obj_t* p_i2c = p_i2c_obj[i2c_num];
portTickType ticks_end = xTaskGetTickCount() + ticks_to_wait; portTickType ticks_start = xTaskGetTickCount();
portBASE_TYPE res = xSemaphoreTake(p_i2c->cmd_mux, ticks_to_wait); portBASE_TYPE res = xSemaphoreTake(p_i2c->cmd_mux, ticks_to_wait);
if (res == pdFALSE) { if (res == pdFALSE) {
return ESP_ERR_TIMEOUT; return ESP_ERR_TIMEOUT;
} }
xEventGroupClearBits(p_i2c->cmd_evt, I2C_CMD_EVT_DONE | I2C_CMD_EVT_ALIVE); xQueueReset(p_i2c->cmd_evt_queue);
if (p_i2c->status == I2C_STATUS_TIMEOUT if (p_i2c->status == I2C_STATUS_TIMEOUT
|| I2C[i2c_num]->status_reg.bus_busy == 1) { || I2C[i2c_num]->status_reg.bus_busy == 1) {
i2c_hw_fsm_reset(i2c_num); i2c_hw_fsm_reset(i2c_num);
@@ -1129,24 +1167,25 @@ esp_err_t i2c_master_cmd_begin(i2c_port_t i2c_num, i2c_cmd_handle_t cmd_handle,
//start send commands, at most 32 bytes one time, isr handler will process the remaining commands. //start send commands, at most 32 bytes one time, isr handler will process the remaining commands.
i2c_master_cmd_begin_static(i2c_num); i2c_master_cmd_begin_static(i2c_num);
if (ticks_to_wait == portMAX_DELAY) {
} else if (ticks_to_wait == 0) {
} else {
ticks_to_wait = ticks_end - xTaskGetTickCount();
}
// Wait event bits // Wait event bits
EventBits_t uxBits; i2c_cmd_evt_t evt;
while (1) { while (1) {
TickType_t wait_time = (ticks_to_wait < (I2C_CMD_ALIVE_INTERVAL_TICK) ? ticks_to_wait : (I2C_CMD_ALIVE_INTERVAL_TICK)); TickType_t wait_time = xTaskGetTickCount();
if (wait_time - ticks_start > ticks_to_wait) { // out of time
wait_time = I2C_CMD_ALIVE_INTERVAL_TICK;
} else {
wait_time = ticks_to_wait - (wait_time - ticks_start);
if (wait_time < I2C_CMD_ALIVE_INTERVAL_TICK) {
wait_time = I2C_CMD_ALIVE_INTERVAL_TICK;
}
}
// In master mode, since we don't have an interrupt to detective bus error or FSM state, what we do here is to make // In master mode, since we don't have an interrupt to detective bus error or FSM state, what we do here is to make
// sure the interrupt mechanism for master mode is still working. // sure the interrupt mechanism for master mode is still working.
// If the command sending is not finished and there is no interrupt any more, the bus is probably dead caused by external noise. // If the command sending is not finished and there is no interrupt any more, the bus is probably dead caused by external noise.
uxBits = xEventGroupWaitBits(p_i2c->cmd_evt, I2C_CMD_EVT_ALIVE | I2C_CMD_EVT_DONE, false, false, wait_time); portBASE_TYPE evt_res = xQueueReceive(p_i2c->cmd_evt_queue, &evt, wait_time);
if (uxBits) { if (evt_res == pdTRUE) {
if (uxBits & I2C_CMD_EVT_DONE) { if (evt.type == I2C_CMD_EVT_DONE) {
xEventGroupClearBits(p_i2c->cmd_evt, I2C_CMD_EVT_DONE);
if (p_i2c->status == I2C_STATUS_TIMEOUT) { if (p_i2c->status == I2C_STATUS_TIMEOUT) {
// If the I2C slave are powered off or the SDA/SCL are connected to ground, for example, // If the I2C slave are powered off or the SDA/SCL are connected to ground, for example,
// I2C hw FSM would get stuck in wrong state, we have to reset the I2C module in this case. // I2C hw FSM would get stuck in wrong state, we have to reset the I2C module in this case.
@@ -1159,8 +1198,7 @@ esp_err_t i2c_master_cmd_begin(i2c_port_t i2c_num, i2c_cmd_handle_t cmd_handle,
} }
break; break;
} }
if (uxBits & I2C_CMD_EVT_ALIVE) { if (evt.type == I2C_CMD_EVT_ALIVE) {
xEventGroupClearBits(p_i2c->cmd_evt, I2C_CMD_EVT_ALIVE);
} }
} else { } else {
ret = ESP_ERR_TIMEOUT; ret = ESP_ERR_TIMEOUT;
@@ -1169,12 +1207,6 @@ esp_err_t i2c_master_cmd_begin(i2c_port_t i2c_num, i2c_cmd_handle_t cmd_handle,
i2c_hw_fsm_reset(i2c_num); i2c_hw_fsm_reset(i2c_num);
break; break;
} }
if (ticks_to_wait == portMAX_DELAY) {
} else {
TickType_t now = xTaskGetTickCount();
ticks_to_wait = ticks_end > now ? (ticks_end - now) : 0;
}
} }
p_i2c->status = I2C_STATUS_DONE; p_i2c->status = I2C_STATUS_DONE;
xSemaphoreGive(p_i2c->cmd_mux); xSemaphoreGive(p_i2c->cmd_mux);
@@ -1252,6 +1284,3 @@ int i2c_slave_read_buffer(i2c_port_t i2c_num, uint8_t* data, size_t max_size, Ti
xSemaphoreGive(p_i2c->slv_rx_mux); xSemaphoreGive(p_i2c->slv_rx_mux);
return cnt; return cnt;
} }

View File

@@ -69,6 +69,13 @@ typedef enum {
I2C_ADDR_BIT_MAX, I2C_ADDR_BIT_MAX,
} i2c_addr_mode_t; } i2c_addr_mode_t;
typedef enum {
I2C_MASTER_ACK = 0x0, /*!< I2C ack for each byte read */
I2C_MASTER_NACK = 0x1, /*!< I2C nack for each byte read */
I2C_MASTER_LAST_NACK = 0x2, /*!< I2C nack for the last byte*/
I2C_MASTER_ACK_MAX,
} i2c_ack_type_t;
/** /**
* @brief I2C initialization parameters * @brief I2C initialization parameters
*/ */
@@ -288,7 +295,7 @@ esp_err_t i2c_master_write(i2c_cmd_handle_t cmd_handle, uint8_t* data, size_t da
* - ESP_OK Success * - ESP_OK Success
* - ESP_ERR_INVALID_ARG Parameter error * - ESP_ERR_INVALID_ARG Parameter error
*/ */
esp_err_t i2c_master_read_byte(i2c_cmd_handle_t cmd_handle, uint8_t* data, int ack); esp_err_t i2c_master_read_byte(i2c_cmd_handle_t cmd_handle, uint8_t* data, i2c_ack_type_t ack);
/** /**
* @brief Queue command for I2C master to read data from I2C bus * @brief Queue command for I2C master to read data from I2C bus
@@ -305,7 +312,7 @@ esp_err_t i2c_master_read_byte(i2c_cmd_handle_t cmd_handle, uint8_t* data, int a
* - ESP_OK Success * - ESP_OK Success
* - ESP_ERR_INVALID_ARG Parameter error * - ESP_ERR_INVALID_ARG Parameter error
*/ */
esp_err_t i2c_master_read(i2c_cmd_handle_t cmd_handle, uint8_t* data, size_t data_len, int ack); esp_err_t i2c_master_read(i2c_cmd_handle_t cmd_handle, uint8_t* data, size_t data_len, i2c_ack_type_t ack);
/** /**
* @brief Queue command for I2C master to generate a stop signal * @brief Queue command for I2C master to generate a stop signal

View File

@@ -51,7 +51,7 @@ typedef enum {
}rmt_mem_owner_t; }rmt_mem_owner_t;
typedef enum { typedef enum {
RMT_BASECLK_REF = 0, /*!< RMT source clock system reference tick, 1MHz by default(Not supported in this version) */ RMT_BASECLK_REF = 0, /*!< RMT source clock system reference tick, 1MHz by default (not supported in this version) */
RMT_BASECLK_APB, /*!< RMT source clock is APB CLK, 80Mhz by default */ RMT_BASECLK_APB, /*!< RMT source clock is APB CLK, 80Mhz by default */
RMT_BASECLK_MAX, RMT_BASECLK_MAX,
} rmt_source_clk_t; } rmt_source_clk_t;
@@ -84,10 +84,10 @@ typedef enum {
* @brief Data struct of RMT TX configure parameters * @brief Data struct of RMT TX configure parameters
*/ */
typedef struct { typedef struct {
bool loop_en; /*!< RMT loop output mode*/ bool loop_en; /*!< Enable sending RMT items in a loop */
uint32_t carrier_freq_hz; /*!< RMT carrier frequency */ uint32_t carrier_freq_hz; /*!< RMT carrier frequency */
uint8_t carrier_duty_percent; /*!< RMT carrier duty (%) */ uint8_t carrier_duty_percent; /*!< RMT carrier duty (%) */
rmt_carrier_level_t carrier_level; /*!< RMT carrier level */ rmt_carrier_level_t carrier_level; /*!< Level of the RMT output, when the carrier is applied */
bool carrier_en; /*!< RMT carrier enable */ bool carrier_en; /*!< RMT carrier enable */
rmt_idle_level_t idle_level; /*!< RMT idle level */ rmt_idle_level_t idle_level; /*!< RMT idle level */
bool idle_output_en; /*!< RMT idle level output enable */ bool idle_output_en; /*!< RMT idle level output enable */
@@ -97,7 +97,7 @@ typedef struct {
* @brief Data struct of RMT RX configure parameters * @brief Data struct of RMT RX configure parameters
*/ */
typedef struct { typedef struct {
bool filter_en; /*!< RMT receiver filer enable*/ bool filter_en; /*!< RMT receiver filter enable */
uint8_t filter_ticks_thresh; /*!< RMT filter tick number */ uint8_t filter_ticks_thresh; /*!< RMT filter tick number */
uint16_t idle_threshold; /*!< RMT RX idle threshold */ uint16_t idle_threshold; /*!< RMT RX idle threshold */
}rmt_rx_config_t; }rmt_rx_config_t;
@@ -186,10 +186,12 @@ esp_err_t rmt_get_rx_idle_thresh(rmt_channel_t channel, uint16_t *thresh);
* The 8 channels share a 512x32-bit RAM block which can be read and written * The 8 channels share a 512x32-bit RAM block which can be read and written
* by the processor cores over the APB bus, as well as read by the transmitters * by the processor cores over the APB bus, as well as read by the transmitters
* and written by the receivers. * and written by the receivers.
*
* The RAM address range for channel n is start_addr_CHn to end_addr_CHn, which are defined by: * The RAM address range for channel n is start_addr_CHn to end_addr_CHn, which are defined by:
* Memory block start address is RMT_CHANNEL_MEM(n) (in soc/rmt_reg.h), * Memory block start address is RMT_CHANNEL_MEM(n) (in soc/rmt_reg.h),
* that is, start_addr_chn = RMT base address + 0x800 + 64 4 n, and * that is, start_addr_chn = RMT base address + 0x800 + 64 4 n, and
* end_addr_chn = RMT base address + 0x800 + 64 4 n + 64 4 RMT_MEM_SIZE_CHn mod 512 4 * end_addr_chn = RMT base address + 0x800 + 64 4 n + 64 4 RMT_MEM_SIZE_CHn mod 512 4
*
* @note * @note
* If memory block number of one channel is set to a value greater than 1, this channel will occupy the memory * If memory block number of one channel is set to a value greater than 1, this channel will occupy the memory
* block of the next channel. * block of the next channel.
@@ -233,10 +235,8 @@ esp_err_t rmt_get_mem_block_num(rmt_channel_t channel, uint8_t* rmt_mem_num);
* @param low_level Low level duration of carrier. * @param low_level Low level duration of carrier.
* *
* @param carrier_level Configure the way carrier wave is modulated for channel 0-7. * @param carrier_level Configure the way carrier wave is modulated for channel 0-7.
* * - 1'b1:transmit on low output level
* 1'b1:transmit on low output level * - 1'b0:transmit on high output level
*
* 1'b0:transmit on high output level
* *
* @return * @return
* - ESP_ERR_INVALID_ARG Parameter error * - ESP_ERR_INVALID_ARG Parameter error
@@ -364,10 +364,9 @@ esp_err_t rmt_get_memory_owner(rmt_channel_t channel, rmt_mem_owner_t* owner);
* *
* @param channel RMT channel (0-7) * @param channel RMT channel (0-7)
* *
* @param loop_en To enable RMT transmitter loop sending mode. * @param loop_en Enable RMT transmitter loop sending mode.
*
* If set true, transmitter will continue sending from the first data * If set true, transmitter will continue sending from the first data
* to the last data in channel0-7 again and again. * to the last data in channel 0-7 over and over again in a loop.
* *
* @return * @return
* - ESP_ERR_INVALID_ARG Parameter error * - ESP_ERR_INVALID_ARG Parameter error
@@ -409,7 +408,7 @@ esp_err_t rmt_set_rx_filter(rmt_channel_t channel, bool rx_filter_en, uint8_t th
/** /**
* @brief Set RMT source clock * @brief Set RMT source clock
* *
* RMT module has two source clock: * RMT module has two clock sources:
* 1. APB clock which is 80Mhz * 1. APB clock which is 80Mhz
* 2. REF tick clock, which would be 1Mhz (not supported in this version). * 2. REF tick clock, which would be 1Mhz (not supported in this version).
* *
@@ -426,7 +425,7 @@ esp_err_t rmt_set_source_clk(rmt_channel_t channel, rmt_source_clk_t base_clk);
/** /**
* @brief Get RMT source clock * @brief Get RMT source clock
* *
* RMT module has two source clock: * RMT module has two clock sources:
* 1. APB clock which is 80Mhz * 1. APB clock which is 80Mhz
* 2. REF tick clock, which would be 1Mhz (not supported in this version). * 2. REF tick clock, which would be 1Mhz (not supported in this version).
* *
@@ -526,7 +525,7 @@ esp_err_t rmt_set_tx_intr_en(rmt_channel_t channel, bool en);
/** /**
* @brief Set RMT TX threshold event interrupt enable * @brief Set RMT TX threshold event interrupt enable
* *
* Causes an interrupt when a threshold number of items have been transmitted. * An interrupt will be triggered when the number of transmitted items reaches the threshold value
* *
* @param channel RMT channel (0 - 7) * @param channel RMT channel (0 - 7)
* *
@@ -541,7 +540,7 @@ esp_err_t rmt_set_tx_intr_en(rmt_channel_t channel, bool en);
esp_err_t rmt_set_tx_thr_intr_en(rmt_channel_t channel, bool en, uint16_t evt_thresh); esp_err_t rmt_set_tx_thr_intr_en(rmt_channel_t channel, bool en, uint16_t evt_thresh);
/** /**
* @brief Set RMT pins * @brief Set RMT pin
* *
* @param channel RMT channel (0 - 7) * @param channel RMT channel (0 - 7)
* *
@@ -558,7 +557,7 @@ esp_err_t rmt_set_pin(rmt_channel_t channel, rmt_mode_t mode, gpio_num_t gpio_nu
/** /**
* @brief Configure RMT parameters * @brief Configure RMT parameters
* *
* @param rmt_param RMT parameter structor * @param rmt_param RMT parameter struct
* *
* @return * @return
* - ESP_ERR_INVALID_ARG Parameter error * - ESP_ERR_INVALID_ARG Parameter error
@@ -567,14 +566,15 @@ esp_err_t rmt_set_pin(rmt_channel_t channel, rmt_mode_t mode, gpio_num_t gpio_nu
esp_err_t rmt_config(const rmt_config_t* rmt_param); esp_err_t rmt_config(const rmt_config_t* rmt_param);
/** /**
* @brief register RMT interrupt handler, the handler is an ISR. * @brief Register RMT interrupt handler, the handler is an ISR.
* *
* The handler will be attached to the same CPU core that this function is running on. * The handler will be attached to the same CPU core that this function is running on.
*
* @note If you already called rmt_driver_install to use system RMT driver, * @note If you already called rmt_driver_install to use system RMT driver,
* please do not register ISR handler again. * please do not register ISR handler again.
* *
* @param fn Interrupt handler function. * @param fn Interrupt handler function.
* @param arg Parameter for handler function * @param arg Parameter for the handler function
* @param intr_alloc_flags Flags used to allocate the interrupt. One or multiple (ORred) * @param intr_alloc_flags Flags used to allocate the interrupt. One or multiple (ORred)
* ESP_INTR_FLAG_* values. See esp_intr_alloc.h for more info. * ESP_INTR_FLAG_* values. See esp_intr_alloc.h for more info.
* @param handle If non-zero, a handle to later clean up the ISR gets stored here. * @param handle If non-zero, a handle to later clean up the ISR gets stored here.
@@ -653,9 +653,9 @@ esp_err_t rmt_driver_uninstall(rmt_channel_t channel);
* *
* @param item_num RMT data item number. * @param item_num RMT data item number.
* *
* @param wait_tx_done If set 1, it will block the task and wait for sending done. * @param wait_tx_done
* * - If set 1, it will block the task and wait for sending done.
* If set 0, it will not wait and return immediately. * - If set 0, it will not wait and return immediately.
* *
* @note * @note
* This function will not copy data, instead, it will point to the original items, * This function will not copy data, instead, it will point to the original items,
@@ -677,24 +677,24 @@ esp_err_t rmt_write_items(rmt_channel_t channel, const rmt_item32_t* rmt_item, i
* *
* @param channel RMT channel (0 - 7) * @param channel RMT channel (0 - 7)
* *
* @param wait_time Maximum time to wait for transmission to be complete * @param wait_time Maximum time in ticks to wait for transmission to be complete
* *
* @return * @return
* - ESP_OK RMT Tx done successfully * - ESP_OK RMT Tx done successfully
* - ESP_ERR_TIMEOUT Crossed the 'wait_time' given * - ESP_ERR_TIMEOUT Exceeded the 'wait_time' given
* - ESP_ERR_INVALID_ARG Parameter error * - ESP_ERR_INVALID_ARG Parameter error
* - ESP_FAIL Driver not installed * - ESP_FAIL Driver not installed
*/ */
esp_err_t rmt_wait_tx_done(rmt_channel_t channel, TickType_t wait_time); esp_err_t rmt_wait_tx_done(rmt_channel_t channel, TickType_t wait_time);
/** /**
* @brief Get ringbuffer from UART. * @brief Get ringbuffer from RMT.
* *
* Users can get the RMT RX ringbuffer handler, and process the RX data. * Users can get the RMT RX ringbuffer handle, and process the RX data.
* *
* @param channel RMT channel (0 - 7) * @param channel RMT channel (0 - 7)
* *
* @param buf_handle Pointer to buffer handler to accept RX ringbuffer handler. * @param buf_handle Pointer to buffer handle to accept RX ringbuffer handle.
* *
* @return * @return
* - ESP_ERR_INVALID_ARG Parameter error * - ESP_ERR_INVALID_ARG Parameter error
@@ -702,62 +702,7 @@ esp_err_t rmt_wait_tx_done(rmt_channel_t channel, TickType_t wait_time);
*/ */
esp_err_t rmt_get_ringbuf_handle(rmt_channel_t channel, RingbufHandle_t* buf_handle); esp_err_t rmt_get_ringbuf_handle(rmt_channel_t channel, RingbufHandle_t* buf_handle);
/***************************EXAMPLE********************************** /*
*
* @note
* You can also refer to example/09_rmt_nec_tx_rx to have more information about how to use RMT module.
*
* ----------------EXAMPLE OF RMT SETTING ---------------------
* @code{c}
* //1. enable RMT
* //enable RMT module, or you can not set any register of it.
* //this will be done in rmt_config API.
* periph_module_enable(PERIPH_RMT_MODULE);
* @endcode
*
* @code{c}
* //2. set RMT transmitter
* void rmt_tx_init()
* {
* rmt_config_t rmt_tx;
* rmt_tx.channel = 0;
* rmt_tx.gpio_num = 16;
* rmt_tx.mem_block_num = 1;
* rmt_tx.clk_div = 100;
* rmt_tx.tx_config.loop_en = false;
* rmt_tx.tx_config.carrier_duty_percent = 50;
* rmt_tx.tx_config.carrier_freq_hz = 38000;
* rmt_tx.tx_config.carrier_level = 1;
* rmt_tx.tx_config.carrier_en = RMT_TX_CARRIER_EN;
* rmt_tx.tx_config.idle_level = 0;
* rmt_tx.tx_config.idle_output_en = true;
* rmt_tx.rmt_mode = 0;
* rmt_config(&rmt_tx);
*
* //install system RMT driver, disable rx ringbuffer for transmitter.
* rmt_driver_install(rmt_tx.channel, 0, 0);
* }
*
* @endcode
* @code{c}
* //3. set RMT receiver
* void rmt_rx_init()
* {
* rmt_config_t rmt_rx;
* rmt_rx.channel = 1;
* rmt_rx.gpio_num = 19;
* rmt_rx.clk_div = 100;
* rmt_rx.mem_block_num = 1;
* rmt_rx.rmt_mode = RMT_MODE_RX;
* rmt_rx.rx_config.filter_en = true;
* rmt_rx.rx_config.filter_ticks_thresh = 100;
* rmt_rx.rx_config.idle_threshold = 0xffff;
* rmt_config(&rmt_rx);
*
* //install system RMT driver.
* rmt_driver_install(rmt_rx.channel, 1000, 0);
* }
*
* ----------------EXAMPLE OF RMT INTERRUPT ------------------ * ----------------EXAMPLE OF RMT INTERRUPT ------------------
* @code{c} * @code{c}
* *
@@ -774,7 +719,7 @@ esp_err_t rmt_get_ringbuf_handle(rmt_channel_t channel, RingbufHandle_t* buf_han
* //read RMT interrupt status. * //read RMT interrupt status.
* uint32_t intr_st = RMT.int_st.val; * uint32_t intr_st = RMT.int_st.val;
* *
* //you will find which channels have triggered fade_end interrupt here, * //you will find which channels have triggered an interrupt here,
* //then, you can post some event to RTOS queue to process the event. * //then, you can post some event to RTOS queue to process the event.
* //later we will add a queue in the driver code. * //later we will add a queue in the driver code.
* *
@@ -788,8 +733,6 @@ esp_err_t rmt_get_ringbuf_handle(rmt_channel_t channel, RingbufHandle_t* buf_han
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@@ -25,12 +25,12 @@
#include "driver/sdspi_host.h" #include "driver/sdspi_host.h"
#include "sdspi_private.h" #include "sdspi_private.h"
#include "sdspi_crc.h" #include "sdspi_crc.h"
#include "esp_timer.h"
/// Max number of transactions in flight (used in start_command_write_blocks) /// Max number of transactions in flight (used in start_command_write_blocks)
#define SDSPI_TRANSACTION_COUNT 4 #define SDSPI_TRANSACTION_COUNT 4
#define SDSPI_MOSI_IDLE_VAL 0xff //!< Data value which causes MOSI to stay high #define SDSPI_MOSI_IDLE_VAL 0xff //!< Data value which causes MOSI to stay high
/// FIXME: this has to be replaced with a timeout expressed in ms, rather in retries
#define SDSPI_RETRY_COUNT 1000
#define GPIO_UNUSED 0xff //!< Flag indicating that CD/WP is unused #define GPIO_UNUSED 0xff //!< Flag indicating that CD/WP is unused
/// Size of the buffer returned by get_block_buf /// Size of the buffer returned by get_block_buf
#define SDSPI_BLOCK_BUF_SIZE (SDSPI_MAX_DATA_LEN + 4) #define SDSPI_BLOCK_BUF_SIZE (SDSPI_MAX_DATA_LEN + 4)
@@ -426,7 +426,7 @@ static esp_err_t start_command_default(int slot, int flags, sdspi_hw_cmd_t *cmd)
} }
// Wait until MISO goes high // Wait until MISO goes high
static esp_err_t poll_busy(int slot, spi_transaction_t* t) static esp_err_t poll_busy(int slot, spi_transaction_t* t, int timeout_ms)
{ {
uint8_t t_rx; uint8_t t_rx;
*t = (spi_transaction_t) { *t = (spi_transaction_t) {
@@ -436,7 +436,9 @@ static esp_err_t poll_busy(int slot, spi_transaction_t* t)
}; };
esp_err_t ret; esp_err_t ret;
for (int i = 0; i < SDSPI_RETRY_COUNT; i++) { uint64_t t_end = esp_timer_get_time() + timeout_ms * 1000;
int nonzero_count = 0;
do {
t_rx = SDSPI_MOSI_IDLE_VAL; t_rx = SDSPI_MOSI_IDLE_VAL;
t->rx_data[0] = 0; t->rx_data[0] = 0;
ret = spi_device_transmit(spi_handle(slot), t); ret = spi_device_transmit(spi_handle(slot), t);
@@ -444,16 +446,17 @@ static esp_err_t poll_busy(int slot, spi_transaction_t* t)
return ret; return ret;
} }
if (t->rx_data[0] != 0) { if (t->rx_data[0] != 0) {
if (i < SDSPI_RETRY_COUNT - 2) { if (++nonzero_count == 2) {
i = SDSPI_RETRY_COUNT - 2;
}
}
}
return ESP_OK; return ESP_OK;
} }
}
} while(esp_timer_get_time() < t_end);
ESP_LOGD(TAG, "%s: timeout", __func__);
return ESP_ERR_TIMEOUT;
}
// Wait for response token // Wait for response token
static esp_err_t poll_response_token(int slot, spi_transaction_t* t) static esp_err_t poll_response_token(int slot, spi_transaction_t* t, int timeout_ms)
{ {
uint8_t t_rx; uint8_t t_rx;
*t = (spi_transaction_t) { *t = (spi_transaction_t) {
@@ -462,8 +465,8 @@ static esp_err_t poll_response_token(int slot, spi_transaction_t* t)
.length = 8, .length = 8,
}; };
esp_err_t ret; esp_err_t ret;
uint64_t t_end = esp_timer_get_time() + timeout_ms * 1000;
for (int retry = 0; retry < SDSPI_RETRY_COUNT; retry++) { do {
t_rx = SDSPI_MOSI_IDLE_VAL; t_rx = SDSPI_MOSI_IDLE_VAL;
t->rx_data[0] = 0; t->rx_data[0] = 0;
ret = spi_device_transmit(spi_handle(slot), t); ret = spi_device_transmit(spi_handle(slot), t);
@@ -471,7 +474,7 @@ static esp_err_t poll_response_token(int slot, spi_transaction_t* t)
return ret; return ret;
} }
if ((t->rx_data[0] & TOKEN_RSP_MASK) == TOKEN_RSP_OK) { if ((t->rx_data[0] & TOKEN_RSP_MASK) == TOKEN_RSP_OK) {
break; return ESP_OK;
} }
if ((t->rx_data[0] & TOKEN_RSP_MASK) == TOKEN_RSP_CRC_ERR) { if ((t->rx_data[0] & TOKEN_RSP_MASK) == TOKEN_RSP_CRC_ERR) {
return ESP_ERR_INVALID_CRC; return ESP_ERR_INVALID_CRC;
@@ -479,19 +482,17 @@ static esp_err_t poll_response_token(int slot, spi_transaction_t* t)
if ((t->rx_data[0] & TOKEN_RSP_MASK) == TOKEN_RSP_WRITE_ERR) { if ((t->rx_data[0] & TOKEN_RSP_MASK) == TOKEN_RSP_WRITE_ERR) {
return ESP_ERR_INVALID_RESPONSE; return ESP_ERR_INVALID_RESPONSE;
} }
if (retry == SDSPI_RETRY_COUNT - 1) { } while (esp_timer_get_time() < t_end);
return ESP_ERR_TIMEOUT;
}
}
return ESP_OK; ESP_LOGD(TAG, "%s: timeout", __func__);
return ESP_ERR_TIMEOUT;
} }
// Wait for data token, reading 8 bytes at a time. // Wait for data token, reading 8 bytes at a time.
// If the token is found, write all subsequent bytes to extra_ptr, // If the token is found, write all subsequent bytes to extra_ptr,
// and store the number of bytes written to extra_size. // and store the number of bytes written to extra_size.
static esp_err_t poll_data_token(int slot, spi_transaction_t* t, static esp_err_t poll_data_token(int slot, spi_transaction_t* t,
uint8_t* extra_ptr, size_t* extra_size) uint8_t* extra_ptr, size_t* extra_size, int timeout_ms)
{ {
uint8_t t_rx[8]; uint8_t t_rx[8];
*t = (spi_transaction_t) { *t = (spi_transaction_t) {
@@ -500,7 +501,8 @@ static esp_err_t poll_data_token(int slot, spi_transaction_t* t,
.length = sizeof(t_rx) * 8, .length = sizeof(t_rx) * 8,
}; };
esp_err_t ret; esp_err_t ret;
for (int retry = 0; retry < SDSPI_RETRY_COUNT; retry++) { uint64_t t_end = esp_timer_get_time() + timeout_ms * 1000;
do {
memset(t_rx, SDSPI_MOSI_IDLE_VAL, sizeof(t_rx)); memset(t_rx, SDSPI_MOSI_IDLE_VAL, sizeof(t_rx));
ret = spi_device_transmit(spi_handle(slot), t); ret = spi_device_transmit(spi_handle(slot), t);
if (ret != ESP_OK) { if (ret != ESP_OK) {
@@ -522,14 +524,12 @@ static esp_err_t poll_data_token(int slot, spi_transaction_t* t,
} }
} }
if (found) { if (found) {
break;
}
if (retry == SDSPI_RETRY_COUNT - 1) {
return ESP_ERR_TIMEOUT;
}
}
return ESP_OK; return ESP_OK;
} }
} while (esp_timer_get_time() < t_end);
ESP_LOGD(TAG, "%s: timeout", __func__);
return ESP_ERR_TIMEOUT;
}
/** /**
@@ -608,11 +608,14 @@ static esp_err_t start_command_read_blocks(int slot, sdspi_hw_cmd_t *cmd,
if (need_poll) { if (need_poll) {
// Wait for data to be ready // Wait for data to be ready
spi_transaction_t* t_poll = get_transaction(slot); spi_transaction_t* t_poll = get_transaction(slot);
poll_data_token(slot, t_poll, cmd_u8 + SDSPI_CMD_R1_SIZE, &extra_data_size); ret = poll_data_token(slot, t_poll, cmd_u8 + SDSPI_CMD_R1_SIZE, &extra_data_size, cmd->timeout_ms);
release_transaction(slot);
if (ret != ESP_OK) {
return ret;
}
if (extra_data_size) { if (extra_data_size) {
extra_data_ptr = cmd_u8 + SDSPI_CMD_R1_SIZE; extra_data_ptr = cmd_u8 + SDSPI_CMD_R1_SIZE;
} }
release_transaction(slot);
} }
// Arrange RX buffer // Arrange RX buffer
@@ -674,14 +677,17 @@ static esp_err_t start_command_read_blocks(int slot, sdspi_hw_cmd_t *cmd,
// To end multi block transfer, send stop command and wait for the // To end multi block transfer, send stop command and wait for the
// card to process it // card to process it
sdspi_hw_cmd_t stop_cmd; sdspi_hw_cmd_t stop_cmd;
make_hw_cmd(MMC_STOP_TRANSMISSION, 0, &stop_cmd); make_hw_cmd(MMC_STOP_TRANSMISSION, 0, cmd->timeout_ms, &stop_cmd);
ret = start_command_default(slot, SDSPI_CMD_FLAG_RSP_R1, &stop_cmd); ret = start_command_default(slot, SDSPI_CMD_FLAG_RSP_R1, &stop_cmd);
if (ret != ESP_OK) { if (ret != ESP_OK) {
return ret; return ret;
} }
spi_transaction_t* t_poll = get_transaction(slot); spi_transaction_t* t_poll = get_transaction(slot);
ret = poll_busy(slot, t_poll); ret = poll_busy(slot, t_poll, cmd->timeout_ms);
release_transaction(slot); release_transaction(slot);
if (ret != ESP_OK) {
return ret;
}
} }
return ESP_OK; return ESP_OK;
} }
@@ -768,7 +774,7 @@ static esp_err_t start_command_write_blocks(int slot, sdspi_hw_cmd_t *cmd,
// Poll for response // Poll for response
spi_transaction_t* t_poll = get_transaction(slot); spi_transaction_t* t_poll = get_transaction(slot);
ret = poll_response_token(slot, t_poll); ret = poll_response_token(slot, t_poll, cmd->timeout_ms);
release_transaction(slot); release_transaction(slot);
if (ret != ESP_OK) { if (ret != ESP_OK) {
return ret; return ret;
@@ -776,7 +782,7 @@ static esp_err_t start_command_write_blocks(int slot, sdspi_hw_cmd_t *cmd,
// Wait for the card to finish writing data // Wait for the card to finish writing data
t_poll = get_transaction(slot); t_poll = get_transaction(slot);
ret = poll_busy(slot, t_poll); ret = poll_busy(slot, t_poll, cmd->timeout_ms);
release_transaction(slot); release_transaction(slot);
if (ret != ESP_OK) { if (ret != ESP_OK) {
return ret; return ret;
@@ -803,7 +809,7 @@ static esp_err_t start_command_write_blocks(int slot, sdspi_hw_cmd_t *cmd,
wait_for_transactions(slot); wait_for_transactions(slot);
spi_transaction_t* t_poll = get_transaction(slot); spi_transaction_t* t_poll = get_transaction(slot);
ret = poll_busy(slot, t_poll); ret = poll_busy(slot, t_poll, cmd->timeout_ms);
release_transaction(slot); release_transaction(slot);
if (ret != ESP_OK) { if (ret != ESP_OK) {
return ret; return ret;

View File

@@ -73,6 +73,8 @@ typedef struct {
uint8_t r1; uint8_t r1;
/// Up to 16 bytes of response. Luckily, this is aligned on 4 byte boundary. /// Up to 16 bytes of response. Luckily, this is aligned on 4 byte boundary.
uint32_t response[4]; uint32_t response[4];
/// response timeout, in milliseconds
int timeout_ms;
} sdspi_hw_cmd_t; } sdspi_hw_cmd_t;
#define SDSPI_CMD_NORESP_SIZE 6 //!< Size of the command without any response #define SDSPI_CMD_NORESP_SIZE 6 //!< Size of the command without any response
@@ -90,7 +92,7 @@ typedef struct {
#define SDSPI_MAX_DATA_LEN 512 //!< Max size of single block transfer #define SDSPI_MAX_DATA_LEN 512 //!< Max size of single block transfer
void make_hw_cmd(uint32_t opcode, uint32_t arg, sdspi_hw_cmd_t *hw_cmd); void make_hw_cmd(uint32_t opcode, uint32_t arg, int timeout_ms, sdspi_hw_cmd_t *hw_cmd);
esp_err_t sdspi_host_start_command(int slot, sdspi_hw_cmd_t *cmd, esp_err_t sdspi_host_start_command(int slot, sdspi_hw_cmd_t *cmd,
void *data, uint32_t data_size, int flags); void *data, uint32_t data_size, int flags);

View File

@@ -36,7 +36,7 @@ static uint8_t sdspi_msg_crc7(sdspi_hw_cmd_t* hw_cmd)
return sdspi_crc7((const uint8_t *)hw_cmd, bytes_to_crc); return sdspi_crc7((const uint8_t *)hw_cmd, bytes_to_crc);
} }
void make_hw_cmd(uint32_t opcode, uint32_t arg, sdspi_hw_cmd_t *hw_cmd) void make_hw_cmd(uint32_t opcode, uint32_t arg, int timeout_ms, sdspi_hw_cmd_t *hw_cmd)
{ {
hw_cmd->start_bit = 0; hw_cmd->start_bit = 0;
hw_cmd->transmission_bit = 1; hw_cmd->transmission_bit = 1;
@@ -48,6 +48,7 @@ void make_hw_cmd(uint32_t opcode, uint32_t arg, sdspi_hw_cmd_t *hw_cmd)
uint32_t arg_s = __builtin_bswap32(arg); uint32_t arg_s = __builtin_bswap32(arg);
memcpy(hw_cmd->arguments, &arg_s, sizeof(arg_s)); memcpy(hw_cmd->arguments, &arg_s, sizeof(arg_s));
hw_cmd->crc7 = sdspi_msg_crc7(hw_cmd); hw_cmd->crc7 = sdspi_msg_crc7(hw_cmd);
hw_cmd->timeout_ms = timeout_ms;
} }
esp_err_t sdspi_host_do_transaction(int slot, sdmmc_command_t *cmdinfo) esp_err_t sdspi_host_do_transaction(int slot, sdmmc_command_t *cmdinfo)
@@ -55,7 +56,7 @@ esp_err_t sdspi_host_do_transaction(int slot, sdmmc_command_t *cmdinfo)
_lock_acquire(&s_lock); _lock_acquire(&s_lock);
// Convert the command to wire format // Convert the command to wire format
sdspi_hw_cmd_t hw_cmd; sdspi_hw_cmd_t hw_cmd;
make_hw_cmd(cmdinfo->opcode, cmdinfo->arg, &hw_cmd); make_hw_cmd(cmdinfo->opcode, cmdinfo->arg, cmdinfo->timeout_ms, &hw_cmd);
// Flags indicate which of the transfer types should be used // Flags indicate which of the transfer types should be used
int flags = 0; int flags = 0;

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
@@ -772,6 +776,22 @@ config ESP_TIMER_PROFILING
used for timer storage, and should only be used for debugging/testing used for timer storage, and should only be used for debugging/testing
purposes. purposes.
config COMPATIBLE_PRE_V2_1_BOOTLOADERS
bool "App compatible with bootloaders before IDF v2.1"
default n
help
Bootloaders before IDF v2.1 did less initialisation of the
system clock. This setting needs to be enabled to build an app
which can be booted by these older bootloaders.
If this setting is enabled, the app can be booted by any bootloader
from IDF v1.0 up to the current version.
If this setting is disabled, the app can only be booted by bootloaders
from IDF v2.1 or newer.
Enabling this setting adds approximately 1KB to the app's IRAM usage.
endmenu # ESP32-Specific endmenu # ESP32-Specific
menu Wi-Fi menu Wi-Fi

View File

@@ -27,10 +27,10 @@
#include "soc/soc.h" #include "soc/soc.h"
#include "soc/rtc.h" #include "soc/rtc.h"
#include "soc/rtc_cntl_reg.h" #include "soc/rtc_cntl_reg.h"
#include "soc/dport_reg.h"
#include "soc/i2s_reg.h" #include "soc/i2s_reg.h"
#include "driver/periph_ctrl.h" #include "driver/periph_ctrl.h"
#include "xtensa/core-macros.h" #include "xtensa/core-macros.h"
#include "bootloader_clock.h"
/* Number of cycles to wait from the 32k XTAL oscillator to consider it running. /* Number of cycles to wait from the 32k XTAL oscillator to consider it running.
* Larger values increase startup delay. Smaller values may cause false positive * Larger values increase startup delay. Smaller values may cause false positive
@@ -54,6 +54,22 @@ void esp_clk_init(void)
{ {
rtc_config_t cfg = RTC_CONFIG_DEFAULT(); rtc_config_t cfg = RTC_CONFIG_DEFAULT();
rtc_init(cfg); rtc_init(cfg);
#ifdef CONFIG_COMPATIBLE_PRE_V2_1_BOOTLOADERS
/* Check the bootloader set the XTAL frequency.
Bootloaders pre-v2.1 don't do this.
*/
rtc_xtal_freq_t xtal_freq = rtc_clk_xtal_freq_get();
if (xtal_freq == RTC_XTAL_FREQ_AUTO) {
ESP_EARLY_LOGW(TAG, "RTC domain not initialised by bootloader");
bootloader_clock_configure();
}
#else
/* If this assertion fails, either upgrade the bootloader or enable CONFIG_COMPATIBLE_PRE_V2_1_BOOTLOADERS */
assert(rtc_clk_xtal_freq_get() != RTC_XTAL_FREQ_AUTO);
#endif
rtc_clk_fast_freq_set(RTC_FAST_FREQ_8M); rtc_clk_fast_freq_set(RTC_FAST_FREQ_8M);
#ifdef CONFIG_ESP32_RTC_CLOCK_SOURCE_EXTERNAL_CRYSTAL #ifdef CONFIG_ESP32_RTC_CLOCK_SOURCE_EXTERNAL_CRYSTAL
@@ -187,7 +203,7 @@ void esp_perip_clk_init(void)
common_perip_clk = DPORT_WDG_CLK_EN | common_perip_clk = DPORT_WDG_CLK_EN |
DPORT_I2S0_CLK_EN | DPORT_I2S0_CLK_EN |
#if CONFIG_CONSOLE_UART_NUM != 0 #if CONFIG_CONSOLE_UART_NUM != 0
DPORT_UART0_CLK_EN | DPORT_UART_CLK_EN |
#endif #endif
#if CONFIG_CONSOLE_UART_NUM != 1 #if CONFIG_CONSOLE_UART_NUM != 1
DPORT_UART1_CLK_EN | DPORT_UART1_CLK_EN |
@@ -203,10 +219,7 @@ void esp_perip_clk_init(void)
DPORT_LEDC_CLK_EN | DPORT_LEDC_CLK_EN |
DPORT_UHCI1_CLK_EN | DPORT_UHCI1_CLK_EN |
DPORT_TIMERGROUP1_CLK_EN | DPORT_TIMERGROUP1_CLK_EN |
//80MHz SPIRAM uses SPI2 as well; it's initialized before this is called. Do not disable the clock for that if this is enabled.
#if !CONFIG_SPIRAM_SPEED_80M
DPORT_SPI_CLK_EN_2 | DPORT_SPI_CLK_EN_2 |
#endif
DPORT_PWM0_CLK_EN | DPORT_PWM0_CLK_EN |
DPORT_I2C_EXT1_CLK_EN | DPORT_I2C_EXT1_CLK_EN |
DPORT_CAN_CLK_EN | DPORT_CAN_CLK_EN |
@@ -228,6 +241,15 @@ void esp_perip_clk_init(void)
DPORT_WIFI_CLK_EMAC_EN; DPORT_WIFI_CLK_EMAC_EN;
} }
#if CONFIG_SPIRAM_SPEED_80M
//80MHz SPIRAM uses SPI2 as well; it's initialized before this is called. Because it is used in
//a weird mode where clock to the peripheral is disabled but reset is also disabled, it 'hangs'
//in a state where it outputs a continuous 80MHz signal. Mask its bit here because we should
//not modify that state, regardless of what we calculated earlier.
common_perip_clk &= ~DPORT_SPI_CLK_EN_2;
#endif
/* Change I2S clock to audio PLL first. Because if I2S uses 160MHz clock, /* Change I2S clock to audio PLL first. Because if I2S uses 160MHz clock,
* the current is not reduced when disable I2S clock. * the current is not reduced when disable I2S clock.
*/ */

View File

@@ -35,7 +35,7 @@
#include "tcpip_adapter.h" #include "tcpip_adapter.h"
#include "esp_log.h" #include "esp_log.h"
const char* TAG = "event"; static const char* TAG = "event";
#define WIFI_API_CALL_CHECK(info, api_call, ret) \ #define WIFI_API_CALL_CHECK(info, api_call, ret) \
do{\ do{\

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

@@ -34,6 +34,7 @@
#include "soc/efuse_reg.h" #include "soc/efuse_reg.h"
#include "driver/gpio.h" #include "driver/gpio.h"
#include "driver/spi_common.h" #include "driver/spi_common.h"
#include "driver/periph_ctrl.h"
#if CONFIG_SPIRAM_SUPPORT #if CONFIG_SPIRAM_SUPPORT
@@ -492,6 +493,7 @@ esp_err_t IRAM_ATTR psram_enable(psram_cache_mode_t mode, psram_vaddr_mode_t vad
Application code should never touch VSPI hardware in this case. We try to stop applications Application code should never touch VSPI hardware in this case. We try to stop applications
from doing this using the drivers by claiming the port for ourselves*/ from doing this using the drivers by claiming the port for ourselves*/
if (mode == PSRAM_CACHE_F80M_S80M) { if (mode == PSRAM_CACHE_F80M_S80M) {
periph_module_enable(PERIPH_VSPI_MODULE);
bool r=spicommon_periph_claim(VSPI_HOST); bool r=spicommon_periph_claim(VSPI_HOST);
if (!r) { if (!r) {
return ESP_ERR_INVALID_STATE; return ESP_ERR_INVALID_STATE;
@@ -502,12 +504,7 @@ esp_err_t IRAM_ATTR psram_enable(psram_cache_mode_t mode, psram_vaddr_mode_t vad
assert(mode < PSRAM_CACHE_MAX && "we don't support any other mode for now."); assert(mode < PSRAM_CACHE_MAX && "we don't support any other mode for now.");
s_psram_mode = mode; s_psram_mode = mode;
DPORT_SET_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_SPI_CLK_EN); periph_module_enable(PERIPH_SPI_MODULE);
DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_SPI_RST);
DPORT_SET_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_SPI_CLK_EN_1);
DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_SPI_RST_1);
DPORT_SET_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_SPI_CLK_EN_2);
DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_SPI_RST_2);
WRITE_PERI_REG(SPI_EXT3_REG(0), 0x1); WRITE_PERI_REG(SPI_EXT3_REG(0), 0x1);
CLEAR_PERI_REG_MASK(SPI_USER_REG(PSRAM_SPI_1), SPI_USR_PREP_HOLD_M); CLEAR_PERI_REG_MASK(SPI_USER_REG(PSRAM_SPI_1), SPI_USR_PREP_HOLD_M);
@@ -519,6 +516,8 @@ esp_err_t IRAM_ATTR psram_enable(psram_cache_mode_t mode, psram_vaddr_mode_t vad
gpio_matrix_out(PSRAM_CS_IO, SPICS1_OUT_IDX, 0, 0); gpio_matrix_out(PSRAM_CS_IO, SPICS1_OUT_IDX, 0, 0);
gpio_matrix_out(PSRAM_CLK_IO, VSPICLK_OUT_IDX, 0, 0); gpio_matrix_out(PSRAM_CLK_IO, VSPICLK_OUT_IDX, 0, 0);
//use spi3 clock,but use spi1 data/cs wires //use spi3 clock,but use spi1 data/cs wires
//We get a solid 80MHz clock from SPI3 by setting it up, starting a transaction, waiting until it
//is in progress, then cutting the clock (but not the reset!) to that peripheral.
WRITE_PERI_REG(SPI_ADDR_REG(PSRAM_SPI_3), 32 << 24); WRITE_PERI_REG(SPI_ADDR_REG(PSRAM_SPI_3), 32 << 24);
WRITE_PERI_REG(SPI_CLOCK_REG(PSRAM_SPI_3), SPI_CLK_EQU_SYSCLK_M); //SET 80M AND CLEAR OTHERS WRITE_PERI_REG(SPI_CLOCK_REG(PSRAM_SPI_3), SPI_CLK_EQU_SYSCLK_M); //SET 80M AND CLEAR OTHERS
SET_PERI_REG_MASK(SPI_CMD_REG(PSRAM_SPI_3), SPI_FLASH_READ_M); SET_PERI_REG_MASK(SPI_CMD_REG(PSRAM_SPI_3), SPI_FLASH_READ_M);
@@ -526,7 +525,7 @@ esp_err_t IRAM_ATTR psram_enable(psram_cache_mode_t mode, psram_vaddr_mode_t vad
while (1) { while (1) {
spi_status = READ_PERI_REG(SPI_EXT2_REG(PSRAM_SPI_3)); spi_status = READ_PERI_REG(SPI_EXT2_REG(PSRAM_SPI_3));
if (spi_status != 0 && spi_status != 1) { if (spi_status != 0 && spi_status != 1) {
DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, BIT(PSRAM_CS_IO)); //DPORT_SPI_CLK_EN DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_SPI_CLK_EN_2);
break; break;
} }
} }

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

@@ -128,6 +128,7 @@ esp_err_t esp_vfs_fat_sdmmc_mount(const char* base_path,
goto fail; goto fail;
} }
free(workbuf); free(workbuf);
workbuf = NULL;
ESP_LOGW(TAG, "mounting again"); ESP_LOGW(TAG, "mounting again");
res = f_mount(fs, drv, 0); res = f_mount(fs, drv, 0);
if (res != FR_OK) { if (res != FR_OK) {
@@ -139,7 +140,7 @@ esp_err_t esp_vfs_fat_sdmmc_mount(const char* base_path,
return ESP_OK; return ESP_OK;
fail: fail:
sdmmc_host_deinit(); host_config->deinit();
free(workbuf); free(workbuf);
if (fs) { if (fs) {
f_mount(NULL, drv, 0); f_mount(NULL, drv, 0);
@@ -160,10 +161,11 @@ esp_err_t esp_vfs_fat_sdmmc_unmount()
char drv[3] = {(char)('0' + s_pdrv), ':', 0}; char drv[3] = {(char)('0' + s_pdrv), ':', 0};
f_mount(0, drv, 0); f_mount(0, drv, 0);
// release SD driver // release SD driver
esp_err_t (*host_deinit)() = s_card->host.deinit;
ff_diskio_unregister(s_pdrv); ff_diskio_unregister(s_pdrv);
free(s_card); free(s_card);
s_card = NULL; s_card = NULL;
sdmmc_host_deinit(); (*host_deinit)();
esp_err_t err = esp_vfs_fat_unregister_path(s_base_path); esp_err_t err = esp_vfs_fat_unregister_path(s_base_path);
free(s_base_path); free(s_base_path);
s_base_path = NULL; s_base_path = NULL;

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,32 @@ 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 Create a ring buffer of type RINGBUF_TYPE_NOSPLIT for a fixed item_size
*
* This API is similar to xRingbufferCreate(), but it will internally allocate
* additional space for the headers.
*
* @param item_size Size of each item to be put into the ring buffer
* @param num_item Maximum number of items the buffer needs to hold simultaneously
*
* @return A RingbufHandle_t handle to the created ringbuffer, or NULL in case of error.
*/
RingbufHandle_t xRingbufferCreateNoSplit(size_t item_size, size_t num_item);
/** /**
* @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,23 +101,57 @@ 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.
*/ */
size_t xRingbufferGetMaxItemSize(RingbufHandle_t ringbuf); size_t xRingbufferGetMaxItemSize(RingbufHandle_t ringbuf);
/**
* @brief Get current free size available in the buffer
*
* This gives the real time free space available in the ring buffer. So basically,
* this will be the maximum size of the entry that can be sent into the buffer.
*
* @note This API is not thread safe. So, if multiple threads are accessing the same
* ring buffer, it is the application's responsibility to ensure atomic access to this
* API and the subsequent Send
*
* @param ringbuf - Ring buffer to query
*
* @return Current free size, in bytes, available for an entry
*/
size_t xRingbufferGetCurFreeSize(RingbufHandle_t ringbuf);
/**
* @brief Check if the next item is wrapped
*
* This API tells if the next item that is available for a Receive is wrapped
* or not. This is valid only if the ring buffer type is RINGBUF_TYPE_ALLOWSPLIT
*
* @note This API is not thread safe. So, if multiple threads are accessing the same
* ring buffer, it is the application's responsibility to ensure atomic access to this
* API and the subsequent Receive
*
* @param ringbuf - Ring buffer to query
*
* @return true if the next item is wrapped around
* @return false if the next item is not wrapped
*/
bool xRingbufferIsNextItemWrapped(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 +159,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 +172,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 +191,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 +251,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 +261,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 +299,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 +313,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 +329,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

@@ -32,6 +32,7 @@ typedef enum {
typedef enum { typedef enum {
iflag_free = 1, //Buffer is not read and given back by application, free to overwrite iflag_free = 1, //Buffer is not read and given back by application, free to overwrite
iflag_dummydata = 2, //Data from here to end of ringbuffer is dummy. Restart reading at start of ringbuffer. iflag_dummydata = 2, //Data from here to end of ringbuffer is dummy. Restart reading at start of ringbuffer.
iflag_wrap = 4, //Valid for RINGBUF_TYPE_ALLOWSPLIT, indicating that rest of the data is wrapped around
} itemflag_t; } itemflag_t;
@@ -53,6 +54,7 @@ struct ringbuf_t {
BaseType_t (*copyItemToRingbufImpl)(ringbuf_t *rb, uint8_t *buffer, size_t buffer_size); BaseType_t (*copyItemToRingbufImpl)(ringbuf_t *rb, uint8_t *buffer, size_t buffer_size);
uint8_t *(*getItemFromRingbufImpl)(ringbuf_t *rb, size_t *length, int wanted_length); uint8_t *(*getItemFromRingbufImpl)(ringbuf_t *rb, size_t *length, int wanted_length);
void (*returnItemToRingbufImpl)(ringbuf_t *rb, void *item); void (*returnItemToRingbufImpl)(ringbuf_t *rb, void *item);
size_t (*getFreeSizeImpl)(ringbuf_t *rb);
}; };
@@ -82,7 +84,6 @@ static int ringbufferFreeMem(ringbuf_t *rb)
return free_size-1; return free_size-1;
} }
//Copies a single item to the ring buffer; refuses to split items. Assumes there is space in the ringbuffer and //Copies a single item to the ring buffer; refuses to split items. Assumes there is space in the ringbuffer and
//the ringbuffer is locked. Increases write_ptr to the next item. Returns pdTRUE on //the ringbuffer is locked. Increases write_ptr to the next item. Returns pdTRUE on
//success, pdFALSE if it can't make the item fit and the calling routine needs to retry //success, pdFALSE if it can't make the item fit and the calling routine needs to retry
@@ -203,6 +204,9 @@ static BaseType_t copyItemToRingbufAllowSplit(ringbuf_t *rb, uint8_t *buffer, si
if (buffer_size == 0) { if (buffer_size == 0) {
rb->write_ptr=rb->data; rb->write_ptr=rb->data;
return pdTRUE; return pdTRUE;
} else {
/* Indicate the wrapping */
hdr->flags|=iflag_wrap;
} }
} else { } else {
//Huh, only the header fit. Mark as dummy so the receive function doesn't receive //Huh, only the header fit. Mark as dummy so the receive function doesn't receive
@@ -359,6 +363,7 @@ static void returnItemToRingbufDefault(ringbuf_t *rb, void *item) {
configASSERT((hdr->flags & iflag_dummydata)==0); configASSERT((hdr->flags & iflag_dummydata)==0);
configASSERT((hdr->flags & iflag_free)==0); configASSERT((hdr->flags & iflag_free)==0);
//Mark the buffer as free. //Mark the buffer as free.
hdr->flags&=~iflag_wrap;
hdr->flags|=iflag_free; hdr->flags|=iflag_free;
//Do a cleanup pass. //Do a cleanup pass.
@@ -415,6 +420,67 @@ void xRingbufferPrintInfo(RingbufHandle_t ringbuf)
} }
size_t xRingbufferGetCurFreeSize(RingbufHandle_t ringbuf)
{
ringbuf_t *rb=(ringbuf_t *)ringbuf;
configASSERT(rb);
configASSERT(rb->getFreeSizeImpl);
int free_size = rb->getFreeSizeImpl(rb);
//Reserve one byte. If we do not do this and the entire buffer is filled, we get a situation
//where read_ptr == free_ptr, messing up the next calculation.
return free_size - 1;
}
static size_t getCurFreeSizeByteBuf(ringbuf_t *rb)
{
//Return whatever space is available depending on relative positions of
//the free pointer and write pointer. There is no overhead of headers in
//this mode
int free_size = rb->free_ptr-rb->write_ptr;
if (free_size <= 0)
free_size += rb->size;
return free_size;
}
static size_t getCurFreeSizeAllowSplit(ringbuf_t *rb)
{
int free_size;
//If Both, the write and free pointer are at the start. Hence, the entire buffer
//is available (minus the space for the header)
if (rb->write_ptr == rb->free_ptr && rb->write_ptr == rb->data) {
free_size = rb->size - sizeof(buf_entry_hdr_t);
} else if (rb->write_ptr < rb->free_ptr) {
//Else if the free pointer is beyond the write pointer, only the space between
//them would be available (minus the space for the header)
free_size = rb->free_ptr - rb->write_ptr - sizeof(buf_entry_hdr_t);
} else {
//Else the data can wrap around and 2 headers will be required
free_size = rb->free_ptr - rb->write_ptr + rb->size - (2 * sizeof(buf_entry_hdr_t));
}
return free_size;
}
static size_t getCurFreeSizeNoSplit(ringbuf_t *rb)
{
int free_size;
//If the free pointer is beyond the write pointer, only the space between
//them would be available
if (rb->write_ptr < rb->free_ptr) {
free_size = rb->free_ptr - rb->write_ptr;
} else {
//Else check which one is bigger amongst the below 2
//1) Space from the write pointer to the end of buffer
int size1 = rb->data + rb->size - rb->write_ptr;
//2) Space from the start of buffer to the free pointer
int size2 = rb->free_ptr - rb->data;
//And then select the larger of the two
free_size = size1 > size2 ? size1 : size2;
}
//In any case, a single header will be used, so subtracting the space that
//would be required for it
return free_size - sizeof(buf_entry_hdr_t);
}
RingbufHandle_t xRingbufferCreate(size_t buf_length, ringbuf_type_t type) RingbufHandle_t xRingbufferCreate(size_t buf_length, ringbuf_type_t type)
{ {
@@ -437,6 +503,7 @@ RingbufHandle_t xRingbufferCreate(size_t buf_length, ringbuf_type_t type)
rb->returnItemToRingbufImpl=returnItemToRingbufDefault; rb->returnItemToRingbufImpl=returnItemToRingbufDefault;
//Calculate max item size. Worst case, we need to split an item into two, which means two headers of overhead. //Calculate max item size. Worst case, we need to split an item into two, which means two headers of overhead.
rb->maxItemSize=rb->size-(sizeof(buf_entry_hdr_t)*2)-4; rb->maxItemSize=rb->size-(sizeof(buf_entry_hdr_t)*2)-4;
rb->getFreeSizeImpl=getCurFreeSizeAllowSplit;
} else if (type==RINGBUF_TYPE_BYTEBUF) { } else if (type==RINGBUF_TYPE_BYTEBUF) {
rb->flags|=flag_bytebuf; rb->flags|=flag_bytebuf;
rb->copyItemToRingbufImpl=copyItemToRingbufByteBuf; rb->copyItemToRingbufImpl=copyItemToRingbufByteBuf;
@@ -444,6 +511,7 @@ RingbufHandle_t xRingbufferCreate(size_t buf_length, ringbuf_type_t type)
rb->returnItemToRingbufImpl=returnItemToRingbufBytebuf; rb->returnItemToRingbufImpl=returnItemToRingbufBytebuf;
//Calculate max item size. We have no headers and can split anywhere -> size is total size minus one. //Calculate max item size. We have no headers and can split anywhere -> size is total size minus one.
rb->maxItemSize=rb->size-1; rb->maxItemSize=rb->size-1;
rb->getFreeSizeImpl=getCurFreeSizeByteBuf;
} else if (type==RINGBUF_TYPE_NOSPLIT) { } else if (type==RINGBUF_TYPE_NOSPLIT) {
rb->copyItemToRingbufImpl=copyItemToRingbufNoSplit; rb->copyItemToRingbufImpl=copyItemToRingbufNoSplit;
rb->getItemFromRingbufImpl=getItemFromRingbufDefault; rb->getItemFromRingbufImpl=getItemFromRingbufDefault;
@@ -453,6 +521,7 @@ RingbufHandle_t xRingbufferCreate(size_t buf_length, ringbuf_type_t type)
//(item_data-4) bytes of buffer, then we only have (size-(item_data-4) bytes left to fill //(item_data-4) bytes of buffer, then we only have (size-(item_data-4) bytes left to fill
//with the real item. (item size being header+data) //with the real item. (item size being header+data)
rb->maxItemSize=(rb->size/2)-sizeof(buf_entry_hdr_t)-4; rb->maxItemSize=(rb->size/2)-sizeof(buf_entry_hdr_t)-4;
rb->getFreeSizeImpl=getCurFreeSizeNoSplit;
} else { } else {
configASSERT(0); configASSERT(0);
} }
@@ -472,6 +541,12 @@ err:
return NULL; return NULL;
} }
RingbufHandle_t xRingbufferCreateNoSplit(size_t item_size, size_t num_item)
{
size_t aligned_size = (item_size+3)&~3;
return xRingbufferCreate((aligned_size + sizeof(buf_entry_hdr_t)) * num_item, RINGBUF_TYPE_NOSPLIT);
}
void vRingbufferDelete(RingbufHandle_t ringbuf) { void vRingbufferDelete(RingbufHandle_t ringbuf) {
ringbuf_t *rb=(ringbuf_t *)ringbuf; ringbuf_t *rb=(ringbuf_t *)ringbuf;
if (rb) { if (rb) {
@@ -489,6 +564,15 @@ size_t xRingbufferGetMaxItemSize(RingbufHandle_t ringbuf)
return rb->maxItemSize; return rb->maxItemSize;
} }
bool xRingbufferIsNextItemWrapped(RingbufHandle_t ringbuf)
{
ringbuf_t *rb=(ringbuf_t *)ringbuf;
configASSERT(rb);
buf_entry_hdr_t *hdr=(buf_entry_hdr_t *)rb->read_ptr;
return hdr->flags & iflag_wrap;
}
BaseType_t xRingbufferSend(RingbufHandle_t ringbuf, void *data, size_t dataSize, TickType_t ticks_to_wait) BaseType_t xRingbufferSend(RingbufHandle_t ringbuf, void *data, size_t dataSize, TickType_t ticks_to_wait)
{ {
ringbuf_t *rb=(ringbuf_t *)ringbuf; ringbuf_t *rb=(ringbuf_t *)ringbuf;

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,53 @@ 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) ){ //Walk the list
--uxCurrentNumberOfTasks; TCB_t *tgt_tcb = ( TCB_t * )listGET_LIST_ITEM_OWNER(target);
--uxTasksDeleted; int affinity = tgt_tcb->xCoreID;
} else { //Self deleting tasks are added to Termination List before they switch context. Ensure they aren't still currently running
/* Need to wait until the idle task on the other processor kills that task first. */ if( pxCurrentTCB[core] == tgt_tcb || (portNUM_PROCESSORS > 1 && pxCurrentTCB[!core] == tgt_tcb) ){
taskEXIT_CRITICAL(&xTaskQueueMutex); continue; //Can't free memory of task that is still running
}
if(affinity == core || affinity == tskNO_AFFINITY){ //Find first item not pinned to other core
pxTCB = tgt_tcb;
break; break;
} }
} }
}
taskEXIT_CRITICAL(&xTaskQueueMutex);
if(pxTCB != NULL){ if(pxTCB != NULL){
( void ) uxListRemove( target ); //Remove list item from list
--uxCurrentNumberOfTasks;
--uxTasksDeleted;
}
}
}
taskEXIT_CRITICAL(&xTaskQueueMutex); //Need to call deletion callbacks outside critical section
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 +3876,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 +3930,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,83 @@
/*
* 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 "rom/ets_sys.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)
#define DELAY_US_ITERATIONS 1000
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); }
static void tsk_extern_del(void *param)
{
vTaskDelay(portMAX_DELAY); //Await external deletion
}
static void tsk_self_del_us_delay(void *param)
{
uint32_t delay = (uint32_t)param;
ets_delay_us(delay);
vTaskDelete(NULL); vTaskDelete(NULL);
} }
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); }
TEST_ASSERT_EQUAL(before_count, uxTaskGetNumberOfTasks()); }
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_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));
/* Test self deleting no affinity task is not removed by idle task of other core before context switch */
for(int i = 0; i < DELAY_US_ITERATIONS; i+= 10){
vTaskDelay(1); //Sync to next tick interrupt
xTaskCreatePinnedToCore(tsk_self_del_us_delay, "delay", 1024, (void *)i, UNITY_FREERTOS_PRIORITY - 1, NULL, tskNO_AFFINITY);
ets_delay_us(10); //Busy wait to ensure no affinity task runs on opposite core
}
} }

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

@@ -24,11 +24,11 @@ extern "C" {
/* Please use heap_caps_malloc() instead of this function */ /* Please use heap_caps_malloc() instead of this function */
void *pvPortMallocCaps(size_t xWantedSize, uint32_t caps) asm("heap_caps_malloc") __attribute__((deprecated)); void *pvPortMallocCaps(size_t xWantedSize, uint32_t caps) asm("heap_caps_malloc") __attribute__((deprecated));
/* Please use heap_caps_get_minimum_free_heap_size() instead of this function */ /* Please use heap_caps_get_minimum_free_size() instead of this function */
size_t xPortGetMinimumEverFreeHeapSizeCaps( uint32_t caps ) asm("heap_caps_get_minimum_free_heap_size") __attribute__((deprecated)); size_t xPortGetMinimumEverFreeHeapSizeCaps( uint32_t caps ) asm("heap_caps_get_minimum_free_size") __attribute__((deprecated));
/* Please use heap_caps_get_free_size() instead of this function */ /* Please use heap_caps_get_free_size() instead of this function */
size_t xPortGetFreeHeapSizeCaps( uint32_t caps ) asm("heap_caps_get_free_heap_size") __attribute__((deprecated)); size_t xPortGetFreeHeapSizeCaps( uint32_t caps ) asm("heap_caps_get_free_size") __attribute__((deprecated));
#ifdef __cplusplus #ifdef __cplusplus
} }

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,12 +1,40 @@
/*
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:
@@ -14,17 +42,33 @@ int cJSONUtils_ApplyPatches(cJSON *object,cJSON *patches); /* Returns 0 for succ
//{ //{
// 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

@@ -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

@@ -149,13 +149,24 @@ void rtc_clk_init(rtc_clk_config_t cfg);
/** /**
* @brief Get main XTAL frequency * @brief Get main XTAL frequency
* *
* This is the value passed to rtc_clk_init function, or if the value was * This is the value stored in RTC register RTC_XTAL_FREQ_REG by the bootloader. As passed to
* RTC_XTAL_FREQ_AUTO, the detected XTAL frequency. * rtc_clk_init function, or if the value was RTC_XTAL_FREQ_AUTO, the detected
* XTAL frequency.
* *
* @return XTAL frequency, one of rtc_xtal_freq_t * @return XTAL frequency, one of rtc_xtal_freq_t
*/ */
rtc_xtal_freq_t rtc_clk_xtal_freq_get(); rtc_xtal_freq_t rtc_clk_xtal_freq_get();
/**
* @brief Update XTAL frequency
*
* Updates the XTAL value stored in RTC_XTAL_FREQ_REG. Usually this value is ignored
* after startup.
*
* @param xtal_freq New frequency value
*/
void rtc_clk_xtal_freq_update(rtc_xtal_freq_t xtal_freq);
/** /**
* @brief Enable or disable 32 kHz XTAL oscillator * @brief Enable or disable 32 kHz XTAL oscillator
* @param en true to enable, false to disable * @param en true to enable, false to disable

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
## ##

BIN
docs/_static/rmt-carrier.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

BIN
docs/_static/rmt-waveform-modulated.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

BIN
docs/_static/rmt-waveform.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 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.
@@ -321,6 +321,7 @@ Second Level: Component Makefiles
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- Each call to a component makefile goes via the ``$(IDF_PATH)/make/component_wrapper.mk`` wrapper makefile. - Each call to a component makefile goes via the ``$(IDF_PATH)/make/component_wrapper.mk`` wrapper makefile.
- This component wrapper includes all component ``Makefile.componentbuild`` files, making any recipes, variables etc in these files available to every component.
- The ``component_wrapper.mk`` is called with the current directory set to the component build directory, and the ``COMPONENT_MAKEFILE`` variable is set to the absolute path to ``component.mk``. - The ``component_wrapper.mk`` is called with the current directory set to the component build directory, and the ``COMPONENT_MAKEFILE`` variable is set to the absolute path to ``component.mk``.
- ``component_wrapper.mk`` sets default values for all `component variables`, then includes the `component.mk` file which can override or modify these. - ``component_wrapper.mk`` sets default values for all `component variables`, then includes the `component.mk` file which can override or modify these.
- If ``COMPONENT_OWNBUILDTARGET`` and ``COMPONENT_OWNCLEANTARGET`` are not defined, default build and clean targets are created for the component's source files and the prerequisite ``COMPONENT_LIBRARY`` static library file. - If ``COMPONENT_OWNBUILDTARGET`` and ``COMPONENT_OWNCLEANTARGET`` are not defined, default build and clean targets are created for the component's source files and the prerequisite ``COMPONENT_LIBRARY`` static library file.
@@ -390,6 +391,15 @@ configuration options at the top-level of menuconfig, rather than inside the "Co
Take care when adding configuration values in this file, as they will be included across the entire project configuration. Where possible, it's generally better to create a KConfig file for `component configuration`. Take care when adding configuration values in this file, as they will be included across the entire project configuration. Where possible, it's generally better to create a KConfig file for `component configuration`.
Makefile.componentbuild
^^^^^^^^^^^^^^^^^^^^^^^
For components that e.g. include tools to generate source files from other files, it is necessary to be able to add recipes, macros or variable definitions
into the component build process of every components. This is done by having a ``Makefile.componentbuild`` in a component directory. This file gets included
in ``component_wrapper.mk``, before the ``component.mk`` of the component is included. As with the Makefile.projbuild, take care with these files: as they're
included in each component build, a ``Makefile.componentbuild`` error may only show up when compiling an entirely different component.
Configuration-Only Components Configuration-Only Components
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

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

@@ -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

@@ -1,15 +1,264 @@
RMT RMT
======== ===
Overview The RMT (Remote Control) module driver can be used to send and receive infrared remote control signals. Due to flexibility of RMT module, the driver can also be used to generate or receive many other types of signals.
--------
The RMT (Remote Control) module driver can be used to send and receive infrared remote control signals. Due to flexibility of RMT module, the driver can also be used to generate many other types of signals. The signal, which consists of a series of pulses, is generated by RMT's transmitter based on a list of values. The values define the pulse duration and a binary level, see below. The transmitter can also provide a carrier and modulate it with provided pulses.
Application Example .. blockdiag::
------------------- :scale: 100
:caption: RMT Transmitter Overview
:align: center
blockdiag rmt_tx {
node_width = 80;
node_height = 60;
default_group_color = lightgrey;
a -> b -> c -> d;
e -> f -> g -- h;
d -> o [label=GPIO];
h -> d [folded];
a [style=none, width=100, label="{11,high,7,low},\n{5,high,5,low},\n..."]
b [label="Waveform\nGenerator"]
c [style=none, label="", background="_static/rmt-waveform.png"]
d [shape=beginpoint, label="mod"]
e [style=none, width=60, height=40, label="Carrier\nenable"]
f [label="Carrier\nGenerator"]
g [style=none, label="", background="_static/rmt-carrier.png"]
h [shape=none]
o [style=none, label="", background="_static/rmt-waveform-modulated.png"]
group {
label = Input
a,e;
}
group {
label = "RMT Transmitter"
b,f,c,g,d,h;
}
group {
label = Output
o;
}
}
The reverse operation is performed by the receiver, where a series of pulses is decoded into a list of values containing the pulse duration and binary level. A filter may be applied to remove high frequency noise from the input signal.
.. blockdiag::
:scale: 90
:caption: RMT Receiver Overview
:align: center
blockdiag rmt_rx {
node_width = 80;
node_height = 60;
default_group_color = lightgrey;
a -> b [label=GPIO];
b -> c -> d;
e -- f;
f -> b [folded];
a [style=none, label="", background="_static/rmt-waveform.png"]
b [label=Filter]
c [label="Edge\nDetect"]
d [style=none, width=100, label="{11,high,7,low},\n{5,high,5,low},\n..."]
e [style=none, width=60, height=40, label="Filter\nenable"]
f [shape=none, label=""]
group {
label = Input
a,e;
}
group {
label = "RMT Receiver"
b,c;
}
group {
label = Output
d;
}
}
There couple of typical steps to setup and operate the RMT and they are discussed in the following sections:
1. `Configure Driver`_
2. `Transmit Data`_ or `Receive Data`_
3. `Change Operation Parameters`_
4. `Use Interrupts`_
The RMT has eight channels numbered from zero to seven. Each channel is able to independently transmit or receive data. They are referred to using indexes defined in structure :cpp:type:`rmt_channel_t`.
Configure Driver
----------------
There are several parameters that define how particular channel operates. Most of these parameters are configured by setting specific members of :cpp:type:`rmt_config_t` structure. Some of the parameters are common to both transmit or receive mode, and some are mode specific. They are all discussed below.
Common Parameters
^^^^^^^^^^^^^^^^^
* The **channel** to be configured, select one from the :cpp:type:`rmt_channel_t` enumerator.
* The RMT **operation mode** - whether this channel is used to transmit or receive data, selected by setting a **rmt_mode** members to one of the values from :cpp:type:`rmt_mode_t`.
* What is the **pin number** to transmit or receive RMT signals, selected by setting **gpio_num**.
* How many **memory blocks** will be used by the channel, set with **mem_block_num**.
* A **clock divider**, that will determine the range of pulse length generated by the RMT transmitter or discriminated by the receiver. Selected by setting **clk_div** to a value within [1 .. 255] range. The RMT source clock is typically APB CLK, 80Mhz by default.
.. note::
The period of a square wave after the clock divider is called a 'tick'. The length of the pulses generated by the RMT transmitter or discriminated by the receiver is configured in number of 'ticks'.
There are also couple of specific parameters that should be set up depending if selected channel is configured in `Transmit Mode`_ or `Receive Mode`_:
Transmit Mode
^^^^^^^^^^^^^
When configuring channel in transmit mode, set **tx_config** and the following members of :cpp:type:`rmt_tx_config_t`:
* Transmit the currently configured data items in a loop - **loop_en**
* Enable the RMT carrier signal - **carrier_en**
* Frequency of the carrier in Hz - **carrier_freq_hz**
* Duty cycle of the carrier signal in percent (%) - **carrier_duty_percent**
* Level of the RMT output, when the carrier is applied - **carrier_level**
* Enable the RMT output if idle - **idle_output_en**
* Set the signal level on the RMT output if idle - **idle_level**
Receive Mode
^^^^^^^^^^^^
In receive mode, set **rx_config** and the following members of :cpp:type:`rmt_rx_config_t`:
* Enable a filter on the input of the RMT receiver - **filter_en**
* A threshold of the filter, set in the number of ticks - **filter_ticks_thresh**. Pulses shorter than this setting will be filtered out. Note, that the range of entered tick values is [0..255].
* A pulse length threshold that will turn the RMT receiver idle, set in number of ticks - **idle_threshold**. The receiver will ignore pulses longer than this setting.
Finalize Configuration
^^^^^^^^^^^^^^^^^^^^^^
Once the :cpp:type:`rmt_config_t` structure is populated with parameters, it should be then invoked with :cpp:func:`rmt_config` to make the configuration effective.
The last configuration step is installation of the driver in memory by calling :cpp:func:`rmt_driver_install`. If :cpp:type:`rx_buf_size` parameter of this function is > 0, then a ring buffer for incoming data will be allocated. A default ISR handler will be installed, see a note in `Use Interrupts`_.
Now, depending on how the channel is configured, we are ready to either `Transmit Data`_ or `Receive Data`_. This is described in next two sections.
Transmit Data
-------------
Before being able to transmit some RMT pulses, we need to define the pulse pattern. The minimum pattern recognized by the RMT controller, later called an 'item', is provided in a structure :cpp:type:`rmt_item32_t`, see :component_file:`soc/esp32/include/soc/rmt_struct.h`. Each item consists of two pairs of two values. The first value in a pair describes the signal duration in ticks and is 15 bits long, the second provides the signal level (high or low) and is contained in a single bit. A block of couple of items and the structure of an item is presented below.
.. packetdiag::
:caption: Structure of RMT items (L - signal level)
:align: center
packetdiag rmt_items {
colwidth = 32
node_width = 10
node_height = 24
default_fontsize = 12
0-14: Period (15)
15: L
16-30: Period (15)
31: L
32-95: ... [colheight=2]
96-110: Period (15)
111: L
112-126: Period (15)
127: L
}
For a simple example how to define a block of items see :example:`peripherals/rmt_tx`.
The items are provided to the RMT controller by calling function :cpp:func:`rmt_write_items`. This function also automatically triggers start of transmission. It may be called to wait for transmission completion or exit just after transmission start. In such case you can wait for the transmission end by calling :cpp:func:`rmt_wait_tx_done`. This function does not limit the number of data items to transmit. It is using an interrupt to successively copy the new data chunks to RMT's internal memory as previously provided data are sent out.
Another way to provide data for transmission is by calling :cpp:func:`rmt_fill_tx_items`. In this case transmission is not started automatically. To control the transmission process use :cpp:func:`rmt_tx_start` and :cpp:func:`rmt_tx_stop`. The number of items to sent is restricted by the size of memory blocks allocated in the RMT controller's internal memory, see :cpp:func:`rmt_set_mem_block_num`.
Receive Data
------------
Before starting the receiver we need some storage for incoming items. The RMT controller has 512 x 32-bits of internal RAM shared between all eight channels. In typical scenarios it is not enough as an ultimate storage for all incoming (and outgoing) items. Therefore this API supports retrieval of incoming items on the fly to save them in a ring buffer of a size defined by the user. The size is provided when calling :cpp:func:`rmt_driver_install` discussed above. To get a handle to this buffer call :cpp:func:`rmt_get_ringbuf_handle`.
With the above steps complete we can start the receiver by calling :cpp:func:`rmt_rx_start` and then move to checking what's inside the buffer. To do so, you can use common FreeRTOS functions that interact with the ring buffer. Please see an example how to do it in :example:`peripherals/rmt_nec_tx_rx`.
To stop the receiver, call :cpp:func:`rmt_rx_stop`.
Change Operation Parameters
---------------------------
Previously described function :cpp:func:`rmt_config` provides a convenient way to set several configuration parameters in one shot. This is usually done on application start. Then, when the application is running, the API provides an alternate way to update individual parameters by calling dedicated functions. Each function refers to the specific RMT channel provided as the first input parameter. Most of the functions have `_get_` counterpart to read back the currently configured value.
Parameters Common to Transmit and Receive Mode
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
* Selection of a GPIO pin number on the input or output of the RMT - :cpp:func:`rmt_set_pin`
* Number of memory blocks allocated for the incoming or outgoing data - :cpp:func:`rmt_set_mem_pd`
* Setting of the clock divider - :cpp:func:`rmt_set_clk_div`
* Selection of the clock source, note that currently one clock source is supported, the APB clock which is 80Mhz - :cpp:func:`rmt_set_source_clk`
Transmit Mode Parameters
^^^^^^^^^^^^^^^^^^^^^^^^
* Enable or disable the loop back mode for the transmitter - :cpp:func:`rmt_set_tx_loop_mode`
* Binary level on the output to apply the carrier - :cpp:func:`rmt_set_tx_carrier`, selected from :cpp:type:`rmt_carrier_level_t`
* Determines the binary level on the output when transmitter is idle - :cpp:func:`rmt_set_idle_level()`, selected from :cpp:type:`rmt_idle_level_t`
Receive Mode Parameters
^^^^^^^^^^^^^^^^^^^^^^^
* The filter setting - :cpp:func:`rmt_set_rx_filter`
* The receiver threshold setting - :cpp:func:`rmt_set_rx_idle_thresh`
* Whether the transmitter or receiver is entitled to access RMT's memory - :cpp:func:`rmt_set_memory_owner`, selection is from :cpp:type:`rmt_mem_owner_t`.
Use Interrupts
--------------
Registering of an interrupt handler for the RMT controller is done be calling :cpp:func:`rmt_isr_register`.
.. note::
When calling :cpp:func:`rmt_driver_install` to use the system RMT driver, a default ISR is being installed. In such a case you cannot register a generic ISR handler with :cpp:func:`rmt_isr_register`.
The RMT controller triggers interrupts on four specific events describes below. To enable interrupts on these events, the following functions are provided:
* The RMT receiver has finished receiving a signal - :cpp:func:`rmt_set_rx_intr_en`
* The RMT transmitter has finished transmitting the signal - :cpp:func:`rmt_set_tx_intr_en`
* The number of events the transmitter has sent matches a threshold value :cpp:func:`rmt_set_tx_thr_intr_en`
* Ownership to the RMT memory block has been violated - :cpp:func:`rmt_set_err_intr_en`
Setting or clearing an interrupt enable mask for specific channels and events may be also done by calling :cpp:func:`rmt_set_intr_enable_mask` or :cpp:func:`rmt_clr_intr_enable_mask`.
When servicing an interrupt within an ISR, the interrupt need to explicitly cleared. To do so, set specific bits described as ``RMT.int_clr.val.chN_event_name`` and defined as a ``volatile struct`` in :component_file:`soc/esp32/include/soc/rmt_struct.h`, where N is the RMT channel number [0, 7] and the ``event_name`` is one of four events described above.
If you do not need an ISR anymore, you can deregister it by calling a function :cpp:func:`rmt_isr_deregister`.
Uninstall Driver
----------------
If the RMT driver has been installed with :cpp:func:`rmt_driver_install` for some specific period of time and then not required, the driver may be removed to free allocated resources by calling :cpp:func:`rmt_driver_uninstall`.
Application Examples
--------------------
* A simple RMT TX example: :example:`peripherals/rmt_tx`.
* NEC remote control TX and RX example: :example:`peripherals/rmt_nec_tx_rx`.
NEC remote control TX and RX example: :example:`peripherals/rmt_nec_tx_rx`.
API Reference API Reference
------------- -------------

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

@@ -155,6 +155,7 @@ The following roles are provided:
A check is added to the CI build script, which searches RST files for presence of hard-coded links (identified by tree/master, blob/master, or raw/master part of the URL). This check can be run manually: ``cd docs`` and then ``make gh-linkcheck``. A check is added to the CI build script, which searches RST files for presence of hard-coded links (identified by tree/master, blob/master, or raw/master part of the URL). This check can be run manually: ``cd docs`` and then ``make gh-linkcheck``.
.. _add-illustrations:
Add Illustrations Add Illustrations
----------------- -----------------
@@ -172,7 +173,7 @@ The following types of diagrams are supported:
With this suite of tools it is possible to generate beautiful diagram images from simple text format (similar to graphvizs DOT format). The diagram elements are laid out automatically. The diagram code is then converted into ".png" graphics and integrated "behind the scenes" into **Sphinx** documents. With this suite of tools it is possible to generate beautiful diagram images from simple text format (similar to graphvizs DOT format). The diagram elements are laid out automatically. The diagram code is then converted into ".png" graphics and integrated "behind the scenes" into **Sphinx** documents.
For the diagram preparation you can use an on-line `interactive shell <http://interactive.blockdiag.com/?compression=deflate&src=eJxlUMFOwzAMvecrrO3aITYQQirlAIIzEseJQ5q4TUSIq8TVGIh_J2m7jbKc7Ge_5_dSO1Lv2soWvoVYgieNoMh7VGzJR9FJtugZ7lYQ0UcKEbYNOY36rRQHZHUPT68vV5tceGLbWCUzPfeaFFMoBZzecVc56vWwJFnWMmJ59CCZg617xpOFbTSyw0pmvT_HJ7hxtFNGBr6wvuu5SCkchcrZ1vAeXZomznh5YgTqfcpR02cBO6vZVDeXBRjMjKEcFRbLh8f18-Z2UUBDnqP9wmp9ncRmSSfND2ldGo2h_zse407g0Mxc1q7HzJ3-4jzYYTJjtQH3iSV-fgFzx50J>`_ that instantly shows the rendered image. For the diagram preparation you can use an on-line `interactive shell`_ that instantly shows the rendered image.
Below are couple of diagram examples: Below are couple of diagram examples:
@@ -185,7 +186,7 @@ Try them out by modifying the source code and see the diagram instantly renderin
.. note:: .. note::
There may be slight differences in rendering of font used by the interactive shell compared to the font used in the esp-idf documentation. There may be slight differences in rendering of font used by the `interactive shell`_ compared to the font used in the esp-idf documentation.
Put it all together Put it all together
@@ -207,7 +208,7 @@ OK, but I am new to Sphinx!
* Set up an account on `Read the Docs <https://readthedocs.org/>`_ and build documentation in the cloud. Read the Docs provides document building and hosting for free and their service works really quick and great. * Set up an account on `Read the Docs <https://readthedocs.org/>`_ and build documentation in the cloud. Read the Docs provides document building and hosting for free and their service works really quick and great.
4. To preview documentation before building use `Sublime Text <https://www.sublimetext.com/>`_ editor together with `OmniMarkupPreviewer <https://github.com/timonwong/OmniMarkupPreviewer>`_ plugin. 4. To preview documentation before building, use `Sublime Text <https://www.sublimetext.com/>`_ editor together with `OmniMarkupPreviewer <https://github.com/timonwong/OmniMarkupPreviewer>`_ plugin.
Setup for building documentation locally Setup for building documentation locally
@@ -245,6 +246,28 @@ Installation of Doxygen is OS dependent:
brew install doxygen brew install doxygen
.. note::
If you are installing on Windows system (Linux and MacOS users should skip this note), **before** going further, execute two extra steps below. These steps are required for the :ref:`blockdiag <add-illustrations>` to install:
1. Update all the system packages:
::
$ pacman -Syu
This process will likely require restarting of the MSYS2 MINGW32 console and repeating above commands, until update is complete.
2. Install *pillow*, that is one of dependences of the *blockdiag*:
::
$ pacman -S mingw32/mingw-w64-i686-python2-pillow
Check the log on the screen that ``mingw-w64-i686-python2-pillow-4.3.0-1`` is installed. Previous versions of *pillow* will not work.
A downside of Windows installation is that fonts of the `blockdiag pictures <add-illustrations>` do not render correctly, you will see some random characters instead. Until this issue is fixed, you can use the `interactive shell`_ to see how the complete picture looks like.
All remaining applications are `Python <https://www.python.org/>`_ packages and you can install them in one step as follows: All remaining applications are `Python <https://www.python.org/>`_ packages and you can install them in one step as follows:
:: ::
@@ -279,3 +302,6 @@ Related Documents
.. _espressif/esp-idf: https://github.com/espressif/esp-idf/ .. _espressif/esp-idf: https://github.com/espressif/esp-idf/
.. _interactive shell: http://interactive.blockdiag.com/?compression=deflate&src=eJxlUMFOwzAMvecrrO3aITYQQirlAIIzEseJQ5q4TUSIq8TVGIh_J2m7jbKc7Ge_5_dSO1Lv2soWvoVYgieNoMh7VGzJR9FJtugZ7lYQ0UcKEbYNOY36rRQHZHUPT68vV5tceGLbWCUzPfeaFFMoBZzecVc56vWwJFnWMmJ59CCZg617xpOFbTSyw0pmvT_HJ7hxtFNGBr6wvuu5SCkchcrZ1vAeXZomznh5YgTqfcpR02cBO6vZVDeXBRjMjKEcFRbLh8f18-Z2UUBDnqP9wmp9ncRmSSfND2ldGo2h_zse407g0Mxc1q7HzJ3-4jzYYTJjtQH3iSV-fgFzx50J

View File

@@ -169,6 +169,15 @@ To re-format a file, run::
tools/format.sh components/my_component/file.c tools/format.sh components/my_component/file.c
Configuring the code style for a project using EditorConfig
-----------------------------------------------------------
EditorConfig helps developers define and maintain consistent coding styles between different editors and IDEs. The EditorConfig project consists of a file format for defining coding styles and a collection of text editor plugins that enable editors to read the file format and adhere to defined styles. EditorConfig files are easily readable and they work nicely with version control systems.
For more information, see `EditorConfig <http://editorconfig.org>`_ Website.
Documenting code Documenting code
---------------- ----------------

View File

@@ -89,6 +89,22 @@ Quick Reset
The keyboard shortcut ``Ctrl-T Ctrl-R`` will reset the target board via the RTS line (if it is connected.) The keyboard shortcut ``Ctrl-T Ctrl-R`` will reset the target board via the RTS line (if it is connected.)
Pause the Application
=====================
The keyboard shortcut ``Ctrl-T Ctrl-P`` will reset the target into bootloader, so that the board will run nothing. This is
useful when you want to wait for another device to startup. Then shortcut ``Ctrl-T Ctrl-R`` can be used to restart the
application.
Toggle Output Display
=====================
Sometimes you may want to stop new output printed to screen, to see the log before. The keyboard shortcut ``Ctrl-T Ctrl-Y`` will
toggle the display (discard all serial data when the display is off) so that you can stop to see the log, and revert
again quickly without quitting the monitor.
Simple Monitor Simple Monitor
============== ==============

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<EFBFBD><EFBFBD><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

@@ -12,3 +12,4 @@ ESP32 Hardware Reference
Modules and Boards <modules-and-boards> Modules and Boards <modules-and-boards>
Previous Versions of Modules and Boards <modules-and-boards-previous> Previous Versions of Modules and Boards <modules-and-boards-previous>
Espressif Products Ordering Information (PDF) <http://www.espressif.com/sites/default/files/documentation/espressif_products_ordering_information_en.pdf> Espressif Products Ordering Information (PDF) <http://www.espressif.com/sites/default/files/documentation/espressif_products_ordering_information_en.pdf>
Regulatory Certificates <http://espressif.com/en/certificates?field_product_value%5B%5D=ESP32&field_product_value%5B%5D=ESP-WROOM-32&field_product_value%5B%5D=ESP32-WROVER>

View File

@@ -1,7 +1,8 @@
.. _esp-modules-and-boards: .. _esp-modules-and-boards:
************************
ESP32 Modules and Boards ESP32 Modules and Boards
======================== ************************
Espressif designed and manufactured several development modules and boards to help users evaluate functionality of the ESP32 family of chips. Development boards, depending on intended functionality, have exposed GPIO pins headers, provide USB programming interface, JTAG interface as well as peripherals like touch pads, LCD screen, SD card slot, camera module header, etc. Espressif designed and manufactured several development modules and boards to help users evaluate functionality of the ESP32 family of chips. Development boards, depending on intended functionality, have exposed GPIO pins headers, provide USB programming interface, JTAG interface as well as peripherals like touch pads, LCD screen, SD card slot, camera module header, etc.
@@ -12,12 +13,34 @@ For details please refer to documentation below, provided together with descript
This section describes the latest versions of boards. Previous versions of boards, including these not produced anymore, are described in section :ref:`esp-modules-and-boards-previous`. This section describes the latest versions of boards. Previous versions of boards, including these not produced anymore, are described in section :ref:`esp-modules-and-boards-previous`.
WROOM and WROVER Modules
========================
A family of small modules that contain ESP32 chip on board together with some key components including a crystal oscillator and an antenna matching circuit. This makes it easier to provide an ESP32 based solution ready to integrate into final products. Such modules can be also used for evaluation after adding a few extra components like a programming interface, bootstrapping resistors and break out headers. The key characteristics of these modules are summarized in the following table. Some additional details are covered in the following chapters.
=============== ============ ===== ==== ==== ==== ==== ====
-- Key Components Dimensions [mm]
--------------- ------------------------------- ----------------
Module Chip Flash RAM Ant. L W D
=============== ============ ===== ==== ==== ==== ==== ====
ESP-WROOM-32 ESP32-D0WDQ6 4MB -- MIFA 25.5 18 3.1
ESP-WROOM-32D ESP32-D0WD 4MB -- MIFA 25.5 18 3.1
ESP32-WROOM-32U ESP32-D0WD 4MB -- U.FL 19.2 18 3.2
ESP32-WROVER ESP32-D0WDQ6 4MB 4MB MIFA 31.4 18 3.2
ESP32-WROVER-I ESP32-D0WDQ6 4MB 4MB U.FL 31.4 18 3.5
=============== ============ ===== ==== ==== ==== ==== ====
* MIFA - Meandered Inverted-F Antenna
* U.FL - U.FL / IPEX antenna connector
* `ESP32 Chip Datasheet <https://espressif.com/sites/default/files/documentation/esp32_datasheet_en.pdf>`__ (PDF)
.. _esp-modules-and-boards-esp-wroom-32: .. _esp-modules-and-boards-esp-wroom-32:
ESP-WROOM-32 ESP-WROOM-32
------------ ------------
The smallest module intended for installation in final products. Can be also used for evaluation after adding extra components like programming interface, boot strapping resistors and break out headers. A basic and commonly adopted ESP32 module with ESP32-D0WDQ6 chip on board. The first one of the WROOM / WROVER family released to the market.
.. figure:: https://dl.espressif.com/dl/schematics/pictures/esp-wroom-32.jpg .. figure:: https://dl.espressif.com/dl/schematics/pictures/esp-wroom-32.jpg
:align: center :align: center
@@ -26,17 +49,48 @@ The smallest module intended for installation in final products. Can be also use
ESP-WROOM-32 module (front and back) ESP-WROOM-32 module (front and back)
Documentation
^^^^^^^^^^^^^
* `ESP-WROOM-32 Schematic <https://dl.espressif.com/dl/schematics/ESP-WROOM-32-v3.2_sch.pdf>`__ (PDF) * `ESP-WROOM-32 Schematic <https://dl.espressif.com/dl/schematics/ESP-WROOM-32-v3.2_sch.pdf>`__ (PDF)
* `ESP-WROOM-32 Datasheet <https://espressif.com/sites/default/files/documentation/esp-wroom-32_datasheet_en.pdf>`__ (PDF) * `ESP-WROOM-32 Datasheet <https://espressif.com/sites/default/files/documentation/esp-wroom-32_datasheet_en.pdf>`__ (PDF)
* `ESP32 Module Reference Design <https://espressif.com/sites/default/files/documentation/esp32_module_reference_design.zip>`_ (ZIP) containing OrCAD schematic, PCB layout, gerbers and BOM * `ESP32 Module Reference Design <https://espressif.com/sites/default/files/documentation/esp32_module_reference_design.zip>`_ (ZIP) containing OrCAD schematic, PCB layout, gerbers and BOM
.. _esp-modules-and-boards-esp-wroom-32d-and-u:
ESP-WROOM-32D / ESP32-WROOM-32U
-------------------------------
Both modules have ESP32-D0WD chip on board of a smaller footprint than ESP32-D0WDQ6 installed in :ref:`ESP-WROOM-32 <esp-modules-and-boards-esp-wroom-32>`. Version "D" has a MIFA antenna. Version "U" has just an U.FL / IPEX antenna connector. That makes it 6.3 mm shorter comparing to "D", and also the smallest representative of the whole WROOM / WROVER family of modules.
.. figure:: https://dl.espressif.com/dl/schematics/pictures/esp-wroom-32d-front-back.jpg
:align: center
:alt: ESP-WROOM-32D module (back and front)
:width: 40%
ESP-WROOM-32D module (back and front)
.. figure:: https://dl.espressif.com/dl/schematics/pictures/esp32-wroom-32u-front-back.jpg
:align: center
:alt: ESP32-WROOM-32U module (back and front)
:width: 40%
ESP32-WROOM-32U module (back and front)
Documentation
^^^^^^^^^^^^^
* `ESP-WROOM-32D / ESP32-WROOM-32U Datasheet <http://espressif.com/sites/default/files/documentation/esp-wroom-32d_esp32-wroom-32u_datasheet_en.pdf>`__ (PDF)
.. _esp-modules-and-boards-esp32-wrover: .. _esp-modules-and-boards-esp32-wrover:
ESP32-WROVER ESP32-WROVER
------------ ------------
A step upgrade of ESP-WROOM-32 described above with an additional 4 MB SPI PSRAM (Pseudo static RAM). Module is provided in two versions: 'ESP32-WROVER' with PCB antenna (shown below) and 'ESP32-WROVER-I' with an IPEX antenna. A step upgrade of :ref:`esp-modules-and-boards-esp-wroom-32` with an additional 4 MB SPI PSRAM (Pseudo static RAM). This module is provided in two versions: 'ESP32-WROVER' with PCB antenna (shown below) and 'ESP32-WROVER-I' with an U.FL / IPEX antenna connector. Because of additional components inside, this module is 5.9 mm longer than :ref:`esp-modules-and-boards-esp-wroom-32`.
.. figure:: https://dl.espressif.com/dl/schematics/pictures/esp32-wrover.jpg .. figure:: https://dl.espressif.com/dl/schematics/pictures/esp32-wrover.jpg
:align: center :align: center
@@ -46,7 +100,7 @@ A step upgrade of ESP-WROOM-32 described above with an additional 4 MB SPI PSRAM
ESP32-WROVER module (front and back) ESP32-WROVER module (front and back)
Documentation Documentation
""""""""""""" ^^^^^^^^^^^^^
* `ESP32-WROVER Datasheet <https://espressif.com/sites/default/files/documentation/esp32-wrover_datasheet_en.pdf>`__ (PDF) * `ESP32-WROVER Datasheet <https://espressif.com/sites/default/files/documentation/esp32-wrover_datasheet_en.pdf>`__ (PDF)
* `ESP-PSRAM32 Datasheet <https://espressif.com/sites/default/files/documentation/esp-psram32_datasheet_en.pdf>`__ (PDF) * `ESP-PSRAM32 Datasheet <https://espressif.com/sites/default/files/documentation/esp-psram32_datasheet_en.pdf>`__ (PDF)
@@ -55,7 +109,7 @@ Documentation
.. _esp-modules-and-boards-esp32-pico-pit-v4: .. _esp-modules-and-boards-esp32-pico-pit-v4:
ESP32-PICO-KIT V4 ESP32-PICO-KIT V4
----------------- =================
The smallest ESP32 development board with all the components required to connect it directly to a PC USB port, and pin headers to plug into a mini breadboard. It is equipped with ESP32-PICO-D4 chip that integrates 4MB flash memory, a crystal oscillator, filter capacitors and RF matching circuit in one single package. As result the fully functional development board requires only a few external components that can easy fit on a 20 x 52 mm PCB including antenna, LDO, USB-UART bridge and two buttons to reset it and put into download mode. The smallest ESP32 development board with all the components required to connect it directly to a PC USB port, and pin headers to plug into a mini breadboard. It is equipped with ESP32-PICO-D4 chip that integrates 4MB flash memory, a crystal oscillator, filter capacitors and RF matching circuit in one single package. As result the fully functional development board requires only a few external components that can easy fit on a 20 x 52 mm PCB including antenna, LDO, USB-UART bridge and two buttons to reset it and put into download mode.
@@ -70,14 +124,14 @@ Comparing to ESP32-PICO-KIT V3, this version has revised printout and reduced nu
Documentation Documentation
""""""""""""" -------------
* :doc:`../get-started/get-started-pico-kit` * :doc:`../get-started/get-started-pico-kit`
* `ESP32-PICO-KIT V4 Schematic <https://dl.espressif.com/dl/schematics/esp32-pico-kit-v4_schematic.pdf>`_ (PDF) * `ESP32-PICO-KIT V4 Schematic <https://dl.espressif.com/dl/schematics/esp32-pico-kit-v4_schematic.pdf>`_ (PDF)
* `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)
Previous Versions Previous Versions
""""""""""""""""" -----------------
* :ref:`esp-modules-and-boards-esp32-pico-pit-v3` * :ref:`esp-modules-and-boards-esp32-pico-pit-v3`
@@ -85,9 +139,9 @@ Previous Versions
.. _esp-modules-and-boards-esp32-devkitc: .. _esp-modules-and-boards-esp32-devkitc:
ESP32 Core Board V2 / ESP32 DevKitC ESP32 Core Board V2 / ESP32 DevKitC
----------------------------------- ===================================
Small and convenient development board with ESP-WROOM-32 module installed, break out pin headers and minimum additional components. Includes USB to serial programming interface, that also provides power supply for the board. Has press buttons to reset the board and put it in upload mode. Small and convenient development board with :ref:`esp-modules-and-boards-esp-wroom-32` module installed, break out pin headers and minimum additional components. Includes USB to serial programming interface, that also provides power supply for the board. Has pushbuttons to reset the board and put it in upload mode.
.. figure:: https://dl.espressif.com/dl/schematics/pictures/esp32-core-board-v2.png .. figure:: https://dl.espressif.com/dl/schematics/pictures/esp32-core-board-v2.png
:align: center :align: center
@@ -97,7 +151,7 @@ Small and convenient development board with ESP-WROOM-32 module installed, break
ESP32 Core Board V2 / ESP32 DevKitC board ESP32 Core Board V2 / ESP32 DevKitC board
Documentation Documentation
""""""""""""" -------------
* :doc:`../get-started/get-started-devkitc` * :doc:`../get-started/get-started-devkitc`
* `ESP32 DevKitC Schematic <https://dl.espressif.com/dl/schematics/ESP32-Core-Board-V2_sch.pdf>`__ (PDF) * `ESP32 DevKitC Schematic <https://dl.espressif.com/dl/schematics/ESP32-Core-Board-V2_sch.pdf>`__ (PDF)
@@ -108,13 +162,13 @@ Documentation
.. _esp-modules-and-boards-esp-wrover-kit-v3: .. _esp-modules-and-boards-esp-wrover-kit-v3:
ESP-WROVER-KIT V3 ESP-WROVER-KIT V3
----------------- =================
The ESP-WROVER-KIT V3 development board has dual port USB to serial converter for programming and JTAG interface for debugging. Power supply is provided by USB interface or from standard 5 mm power supply jack. Power supply selection is done with a jumper and may be put on/off with a separate switch. This board has MicroSD card slot, 3.2” SPI LCD screen and dedicated header to connect a camera. It provides RGB diode for diagnostics. Includes 32.768 kHz XTAL for internal RTC to operate it in low power modes. The ESP-WROVER-KIT V3 development board has dual port USB to serial converter for programming and JTAG interface for debugging. Power supply is provided by USB interface or from standard 5 mm power supply jack. Power supply selection is done with a jumper and may be put on/off with a separate switch. This board has MicroSD card slot, 3.2” SPI LCD screen and dedicated header to connect a camera. It provides RGB diode for diagnostics. Includes 32.768 kHz XTAL for internal RTC to operate it in low power modes.
As all previous version of ESP-WROVER-KIT boards, it is ready to accommodate an :ref:`esp-modules-and-boards-esp-wroom-32` or :ref:`esp-modules-and-boards-esp32-wrover` module. As all previous version of ESP-WROVER-KIT boards, it is ready to accommodate an :ref:`esp-modules-and-boards-esp-wroom-32` or :ref:`esp-modules-and-boards-esp32-wrover` module.
This is the first release of ESP-WROVER-KIT shipped with ESP32-WROVER module installed by default. This release also introduced several design changes to conditioning and interlocking of signals to the bootstrapping pins. Also, a zero Ohm resistor (R166) has been added between WROVER/WROOM module and VDD33 net, which can be desoldered, or replaced with a shunt resistor, for current measurement. This is intended to facilitate power consumption analysis in various operation modes of ESP32. Refer to schematic - the changes are enclosed in green border. This is the first release of ESP-WROVER-KIT shipped with :ref:`esp-modules-and-boards-esp32-wrover` module installed by default. This release also introduced several design changes to conditioning and interlocking of signals to the bootstrapping pins. Also, a zero Ohm resistor (R166) has been added between WROVER/WROOM module and VDD33 net, which can be desoldered, or replaced with a shunt resistor, for current measurement. This is intended to facilitate power consumption analysis in various operation modes of ESP32. Refer to schematic - the changes are enclosed in green border.
.. figure:: https://dl.espressif.com/dl/schematics/pictures/esp-wrover-kit-v3.jpg .. figure:: https://dl.espressif.com/dl/schematics/pictures/esp-wrover-kit-v3.jpg
:align: center :align: center
@@ -123,10 +177,10 @@ This is the first release of ESP-WROVER-KIT shipped with ESP32-WROVER module ins
ESP-WROVER-KIT V3 board ESP-WROVER-KIT V3 board
The camera header has been changed from male back to female. The board soldermask is matte black. The board on picture above has ESP32-WROVER is installed. The camera header has been changed from male back to female. The board soldermask is matte black. The board on picture above has :ref:`esp-modules-and-boards-esp32-wrover` is installed.
Documentation Documentation
""""""""""""" -------------
* :doc:`../get-started/get-started-wrover-kit` * :doc:`../get-started/get-started-wrover-kit`
* `ESP-WROVER-KIT V3 Schematic <https://dl.espressif.com/dl/schematics/ESP-WROVER-KIT_SCH-3.pdf>`__ (PDF) * `ESP-WROVER-KIT V3 Schematic <https://dl.espressif.com/dl/schematics/ESP-WROVER-KIT_SCH-3.pdf>`__ (PDF)
@@ -134,14 +188,14 @@ Documentation
* `FTDI Virtual COM Port Drivers`_ * `FTDI Virtual COM Port Drivers`_
Previous Versions Previous Versions
""""""""""""""""" -----------------
* :ref:`esp-modules-and-boards-esp-wrover-kit-v1` * :ref:`esp-modules-and-boards-esp-wrover-kit-v1`
* :ref:`esp-modules-and-boards-esp-wrover-kit-v2` * :ref:`esp-modules-and-boards-esp-wrover-kit-v2`
Related Documents Related Documents
----------------- =================
* :doc:`modules-and-boards-previous` * :doc:`modules-and-boards-previous`

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

@@ -0,0 +1,9 @@
#
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
# project subdirectory.
#
PROJECT_NAME := rmt_tx
include $(IDF_PATH)/make/project.mk

View File

@@ -0,0 +1,25 @@
# RMT Transmit Example
This example shows how to configure and operate the remote control (RMT) peripheral to transmit a sample message in the [Morse code](https://en.wikipedia.org/wiki/Morse_code).
Configuration (pin number, etc.) can be modified in the top of the `main/rmt_tx_main.c` file.
To be able to see and hear the message output by the RMT, connect a LED and a speaker or an earphone (be careful - it may be loud) to the GPIO configured under `RMT_TX_GPIO` define (default is GPIO 18).
Example connections:
```
330R LED
GPIO18 +----/\/\/\----+------|>|-----+ GND
|
| /|
+-+ | Speaker
| | | or
+-+ | earphone
| \|
|
+--------------+ GND
```
See the README.md file in the upper level 'examples' directory for more information about examples.

View File

@@ -0,0 +1,4 @@
#
# Main Makefile. This is basically the same as a component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)

View File

@@ -0,0 +1,95 @@
/* RMT transmit example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "driver/rmt.h"
static const char *RMT_TX_TAG = "RMT Tx";
#define RMT_TX_CHANNEL RMT_CHANNEL_0
#define RMT_TX_GPIO 18
/*
* Prepare a raw table with a message in the Morse code
*
* The message is "ESP" : . ... .--.
*
* The table structure represents the RMT item structure:
* {duration, level, duration, level}
*
*/
rmt_item32_t items[] = {
// E : dot
{{{ 32767, 1, 32767, 0 }}}, // dot
//
{{{ 32767, 0, 32767, 0 }}}, // SPACE
// S : dot, dot, dot
{{{ 32767, 1, 32767, 0 }}}, // dot
{{{ 32767, 1, 32767, 0 }}}, // dot
{{{ 32767, 1, 32767, 0 }}}, // dot
//
{{{ 32767, 0, 32767, 0 }}}, // SPACE
// P : dot, dash, dash, dot
{{{ 32767, 1, 32767, 0 }}}, // dot
{{{ 32767, 1, 32767, 1 }}},
{{{ 32767, 1, 32767, 0 }}}, // dash
{{{ 32767, 1, 32767, 1 }}},
{{{ 32767, 1, 32767, 0 }}}, // dash
{{{ 32767, 1, 32767, 0 }}}, // dot
// RMT end marker
{{{ 0, 1, 0, 0 }}}
};
/*
* Initialize the RMT Tx channel
*/
static void rmt_tx_int()
{
rmt_config_t config;
config.rmt_mode = RMT_MODE_TX;
config.channel = RMT_TX_CHANNEL;
config.gpio_num = RMT_TX_GPIO;
config.mem_block_num = 1;
config.tx_config.loop_en = 0;
// enable the carrier to be able to hear the Morse sound
// if the RMT_TX_GPIO is connected to a speaker
config.tx_config.carrier_en = 1;
config.tx_config.idle_output_en = 1;
config.tx_config.idle_level = 0;
config.tx_config.carrier_duty_percent = 50;
// set audible career frequency of 611 Hz
// actually 611 Hz is the minimum, that can be set
// with current implementation of the RMT API
config.tx_config.carrier_freq_hz = 611;
config.tx_config.carrier_level = 1;
// set the maximum clock divider to be able to output
// RMT pulses in range of about one hundred milliseconds
config.clk_div = 255;
ESP_ERROR_CHECK(rmt_config(&config));
ESP_ERROR_CHECK(rmt_driver_install(config.channel, 0, 0));
}
void app_main(void *ignore)
{
ESP_LOGI(RMT_TX_TAG, "Configuring transmitter");
rmt_tx_int();
int number_of_items = sizeof(items) / sizeof(items[0]);
while (1) {
ESP_ERROR_CHECK(rmt_write_items(RMT_TX_CHANNEL, items, number_of_items, true));
ESP_LOGI(RMT_TX_TAG, "Transmission complete");
vTaskDelay(2000 / portTICK_PERIOD_MS);
}
vTaskDelete(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

@@ -73,11 +73,36 @@ COMPONENT_SUBMODULES ?=
################################################################################ ################################################################################
# 2) Include the component.mk for the specific component (COMPONENT_MAKEFILE) to # 2) Include the component.mk for the specific component (COMPONENT_MAKEFILE) to
# override variables & optionally define custom targets. # override variables & optionally define custom targets. Also include global
# component makefiles.
################################################################################ ################################################################################
# Include any Makefile.componentbuild file letting components add
# configuration at the global component level
# Save component_path; we pass it to the called Makefile.componentbuild
# as COMPILING_COMPONENT_PATH, and we use it to restore the current
# COMPONENT_PATH later.
COMPILING_COMPONENT_PATH := $(COMPONENT_PATH)
define includeCompBuildMakefile
$(if $(V),$(info including $(1)/Makefile.componentbuild...))
COMPONENT_PATH := $(1)
include $(1)/Makefile.componentbuild
endef
$(foreach componentpath,$(COMPONENT_PATHS), \
$(if $(wildcard $(componentpath)/Makefile.componentbuild), \
$(eval $(call includeCompBuildMakefile,$(componentpath)))))
#Restore COMPONENT_PATH to what it was
COMPONENT_PATH := $(COMPILING_COMPONENT_PATH)
# Include component.mk for this component.
include $(COMPONENT_MAKEFILE) include $(COMPONENT_MAKEFILE)
################################################################################ ################################################################################
# 3) Set variables that depend on values that may changed by component.mk # 3) Set variables that depend on values that may changed by component.mk
################################################################################ ################################################################################
@@ -85,11 +110,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 +247,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

@@ -154,6 +154,7 @@ export COMPONENTS
# NOTE: These paths must be generated WITHOUT a trailing / so we # NOTE: These paths must be generated WITHOUT a trailing / so we
# can use $(notdir x) to get the component name. # can use $(notdir x) to get the component name.
COMPONENT_PATHS := $(foreach comp,$(COMPONENTS),$(firstword $(foreach cd,$(COMPONENT_DIRS),$(wildcard $(dir $(cd))$(comp) $(cd)/$(comp))))) COMPONENT_PATHS := $(foreach comp,$(COMPONENTS),$(firstword $(foreach cd,$(COMPONENT_DIRS),$(wildcard $(dir $(cd))$(comp) $(cd)/$(comp)))))
export COMPONENT_PATHS
TEST_COMPONENTS ?= TEST_COMPONENTS ?=
TESTS_ALL ?= TESTS_ALL ?=

View File

@@ -55,6 +55,8 @@ CTRL_F = '\x06'
CTRL_H = '\x08' CTRL_H = '\x08'
CTRL_R = '\x12' CTRL_R = '\x12'
CTRL_T = '\x14' CTRL_T = '\x14'
CTRL_Y = '\x19'
CTRL_P = '\x10'
CTRL_RBRACKET = '\x1d' # Ctrl+] CTRL_RBRACKET = '\x1d' # Ctrl+]
# ANSI terminal codes # ANSI terminal codes
@@ -256,6 +258,7 @@ class Monitor(object):
self._pressed_menu_key = False self._pressed_menu_key = False
self._read_line = b"" self._read_line = b""
self._gdb_buffer = b"" self._gdb_buffer = b""
self._output_enabled = True
def main_loop(self): def main_loop(self):
self.console_reader.start() self.console_reader.start()
@@ -299,6 +302,7 @@ class Monitor(object):
# this may need to be made more efficient, as it pushes out a byte # this may need to be made more efficient, as it pushes out a byte
# at a time to the console # at a time to the console
for b in data: for b in data:
if self._output_enabled:
self.console.write_bytes(b) self.console.write_bytes(b)
if b == b'\n': # end of line if b == b'\n': # end of line
self.handle_serial_input_line(self._read_line.strip()) self.handle_serial_input_line(self._read_line.strip())
@@ -320,10 +324,23 @@ class Monitor(object):
self.serial.setRTS(True) self.serial.setRTS(True)
time.sleep(0.2) time.sleep(0.2)
self.serial.setRTS(False) self.serial.setRTS(False)
self.output_enable(True)
elif c == CTRL_F: # Recompile & upload elif c == CTRL_F: # Recompile & upload
self.run_make("flash") self.run_make("flash")
elif c == CTRL_A: # Recompile & upload app only elif c == CTRL_A: # Recompile & upload app only
self.run_make("app-flash") self.run_make("app-flash")
elif c == CTRL_Y: # Toggle output display
self.output_toggle()
elif c == CTRL_P:
yellow_print("Pause app (enter bootloader mode), press Ctrl-T Ctrl-R to restart")
# to fast trigger pause without press menu key
self.serial.setDTR(False) # IO0=HIGH
self.serial.setRTS(True) # EN=LOW, chip in reset
time.sleep(1.3) # timeouts taken from esptool.py, includes esp32r0 workaround. defaults: 0.1
self.serial.setDTR(True) # IO0=LOW
self.serial.setRTS(False) # EN=HIGH, chip out of reset
time.sleep(0.45) # timeouts taken from esptool.py, includes esp32r0 workaround. defaults: 0.05
self.serial.setDTR(False) # IO0=HIGH, done
else: else:
red_print('--- unknown menu character {} --'.format(key_description(c))) red_print('--- unknown menu character {} --'.format(key_description(c)))
@@ -340,13 +357,16 @@ class Monitor(object):
--- {reset:7} Reset target board via RTS line --- {reset:7} Reset target board via RTS line
--- {make:7} Run 'make flash' to build & flash --- {make:7} Run 'make flash' to build & flash
--- {appmake:7} Run 'make app-flash to build & flash app --- {appmake:7} Run 'make app-flash to build & flash app
--- {output:7} Toggle output display
--- {pause:7} Reset target into bootloader to pause app via RTS line
""".format(version=__version__, """.format(version=__version__,
exit=key_description(self.exit_key), exit=key_description(self.exit_key),
menu=key_description(self.menu_key), menu=key_description(self.menu_key),
reset=key_description(CTRL_R), reset=key_description(CTRL_R),
make=key_description(CTRL_F), make=key_description(CTRL_F),
appmake=key_description(CTRL_A), appmake=key_description(CTRL_A),
output=key_description(CTRL_Y),
pause=key_description(CTRL_P),
) )
def __enter__(self): def __enter__(self):
@@ -393,6 +413,8 @@ class Monitor(object):
p.wait() p.wait()
if p.returncode != 0: if p.returncode != 0:
self.prompt_next_action("Build failed") self.prompt_next_action("Build failed")
else:
self.output_enable(True)
def lookup_pc_address(self, pc_addr): def lookup_pc_address(self, pc_addr):
translation = subprocess.check_output( translation = subprocess.check_output(
@@ -430,6 +452,13 @@ class Monitor(object):
pass # happens on Windows, maybe other OSes pass # happens on Windows, maybe other OSes
self.prompt_next_action("gdb exited") self.prompt_next_action("gdb exited")
def output_enable(self, enable):
self._output_enabled = enable
def output_toggle(self):
self._output_enabled = not self._output_enabled
yellow_print("\nToggle output display: {}, Type Ctrl-T Ctrl-Y to show/disable output again.".format(self._output_enabled))
def main(): def main():
parser = argparse.ArgumentParser("idf_monitor - a serial output monitor for esp-idf") parser = argparse.ArgumentParser("idf_monitor - a serial output monitor for esp-idf")

View File

@@ -23,6 +23,22 @@ CFLAGS :=
CPPFLAGS := CPPFLAGS :=
LDFLAGS := LDFLAGS :=
# Workaround for a bug on Windows if the mingw32 host compilers
# are installed in addition to the MSYS ones. The kconfig tools
# need to be compiled using the MSYS compiler.
#
# See https://github.com/espressif/esp-idf/issues/1296
ifdef MSYSTEM
ifeq ("$(MSYSTEM)", "MINGW32")
ifeq ("$(CC)", "cc")
CC := /usr/bin/gcc
endif
ifeq ("$(LD)", "ld")
LD := /usr/bin/ld
endif
endif # MING32
endif # MSYSTEM
default: mconf conf default: mconf conf
xconfig: qconf xconfig: qconf

View File

@@ -1,5 +1,5 @@
%option nostdinit noyywrap never-interactive full ecs %option nostdinit noyywrap never-interactive full ecs
%option 8bit nodefault perf-report perf-report %option 8bit perf-report perf-report
%option noinput %option noinput
%x COMMAND HELP STRING PARAM %x COMMAND HELP STRING PARAM
%{ %{

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 */
} }