From ccea4a0f8f688abc0ab66d340212074864fd3229 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Thu, 23 Nov 2017 14:18:06 +1100 Subject: [PATCH 01/56] windows: Allow "make menuconfig" to work if ming32 gcc is also installed Closes https://github.com/espressif/esp-idf/issues/1296 --- tools/kconfig/Makefile | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tools/kconfig/Makefile b/tools/kconfig/Makefile index 6675294b79..2454e47af7 100644 --- a/tools/kconfig/Makefile +++ b/tools/kconfig/Makefile @@ -23,6 +23,22 @@ CFLAGS := CPPFLAGS := 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 xconfig: qconf From 38170d465c8907bbc79ccf62c8d8ea01c2f9e8c0 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Fri, 6 Oct 2017 15:38:01 +1100 Subject: [PATCH 02/56] spi_flash: Add option to verify all writes by reading back data Helpful when debugging SPI flash hardware related issues. TW15203 --- components/spi_flash/Kconfig | 17 ++++++++ components/spi_flash/flash_ops.c | 69 ++++++++++++++++++++++++++++++-- 2 files changed, 82 insertions(+), 4 deletions(-) diff --git a/components/spi_flash/Kconfig b/components/spi_flash/Kconfig index 4028cf3894..5a05d859bd 100644 --- a/components/spi_flash/Kconfig +++ b/components/spi_flash/Kconfig @@ -1,5 +1,22 @@ 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_ENABLE_COUNTERS bool "Enable operation counters" default 0 diff --git a/components/spi_flash/flash_ops.c b/components/spi_flash/flash_ops.c index 7fd47dcca4..eb2b23cf49 100644 --- a/components/spi_flash/flash_ops.c +++ b/components/spi_flash/flash_ops.c @@ -44,8 +44,9 @@ #define MAX_WRITE_CHUNK 8192 #define MAX_READ_CHUNK 16384 +static const char *TAG __attribute__((unused)) = "spi_flash"; + #if CONFIG_SPI_FLASH_ENABLE_COUNTERS -static const char *TAG = "spi_flash"; static spi_flash_counters_t s_flash_stats; #define COUNTER_START() uint32_t ts_begin = xthal_get_ccount() @@ -233,6 +234,66 @@ esp_err_t IRAM_ATTR spi_flash_erase_range(uint32_t start_addr, uint32_t size) 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; + } + + 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) { CHECK_WRITE_ADDRESS(dst, size); @@ -269,7 +330,7 @@ esp_err_t IRAM_ATTR spi_flash_write(size_t dst, const void *srcv, size_t size) uint32_t t = 0xffffffff; memcpy(((uint8_t *) &t) + (dst - left_off), srcc, left_size); 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(); if (rc != ESP_ROM_SPIFLASH_RESULT_OK) { goto out; @@ -296,7 +357,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; } 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(); COUNTER_ADD_BYTES(write, write_size); mid_size -= write_size; @@ -311,7 +372,7 @@ esp_err_t IRAM_ATTR spi_flash_write(size_t dst, const void *srcv, size_t size) uint32_t t = 0xffffffff; memcpy(&t, srcc + right_off, right_size); 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(); if (rc != ESP_ROM_SPIFLASH_RESULT_OK) { goto out; From a4d45a0a4dfb894e135c8378e4a032c3253a5e95 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 29 Nov 2017 14:26:57 +1100 Subject: [PATCH 03/56] spi_flash: Add option to log warnings if (spuriously) writing zero bits to ones Won't work for SPIFFS, maybe some other implementations? --- components/spi_flash/Kconfig | 14 ++++++++++++++ components/spi_flash/flash_ops.c | 15 +++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/components/spi_flash/Kconfig b/components/spi_flash/Kconfig index 5a05d859bd..f5e3c7ecd9 100644 --- a/components/spi_flash/Kconfig +++ b/components/spi_flash/Kconfig @@ -17,6 +17,20 @@ config SPI_FLASH_LOG_FAILED_WRITE 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 bool "Enable operation counters" default 0 diff --git a/components/spi_flash/flash_ops.c b/components/spi_flash/flash_ops.c index eb2b23cf49..aab0c1210f 100644 --- a/components/spi_flash/flash_ops.c +++ b/components/spi_flash/flash_ops.c @@ -260,6 +260,21 @@ static IRAM_ATTR esp_rom_spiflash_result_t spi_flash_write_inner(uint32_t target 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; From 36d6e4e2c78f15c2e2c89e7750f586e4d47ddbf5 Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Wed, 25 Oct 2017 23:23:42 +0200 Subject: [PATCH 04/56] Fix compilation errors when using gcc-7.2.0 for the crosstool-ng toolchain * Change snprintf for strlcat does not complain w/gcc7.2.0 and it is safer, thanks @projectgus * Use proper quotes for character literals Merges https://github.com/espressif/esp-idf/pull/1163 --- components/console/argtable3/argtable3.c | 2 +- components/mdns/mdns.c | 3 ++- components/nvs_flash/src/nvs_item_hash_list.cpp | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/components/console/argtable3/argtable3.c b/components/console/argtable3/argtable3.c index f3772f1a9b..ed577c83aa 100644 --- a/components/console/argtable3/argtable3.c +++ b/components/console/argtable3/argtable3.c @@ -270,7 +270,7 @@ extern char *suboptarg; /* getsubopt(3) external variable */ */ #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 */ /*- * Copyright (c) 2000 The NetBSD Foundation, Inc. diff --git a/components/mdns/mdns.c b/components/mdns/mdns.c index c77283e071..171da177c4 100644 --- a/components/mdns/mdns.c +++ b/components/mdns/mdns.c @@ -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, "ip6") != 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) { name->sub = 1; } else { diff --git a/components/nvs_flash/src/nvs_item_hash_list.cpp b/components/nvs_flash/src/nvs_item_hash_list.cpp index cf48477d61..e483c59697 100644 --- a/components/nvs_flash/src/nvs_item_hash_list.cpp +++ b/components/nvs_flash/src/nvs_item_hash_list.cpp @@ -62,7 +62,7 @@ void HashList::insert(const Item& item, 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; for (size_t i = 0; i < it->mCount; ++i) { 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) { 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) { HashListNode& e = it->mNodes[index]; if (e.mIndex >= start && From ce3ccc18fab8d74981ae4644296c5b321031fccf Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Sun, 19 Nov 2017 02:13:52 +1100 Subject: [PATCH 05/56] Update cJSON to 1.6.0 * Fixes compilation errors/warnings with gcc 7.2.0 Merges https://github.com/espressif/esp-idf/pull/1163 --- components/json/include/cJSON.h | 261 +- components/json/library/cJSON.c | 3189 ++++++++++++++++---- components/json/port/cJSON_Utils.c | 1692 +++++++++-- components/json/port/include/cJSON_Utils.h | 74 +- 4 files changed, 4212 insertions(+), 1004 deletions(-) diff --git a/components/json/include/cJSON.h b/components/json/include/cJSON.h index 92ed8c3b48..7c4f8e7cdb 100644 --- a/components/json/include/cJSON.h +++ b/components/json/include/cJSON.h @@ -1,16 +1,16 @@ /* - 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 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 @@ -28,120 +28,233 @@ extern "C" { #endif +/* project version */ +#define CJSON_VERSION_MAJOR 1 +#define CJSON_VERSION_MINOR 6 +#define CJSON_VERSION_PATCH 0 + +#include + /* cJSON Types: */ -#define cJSON_False 0 -#define cJSON_True 1 -#define cJSON_NULL 2 -#define cJSON_Number 3 -#define cJSON_String 4 -#define cJSON_Array 5 -#define cJSON_Object 6 - +#define cJSON_Invalid (0) +#define cJSON_False (1 << 0) +#define cJSON_True (1 << 1) +#define cJSON_NULL (1 << 2) +#define cJSON_Number (1 << 3) +#define cJSON_String (1 << 4) +#define cJSON_Array (1 << 5) +#define cJSON_Object (1 << 6) +#define cJSON_Raw (1 << 7) /* raw json */ + #define cJSON_IsReference 256 #define cJSON_StringIsConst 512 /* The cJSON structure: */ -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. */ +typedef struct cJSON +{ + /* 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 */ - int valueint; /* The item's number, if type==cJSON_Number */ - double valuedouble; /* The item's number, if type==cJSON_Number */ + /* The item's string, if type==cJSON_String and type == cJSON_Raw */ + char *valuestring; + /* 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; -typedef struct cJSON_Hooks { +typedef struct cJSON_Hooks +{ void *(*malloc_fn)(size_t sz); void (*free_fn)(void *ptr); } 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 */ -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. */ -extern cJSON *cJSON_Parse(const char *value); -/* Render a cJSON entity to text for transfer/storage. Free the char* when finished. */ -extern char *cJSON_Print(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 for transfer/storage. */ +CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item); +/* Render a cJSON entity to text for transfer/storage without any formatting. */ +CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const 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 */ -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. */ -extern void cJSON_Delete(cJSON *c); +CJSON_PUBLIC(void) cJSON_Delete(cJSON *c); /* 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. */ -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. */ -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. */ -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. */ -extern cJSON *cJSON_CreateNull(void); -extern cJSON *cJSON_CreateTrue(void); -extern cJSON *cJSON_CreateFalse(void); -extern cJSON *cJSON_CreateBool(int b); -extern cJSON *cJSON_CreateNumber(double num); -extern cJSON *cJSON_CreateDouble(double num,int i_num); -extern cJSON *cJSON_CreateString(const char *string); -extern cJSON *cJSON_CreateArray(void); -extern cJSON *cJSON_CreateObject(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean); +CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num); +CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string); +/* raw json */ +CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw); +CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void); /* These utilities create an Array of count items. */ -extern cJSON *cJSON_CreateIntArray(const int *numbers,int count); -extern cJSON *cJSON_CreateFloatArray(const float *numbers,int count); -extern cJSON *cJSON_CreateDoubleArray(const double *numbers,int count); -extern cJSON *cJSON_CreateStringArray(const char **strings,int count); +CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count); +CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count); +CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count); +CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char **strings, int count); /* Append item to the specified array/object. */ -extern void cJSON_AddItemToArray(cJSON *array, cJSON *item); -extern 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 */ +CJSON_PUBLIC(void) cJSON_AddItemToArray(cJSON *array, cJSON *item); +CJSON_PUBLIC(void) cJSON_AddItemToObject(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. + * 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. */ -extern void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item); -extern void cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item); +CJSON_PUBLIC(void) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item); +CJSON_PUBLIC(void) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item); /* Remove/Detatch items from Arrays/Objects. */ -extern cJSON *cJSON_DetachItemFromArray(cJSON *array,int which); -extern void cJSON_DeleteItemFromArray(cJSON *array,int which); -extern cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string); -extern void cJSON_DeleteItemFromObject(cJSON *object,const char *string); - +CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item); +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which); +CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which); +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. */ -extern 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); -extern void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem); +CJSON_PUBLIC(void) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */ +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement); +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 */ -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 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. */ +/* 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. */ -#define cJSON_AddNullToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateNull()) -#define cJSON_AddTrueToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateTrue()) -#define cJSON_AddFalseToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateFalse()) -#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_AddStringToObject(object,name,s) cJSON_AddItemToObject(object, name, cJSON_CreateString(s)) +#define cJSON_AddNullToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateNull()) +#define cJSON_AddTrueToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateTrue()) +#define cJSON_AddFalseToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateFalse()) +#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_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. */ -#define cJSON_SetIntValue(object,val) ((object)?(object)->valueint=(object)->valuedouble=(val):(val)) -#define cJSON_SetNumberValue(object,val) ((object)?(object)->valueint=(object)->valuedouble=(val):(val)) +#define cJSON_SetIntValue(object, number) ((object) ? (object)->valueint = (object)->valuedouble = (number) : (number)) +/* 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 } diff --git a/components/json/library/cJSON.c b/components/json/library/cJSON.c index 2a5c392161..cef719f576 100644 --- a/components/json/library/cJSON.c +++ b/components/json/library/cJSON.c @@ -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 of this software and associated documentation files (the "Software"), to deal @@ -23,6 +23,20 @@ /* cJSON */ /* JSON parser in C. */ +/* disable warnings about old C89 functions in MSVC */ +#if !defined(_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER) +#define _CRT_SECURE_NO_DEPRECATE +#endif + +#ifdef __GNUC__ +#pragma GCC visibility push(default) +#endif +#if defined(_MSC_VER) +#pragma warning (push) +/* disable warning about single line comments in system headers */ +#pragma warning (disable : 4001) +#endif + #include #include #include @@ -30,722 +44,2711 @@ #include #include #include + +#ifdef ENABLE_LOCALES +#include +#endif + +#if defined(_MSC_VER) +#pragma warning (pop) +#endif +#ifdef __GNUC__ +#pragma GCC visibility pop +#endif + #include "cJSON.h" -static const char *ep; +/* define our own boolean type */ +#define true ((cJSON_bool)1) +#define false ((cJSON_bool)0) -const char *cJSON_GetErrorPtr(void) {return ep;} +typedef struct { + const unsigned char *json; + size_t position; +} error; +static error global_error = { NULL, 0 }; -static int cJSON_strcasecmp(const char *s1,const char *s2) +CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void) { - if (!s1) return (s1==s2)?0:1;if (!s2) return 1; - for(; tolower(*(const unsigned char *)s1) == tolower(*(const unsigned char *)s2); ++s1, ++s2) if(*s1 == 0) return 0; - return tolower(*(const unsigned char *)s1) - tolower(*(const unsigned char *)s2); + return (const char*) (global_error.json + global_error.position); } -static void *(*cJSON_malloc)(size_t sz) = malloc; -static void (*cJSON_free)(void *ptr) = free; +/* This is a safeguard to prevent copy-pasters from using incompatible C and header files */ +#if (CJSON_VERSION_MAJOR != 1) || (CJSON_VERSION_MINOR != 6) || (CJSON_VERSION_PATCH != 0) + #error cJSON.h and cJSON.c have different versions. Make sure that both have the same. +#endif -static char* cJSON_strdup(const char* str) +CJSON_PUBLIC(const char*) cJSON_Version(void) { - size_t len; - char* copy; + static char version[15]; + sprintf(version, "%i.%i.%i", CJSON_VERSION_MAJOR, CJSON_VERSION_MINOR, CJSON_VERSION_PATCH); - len = strlen(str) + 1; - if (!(copy = (char*)cJSON_malloc(len))) return 0; - memcpy(copy,str,len); - return copy; + return version; } -void cJSON_InitHooks(cJSON_Hooks* hooks) +/* Case insensitive string comparison, doesn't consider two NULL pointers equal though */ +static int case_insensitive_strcmp(const unsigned char *string1, const unsigned char *string2) { - if (!hooks) { /* Reset hooks */ - cJSON_malloc = malloc; - cJSON_free = free; + if ((string1 == NULL) || (string2 == NULL)) + { + return 1; + } + + if (string1 == string2) + { + return 0; + } + + for(; tolower(*string1) == tolower(*string2); (void)string1++, string2++) + { + if (*string1 == '\0') + { + return 0; + } + } + + return tolower(*string1) - tolower(*string2); +} + +typedef struct internal_hooks +{ + void *(*allocate)(size_t size); + void (*deallocate)(void *pointer); + void *(*reallocate)(void *pointer, size_t size); +} internal_hooks; + +#if defined(_MSC_VER) +/* work around MSVC error C2322: '...' address of dillimport '...' is not static */ +static void *internal_malloc(size_t size) +{ + return malloc(size); +} +static void internal_free(void *pointer) +{ + free(pointer); +} +static void *internal_realloc(void *pointer, size_t size) +{ + return realloc(pointer, size); +} +#else +#define internal_malloc malloc +#define internal_free free +#define internal_realloc realloc +#endif + +static internal_hooks global_hooks = { internal_malloc, internal_free, internal_realloc }; + +static unsigned char* cJSON_strdup(const unsigned char* string, const internal_hooks * const hooks) +{ + size_t length = 0; + unsigned char *copy = NULL; + + if (string == NULL) + { + return NULL; + } + + length = strlen((const char*)string) + sizeof(""); + copy = (unsigned char*)hooks->allocate(length); + if (copy == NULL) + { + return NULL; + } + memcpy(copy, string, length); + + return copy; +} + +CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks) +{ + if (hooks == NULL) + { + /* Reset hooks */ + global_hooks.allocate = malloc; + global_hooks.deallocate = free; + global_hooks.reallocate = realloc; return; } - cJSON_malloc = (hooks->malloc_fn)?hooks->malloc_fn:malloc; - cJSON_free = (hooks->free_fn)?hooks->free_fn:free; + global_hooks.allocate = malloc; + if (hooks->malloc_fn != NULL) + { + global_hooks.allocate = hooks->malloc_fn; + } + + global_hooks.deallocate = free; + if (hooks->free_fn != NULL) + { + global_hooks.deallocate = hooks->free_fn; + } + + /* use realloc only if both free and malloc are used */ + global_hooks.reallocate = NULL; + if ((global_hooks.allocate == malloc) && (global_hooks.deallocate == free)) + { + global_hooks.reallocate = realloc; + } } /* Internal constructor. */ -static cJSON *cJSON_New_Item(void) +static cJSON *cJSON_New_Item(const internal_hooks * const hooks) { - cJSON* node = (cJSON*)cJSON_malloc(sizeof(cJSON)); - if (node) memset(node,0,sizeof(cJSON)); - return node; + cJSON* node = (cJSON*)hooks->allocate(sizeof(cJSON)); + if (node) + { + memset(node, '\0', sizeof(cJSON)); + } + + return node; } /* Delete a cJSON structure. */ -void cJSON_Delete(cJSON *c) +CJSON_PUBLIC(void) cJSON_Delete(cJSON *item) { - cJSON *next; - while (c) - { - next=c->next; - if (!(c->type&cJSON_IsReference) && c->child) cJSON_Delete(c->child); - if (!(c->type&cJSON_IsReference) && c->valuestring) cJSON_free(c->valuestring); - if (!(c->type&cJSON_StringIsConst) && c->string) cJSON_free(c->string); - cJSON_free(c); - c=next; - } + cJSON *next = NULL; + while (item != NULL) + { + next = item->next; + if (!(item->type & cJSON_IsReference) && (item->child != NULL)) + { + cJSON_Delete(item->child); + } + if (!(item->type & cJSON_IsReference) && (item->valuestring != NULL)) + { + global_hooks.deallocate(item->valuestring); + } + if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) + { + global_hooks.deallocate(item->string); + } + global_hooks.deallocate(item); + item = next; + } } +/* get the decimal point character of the current locale */ +static unsigned char get_decimal_point(void) +{ +#ifdef ENABLE_LOCALES + struct lconv *lconv = localeconv(); + return (unsigned char) lconv->decimal_point[0]; +#else + return '.'; +#endif +} + +typedef struct +{ + const unsigned char *content; + size_t length; + size_t offset; + size_t depth; /* How deeply nested (in arrays/objects) is the input at the current offset. */ + internal_hooks hooks; +} parse_buffer; + +/* check if the given size is left to read in a given parse buffer (starting with 1) */ +#define can_read(buffer, size) ((buffer != NULL) && (((buffer)->offset + size) <= (buffer)->length)) +/* check if the buffer can be accessed at the given index (starting with 0) */ +#define can_access_at_index(buffer, index) ((buffer != NULL) && (((buffer)->offset + index) < (buffer)->length)) +#define cannot_access_at_index(buffer, index) (!can_access_at_index(buffer, index)) +/* get a pointer to the buffer at the position */ +#define buffer_at_offset(buffer) ((buffer)->content + (buffer)->offset) + /* Parse the input text to generate a number, and populate the result into item. */ -static const char *parse_number(cJSON *item,const char *num) +static cJSON_bool parse_number(cJSON * const item, parse_buffer * const input_buffer) { - double n=0,sign=1,scale=0;int subscale=0,signsubscale=1; + double number = 0; + unsigned char *after_end = NULL; + unsigned char number_c_string[64]; + unsigned char decimal_point = get_decimal_point(); + size_t i = 0; - if (*num=='-') sign=-1,num++; /* Has sign? */ - if (*num=='0') num++; /* is zero */ - if (*num>='1' && *num<='9') do n=(n*10.0)+(*num++ -'0'); while (*num>='0' && *num<='9'); /* Number? */ - if (*num=='.' && num[1]>='0' && num[1]<='9') {num++; do n=(n*10.0)+(*num++ -'0'),scale--; while (*num>='0' && *num<='9');} /* Fractional part? */ - if (*num=='e' || *num=='E') /* Exponent? */ - { num++;if (*num=='+') num++; else if (*num=='-') signsubscale=-1,num++; /* With sign? */ - while (*num>='0' && *num<='9') subscale=(subscale*10)+(*num++ - '0'); /* Number? */ - } + if ((input_buffer == NULL) || (input_buffer->content == NULL)) + { + return false; + } - n=sign*n*pow(10.0,(scale+subscale*signsubscale)); /* number = +/- number.fraction * 10^+/- exponent */ - - item->valuedouble=n; - item->valueint=(int)n; - item->type=cJSON_Number; - return num; + /* copy the number into a temporary buffer and replace '.' with the decimal point + * of the current locale (for strtod) + * This also takes care of '\0' not necessarily being available for marking the end of the input */ + for (i = 0; (i < (sizeof(number_c_string) - 1)) && can_access_at_index(input_buffer, i); i++) + { + switch (buffer_at_offset(input_buffer)[i]) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '+': + case '-': + case 'e': + case 'E': + number_c_string[i] = buffer_at_offset(input_buffer)[i]; + break; + + case '.': + number_c_string[i] = decimal_point; + break; + + default: + goto loop_end; + } + } +loop_end: + number_c_string[i] = '\0'; + + number = strtod((const char*)number_c_string, (char**)&after_end); + if (number_c_string == after_end) + { + return false; /* parse_error */ + } + + item->valuedouble = number; + + /* use saturation in case of overflow */ + if (number >= INT_MAX) + { + item->valueint = INT_MAX; + } + else if (number <= INT_MIN) + { + item->valueint = INT_MIN; + } + else + { + item->valueint = (int)number; + } + + item->type = cJSON_Number; + + input_buffer->offset += (size_t)(after_end - number_c_string); + return true; } -static int pow2gt (int x) { --x; x|=x>>1; x|=x>>2; x|=x>>4; x|=x>>8; x|=x>>16; return x+1; } - -typedef struct {char *buffer; int length; int offset; } printbuffer; - -static char* ensure(printbuffer *p,int needed) +/* don't ask me, but the original cJSON_SetNumberValue returns an integer or double */ +CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number) { - char *newbuffer;int newsize; - if (!p || !p->buffer) return 0; - needed+=p->offset; - if (needed<=p->length) return p->buffer+p->offset; + if (number >= INT_MAX) + { + object->valueint = INT_MAX; + } + else if (number <= INT_MIN) + { + object->valueint = INT_MIN; + } + else + { + object->valueint = (int)number; + } - newsize=pow2gt(needed); - newbuffer=(char*)cJSON_malloc(newsize); - if (!newbuffer) {cJSON_free(p->buffer);p->length=0,p->buffer=0;return 0;} - if (newbuffer) memcpy(newbuffer,p->buffer,p->length); - cJSON_free(p->buffer); - p->length=newsize; - p->buffer=newbuffer; - return newbuffer+p->offset; + return object->valuedouble = number; } -static int update(printbuffer *p) +typedef struct { - char *str; - if (!p || !p->buffer) return 0; - str=p->buffer+p->offset; - return p->offset+strlen(str); + unsigned char *buffer; + size_t length; + size_t offset; + size_t depth; /* current nesting depth (for formatted printing) */ + cJSON_bool noalloc; + cJSON_bool format; /* is this print a formatted print */ + internal_hooks hooks; +} printbuffer; + +/* realloc printbuffer if necessary to have at least "needed" bytes more */ +static unsigned char* ensure(printbuffer * const p, size_t needed) +{ + unsigned char *newbuffer = NULL; + size_t newsize = 0; + + if ((p == NULL) || (p->buffer == NULL)) + { + return NULL; + } + + if ((p->length > 0) && (p->offset >= p->length)) + { + /* make sure that offset is valid */ + return NULL; + } + + if (needed > INT_MAX) + { + /* sizes bigger than INT_MAX are currently not supported */ + return NULL; + } + + needed += p->offset + 1; + if (needed <= p->length) + { + return p->buffer + p->offset; + } + + if (p->noalloc) { + return NULL; + } + + /* calculate new buffer size */ + if (needed > (INT_MAX / 2)) + { + /* overflow of int, use INT_MAX if possible */ + if (needed <= INT_MAX) + { + newsize = INT_MAX; + } + else + { + return NULL; + } + } + else + { + newsize = needed * 2; + } + + if (p->hooks.reallocate != NULL) + { + /* reallocate with realloc if available */ + newbuffer = (unsigned char*)p->hooks.reallocate(p->buffer, newsize); + if (newbuffer == NULL) + { + p->hooks.deallocate(p->buffer); + p->length = 0; + p->buffer = NULL; + + return NULL; + } + } + else + { + /* otherwise reallocate manually */ + newbuffer = (unsigned char*)p->hooks.allocate(newsize); + if (!newbuffer) + { + p->hooks.deallocate(p->buffer); + p->length = 0; + p->buffer = NULL; + + return NULL; + } + if (newbuffer) + { + memcpy(newbuffer, p->buffer, p->offset + 1); + } + p->hooks.deallocate(p->buffer); + } + p->length = newsize; + p->buffer = newbuffer; + + return newbuffer + p->offset; +} + +/* calculate the new length of the string in a printbuffer and update the offset */ +static void update_offset(printbuffer * const buffer) +{ + const unsigned char *buffer_pointer = NULL; + if ((buffer == NULL) || (buffer->buffer == NULL)) + { + return; + } + buffer_pointer = buffer->buffer + buffer->offset; + + buffer->offset += strlen((const char*)buffer_pointer); } /* Render the number nicely from the given item into a string. */ -static char *print_number(cJSON *item,printbuffer *p) +static cJSON_bool print_number(const cJSON * const item, printbuffer * const output_buffer) { - char *str=0; - double d=item->valuedouble; - if (d==0) - { - if (p) str=ensure(p,2); - else str=(char*)cJSON_malloc(2); /* special case for 0. */ - if (str) strcpy(str,"0"); - } - else if (fabs(((double)item->valueint)-d)<=DBL_EPSILON && d<=INT_MAX && d>=INT_MIN) - { - if (p) str=ensure(p,21); - else str=(char*)cJSON_malloc(21); /* 2^64+1 can be represented in 21 chars. */ - if (str) sprintf(str,"%d",item->valueint); - } - else - { - if (p) str=ensure(p,64); - else str=(char*)cJSON_malloc(64); /* This is a nice tradeoff. */ - if (str) - { - if (fabs(floor(d)-d)<=DBL_EPSILON && fabs(d)<1.0e60)sprintf(str,"%.0f",d); - else if (fabs(d)<1.0e-6 || fabs(d)>1.0e9) sprintf(str,"%e",d); - else sprintf(str,"%f",d); - } - } - return str; + unsigned char *output_pointer = NULL; + double d = item->valuedouble; + int length = 0; + size_t i = 0; + unsigned char number_buffer[26]; /* temporary buffer to print the number into */ + unsigned char decimal_point = get_decimal_point(); + double test; + + if (output_buffer == NULL) + { + return false; + } + + /* This checks for NaN and Infinity */ + if ((d * 0) != 0) + { + length = sprintf((char*)number_buffer, "null"); + } + else + { + /* Try 15 decimal places of precision to avoid nonsignificant nonzero digits */ + length = sprintf((char*)number_buffer, "%1.15g", d); + + /* Check whether the original double can be recovered */ + if ((sscanf((char*)number_buffer, "%lg", &test) != 1) || ((double)test != d)) + { + /* If not, print with 17 decimal places of precision */ + length = sprintf((char*)number_buffer, "%1.17g", d); + } + } + + /* sprintf failed or buffer overrun occured */ + if ((length < 0) || (length > (int)(sizeof(number_buffer) - 1))) + { + return false; + } + + /* reserve appropriate space in the output */ + output_pointer = ensure(output_buffer, (size_t)length); + if (output_pointer == NULL) + { + return false; + } + + /* copy the printed number to the output and replace locale + * dependent decimal point with '.' */ + for (i = 0; i < ((size_t)length); i++) + { + if (number_buffer[i] == decimal_point) + { + output_pointer[i] = '.'; + continue; + } + + output_pointer[i] = number_buffer[i]; + } + output_pointer[i] = '\0'; + + output_buffer->offset += (size_t)length; + + return true; } -static unsigned parse_hex4(const char *str) +/* parse 4 digit hexadecimal number */ +static unsigned parse_hex4(const unsigned char * const input) { - unsigned h=0; - if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0; - h=h<<4;str++; - if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0; - h=h<<4;str++; - if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0; - h=h<<4;str++; - if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0; - return h; + unsigned int h = 0; + size_t i = 0; + + for (i = 0; i < 4; i++) + { + /* parse digit */ + if ((input[i] >= '0') && (input[i] <= '9')) + { + h += (unsigned int) input[i] - '0'; + } + else if ((input[i] >= 'A') && (input[i] <= 'F')) + { + h += (unsigned int) 10 + input[i] - 'A'; + } + else if ((input[i] >= 'a') && (input[i] <= 'f')) + { + h += (unsigned int) 10 + input[i] - 'a'; + } + else /* invalid */ + { + return 0; + } + + if (i < 3) + { + /* shift left to make place for the next nibble */ + h = h << 4; + } + } + + return h; } -/* Parse the input text into an unescaped cstring, and populate item. */ -static const unsigned char firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; -static const char *parse_string(cJSON *item,const char *str) +/* converts a UTF-16 literal to UTF-8 + * A literal can be one or two sequences of the form \uXXXX */ +static unsigned char utf16_literal_to_utf8(const unsigned char * const input_pointer, const unsigned char * const input_end, unsigned char **output_pointer) { - const char *ptr=str+1;char *ptr2;char *out;int len=0;unsigned uc,uc2; - if (*str!='\"') {ep=str;return 0;} /* not a string! */ - - while (*ptr!='\"' && *ptr && ++len) if (*ptr++ == '\\') ptr++; /* Skip escaped quotes. */ - - out=(char*)cJSON_malloc(len+1); /* This is how long we need for the string, roughly. */ - if (!out) return 0; - - ptr=str+1;ptr2=out; - while (*ptr!='\"' && *ptr) - { - if (*ptr!='\\') *ptr2++=*ptr++; - else - { - ptr++; - switch (*ptr) - { - case 'b': *ptr2++='\b'; break; - case 'f': *ptr2++='\f'; break; - case 'n': *ptr2++='\n'; break; - case 'r': *ptr2++='\r'; break; - case 't': *ptr2++='\t'; break; - case 'u': /* transcode utf16 to utf8. */ - uc=parse_hex4(ptr+1);ptr+=4; /* get the unicode char. */ + long unsigned int codepoint = 0; + unsigned int first_code = 0; + const unsigned char *first_sequence = input_pointer; + unsigned char utf8_length = 0; + unsigned char utf8_position = 0; + unsigned char sequence_length = 0; + unsigned char first_byte_mark = 0; - if ((uc>=0xDC00 && uc<=0xDFFF) || uc==0) break; /* check for invalid. */ + if ((input_end - first_sequence) < 6) + { + /* input ends unexpectedly */ + goto fail; + } - if (uc>=0xD800 && uc<=0xDBFF) /* UTF16 surrogate pairs. */ - { - if (ptr[1]!='\\' || ptr[2]!='u') break; /* missing second-half of surrogate. */ - uc2=parse_hex4(ptr+3);ptr+=6; - if (uc2<0xDC00 || uc2>0xDFFF) break; /* invalid second-half of surrogate. */ - uc=0x10000 + (((uc&0x3FF)<<10) | (uc2&0x3FF)); - } + /* get the first utf16 sequence */ + first_code = parse_hex4(first_sequence + 2); - len=4;if (uc<0x80) len=1;else if (uc<0x800) len=2;else if (uc<0x10000) len=3; ptr2+=len; - - switch (len) { - case 4: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6; - case 3: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6; - case 2: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6; - case 1: *--ptr2 =(uc | firstByteMark[len]); - } - ptr2+=len; - break; - default: *ptr2++=*ptr; break; - } - ptr++; - } - } - *ptr2=0; - if (*ptr=='\"') ptr++; - item->valuestring=out; - item->type=cJSON_String; - return ptr; + /* check that the code is valid */ + if (((first_code >= 0xDC00) && (first_code <= 0xDFFF))) + { + goto fail; + } + + /* UTF16 surrogate pair */ + if ((first_code >= 0xD800) && (first_code <= 0xDBFF)) + { + const unsigned char *second_sequence = first_sequence + 6; + unsigned int second_code = 0; + sequence_length = 12; /* \uXXXX\uXXXX */ + + if ((input_end - second_sequence) < 6) + { + /* input ends unexpectedly */ + goto fail; + } + + if ((second_sequence[0] != '\\') || (second_sequence[1] != 'u')) + { + /* missing second half of the surrogate pair */ + goto fail; + } + + /* get the second utf16 sequence */ + second_code = parse_hex4(second_sequence + 2); + /* check that the code is valid */ + if ((second_code < 0xDC00) || (second_code > 0xDFFF)) + { + /* invalid second half of the surrogate pair */ + goto fail; + } + + + /* calculate the unicode codepoint from the surrogate pair */ + codepoint = 0x10000 + (((first_code & 0x3FF) << 10) | (second_code & 0x3FF)); + } + else + { + sequence_length = 6; /* \uXXXX */ + codepoint = first_code; + } + + /* encode as UTF-8 + * takes at maximum 4 bytes to encode: + * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ + if (codepoint < 0x80) + { + /* normal ascii, encoding 0xxxxxxx */ + utf8_length = 1; + } + else if (codepoint < 0x800) + { + /* two bytes, encoding 110xxxxx 10xxxxxx */ + utf8_length = 2; + first_byte_mark = 0xC0; /* 11000000 */ + } + else if (codepoint < 0x10000) + { + /* three bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx */ + utf8_length = 3; + first_byte_mark = 0xE0; /* 11100000 */ + } + else if (codepoint <= 0x10FFFF) + { + /* four bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx 10xxxxxx */ + utf8_length = 4; + first_byte_mark = 0xF0; /* 11110000 */ + } + else + { + /* invalid unicode codepoint */ + goto fail; + } + + /* encode as utf8 */ + for (utf8_position = (unsigned char)(utf8_length - 1); utf8_position > 0; utf8_position--) + { + /* 10xxxxxx */ + (*output_pointer)[utf8_position] = (unsigned char)((codepoint | 0x80) & 0xBF); + codepoint >>= 6; + } + /* encode first byte */ + if (utf8_length > 1) + { + (*output_pointer)[0] = (unsigned char)((codepoint | first_byte_mark) & 0xFF); + } + else + { + (*output_pointer)[0] = (unsigned char)(codepoint & 0x7F); + } + + *output_pointer += utf8_length; + + return sequence_length; + +fail: + return 0; +} + +/* Parse the input text into an unescaped cinput, and populate item. */ +static cJSON_bool parse_string(cJSON * const item, parse_buffer * const input_buffer) +{ + const unsigned char *input_pointer = buffer_at_offset(input_buffer) + 1; + const unsigned char *input_end = buffer_at_offset(input_buffer) + 1; + unsigned char *output_pointer = NULL; + unsigned char *output = NULL; + + /* not a string */ + if (buffer_at_offset(input_buffer)[0] != '\"') + { + goto fail; + } + + { + /* calculate approximate size of the output (overestimate) */ + size_t allocation_length = 0; + size_t skipped_bytes = 0; + while (((size_t)(input_end - input_buffer->content) < input_buffer->length) && (*input_end != '\"')) + { + /* is escape sequence */ + if (input_end[0] == '\\') + { + if ((size_t)(input_end + 1 - input_buffer->content) >= input_buffer->length) + { + /* prevent buffer overflow when last input character is a backslash */ + goto fail; + } + skipped_bytes++; + input_end++; + } + input_end++; + } + if (((size_t)(input_end - input_buffer->content) >= input_buffer->length) || (*input_end != '\"')) + { + goto fail; /* string ended unexpectedly */ + } + + /* This is at most how much we need for the output */ + allocation_length = (size_t) (input_end - buffer_at_offset(input_buffer)) - skipped_bytes; + output = (unsigned char*)input_buffer->hooks.allocate(allocation_length + sizeof("")); + if (output == NULL) + { + goto fail; /* allocation failure */ + } + } + + output_pointer = output; + /* loop through the string literal */ + while (input_pointer < input_end) + { + if (*input_pointer != '\\') + { + *output_pointer++ = *input_pointer++; + } + /* escape sequence */ + else + { + unsigned char sequence_length = 2; + if ((input_end - input_pointer) < 1) + { + goto fail; + } + + switch (input_pointer[1]) + { + case 'b': + *output_pointer++ = '\b'; + break; + case 'f': + *output_pointer++ = '\f'; + break; + case 'n': + *output_pointer++ = '\n'; + break; + case 'r': + *output_pointer++ = '\r'; + break; + case 't': + *output_pointer++ = '\t'; + break; + case '\"': + case '\\': + case '/': + *output_pointer++ = input_pointer[1]; + break; + + /* UTF-16 literal */ + case 'u': + sequence_length = utf16_literal_to_utf8(input_pointer, input_end, &output_pointer); + if (sequence_length == 0) + { + /* failed to convert UTF16-literal to UTF-8 */ + goto fail; + } + break; + + default: + goto fail; + } + input_pointer += sequence_length; + } + } + + /* zero terminate the output */ + *output_pointer = '\0'; + + item->type = cJSON_String; + item->valuestring = (char*)output; + + input_buffer->offset = (size_t) (input_end - input_buffer->content); + input_buffer->offset++; + + return true; + +fail: + if (output != NULL) + { + input_buffer->hooks.deallocate(output); + } + + if (input_pointer != NULL) + { + input_buffer->offset = (size_t)(input_pointer - input_buffer->content); + } + + return false; } /* Render the cstring provided to an escaped version that can be printed. */ -static char *print_string_ptr(const char *str,printbuffer *p) +static cJSON_bool print_string_ptr(const unsigned char * const input, printbuffer * const output_buffer) { - const char *ptr;char *ptr2,*out;int len=0,flag=0;unsigned char token; - - for (ptr=str;*ptr;ptr++) flag|=((*ptr>0 && *ptr<32)||(*ptr=='\"')||(*ptr=='\\'))?1:0; - if (!flag) - { - len=ptr-str; - if (p) out=ensure(p,len+3); - else out=(char*)cJSON_malloc(len+3); - if (!out) return 0; - ptr2=out;*ptr2++='\"'; - strcpy(ptr2,str); - ptr2[len]='\"'; - ptr2[len+1]=0; - return out; - } - - if (!str) - { - if (p) out=ensure(p,3); - else out=(char*)cJSON_malloc(3); - if (!out) return 0; - strcpy(out,"\"\""); - return out; - } - ptr=str;while ((token=*ptr) && ++len) {if (strchr("\"\\\b\f\n\r\t",token)) len++; else if (token<32) len+=5;ptr++;} - - if (p) out=ensure(p,len+3); - else out=(char*)cJSON_malloc(len+3); - if (!out) return 0; + const unsigned char *input_pointer = NULL; + unsigned char *output = NULL; + unsigned char *output_pointer = NULL; + size_t output_length = 0; + /* numbers of additional characters needed for escaping */ + size_t escape_characters = 0; - ptr2=out;ptr=str; - *ptr2++='\"'; - while (*ptr) - { - if ((unsigned char)*ptr>31 && *ptr!='\"' && *ptr!='\\') *ptr2++=*ptr++; - else - { - *ptr2++='\\'; - switch (token=*ptr++) - { - case '\\': *ptr2++='\\'; break; - case '\"': *ptr2++='\"'; break; - case '\b': *ptr2++='b'; break; - case '\f': *ptr2++='f'; break; - case '\n': *ptr2++='n'; break; - case '\r': *ptr2++='r'; break; - case '\t': *ptr2++='t'; break; - default: sprintf(ptr2,"u%04x",token);ptr2+=5; break; /* escape and print */ - } - } - } - *ptr2++='\"';*ptr2++=0; - return out; + if (output_buffer == NULL) + { + return false; + } + + /* empty string */ + if (input == NULL) + { + output = ensure(output_buffer, sizeof("\"\"")); + if (output == NULL) + { + return false; + } + strcpy((char*)output, "\"\""); + + return true; + } + + /* set "flag" to 1 if something needs to be escaped */ + for (input_pointer = input; *input_pointer; input_pointer++) + { + switch (*input_pointer) + { + case '\"': + case '\\': + case '\b': + case '\f': + case '\n': + case '\r': + case '\t': + /* one character escape sequence */ + escape_characters++; + break; + default: + if (*input_pointer < 32) + { + /* UTF-16 escape sequence uXXXX */ + escape_characters += 5; + } + break; + } + } + output_length = (size_t)(input_pointer - input) + escape_characters; + + output = ensure(output_buffer, output_length + sizeof("\"\"")); + if (output == NULL) + { + return false; + } + + /* no characters have to be escaped */ + if (escape_characters == 0) + { + output[0] = '\"'; + memcpy(output + 1, input, output_length); + output[output_length + 1] = '\"'; + output[output_length + 2] = '\0'; + + return true; + } + + output[0] = '\"'; + output_pointer = output + 1; + /* copy the string */ + for (input_pointer = input; *input_pointer != '\0'; (void)input_pointer++, output_pointer++) + { + if ((*input_pointer > 31) && (*input_pointer != '\"') && (*input_pointer != '\\')) + { + /* normal character, copy */ + *output_pointer = *input_pointer; + } + else + { + /* character needs to be escaped */ + *output_pointer++ = '\\'; + switch (*input_pointer) + { + case '\\': + *output_pointer = '\\'; + break; + case '\"': + *output_pointer = '\"'; + break; + case '\b': + *output_pointer = 'b'; + break; + case '\f': + *output_pointer = 'f'; + break; + case '\n': + *output_pointer = 'n'; + break; + case '\r': + *output_pointer = 'r'; + break; + case '\t': + *output_pointer = 't'; + break; + default: + /* escape and print as unicode codepoint */ + sprintf((char*)output_pointer, "u%04x", *input_pointer); + output_pointer += 4; + break; + } + } + } + output[output_length + 1] = '\"'; + output[output_length + 2] = '\0'; + + return true; +} + +/* Invoke print_string_ptr (which is useful) on an item. */ +static cJSON_bool print_string(const cJSON * const item, printbuffer * const p) +{ + return print_string_ptr((unsigned char*)item->valuestring, p); } -/* Invote print_string_ptr (which is useful) on an item. */ -static char *print_string(cJSON *item,printbuffer *p) {return print_string_ptr(item->valuestring,p);} /* Predeclare these prototypes. */ -static const char *parse_value(cJSON *item,const char *value); -static char *print_value(cJSON *item,int depth,int fmt,printbuffer *p); -static const char *parse_array(cJSON *item,const char *value); -static char *print_array(cJSON *item,int depth,int fmt,printbuffer *p); -static const char *parse_object(cJSON *item,const char *value); -static char *print_object(cJSON *item,int depth,int fmt,printbuffer *p); +static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer); +static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer); +static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer); +static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer); +static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer); +static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer); /* Utility to jump whitespace and cr/lf */ -static const char *skip(const char *in) {while (in && *in && (unsigned char)*in<=32) in++; return in;} +static parse_buffer *buffer_skip_whitespace(parse_buffer * const buffer) +{ + if ((buffer == NULL) || (buffer->content == NULL)) + { + return NULL; + } + + while (can_access_at_index(buffer, 0) && (buffer_at_offset(buffer)[0] <= 32)) + { + buffer->offset++; + } + + if (buffer->offset == buffer->length) + { + buffer->offset--; + } + + return buffer; +} + +/* skip the UTF-8 BOM (byte order mark) if it is at the beginning of a buffer */ +static parse_buffer *skip_utf8_bom(parse_buffer * const buffer) +{ + if ((buffer == NULL) || (buffer->content == NULL) || (buffer->offset != 0)) + { + return NULL; + } + + if (can_access_at_index(buffer, 4) && (strncmp((const char*)buffer_at_offset(buffer), "\xEF\xBB\xBF", 3) == 0)) + { + buffer->offset += 3; + } + + return buffer; +} /* Parse an object - create a new root, and populate. */ -cJSON *cJSON_ParseWithOpts(const char *value,const char **return_parse_end,int require_null_terminated) +CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated) { - const char *end=0; - cJSON *c=cJSON_New_Item(); - ep=0; - if (!c) return 0; /* memory fail */ + parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } }; + cJSON *item = NULL; - end=parse_value(c,skip(value)); - if (!end) {cJSON_Delete(c);return 0;} /* parse failure. ep is set. */ + /* reset error position */ + global_error.json = NULL; + global_error.position = 0; - /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */ - if (require_null_terminated) {end=skip(end);if (*end) {cJSON_Delete(c);ep=end;return 0;}} - if (return_parse_end) *return_parse_end=end; - return c; + if (value == NULL) + { + goto fail; + } + + buffer.content = (const unsigned char*)value; + buffer.length = strlen((const char*)value) + sizeof(""); + buffer.offset = 0; + buffer.hooks = global_hooks; + + item = cJSON_New_Item(&global_hooks); + if (item == NULL) /* memory fail */ + { + goto fail; + } + + if (!parse_value(item, buffer_skip_whitespace(skip_utf8_bom(&buffer)))) + { + /* parse failure. ep is set. */ + goto fail; + } + + /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */ + if (require_null_terminated) + { + buffer_skip_whitespace(&buffer); + if ((buffer.offset >= buffer.length) || buffer_at_offset(&buffer)[0] != '\0') + { + goto fail; + } + } + if (return_parse_end) + { + *return_parse_end = (const char*)buffer_at_offset(&buffer); + } + + return item; + +fail: + if (item != NULL) + { + cJSON_Delete(item); + } + + if (value != NULL) + { + error local_error; + local_error.json = (const unsigned char*)value; + local_error.position = 0; + + if (buffer.offset < buffer.length) + { + local_error.position = buffer.offset; + } + else if (buffer.length > 0) + { + local_error.position = buffer.length - 1; + } + + if (return_parse_end != NULL) + { + *return_parse_end = (const char*)local_error.json + local_error.position; + } + + global_error = local_error; + } + + return NULL; } + /* Default options for cJSON_Parse */ -cJSON *cJSON_Parse(const char *value) {return cJSON_ParseWithOpts(value,0,0);} +CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value) +{ + return cJSON_ParseWithOpts(value, 0, 0); +} + +#define cjson_min(a, b) ((a < b) ? a : b) + +static unsigned char *print(const cJSON * const item, cJSON_bool format, const internal_hooks * const hooks) +{ + printbuffer buffer[1]; + unsigned char *printed = NULL; + + memset(buffer, 0, sizeof(buffer)); + + /* create buffer */ + buffer->buffer = (unsigned char*) hooks->allocate(256); + buffer->format = format; + buffer->hooks = *hooks; + if (buffer->buffer == NULL) + { + goto fail; + } + + /* print the value */ + if (!print_value(item, buffer)) + { + goto fail; + } + update_offset(buffer); + + /* check if reallocate is available */ + if (hooks->reallocate != NULL) + { + printed = (unsigned char*) hooks->reallocate(buffer->buffer, buffer->length); + buffer->buffer = NULL; + if (printed == NULL) { + goto fail; + } + } + else /* otherwise copy the JSON over to a new buffer */ + { + printed = (unsigned char*) hooks->allocate(buffer->offset + 1); + if (printed == NULL) + { + goto fail; + } + memcpy(printed, buffer->buffer, cjson_min(buffer->length, buffer->offset + 1)); + printed[buffer->offset] = '\0'; /* just to be sure */ + + /* free the buffer */ + hooks->deallocate(buffer->buffer); + } + + return printed; + +fail: + if (buffer->buffer != NULL) + { + hooks->deallocate(buffer->buffer); + } + + if (printed != NULL) + { + hooks->deallocate(printed); + } + + return NULL; +} /* Render a cJSON item/entity/structure to text. */ -char *cJSON_Print(cJSON *item) {return print_value(item,0,1,0);} -char *cJSON_PrintUnformatted(cJSON *item) {return print_value(item,0,0,0);} - -char *cJSON_PrintBuffered(cJSON *item,int prebuffer,int fmt) +CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item) { - printbuffer p; - p.buffer=(char*)cJSON_malloc(prebuffer); - p.length=prebuffer; - p.offset=0; - return print_value(item,0,fmt,&p); - return p.buffer; + return (char*)print(item, true, &global_hooks); } +CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item) +{ + return (char*)print(item, false, &global_hooks); +} + +CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt) +{ + printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; + + if (prebuffer < 0) + { + return NULL; + } + + p.buffer = (unsigned char*)global_hooks.allocate((size_t)prebuffer); + if (!p.buffer) + { + return NULL; + } + + p.length = (size_t)prebuffer; + p.offset = 0; + p.noalloc = false; + p.format = fmt; + p.hooks = global_hooks; + + if (!print_value(item, &p)) + { + global_hooks.deallocate(p.buffer); + return NULL; + } + + return (char*)p.buffer; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buf, const int len, const cJSON_bool fmt) +{ + printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; + + if ((len < 0) || (buf == NULL)) + { + return false; + } + + p.buffer = (unsigned char*)buf; + p.length = (size_t)len; + p.offset = 0; + p.noalloc = true; + p.format = fmt; + p.hooks = global_hooks; + + return print_value(item, &p); +} /* Parser core - when encountering text, process appropriately. */ -static const char *parse_value(cJSON *item,const char *value) +static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer) { - if (!value) return 0; /* Fail on null. */ - if (!strncmp(value,"null",4)) { item->type=cJSON_NULL; return value+4; } - if (!strncmp(value,"false",5)) { item->type=cJSON_False; return value+5; } - if (!strncmp(value,"true",4)) { item->type=cJSON_True; item->valueint=1; return value+4; } - if (*value=='\"') { return parse_string(item,value); } - if (*value=='-' || (*value>='0' && *value<='9')) { return parse_number(item,value); } - if (*value=='[') { return parse_array(item,value); } - if (*value=='{') { return parse_object(item,value); } + if ((input_buffer == NULL) || (input_buffer->content == NULL)) + { + return false; /* no input */ + } - ep=value;return 0; /* failure. */ + /* parse the different types of values */ + /* null */ + if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "null", 4) == 0)) + { + item->type = cJSON_NULL; + input_buffer->offset += 4; + return true; + } + /* false */ + if (can_read(input_buffer, 5) && (strncmp((const char*)buffer_at_offset(input_buffer), "false", 5) == 0)) + { + item->type = cJSON_False; + input_buffer->offset += 5; + return true; + } + /* true */ + if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "true", 4) == 0)) + { + item->type = cJSON_True; + item->valueint = 1; + input_buffer->offset += 4; + return true; + } + /* string */ + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '\"')) + { + return parse_string(item, input_buffer); + } + /* number */ + if (can_access_at_index(input_buffer, 0) && ((buffer_at_offset(input_buffer)[0] == '-') || ((buffer_at_offset(input_buffer)[0] >= '0') && (buffer_at_offset(input_buffer)[0] <= '9')))) + { + return parse_number(item, input_buffer); + } + /* array */ + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '[')) + { + return parse_array(item, input_buffer); + } + /* object */ + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '{')) + { + return parse_object(item, input_buffer); + } + + return false; } /* Render a value to text. */ -static char *print_value(cJSON *item,int depth,int fmt,printbuffer *p) +static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer) { - char *out=0; - if (!item) return 0; - if (p) - { - switch ((item->type)&255) - { - case cJSON_NULL: {out=ensure(p,5); if (out) strcpy(out,"null"); break;} - case cJSON_False: {out=ensure(p,6); if (out) strcpy(out,"false"); break;} - case cJSON_True: {out=ensure(p,5); if (out) strcpy(out,"true"); break;} - case cJSON_Number: out=print_number(item,p);break; - case cJSON_String: out=print_string(item,p);break; - case cJSON_Array: out=print_array(item,depth,fmt,p);break; - case cJSON_Object: out=print_object(item,depth,fmt,p);break; - } - } - else - { - switch ((item->type)&255) - { - case cJSON_NULL: out=cJSON_strdup("null"); break; - case cJSON_False: out=cJSON_strdup("false");break; - case cJSON_True: out=cJSON_strdup("true"); break; - case cJSON_Number: out=print_number(item,0);break; - case cJSON_String: out=print_string(item,0);break; - case cJSON_Array: out=print_array(item,depth,fmt,0);break; - case cJSON_Object: out=print_object(item,depth,fmt,0);break; - } - } - return out; + unsigned char *output = NULL; + + if ((item == NULL) || (output_buffer == NULL)) + { + return false; + } + + switch ((item->type) & 0xFF) + { + case cJSON_NULL: + output = ensure(output_buffer, 5); + if (output == NULL) + { + return false; + } + strcpy((char*)output, "null"); + return true; + + case cJSON_False: + output = ensure(output_buffer, 6); + if (output == NULL) + { + return false; + } + strcpy((char*)output, "false"); + return true; + + case cJSON_True: + output = ensure(output_buffer, 5); + if (output == NULL) + { + return false; + } + strcpy((char*)output, "true"); + return true; + + case cJSON_Number: + return print_number(item, output_buffer); + + case cJSON_Raw: + { + size_t raw_length = 0; + if (item->valuestring == NULL) + { + if (!output_buffer->noalloc) + { + output_buffer->hooks.deallocate(output_buffer->buffer); + } + return false; + } + + raw_length = strlen(item->valuestring) + sizeof(""); + output = ensure(output_buffer, raw_length); + if (output == NULL) + { + return false; + } + memcpy(output, item->valuestring, raw_length); + return true; + } + + case cJSON_String: + return print_string(item, output_buffer); + + case cJSON_Array: + return print_array(item, output_buffer); + + case cJSON_Object: + return print_object(item, output_buffer); + + default: + return false; + } } /* Build an array from input text. */ -static const char *parse_array(cJSON *item,const char *value) +static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer) { - cJSON *child; - if (*value!='[') {ep=value;return 0;} /* not an array! */ + cJSON *head = NULL; /* head of the linked list */ + cJSON *current_item = NULL; - item->type=cJSON_Array; - value=skip(value+1); - if (*value==']') return value+1; /* empty array. */ + if (input_buffer->depth >= CJSON_NESTING_LIMIT) + { + return false; /* to deeply nested */ + } + input_buffer->depth++; - item->child=child=cJSON_New_Item(); - if (!item->child) return 0; /* memory fail */ - value=skip(parse_value(child,skip(value))); /* skip any spacing, get the value. */ - if (!value) return 0; + if (buffer_at_offset(input_buffer)[0] != '[') + { + /* not an array */ + goto fail; + } - while (*value==',') - { - cJSON *new_item; - if (!(new_item=cJSON_New_Item())) return 0; /* memory fail */ - child->next=new_item;new_item->prev=child;child=new_item; - value=skip(parse_value(child,skip(value+1))); - if (!value) return 0; /* memory fail */ - } + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ']')) + { + /* empty array */ + goto success; + } - if (*value==']') return value+1; /* end of array */ - ep=value;return 0; /* malformed. */ + /* check if we skipped to the end of the buffer */ + if (cannot_access_at_index(input_buffer, 0)) + { + input_buffer->offset--; + goto fail; + } + + /* step back to character in front of the first element */ + input_buffer->offset--; + /* loop through the comma separated array elements */ + do + { + /* allocate next item */ + cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks)); + if (new_item == NULL) + { + goto fail; /* allocation failure */ + } + + /* attach next item to list */ + if (head == NULL) + { + /* start the linked list */ + current_item = head = new_item; + } + else + { + /* add to the end and advance */ + current_item->next = new_item; + new_item->prev = current_item; + current_item = new_item; + } + + /* parse next value */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (!parse_value(current_item, input_buffer)) + { + goto fail; /* failed to parse value */ + } + buffer_skip_whitespace(input_buffer); + } + while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ',')); + + if (cannot_access_at_index(input_buffer, 0) || buffer_at_offset(input_buffer)[0] != ']') + { + goto fail; /* expected end of array */ + } + +success: + input_buffer->depth--; + + item->type = cJSON_Array; + item->child = head; + + input_buffer->offset++; + + return true; + +fail: + if (head != NULL) + { + cJSON_Delete(head); + } + + return false; } /* Render an array to text */ -static char *print_array(cJSON *item,int depth,int fmt,printbuffer *p) +static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer) { - char **entries; - char *out=0,*ptr,*ret;int len=5; - cJSON *child=item->child; - int numentries=0,i=0,fail=0; - size_t tmplen=0; - - /* How many entries in the array? */ - while (child) numentries++,child=child->next; - /* Explicitly handle numentries==0 */ - if (!numentries) - { - if (p) out=ensure(p,3); - else out=(char*)cJSON_malloc(3); - if (out) strcpy(out,"[]"); - return out; - } + unsigned char *output_pointer = NULL; + size_t length = 0; + cJSON *current_element = item->child; - if (p) - { - /* Compose the output array. */ - i=p->offset; - ptr=ensure(p,1);if (!ptr) return 0; *ptr='['; p->offset++; - child=item->child; - while (child && !fail) - { - print_value(child,depth+1,fmt,p); - p->offset=update(p); - if (child->next) {len=fmt?2:1;ptr=ensure(p,len+1);if (!ptr) return 0;*ptr++=',';if(fmt)*ptr++=' ';*ptr=0;p->offset+=len;} - child=child->next; - } - ptr=ensure(p,2);if (!ptr) return 0; *ptr++=']';*ptr=0; - out=(p->buffer)+i; - } - else - { - /* Allocate an array to hold the values for each */ - entries=(char**)cJSON_malloc(numentries*sizeof(char*)); - if (!entries) return 0; - memset(entries,0,numentries*sizeof(char*)); - /* Retrieve all the results: */ - child=item->child; - while (child && !fail) - { - ret=print_value(child,depth+1,fmt,0); - entries[i++]=ret; - if (ret) len+=strlen(ret)+2+(fmt?1:0); else fail=1; - child=child->next; - } - - /* If we didn't fail, try to malloc the output string */ - if (!fail) out=(char*)cJSON_malloc(len); - /* If that fails, we fail. */ - if (!out) fail=1; + if (output_buffer == NULL) + { + return false; + } - /* Handle failure. */ - if (fail) - { - for (i=0;ioffset++; + output_buffer->depth++; + + while (current_element != NULL) + { + if (!print_value(current_element, output_buffer)) + { + return false; + } + update_offset(output_buffer); + if (current_element->next) + { + length = (size_t) (output_buffer->format ? 2 : 1); + output_pointer = ensure(output_buffer, length + 1); + if (output_pointer == NULL) + { + return false; + } + *output_pointer++ = ','; + if(output_buffer->format) + { + *output_pointer++ = ' '; + } + *output_pointer = '\0'; + output_buffer->offset += length; + } + current_element = current_element->next; + } + + output_pointer = ensure(output_buffer, 2); + if (output_pointer == NULL) + { + return false; + } + *output_pointer++ = ']'; + *output_pointer = '\0'; + output_buffer->depth--; + + return true; } /* Build an object from the text. */ -static const char *parse_object(cJSON *item,const char *value) +static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer) { - cJSON *child; - if (*value!='{') {ep=value;return 0;} /* not an object! */ - - item->type=cJSON_Object; - value=skip(value+1); - if (*value=='}') return value+1; /* empty array. */ - - item->child=child=cJSON_New_Item(); - if (!item->child) return 0; - value=skip(parse_string(child,skip(value))); - if (!value) return 0; - child->string=child->valuestring;child->valuestring=0; - if (*value!=':') {ep=value;return 0;} /* fail! */ - value=skip(parse_value(child,skip(value+1))); /* skip any spacing, get the value. */ - if (!value) return 0; - - while (*value==',') - { - cJSON *new_item; - if (!(new_item=cJSON_New_Item())) return 0; /* memory fail */ - child->next=new_item;new_item->prev=child;child=new_item; - value=skip(parse_string(child,skip(value+1))); - if (!value) return 0; - child->string=child->valuestring;child->valuestring=0; - if (*value!=':') {ep=value;return 0;} /* fail! */ - value=skip(parse_value(child,skip(value+1))); /* skip any spacing, get the value. */ - if (!value) return 0; - } - - if (*value=='}') return value+1; /* end of array */ - ep=value;return 0; /* malformed. */ + cJSON *head = NULL; /* linked list head */ + cJSON *current_item = NULL; + + if (input_buffer->depth >= CJSON_NESTING_LIMIT) + { + return false; /* to deeply nested */ + } + input_buffer->depth++; + + if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '{')) + { + goto fail; /* not an object */ + } + + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '}')) + { + goto success; /* empty object */ + } + + /* check if we skipped to the end of the buffer */ + if (cannot_access_at_index(input_buffer, 0)) + { + input_buffer->offset--; + goto fail; + } + + /* step back to character in front of the first element */ + input_buffer->offset--; + /* loop through the comma separated array elements */ + do + { + /* allocate next item */ + cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks)); + if (new_item == NULL) + { + goto fail; /* allocation failure */ + } + + /* attach next item to list */ + if (head == NULL) + { + /* start the linked list */ + current_item = head = new_item; + } + else + { + /* add to the end and advance */ + current_item->next = new_item; + new_item->prev = current_item; + current_item = new_item; + } + + /* parse the name of the child */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (!parse_string(current_item, input_buffer)) + { + goto fail; /* faile to parse name */ + } + buffer_skip_whitespace(input_buffer); + + /* swap valuestring and string, because we parsed the name */ + current_item->string = current_item->valuestring; + current_item->valuestring = NULL; + + if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != ':')) + { + goto fail; /* invalid object */ + } + + /* parse the value */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (!parse_value(current_item, input_buffer)) + { + goto fail; /* failed to parse value */ + } + buffer_skip_whitespace(input_buffer); + } + while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ',')); + + if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '}')) + { + goto fail; /* expected end of object */ + } + +success: + input_buffer->depth--; + + item->type = cJSON_Object; + item->child = head; + + input_buffer->offset++; + return true; + +fail: + if (head != NULL) + { + cJSON_Delete(head); + } + + return false; } /* Render an object to text. */ -static char *print_object(cJSON *item,int depth,int fmt,printbuffer *p) +static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer) { - char **entries=0,**names=0; - char *out=0,*ptr,*ret,*str;int len=7,i=0,j; - cJSON *child=item->child; - int numentries=0,fail=0; - size_t tmplen=0; - /* Count the number of entries. */ - while (child) numentries++,child=child->next; - /* Explicitly handle empty object case */ - if (!numentries) - { - if (p) out=ensure(p,fmt?depth+4:3); - else out=(char*)cJSON_malloc(fmt?depth+4:3); - if (!out) return 0; - ptr=out;*ptr++='{'; - if (fmt) {*ptr++='\n';for (i=0;ioffset; - len=fmt?2:1; ptr=ensure(p,len+1); if (!ptr) return 0; - *ptr++='{'; if (fmt) *ptr++='\n'; *ptr=0; p->offset+=len; - child=item->child;depth++; - while (child) - { - if (fmt) - { - ptr=ensure(p,depth); if (!ptr) return 0; - for (j=0;joffset+=depth; - } - print_string_ptr(child->string,p); - p->offset=update(p); - - len=fmt?2:1; - ptr=ensure(p,len); if (!ptr) return 0; - *ptr++=':';if (fmt) *ptr++='\t'; - p->offset+=len; - - print_value(child,depth,fmt,p); - p->offset=update(p); + unsigned char *output_pointer = NULL; + size_t length = 0; + cJSON *current_item = item->child; - len=(fmt?1:0)+(child->next?1:0); - ptr=ensure(p,len+1); if (!ptr) return 0; - if (child->next) *ptr++=','; - if (fmt) *ptr++='\n';*ptr=0; - p->offset+=len; - child=child->next; - } - ptr=ensure(p,fmt?(depth+1):2); if (!ptr) return 0; - if (fmt) for (i=0;ibuffer)+i; - } - else - { - /* Allocate space for the names and the objects */ - entries=(char**)cJSON_malloc(numentries*sizeof(char*)); - if (!entries) return 0; - names=(char**)cJSON_malloc(numentries*sizeof(char*)); - if (!names) {cJSON_free(entries);return 0;} - memset(entries,0,sizeof(char*)*numentries); - memset(names,0,sizeof(char*)*numentries); + if (output_buffer == NULL) + { + return false; + } - /* Collect all the results into our arrays: */ - child=item->child;depth++;if (fmt) len+=depth; - while (child) - { - names[i]=str=print_string_ptr(child->string,0); - entries[i++]=ret=print_value(child,depth,fmt,0); - if (str && ret) len+=strlen(ret)+strlen(str)+2+(fmt?2+depth:0); else fail=1; - child=child->next; - } - - /* Try to allocate the output string */ - if (!fail) out=(char*)cJSON_malloc(len); - if (!out) fail=1; + /* Compose the output: */ + length = (size_t) (output_buffer->format ? 2 : 1); /* fmt: {\n */ + output_pointer = ensure(output_buffer, length + 1); + if (output_pointer == NULL) + { + return false; + } - /* Handle failure */ - if (fail) - { - for (i=0;idepth++; + if (output_buffer->format) + { + *output_pointer++ = '\n'; + } + output_buffer->offset += length; + + while (current_item) + { + if (output_buffer->format) + { + size_t i; + output_pointer = ensure(output_buffer, output_buffer->depth); + if (output_pointer == NULL) + { + return false; + } + for (i = 0; i < output_buffer->depth; i++) + { + *output_pointer++ = '\t'; + } + output_buffer->offset += output_buffer->depth; + } + + /* print key */ + if (!print_string_ptr((unsigned char*)current_item->string, output_buffer)) + { + return false; + } + update_offset(output_buffer); + + length = (size_t) (output_buffer->format ? 2 : 1); + output_pointer = ensure(output_buffer, length); + if (output_pointer == NULL) + { + return false; + } + *output_pointer++ = ':'; + if (output_buffer->format) + { + *output_pointer++ = '\t'; + } + output_buffer->offset += length; + + /* print value */ + if (!print_value(current_item, output_buffer)) + { + return false; + } + update_offset(output_buffer); + + /* print comma if not last */ + length = (size_t) ((output_buffer->format ? 1 : 0) + (current_item->next ? 1 : 0)); + output_pointer = ensure(output_buffer, length + 1); + if (output_pointer == NULL) + { + return false; + } + if (current_item->next) + { + *output_pointer++ = ','; + } + + if (output_buffer->format) + { + *output_pointer++ = '\n'; + } + *output_pointer = '\0'; + output_buffer->offset += length; + + current_item = current_item->next; + } + + output_pointer = ensure(output_buffer, output_buffer->format ? (output_buffer->depth + 1) : 2); + if (output_pointer == NULL) + { + return false; + } + if (output_buffer->format) + { + size_t i; + for (i = 0; i < (output_buffer->depth - 1); i++) + { + *output_pointer++ = '\t'; + } + } + *output_pointer++ = '}'; + *output_pointer = '\0'; + output_buffer->depth--; + + return true; } /* Get Array size/item / object item. */ -int cJSON_GetArraySize(cJSON *array) {cJSON *c=array->child;int i=0;while(c)i++,c=c->next;return i;} -cJSON *cJSON_GetArrayItem(cJSON *array,int item) {cJSON *c=array->child; while (c && item>0) item--,c=c->next; return c;} -cJSON *cJSON_GetObjectItem(cJSON *object,const char *string) {cJSON *c=object->child; while (c && cJSON_strcasecmp(c->string,string)) c=c->next; return c;} +CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array) +{ + cJSON *child = NULL; + size_t size = 0; + + if (array == NULL) + { + return 0; + } + + child = array->child; + + while(child != NULL) + { + size++; + child = child->next; + } + + /* FIXME: Can overflow here. Cannot be fixed without breaking the API */ + + return (int)size; +} + +static cJSON* get_array_item(const cJSON *array, size_t index) +{ + cJSON *current_child = NULL; + + if (array == NULL) + { + return NULL; + } + + current_child = array->child; + while ((current_child != NULL) && (index > 0)) + { + index--; + current_child = current_child->next; + } + + return current_child; +} + +CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index) +{ + if (index < 0) + { + return NULL; + } + + return get_array_item(array, (size_t)index); +} + +static cJSON *get_object_item(const cJSON * const object, const char * const name, const cJSON_bool case_sensitive) +{ + cJSON *current_element = NULL; + + if ((object == NULL) || (name == NULL)) + { + return NULL; + } + + current_element = object->child; + if (case_sensitive) + { + while ((current_element != NULL) && (strcmp(name, current_element->string) != 0)) + { + current_element = current_element->next; + } + } + else + { + while ((current_element != NULL) && (case_insensitive_strcmp((const unsigned char*)name, (const unsigned char*)(current_element->string)) != 0)) + { + current_element = current_element->next; + } + } + + return current_element; +} + +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string) +{ + return get_object_item(object, string, false); +} + +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string) +{ + return get_object_item(object, string, true); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string) +{ + return cJSON_GetObjectItem(object, string) ? 1 : 0; +} /* Utility for array list handling. */ -static void suffix_object(cJSON *prev,cJSON *item) {prev->next=item;item->prev=prev;} +static void suffix_object(cJSON *prev, cJSON *item) +{ + prev->next = item; + item->prev = prev; +} + /* Utility for handling references. */ -static cJSON *create_reference(cJSON *item) {cJSON *ref=cJSON_New_Item();if (!ref) return 0;memcpy(ref,item,sizeof(cJSON));ref->string=0;ref->type|=cJSON_IsReference;ref->next=ref->prev=0;return ref;} +static cJSON *create_reference(const cJSON *item, const internal_hooks * const hooks) +{ + cJSON *reference = NULL; + if (item == NULL) + { + return NULL; + } + + reference = cJSON_New_Item(hooks); + if (reference == NULL) + { + return NULL; + } + + memcpy(reference, item, sizeof(cJSON)); + reference->string = NULL; + reference->type |= cJSON_IsReference; + reference->next = reference->prev = NULL; + return reference; +} /* Add item to array/object. */ -void cJSON_AddItemToArray(cJSON *array, cJSON *item) {cJSON *c=array->child;if (!item) return; if (!c) {array->child=item;} else {while (c && c->next) c=c->next; suffix_object(c,item);}} -void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item) {if (!item) return; if (item->string) cJSON_free(item->string);item->string=cJSON_strdup(string);cJSON_AddItemToArray(object,item);} -void cJSON_AddItemToObjectCS(cJSON *object,const char *string,cJSON *item) {if (!item) return; if (!(item->type&cJSON_StringIsConst) && item->string) cJSON_free(item->string);item->string=(char*)string;item->type|=cJSON_StringIsConst;cJSON_AddItemToArray(object,item);} -void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) {cJSON_AddItemToArray(array,create_reference(item));} -void cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item) {cJSON_AddItemToObject(object,string,create_reference(item));} +CJSON_PUBLIC(void) cJSON_AddItemToArray(cJSON *array, cJSON *item) +{ + cJSON *child = NULL; -cJSON *cJSON_DetachItemFromArray(cJSON *array,int which) {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) return 0; - if (c->prev) c->prev->next=c->next;if (c->next) c->next->prev=c->prev;if (c==array->child) array->child=c->next;c->prev=c->next=0;return c;} -void cJSON_DeleteItemFromArray(cJSON *array,int which) {cJSON_Delete(cJSON_DetachItemFromArray(array,which));} -cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string) {int i=0;cJSON *c=object->child;while (c && cJSON_strcasecmp(c->string,string)) i++,c=c->next;if (c) return cJSON_DetachItemFromArray(object,i);return 0;} -void cJSON_DeleteItemFromObject(cJSON *object,const char *string) {cJSON_Delete(cJSON_DetachItemFromObject(object,string));} + if ((item == NULL) || (array == NULL)) + { + return; + } + + child = array->child; + + if (child == NULL) + { + /* list is empty, start new one */ + array->child = item; + } + else + { + /* append to the end */ + while (child->next) + { + child = child->next; + } + suffix_object(child, item); + } +} + +CJSON_PUBLIC(void) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item) +{ + if (item == NULL) + { + return; + } + + /* call cJSON_AddItemToObjectCS for code reuse */ + cJSON_AddItemToObjectCS(object, (char*)cJSON_strdup((const unsigned char*)string, &global_hooks), item); + /* remove cJSON_StringIsConst flag */ + item->type &= ~cJSON_StringIsConst; +} + +#if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) + #pragma GCC diagnostic push +#endif +#ifdef __GNUC__ +#pragma GCC diagnostic ignored "-Wcast-qual" +#endif + +/* Add an item to an object with constant string as key */ +CJSON_PUBLIC(void) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item) +{ + if ((item == NULL) || (string == NULL)) + { + return; + } + if (!(item->type & cJSON_StringIsConst) && item->string) + { + global_hooks.deallocate(item->string); + } + item->string = (char*)string; + item->type |= cJSON_StringIsConst; + cJSON_AddItemToArray(object, item); +} +#if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) + #pragma GCC diagnostic pop +#endif + +CJSON_PUBLIC(void) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) +{ + if (array == NULL) + { + return; + } + + cJSON_AddItemToArray(array, create_reference(item, &global_hooks)); +} + +CJSON_PUBLIC(void) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item) +{ + if ((object == NULL) || (string == NULL)) + { + return; + } + + cJSON_AddItemToObject(object, string, create_reference(item, &global_hooks)); +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item) +{ + if ((parent == NULL) || (item == NULL)) + { + return NULL; + } + + if (item->prev != NULL) + { + /* not the first element */ + item->prev->next = item->next; + } + if (item->next != NULL) + { + /* not the last element */ + item->next->prev = item->prev; + } + + if (item == parent->child) + { + /* first element */ + parent->child = item->next; + } + /* make sure the detached item doesn't point anywhere anymore */ + item->prev = NULL; + item->next = NULL; + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which) +{ + if (which < 0) + { + return NULL; + } + + return cJSON_DetachItemViaPointer(array, get_array_item(array, (size_t)which)); +} + +CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which) +{ + cJSON_Delete(cJSON_DetachItemFromArray(array, which)); +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string) +{ + cJSON *to_detach = cJSON_GetObjectItem(object, string); + + return cJSON_DetachItemViaPointer(object, to_detach); +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string) +{ + cJSON *to_detach = cJSON_GetObjectItemCaseSensitive(object, string); + + return cJSON_DetachItemViaPointer(object, to_detach); +} + +CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string) +{ + cJSON_Delete(cJSON_DetachItemFromObject(object, string)); +} + +CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string) +{ + cJSON_Delete(cJSON_DetachItemFromObjectCaseSensitive(object, string)); +} /* Replace array/object items with new ones. */ -void cJSON_InsertItemInArray(cJSON *array,int which,cJSON *newitem) {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) {cJSON_AddItemToArray(array,newitem);return;} - newitem->next=c;newitem->prev=c->prev;c->prev=newitem;if (c==array->child) array->child=newitem; else newitem->prev->next=newitem;} -void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem) {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) return; - newitem->next=c->next;newitem->prev=c->prev;if (newitem->next) newitem->next->prev=newitem; - if (c==array->child) array->child=newitem; else newitem->prev->next=newitem;c->next=c->prev=0;cJSON_Delete(c);} -void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem){int i=0;cJSON *c=object->child;while(c && cJSON_strcasecmp(c->string,string))i++,c=c->next;if(c){newitem->string=cJSON_strdup(string);cJSON_ReplaceItemInArray(object,i,newitem);}} +CJSON_PUBLIC(void) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem) +{ + cJSON *after_inserted = NULL; + + if (which < 0) + { + return; + } + + after_inserted = get_array_item(array, (size_t)which); + if (after_inserted == NULL) + { + cJSON_AddItemToArray(array, newitem); + return; + } + + newitem->next = after_inserted; + newitem->prev = after_inserted->prev; + after_inserted->prev = newitem; + if (after_inserted == array->child) + { + array->child = newitem; + } + else + { + newitem->prev->next = newitem; + } +} + +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement) +{ + if ((parent == NULL) || (replacement == NULL) || (item == NULL)) + { + return false; + } + + if (replacement == item) + { + return true; + } + + replacement->next = item->next; + replacement->prev = item->prev; + + if (replacement->next != NULL) + { + replacement->next->prev = replacement; + } + if (replacement->prev != NULL) + { + replacement->prev->next = replacement; + } + if (parent->child == item) + { + parent->child = replacement; + } + + item->next = NULL; + item->prev = NULL; + cJSON_Delete(item); + + return true; +} + +CJSON_PUBLIC(void) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem) +{ + if (which < 0) + { + return; + } + + cJSON_ReplaceItemViaPointer(array, get_array_item(array, (size_t)which), newitem); +} + +static cJSON_bool replace_item_in_object(cJSON *object, const char *string, cJSON *replacement, cJSON_bool case_sensitive) +{ + if ((replacement == NULL) || (string == NULL)) + { + return false; + } + + /* replace the name in the replacement */ + if (!(replacement->type & cJSON_StringIsConst) && (replacement->string != NULL)) + { + cJSON_free(replacement->string); + } + replacement->string = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); + replacement->type &= ~cJSON_StringIsConst; + + cJSON_ReplaceItemViaPointer(object, get_object_item(object, string, case_sensitive), replacement); + + return true; +} + +CJSON_PUBLIC(void) cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem) +{ + replace_item_in_object(object, string, newitem, false); +} + +CJSON_PUBLIC(void) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object, const char *string, cJSON *newitem) +{ + replace_item_in_object(object, string, newitem, true); +} /* Create basic types: */ -cJSON *cJSON_CreateNull(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_NULL;return item;} -cJSON *cJSON_CreateTrue(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_True;return item;} -cJSON *cJSON_CreateFalse(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_False;return item;} -cJSON *cJSON_CreateBool(int b) {cJSON *item=cJSON_New_Item();if(item)item->type=b?cJSON_True:cJSON_False;return item;} -cJSON *cJSON_CreateNumber(double num) {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_Number;item->valuedouble=num;item->valueint=(int)num;}return item;} -cJSON *cJSON_CreateDouble(double num,int i_num) {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_Number;item->valuedouble=num;item->valueint=i_num;}return item;} -cJSON *cJSON_CreateString(const char *string) {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_String;item->valuestring=cJSON_strdup(string);}return item;} -cJSON *cJSON_CreateArray(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Array;return item;} -cJSON *cJSON_CreateObject(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Object;return item;} +CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_NULL; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_True; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_False; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool b) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = b ? cJSON_True : cJSON_False; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_Number; + item->valuedouble = num; + + /* use saturation in case of overflow */ + if (num >= INT_MAX) + { + item->valueint = INT_MAX; + } + else if (num <= INT_MIN) + { + item->valueint = INT_MIN; + } + else + { + item->valueint = (int)num; + } + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_String; + item->valuestring = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); + if(!item->valuestring) + { + cJSON_Delete(item); + return NULL; + } + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_Raw; + item->valuestring = (char*)cJSON_strdup((const unsigned char*)raw, &global_hooks); + if(!item->valuestring) + { + cJSON_Delete(item); + return NULL; + } + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type=cJSON_Array; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if (item) + { + item->type = cJSON_Object; + } + + return item; +} /* Create Arrays: */ -cJSON *cJSON_CreateIntArray(const int *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && ichild=n;else suffix_object(p,n);p=n;}return a;} -cJSON *cJSON_CreateFloatArray(const float *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && ichild=n;else suffix_object(p,n);p=n;}return a;} -cJSON *cJSON_CreateDoubleArray(const double *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && ichild=n;else suffix_object(p,n);p=n;}return a;} -cJSON *cJSON_CreateStringArray(const char **strings,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && ichild=n;else suffix_object(p,n);p=n;}return a;} +CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (numbers == NULL)) + { + return NULL; + } + + a = cJSON_CreateArray(); + for(i = 0; a && (i < (size_t)count); i++) + { + n = cJSON_CreateNumber(numbers[i]); + if (!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p, n); + } + p = n; + } + + return a; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (numbers == NULL)) + { + return NULL; + } + + a = cJSON_CreateArray(); + + for(i = 0; a && (i < (size_t)count); i++) + { + n = cJSON_CreateNumber((double)numbers[i]); + if(!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p, n); + } + p = n; + } + + return a; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (numbers == NULL)) + { + return NULL; + } + + a = cJSON_CreateArray(); + + for(i = 0;a && (i < (size_t)count); i++) + { + n = cJSON_CreateNumber(numbers[i]); + if(!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p, n); + } + p = n; + } + + return a; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char **strings, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (strings == NULL)) + { + return NULL; + } + + a = cJSON_CreateArray(); + + for (i = 0; a && (i < (size_t)count); i++) + { + n = cJSON_CreateString(strings[i]); + if(!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p,n); + } + p = n; + } + + return a; +} /* Duplication */ -cJSON *cJSON_Duplicate(cJSON *item,int recurse) +CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse) { - cJSON *newitem,*cptr,*nptr=0,*newchild; - /* Bail on bad ptr */ - if (!item) return 0; - /* Create new item */ - newitem=cJSON_New_Item(); - if (!newitem) return 0; - /* Copy over all vars */ - newitem->type=item->type&(~cJSON_IsReference),newitem->valueint=item->valueint,newitem->valuedouble=item->valuedouble; - if (item->valuestring) {newitem->valuestring=cJSON_strdup(item->valuestring); if (!newitem->valuestring) {cJSON_Delete(newitem);return 0;}} - if (item->string) {newitem->string=cJSON_strdup(item->string); if (!newitem->string) {cJSON_Delete(newitem);return 0;}} - /* If non-recursive, then we're done! */ - if (!recurse) return newitem; - /* Walk the ->next chain for the child. */ - cptr=item->child; - while (cptr) - { - newchild=cJSON_Duplicate(cptr,1); /* Duplicate (with recurse) each item in the ->next chain */ - if (!newchild) {cJSON_Delete(newitem);return 0;} - if (nptr) {nptr->next=newchild,newchild->prev=nptr;nptr=newchild;} /* If newitem->child already set, then crosswire ->prev and ->next and move on */ - else {newitem->child=newchild;nptr=newchild;} /* Set newitem->child and move to it */ - cptr=cptr->next; - } - return newitem; + cJSON *newitem = NULL; + cJSON *child = NULL; + cJSON *next = NULL; + cJSON *newchild = NULL; + + /* Bail on bad ptr */ + if (!item) + { + goto fail; + } + /* Create new item */ + newitem = cJSON_New_Item(&global_hooks); + if (!newitem) + { + goto fail; + } + /* Copy over all vars */ + newitem->type = item->type & (~cJSON_IsReference); + newitem->valueint = item->valueint; + newitem->valuedouble = item->valuedouble; + if (item->valuestring) + { + newitem->valuestring = (char*)cJSON_strdup((unsigned char*)item->valuestring, &global_hooks); + if (!newitem->valuestring) + { + goto fail; + } + } + if (item->string) + { + newitem->string = (item->type&cJSON_StringIsConst) ? item->string : (char*)cJSON_strdup((unsigned char*)item->string, &global_hooks); + if (!newitem->string) + { + goto fail; + } + } + /* If non-recursive, then we're done! */ + if (!recurse) + { + return newitem; + } + /* Walk the ->next chain for the child. */ + child = item->child; + while (child != NULL) + { + newchild = cJSON_Duplicate(child, true); /* Duplicate (with recurse) each item in the ->next chain */ + if (!newchild) + { + goto fail; + } + if (next != NULL) + { + /* If newitem->child already set, then crosswire ->prev and ->next and move on */ + next->next = newchild; + newchild->prev = next; + next = newchild; + } + else + { + /* Set newitem->child and move to it */ + newitem->child = newchild; + next = newchild; + } + child = child->next; + } + + return newitem; + +fail: + if (newitem != NULL) + { + cJSON_Delete(newitem); + } + + return NULL; } -void cJSON_Minify(char *json) +CJSON_PUBLIC(void) cJSON_Minify(char *json) { - char *into=json; - while (*json) - { - if (*json==' ') json++; - else if (*json=='\t') json++; /* Whitespace characters. */ - else if (*json=='\r') json++; - else if (*json=='\n') json++; - else if (*json=='/' && json[1]=='/') while (*json && *json!='\n') json++; /* double-slash comments, to end of line. */ - else if (*json=='/' && json[1]=='*') {while (*json && !(*json=='*' && json[1]=='/')) json++;json+=2;} /* multiline comments. */ - else if (*json=='\"'){*into++=*json++;while (*json && *json!='\"'){if (*json=='\\') *into++=*json++;*into++=*json++;}*into++=*json++;} /* string literals, which are \" sensitive. */ - else *into++=*json++; /* All other characters. */ - } - *into=0; /* and null-terminate. */ + unsigned char *into = (unsigned char*)json; + + if (json == NULL) + { + return; + } + + while (*json) + { + if (*json == ' ') + { + json++; + } + else if (*json == '\t') + { + /* Whitespace characters. */ + json++; + } + else if (*json == '\r') + { + json++; + } + else if (*json=='\n') + { + json++; + } + else if ((*json == '/') && (json[1] == '/')) + { + /* double-slash comments, to end of line. */ + while (*json && (*json != '\n')) + { + json++; + } + } + else if ((*json == '/') && (json[1] == '*')) + { + /* multiline comments. */ + while (*json && !((*json == '*') && (json[1] == '/'))) + { + json++; + } + json += 2; + } + else if (*json == '\"') + { + /* string literals, which are \" sensitive. */ + *into++ = (unsigned char)*json++; + while (*json && (*json != '\"')) + { + if (*json == '\\') + { + *into++ = (unsigned char)*json++; + } + *into++ = (unsigned char)*json++; + } + *into++ = (unsigned char)*json++; + } + else + { + /* All other characters. */ + *into++ = (unsigned char)*json++; + } + } + + /* and null-terminate. */ + *into = '\0'; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Invalid; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_False; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xff) == cJSON_True; +} + + +CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & (cJSON_True | cJSON_False)) != 0; +} +CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_NULL; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Number; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_String; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Array; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Object; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Raw; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive) +{ + if ((a == NULL) || (b == NULL) || ((a->type & 0xFF) != (b->type & 0xFF)) || cJSON_IsInvalid(a)) + { + return false; + } + + /* check if type is valid */ + switch (a->type & 0xFF) + { + case cJSON_False: + case cJSON_True: + case cJSON_NULL: + case cJSON_Number: + case cJSON_String: + case cJSON_Raw: + case cJSON_Array: + case cJSON_Object: + break; + + default: + return false; + } + + /* identical objects are equal */ + if (a == b) + { + return true; + } + + switch (a->type & 0xFF) + { + /* in these cases and equal type is enough */ + case cJSON_False: + case cJSON_True: + case cJSON_NULL: + return true; + + case cJSON_Number: + if (a->valuedouble == b->valuedouble) + { + return true; + } + return false; + + case cJSON_String: + case cJSON_Raw: + if ((a->valuestring == NULL) || (b->valuestring == NULL)) + { + return false; + } + if (strcmp(a->valuestring, b->valuestring) == 0) + { + return true; + } + + return false; + + case cJSON_Array: + { + cJSON *a_element = a->child; + cJSON *b_element = b->child; + + for (; (a_element != NULL) && (b_element != NULL);) + { + if (!cJSON_Compare(a_element, b_element, case_sensitive)) + { + return false; + } + + a_element = a_element->next; + b_element = b_element->next; + } + + /* one of the arrays is longer than the other */ + if (a_element != b_element) { + return false; + } + + return true; + } + + case cJSON_Object: + { + cJSON *a_element = NULL; + cJSON *b_element = NULL; + cJSON_ArrayForEach(a_element, a) + { + /* TODO This has O(n^2) runtime, which is horrible! */ + b_element = get_object_item(b, a_element->string, case_sensitive); + if (b_element == NULL) + { + return false; + } + + if (!cJSON_Compare(a_element, b_element, case_sensitive)) + { + return false; + } + } + + /* doing this twice, once on a and b to prevent true comparison if a subset of b + * TODO: Do this the proper way, this is just a fix for now */ + cJSON_ArrayForEach(b_element, b) + { + a_element = get_object_item(a, b_element->string, case_sensitive); + if (a_element == NULL) + { + return false; + } + + if (!cJSON_Compare(b_element, a_element, case_sensitive)) + { + return false; + } + } + + return true; + } + + default: + return false; + } +} + +CJSON_PUBLIC(void *) cJSON_malloc(size_t size) +{ + return global_hooks.allocate(size); +} + +CJSON_PUBLIC(void) cJSON_free(void *object) +{ + global_hooks.deallocate(object); } diff --git a/components/json/port/cJSON_Utils.c b/components/json/port/cJSON_Utils.c index dd65c7c60c..b83cfcd4ab 100644 --- a/components/json/port/cJSON_Utils.c +++ b/components/json/port/cJSON_Utils.c @@ -1,395 +1,1443 @@ +/* + 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. +*/ + +/* disable warnings about old C89 functions in MSVC */ +#if !defined(_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER) +#define _CRT_SECURE_NO_DEPRECATE +#endif + +#ifdef __GNUCC__ +#pragma GCC visibility push(default) +#endif +#if defined(_MSC_VER) +#pragma warning (push) +/* disable warning about single line comments in system headers */ +#pragma warning (disable : 4001) +#endif + #include #include #include #include +#include + +#if defined(_MSC_VER) +#pragma warning (pop) +#endif +#ifdef __GNUCC__ +#pragma GCC visibility pop +#endif + #include "cJSON_Utils.h" -static int cJSONUtils_strcasecmp(const char *s1,const char *s2) +/* define our own boolean type */ +#define true ((cJSON_bool)1) +#define false ((cJSON_bool)0) + +static unsigned char* cJSONUtils_strdup(const unsigned char* const string) { - if (!s1) return (s1==s2)?0:1;if (!s2) return 1; - for(; tolower(*(const unsigned char *)s1) == tolower(*(const unsigned char *)s2); ++s1, ++s2) if(*s1 == 0) return 0; - return tolower(*(const unsigned char *)s1) - tolower(*(const unsigned char *)s2); + size_t length = 0; + unsigned char *copy = NULL; + + length = strlen((const char*)string) + sizeof(""); + copy = (unsigned char*) cJSON_malloc(length); + if (copy == NULL) + { + return NULL; + } + memcpy(copy, string, length); + + return copy; } -/* JSON Pointer implementation: */ -static int cJSONUtils_Pstrcasecmp(const char *a,const char *e) +/* string comparison which doesn't consider NULL pointers equal */ +static int compare_strings(const unsigned char *string1, const unsigned char *string2, const cJSON_bool case_sensitive) { - if (!a || !e) return (a==e)?0:1; - for (;*a && *e && *e!='/';a++,e++) { - if (*e=='~') {if (!(e[1]=='0' && *a=='~') && !(e[1]=='1' && *a=='/')) return 1; else e++;} - else if (tolower(*(const unsigned char *)a)!=tolower(*(const unsigned char *)e)) return 1; - } - if ((*e!=0 && *e!='/') != (*a!=0)) return 1; - return 0; + if ((string1 == NULL) || (string2 == NULL)) + { + return 1; + } + + if (string1 == string2) + { + return 0; + } + + if (case_sensitive) + { + return strcmp((const char*)string1, (const char*)string2); + } + + for(; tolower(*string1) == tolower(*string2); (void)string1++, string2++) + { + if (*string1 == '\0') + { + return 0; + } + } + + return tolower(*string1) - tolower(*string2); } -static int cJSONUtils_PointerEncodedstrlen(const char *s) {int l=0;for (;*s;s++,l++) if (*s=='~' || *s=='/') l++;return l;} - -static void cJSONUtils_PointerEncodedstrcpy(char *d,const char *s) +/* Compare the next path element of two JSON pointers, two NULL pointers are considered unequal: */ +static cJSON_bool compare_pointers(const unsigned char *name, const unsigned char *pointer, const cJSON_bool case_sensitive) { - for (;*s;s++) - { - if (*s=='/') {*d++='~';*d++='1';} - else if (*s=='~') {*d++='~';*d++='0';} - else *d++=*s; - } - *d=0; + if ((name == NULL) || (pointer == NULL)) + { + return false; + } + + for (; (*name != '\0') && (*pointer != '\0') && (*pointer != '/'); (void)name++, pointer++) /* compare until next '/' */ + { + if (*pointer == '~') + { + /* check for escaped '~' (~0) and '/' (~1) */ + if (((pointer[1] != '0') || (*name != '~')) && ((pointer[1] != '1') || (*name != '/'))) + { + /* invalid escape sequence or wrong character in *name */ + return false; + } + else + { + pointer++; + } + } + else if ((!case_sensitive && (tolower(*name) != tolower(*pointer))) || (case_sensitive && (*name != *pointer))) + { + return false; + } + } + if (((*pointer != 0) && (*pointer != '/')) != (*name != 0)) + { + /* one string has ended, the other not */ + return false;; + } + + return true; } -char *cJSONUtils_FindPointerFromObjectTo(cJSON *object,cJSON *target) +/* calculate the length of a string if encoded as JSON pointer with ~0 and ~1 escape sequences */ +static size_t pointer_encoded_length(const unsigned char *string) { - int type=object->type,c=0;cJSON *obj=0; + size_t length; + for (length = 0; *string != '\0'; (void)string++, length++) + { + /* character needs to be escaped? */ + if ((*string == '~') || (*string == '/')) + { + length++; + } + } - if (object==target) return strdup(""); - - for (obj=object->child;obj;obj=obj->next,c++) - { - char *found=cJSONUtils_FindPointerFromObjectTo(obj,target); - if (found) - { - if (type==cJSON_Array) - { - char *ret=(char*)malloc(strlen(found)+23); - sprintf(ret,"/%d%s",c,found); - free(found); - return ret; - } - else if (type==cJSON_Object) - { - char *ret=(char*)malloc(strlen(found)+cJSONUtils_PointerEncodedstrlen(obj->string)+2); - *ret='/';cJSONUtils_PointerEncodedstrcpy(ret+1,obj->string); - strcat(ret,found); - free(found); - return ret; - } - free(found); - return 0; - } - } - return 0; + return length; } -cJSON *cJSONUtils_GetPointer(cJSON *object,const char *pointer) +/* copy a string while escaping '~' and '/' with ~0 and ~1 JSON pointer escape codes */ +static void encode_string_as_pointer(unsigned char *destination, const unsigned char *source) { - while (*pointer++=='/' && object) - { - if (object->type==cJSON_Array) - { - int which=0; while (*pointer>='0' && *pointer<='9') which=(10*which) + *pointer++ - '0'; - if (*pointer && *pointer!='/') return 0; - object=cJSON_GetArrayItem(object,which); - } - else if (object->type==cJSON_Object) - { - object=object->child; while (object && cJSONUtils_Pstrcasecmp(object->string,pointer)) object=object->next; /* GetObjectItem. */ - while (*pointer && *pointer!='/') pointer++; - } - else return 0; - } - return object; + for (; source[0] != '\0'; (void)source++, destination++) + { + if (source[0] == '/') + { + destination[1] = '1'; + destination++; + } + else if (source[0] == '~') + { + destination[0] = '~'; + destination[1] = '1'; + destination++; + } + else + { + destination[0] = source[0]; + } + } + + destination[0] = '\0'; +} + +CJSON_PUBLIC(char *) cJSONUtils_FindPointerFromObjectTo(const cJSON * const object, const cJSON * const target) +{ + size_t child_index = 0; + cJSON *current_child = 0; + + if ((object == NULL) || (target == NULL)) + { + return NULL; + } + + if (object == target) + { + /* found */ + return (char*)cJSONUtils_strdup((const unsigned char*)""); + } + + /* recursively search all children of the object or array */ + for (current_child = object->child; current_child != NULL; (void)(current_child = current_child->next), child_index++) + { + unsigned char *target_pointer = (unsigned char*)cJSONUtils_FindPointerFromObjectTo(current_child, target); + /* found the target? */ + if (target_pointer != NULL) + { + if (cJSON_IsArray(object)) + { + /* reserve enough memory for a 64 bit integer + '/' and '\0' */ + unsigned char *full_pointer = (unsigned char*)cJSON_malloc(strlen((char*)target_pointer) + 20 + sizeof("/")); + /* check if conversion to unsigned long is valid + * This should be eliminated at compile time by dead code elimination + * if size_t is an alias of unsigned long, or if it is bigger */ + if (child_index > ULONG_MAX) + { + cJSON_free(target_pointer); + return NULL; + } + sprintf((char*)full_pointer, "/%lu%s", (unsigned long)child_index, target_pointer); /* / */ + cJSON_free(target_pointer); + + return (char*)full_pointer; + } + + if (cJSON_IsObject(object)) + { + unsigned char *full_pointer = (unsigned char*)cJSON_malloc(strlen((char*)target_pointer) + pointer_encoded_length((unsigned char*)current_child->string) + 2); + full_pointer[0] = '/'; + encode_string_as_pointer(full_pointer + 1, (unsigned char*)current_child->string); + strcat((char*)full_pointer, (char*)target_pointer); + cJSON_free(target_pointer); + + return (char*)full_pointer; + } + + /* reached leaf of the tree, found nothing */ + cJSON_free(target_pointer); + return NULL; + } + } + + /* not found */ + return NULL; +} + +/* non broken version of cJSON_GetArrayItem */ +static cJSON *get_array_item(const cJSON *array, size_t item) +{ + cJSON *child = array ? array->child : NULL; + while ((child != NULL) && (item > 0)) + { + item--; + child = child->next; + } + + return child; +} + +static cJSON_bool decode_array_index_from_pointer(const unsigned char * const pointer, size_t * const index) +{ + size_t parsed_index = 0; + size_t position = 0; + + if ((pointer[0] == '0') && ((pointer[1] != '\0') && (pointer[1] != '/'))) + { + /* leading zeroes are not permitted */ + return 0; + } + + for (position = 0; (pointer[position] >= '0') && (pointer[0] <= '9'); position++) + { + parsed_index = (10 * parsed_index) + (size_t)(pointer[position] - '0'); + + } + + if ((pointer[position] != '\0') && (pointer[position] != '/')) + { + return 0; + } + + *index = parsed_index; + + return 1; +} + +static cJSON *get_item_from_pointer(cJSON * const object, const char * pointer, const cJSON_bool case_sensitive) +{ + cJSON *current_element = object; + + if (pointer == NULL) + { + return NULL; + } + + /* follow path of the pointer */ + while ((pointer[0] == '/') && (current_element != NULL)) + { + pointer++; + if (cJSON_IsArray(current_element)) + { + size_t index = 0; + if (!decode_array_index_from_pointer((const unsigned char*)pointer, &index)) + { + return NULL; + } + + current_element = get_array_item(current_element, index); + } + else if (cJSON_IsObject(current_element)) + { + current_element = current_element->child; + /* GetObjectItem. */ + while ((current_element != NULL) && !compare_pointers((unsigned char*)current_element->string, (const unsigned char*)pointer, case_sensitive)) + { + current_element = current_element->next; + } + } + else + { + return NULL; + } + + /* skip to the next path token or end of string */ + while ((pointer[0] != '\0') && (pointer[0] != '/')) + { + pointer++; + } + } + + return current_element; +} + +CJSON_PUBLIC(cJSON *) cJSONUtils_GetPointer(cJSON * const object, const char *pointer) +{ + return get_item_from_pointer(object, pointer, false); +} + +CJSON_PUBLIC(cJSON *) cJSONUtils_GetPointerCaseSensitive(cJSON * const object, const char *pointer) +{ + return get_item_from_pointer(object, pointer, true); } /* JSON Patch implementation. */ -static void cJSONUtils_InplaceDecodePointerString(char *string) +static void decode_pointer_inplace(unsigned char *string) { - char *s2=string; - for (;*string;s2++,string++) *s2=(*string!='~')?(*string):((*(++string)=='0')?'~':'/'); - *s2=0; + unsigned char *decoded_string = string; + + if (string == NULL) { + return; + } + + for (; *string; (void)decoded_string++, string++) + { + if (string[0] == '~') + { + if (string[1] == '0') + { + decoded_string[0] = '~'; + } + else if (string[1] == '1') + { + decoded_string[1] = '/'; + } + else + { + /* invalid escape sequence */ + return; + } + + string++; + } + } + + decoded_string[0] = '\0'; } -static cJSON *cJSONUtils_PatchDetach(cJSON *object,const char *path) +/* non-broken cJSON_DetachItemFromArray */ +static cJSON *detach_item_from_array(cJSON *array, size_t which) { - char *parentptr=0,*childptr=0;cJSON *parent=0,*ret=0; + cJSON *c = array->child; + while (c && (which > 0)) + { + c = c->next; + which--; + } + if (!c) + { + /* item doesn't exist */ + return NULL; + } + if (c->prev) + { + /* not the first element */ + c->prev->next = c->next; + } + if (c->next) + { + c->next->prev = c->prev; + } + if (c==array->child) + { + array->child = c->next; + } + /* make sure the detached item doesn't point anywhere anymore */ + c->prev = c->next = NULL; - parentptr=strdup(path); childptr=strrchr(parentptr,'/'); if (childptr) *childptr++=0; - parent=cJSONUtils_GetPointer(object,parentptr); - cJSONUtils_InplaceDecodePointerString(childptr); - - if (!parent) ret=0; /* Couldn't find object to remove child from. */ - else if (parent->type==cJSON_Array) ret=cJSON_DetachItemFromArray(parent,atoi(childptr)); - else if (parent->type==cJSON_Object) ret=cJSON_DetachItemFromObject(parent,childptr); - free(parentptr); - return ret; + return c; } -static int cJSONUtils_Compare(cJSON *a,cJSON *b) +/* detach an item at the given path */ +static cJSON *detach_path(cJSON *object, const unsigned char *path, const cJSON_bool case_sensitive) { - if (a->type!=b->type) return -1; /* mismatched type. */ - switch (a->type) - { - case cJSON_Number: return (a->valueint!=b->valueint || a->valuedouble!=b->valuedouble)?-2:0; /* numeric mismatch. */ - case cJSON_String: return (strcmp(a->valuestring,b->valuestring)!=0)?-3:0; /* string mismatch. */ - case cJSON_Array: for (a=a->child,b=b->child;a && b;a=a->next,b=b->next) {int err=cJSONUtils_Compare(a,b);if (err) return err;} - return (a || b)?-4:0; /* array size mismatch. */ - case cJSON_Object: - cJSONUtils_SortObject(a); - cJSONUtils_SortObject(b); - a=a->child,b=b->child; - while (a && b) - { - int err; - if (cJSONUtils_strcasecmp(a->string,b->string)) return -6; /* missing member */ - err=cJSONUtils_Compare(a,b);if (err) return err; - a=a->next,b=b->next; - } - return (a || b)?-5:0; /* object length mismatch */ + unsigned char *parent_pointer = NULL; + unsigned char *child_pointer = NULL; + cJSON *parent = NULL; + cJSON *detached_item = NULL; - default: break; - } - return 0; + /* copy path and split it in parent and child */ + parent_pointer = cJSONUtils_strdup(path); + if (parent_pointer == NULL) { + goto cleanup; + } + + child_pointer = (unsigned char*)strrchr((char*)parent_pointer, '/'); /* last '/' */ + if (child_pointer == NULL) + { + goto cleanup; + } + /* split strings */ + child_pointer[0] = '\0'; + child_pointer++; + + parent = get_item_from_pointer(object, (char*)parent_pointer, case_sensitive); + decode_pointer_inplace(child_pointer); + + if (cJSON_IsArray(parent)) + { + size_t index = 0; + if (!decode_array_index_from_pointer(child_pointer, &index)) + { + goto cleanup; + } + detached_item = detach_item_from_array(parent, index); + } + else if (cJSON_IsObject(parent)) + { + detached_item = cJSON_DetachItemFromObject(parent, (char*)child_pointer); + } + else + { + /* Couldn't find object to remove child from. */ + goto cleanup; + } + +cleanup: + if (parent_pointer != NULL) + { + cJSON_free(parent_pointer); + } + + return detached_item; } -static int cJSONUtils_ApplyPatch(cJSON *object,cJSON *patch) +/* sort lists using mergesort */ +static cJSON *sort_list(cJSON *list, const cJSON_bool case_sensitive) { - cJSON *op=0,*path=0,*value=0,*parent=0;int opcode=0;char *parentptr=0,*childptr=0; + cJSON *first = list; + cJSON *second = list; + cJSON *current_item = list; + cJSON *result = list; + cJSON *result_tail = NULL; - op=cJSON_GetObjectItem(patch,"op"); - path=cJSON_GetObjectItem(patch,"path"); - if (!op || !path) return 2; /* malformed patch. */ + if ((list == NULL) || (list->next == NULL)) + { + /* One entry is sorted already. */ + return result; + } - if (!strcmp(op->valuestring,"add")) opcode=0; - else if (!strcmp(op->valuestring,"remove")) opcode=1; - else if (!strcmp(op->valuestring,"replace"))opcode=2; - else if (!strcmp(op->valuestring,"move")) opcode=3; - else if (!strcmp(op->valuestring,"copy")) opcode=4; - else if (!strcmp(op->valuestring,"test")) return cJSONUtils_Compare(cJSONUtils_GetPointer(object,path->valuestring),cJSON_GetObjectItem(patch,"value")); - else return 3; /* unknown opcode. */ + while ((current_item != NULL) && (current_item->next != NULL) && (compare_strings((unsigned char*)current_item->string, (unsigned char*)current_item->next->string, case_sensitive) < 0)) + { + /* Test for list sorted. */ + current_item = current_item->next; + } + if ((current_item == NULL) || (current_item->next == NULL)) + { + /* Leave sorted lists unmodified. */ + return result; + } - if (opcode==1 || opcode==2) /* Remove/Replace */ - { - cJSON_Delete(cJSONUtils_PatchDetach(object,path->valuestring)); /* Get rid of old. */ - if (opcode==1) return 0; /* For Remove, this is job done. */ - } + /* reset pointer to the beginning */ + current_item = list; + while (current_item != NULL) + { + /* Walk two pointers to find the middle. */ + second = second->next; + current_item = current_item->next; + /* advances current_item two steps at a time */ + if (current_item != NULL) + { + current_item = current_item->next; + } + } + if ((second != NULL) && (second->prev != NULL)) + { + /* Split the lists */ + second->prev->next = NULL; + } - if (opcode==3 || opcode==4) /* Copy/Move uses "from". */ - { - cJSON *from=cJSON_GetObjectItem(patch,"from"); if (!from) return 4; /* missing "from" for copy/move. */ + /* Recursively sort the sub-lists. */ + first = sort_list(first, case_sensitive); + second = sort_list(second, case_sensitive); + result = NULL; - if (opcode==3) value=cJSONUtils_PatchDetach(object,from->valuestring); - if (opcode==4) value=cJSONUtils_GetPointer(object,from->valuestring); - if (!value) return 5; /* missing "from" for copy/move. */ - if (opcode==4) value=cJSON_Duplicate(value,1); - if (!value) return 6; /* out of memory for copy/move. */ - } - else /* Add/Replace uses "value". */ - { - value=cJSON_GetObjectItem(patch,"value"); - if (!value) return 7; /* missing "value" for add/replace. */ - value=cJSON_Duplicate(value,1); - if (!value) return 8; /* out of memory for add/replace. */ - } - - /* Now, just add "value" to "path". */ + /* Merge the sub-lists */ + while ((first != NULL) && (second != NULL)) + { + cJSON *smaller = NULL; + if (compare_strings((unsigned char*)first->string, (unsigned char*)second->string, false) < 0) + { + smaller = first; + } + else + { + smaller = second; + } - parentptr=strdup(path->valuestring); childptr=strrchr(parentptr,'/'); if (childptr) *childptr++=0; - parent=cJSONUtils_GetPointer(object,parentptr); - cJSONUtils_InplaceDecodePointerString(childptr); + if (result == NULL) + { + /* start merged list with the smaller element */ + result_tail = smaller; + result = smaller; + } + else + { + /* add smaller element to the list */ + result_tail->next = smaller; + smaller->prev = result_tail; + result_tail = smaller; + } - /* add, remove, replace, move, copy, test. */ - if (!parent) {free(parentptr); cJSON_Delete(value); return 9;} /* Couldn't find object to add to. */ - else if (parent->type==cJSON_Array) - { - if (!strcmp(childptr,"-")) cJSON_AddItemToArray(parent,value); - else cJSON_InsertItemInArray(parent,atoi(childptr),value); - } - else if (parent->type==cJSON_Object) - { - cJSON_DeleteItemFromObject(parent,childptr); - cJSON_AddItemToObject(parent,childptr,value); - } - else - { - cJSON_Delete(value); - } - free(parentptr); - return 0; + if (first == smaller) + { + first = first->next; + } + else + { + second = second->next; + } + } + + if (first != NULL) + { + /* Append rest of first list. */ + if (result == NULL) + { + return first; + } + result_tail->next = first; + first->prev = result_tail; + } + if (second != NULL) + { + /* Append rest of second list */ + if (result == NULL) + { + return second; + } + result_tail->next = second; + second->prev = result_tail; + } + + return result; } - -int cJSONUtils_ApplyPatches(cJSON *object,cJSON *patches) +static void sort_object(cJSON * const object, const cJSON_bool case_sensitive) { - int err; - if (patches->type!=cJSON_Array) return 1; /* malformed patches. */ - if (patches) patches=patches->child; - while (patches) - { - if ((err=cJSONUtils_ApplyPatch(object,patches))) return err; - patches=patches->next; - } - return 0; + if (object == NULL) + { + return; + } + object->child = sort_list(object->child, case_sensitive); } -static void cJSONUtils_GeneratePatch(cJSON *patches,const char *op,const char *path,const char *suffix,cJSON *val) +static cJSON_bool compare_json(cJSON *a, cJSON *b, const cJSON_bool case_sensitive) { - cJSON *patch=cJSON_CreateObject(); - cJSON_AddItemToObject(patch,"op",cJSON_CreateString(op)); - if (suffix) - { - char *newpath=(char*)malloc(strlen(path)+cJSONUtils_PointerEncodedstrlen(suffix)+2); - cJSONUtils_PointerEncodedstrcpy(newpath+sprintf(newpath,"%s/",path),suffix); - cJSON_AddItemToObject(patch,"path",cJSON_CreateString(newpath)); - free(newpath); - } - else cJSON_AddItemToObject(patch,"path",cJSON_CreateString(path)); - if (val) cJSON_AddItemToObject(patch,"value",cJSON_Duplicate(val,1)); - cJSON_AddItemToArray(patches,patch); + if ((a == NULL) || (b == NULL) || ((a->type & 0xFF) != (b->type & 0xFF))) + { + /* mismatched type. */ + return false; + } + switch (a->type & 0xFF) + { + case cJSON_Number: + /* numeric mismatch. */ + if ((a->valueint != b->valueint) || (a->valuedouble != b->valuedouble)) + { + return false; + } + else + { + return true; + } + + case cJSON_String: + /* string mismatch. */ + if (strcmp(a->valuestring, b->valuestring) != 0) + { + return false; + } + else + { + return true; + } + + case cJSON_Array: + for ((void)(a = a->child), b = b->child; (a != NULL) && (b != NULL); (void)(a = a->next), b = b->next) + { + cJSON_bool identical = compare_json(a, b, case_sensitive); + if (!identical) + { + return false; + } + } + + /* array size mismatch? (one of both children is not NULL) */ + if ((a != NULL) || (b != NULL)) + { + return false; + } + else + { + return true; + } + + case cJSON_Object: + sort_object(a, case_sensitive); + sort_object(b, case_sensitive); + for ((void)(a = a->child), b = b->child; (a != NULL) && (b != NULL); (void)(a = a->next), b = b->next) + { + cJSON_bool identical = false; + /* compare object keys */ + if (compare_strings((unsigned char*)a->string, (unsigned char*)b->string, case_sensitive)) + { + /* missing member */ + return false; + } + identical = compare_json(a, b, case_sensitive); + if (!identical) + { + return false; + } + } + + /* object length mismatch (one of both children is not null) */ + if ((a != NULL) || (b != NULL)) + { + return false; + } + else + { + return true; + } + + default: + break; + } + + /* null, true or false */ + return true; } -void cJSONUtils_AddPatchToArray(cJSON *array,const char *op,const char *path,cJSON *val) {cJSONUtils_GeneratePatch(array,op,path,0,val);} - -static void cJSONUtils_CompareToPatch(cJSON *patches,const char *path,cJSON *from,cJSON *to) +/* non broken version of cJSON_InsertItemInArray */ +static cJSON_bool insert_item_in_array(cJSON *array, size_t which, cJSON *newitem) { - if (from->type!=to->type) {cJSONUtils_GeneratePatch(patches,"replace",path,0,to); return; } - - switch (from->type) - { - case cJSON_Number: - if (from->valueint!=to->valueint || from->valuedouble!=to->valuedouble) - cJSONUtils_GeneratePatch(patches,"replace",path,0,to); - return; - - case cJSON_String: - if (strcmp(from->valuestring,to->valuestring)!=0) - cJSONUtils_GeneratePatch(patches,"replace",path,0,to); - return; + cJSON *child = array->child; + while (child && (which > 0)) + { + child = child->next; + which--; + } + if (which > 0) + { + /* item is after the end of the array */ + return 0; + } + if (child == NULL) + { + cJSON_AddItemToArray(array, newitem); + return 1; + } - case cJSON_Array: - { - int c;char *newpath=(char*)malloc(strlen(path)+23); /* Allow space for 64bit int. */ - for (c=0,from=from->child,to=to->child;from && to;from=from->next,to=to->next,c++){ - sprintf(newpath,"%s/%d",path,c); cJSONUtils_CompareToPatch(patches,newpath,from,to); - } - for (;from;from=from->next,c++) {sprintf(newpath,"%d",c); cJSONUtils_GeneratePatch(patches,"remove",path,newpath,0); } - for (;to;to=to->next,c++) cJSONUtils_GeneratePatch(patches,"add",path,"-",to); - free(newpath); - return; - } + /* insert into the linked list */ + newitem->next = child; + newitem->prev = child->prev; + child->prev = newitem; - case cJSON_Object: - { - cJSON *a,*b; - cJSONUtils_SortObject(from); - cJSONUtils_SortObject(to); - - a=from->child,b=to->child; - while (a || b) - { - int diff=(!a)?1:(!b)?-1:cJSONUtils_strcasecmp(a->string,b->string); - if (!diff) - { - char *newpath=(char*)malloc(strlen(path)+cJSONUtils_PointerEncodedstrlen(a->string)+2); - cJSONUtils_PointerEncodedstrcpy(newpath+sprintf(newpath,"%s/",path),a->string); - cJSONUtils_CompareToPatch(patches,newpath,a,b); - free(newpath); - a=a->next; - b=b->next; - } - else if (diff<0) {cJSONUtils_GeneratePatch(patches,"remove",path,a->string,0); a=a->next;} - else {cJSONUtils_GeneratePatch(patches,"add",path,b->string,b); b=b->next;} - } - return; - } + /* was it at the beginning */ + if (child == array->child) + { + array->child = newitem; + } + else + { + newitem->prev->next = newitem; + } - default: break; - } + return 1; } - -cJSON* cJSONUtils_GeneratePatches(cJSON *from,cJSON *to) +static cJSON *get_object_item(const cJSON * const object, const char* name, const cJSON_bool case_sensitive) { - cJSON *patches=cJSON_CreateArray(); - cJSONUtils_CompareToPatch(patches,"",from,to); - return patches; + if (case_sensitive) + { + return cJSON_GetObjectItemCaseSensitive(object, name); + } + + return cJSON_GetObjectItem(object, name); } +enum patch_operation { INVALID, ADD, REMOVE, REPLACE, MOVE, COPY, TEST }; -static cJSON *cJSONUtils_SortList(cJSON *list) +static enum patch_operation decode_patch_operation(const cJSON * const patch, const cJSON_bool case_sensitive) { - cJSON *first=list,*second=list,*ptr=list; + cJSON *operation = get_object_item(patch, "op", case_sensitive); + if (!cJSON_IsString(operation)) + { + return INVALID; + } - if (!list || !list->next) return list; /* One entry is sorted already. */ - - while (ptr && ptr->next && cJSONUtils_strcasecmp(ptr->string,ptr->next->string)<0) ptr=ptr->next; /* Test for list sorted. */ - if (!ptr || !ptr->next) return list; /* Leave sorted lists unmodified. */ - ptr=list; + if (strcmp(operation->valuestring, "add") == 0) + { + return ADD; + } - while (ptr) {second=second->next;ptr=ptr->next;if (ptr) ptr=ptr->next;} /* Walk two pointers to find the middle. */ - if (second && second->prev) second->prev->next=0; /* Split the lists */ + if (strcmp(operation->valuestring, "remove") == 0) + { + return REMOVE; + } - first=cJSONUtils_SortList(first); /* Recursively sort the sub-lists. */ - second=cJSONUtils_SortList(second); - list=ptr=0; + if (strcmp(operation->valuestring, "replace") == 0) + { + return REPLACE; + } - while (first && second) /* Merge the sub-lists */ - { - if (cJSONUtils_strcasecmp(first->string,second->string)<0) - { - if (!list) list=ptr=first; - else {ptr->next=first;first->prev=ptr;ptr=first;} - first=first->next; - } - else - { - if (!list) list=ptr=second; - else {ptr->next=second;second->prev=ptr;ptr=second;} - second=second->next; - } - } - if (first) { if (!list) return first; ptr->next=first; first->prev=ptr; } /* Append any tails. */ - if (second) { if (!list) return second; ptr->next=second; second->prev=ptr; } + if (strcmp(operation->valuestring, "move") == 0) + { + return MOVE; + } - return list; + if (strcmp(operation->valuestring, "copy") == 0) + { + return COPY; + } + + if (strcmp(operation->valuestring, "test") == 0) + { + return TEST; + } + + return INVALID; } -void cJSONUtils_SortObject(cJSON *object) {object->child=cJSONUtils_SortList(object->child);} - -cJSON* cJSONUtils_MergePatch(cJSON *target, cJSON *patch) +/* overwrite and existing item with another one and free resources on the way */ +static void overwrite_item(cJSON * const root, const cJSON replacement) { - if (!patch || patch->type != cJSON_Object) {cJSON_Delete(target);return cJSON_Duplicate(patch,1);} - if (!target || target->type != cJSON_Object) {cJSON_Delete(target);target=cJSON_CreateObject();} + if (root == NULL) + { + return; + } - patch=patch->child; - while (patch) - { - if (patch->type == cJSON_NULL) cJSON_DeleteItemFromObject(target,patch->string); - else - { - cJSON *replaceme=cJSON_DetachItemFromObject(target,patch->string); - cJSON_AddItemToObject(target,patch->string,cJSONUtils_MergePatch(replaceme,patch)); - } - patch=patch->next; - } - return target; + if (root->string != NULL) + { + cJSON_free(root->string); + } + if (root->valuestring != NULL) + { + cJSON_free(root->valuestring); + } + if (root->child != NULL) + { + cJSON_Delete(root->child); + } + + memcpy(root, &replacement, sizeof(cJSON)); } -cJSON *cJSONUtils_GenerateMergePatch(cJSON *from,cJSON *to) +static int apply_patch(cJSON *object, const cJSON *patch, const cJSON_bool case_sensitive) { - cJSON *patch=0; - if (!to) return cJSON_CreateNull(); - if (to->type!=cJSON_Object || !from || from->type!=cJSON_Object) return cJSON_Duplicate(to,1); - cJSONUtils_SortObject(from); - cJSONUtils_SortObject(to); - from=from->child;to=to->child; - patch=cJSON_CreateObject(); - while (from || to) - { - int compare=from?(to?strcmp(from->string,to->string):-1):1; - if (compare<0) - { - cJSON_AddItemToObject(patch,from->string,cJSON_CreateNull()); - from=from->next; - } - else if (compare>0) - { - cJSON_AddItemToObject(patch,to->string,cJSON_Duplicate(to,1)); - to=to->next; - } - else - { - if (cJSONUtils_Compare(from,to)) cJSON_AddItemToObject(patch,to->string,cJSONUtils_GenerateMergePatch(from,to)); - from=from->next;to=to->next; - } - } - if (!patch->child) {cJSON_Delete(patch);return 0;} - return patch; -} \ No newline at end of file + cJSON *path = NULL; + cJSON *value = NULL; + cJSON *parent = NULL; + enum patch_operation opcode = INVALID; + unsigned char *parent_pointer = NULL; + unsigned char *child_pointer = NULL; + int status = 0; + + path = get_object_item(patch, "path", case_sensitive); + if (!cJSON_IsString(path)) + { + /* malformed patch. */ + status = 2; + goto cleanup; + } + + opcode = decode_patch_operation(patch, case_sensitive); + if (opcode == INVALID) + { + status = 3; + goto cleanup; + } + else if (opcode == TEST) + { + /* compare value: {...} with the given path */ + status = !compare_json(get_item_from_pointer(object, path->valuestring, case_sensitive), get_object_item(patch, "value", case_sensitive), case_sensitive); + goto cleanup; + } + + /* special case for replacing the root */ + if (path->valuestring[0] == '\0') + { + if (opcode == REMOVE) + { + static const cJSON invalid = { NULL, NULL, NULL, cJSON_Invalid, NULL, 0, 0, NULL}; + + overwrite_item(object, invalid); + + status = 0; + goto cleanup; + } + + if ((opcode == REPLACE) || (opcode == ADD)) + { + value = get_object_item(patch, "value", case_sensitive); + if (value == NULL) + { + /* missing "value" for add/replace. */ + status = 7; + goto cleanup; + } + + value = cJSON_Duplicate(value, 1); + if (value == NULL) + { + /* out of memory for add/replace. */ + status = 8; + goto cleanup; + } + + overwrite_item(object, *value); + + /* delete the duplicated value */ + cJSON_free(value); + value = NULL; + + /* the string "value" isn't needed */ + if (object->string != NULL) + { + cJSON_free(object->string); + object->string = NULL; + } + + status = 0; + goto cleanup; + } + } + + if ((opcode == REMOVE) || (opcode == REPLACE)) + { + /* Get rid of old. */ + cJSON *old_item = detach_path(object, (unsigned char*)path->valuestring, case_sensitive); + if (old_item == NULL) + { + status = 13; + goto cleanup; + } + cJSON_Delete(old_item); + if (opcode == REMOVE) + { + /* For Remove, this job is done. */ + status = 0; + goto cleanup; + } + } + + /* Copy/Move uses "from". */ + if ((opcode == MOVE) || (opcode == COPY)) + { + cJSON *from = get_object_item(patch, "from", case_sensitive); + if (from == NULL) + { + /* missing "from" for copy/move. */ + status = 4; + goto cleanup; + } + + if (opcode == MOVE) + { + value = detach_path(object, (unsigned char*)from->valuestring, case_sensitive); + } + if (opcode == COPY) + { + value = get_item_from_pointer(object, from->valuestring, case_sensitive); + } + if (value == NULL) + { + /* missing "from" for copy/move. */ + status = 5; + goto cleanup; + } + if (opcode == COPY) + { + value = cJSON_Duplicate(value, 1); + } + if (value == NULL) + { + /* out of memory for copy/move. */ + status = 6; + goto cleanup; + } + } + else /* Add/Replace uses "value". */ + { + value = get_object_item(patch, "value", case_sensitive); + if (value == NULL) + { + /* missing "value" for add/replace. */ + status = 7; + goto cleanup; + } + value = cJSON_Duplicate(value, 1); + if (value == NULL) + { + /* out of memory for add/replace. */ + status = 8; + goto cleanup; + } + } + + /* Now, just add "value" to "path". */ + + /* split pointer in parent and child */ + parent_pointer = cJSONUtils_strdup((unsigned char*)path->valuestring); + child_pointer = (unsigned char*)strrchr((char*)parent_pointer, '/'); + if (child_pointer != NULL) + { + child_pointer[0] = '\0'; + child_pointer++; + } + parent = get_item_from_pointer(object, (char*)parent_pointer, case_sensitive); + decode_pointer_inplace(child_pointer); + + /* add, remove, replace, move, copy, test. */ + if ((parent == NULL) || (child_pointer == NULL)) + { + /* Couldn't find object to add to. */ + status = 9; + goto cleanup; + } + else if (cJSON_IsArray(parent)) + { + if (strcmp((char*)child_pointer, "-") == 0) + { + cJSON_AddItemToArray(parent, value); + value = NULL; + } + else + { + size_t index = 0; + if (!decode_array_index_from_pointer(child_pointer, &index)) + { + status = 11; + goto cleanup; + } + + if (!insert_item_in_array(parent, index, value)) + { + status = 10; + goto cleanup; + } + value = NULL; + } + } + else if (cJSON_IsObject(parent)) + { + if (case_sensitive) + { + cJSON_DeleteItemFromObjectCaseSensitive(parent, (char*)child_pointer); + } + else + { + cJSON_DeleteItemFromObject(parent, (char*)child_pointer); + } + cJSON_AddItemToObject(parent, (char*)child_pointer, value); + value = NULL; + } + +cleanup: + if (value != NULL) + { + cJSON_Delete(value); + } + if (parent_pointer != NULL) + { + cJSON_free(parent_pointer); + } + + return status; +} + +CJSON_PUBLIC(int) cJSONUtils_ApplyPatches(cJSON * const object, const cJSON * const patches) +{ + const cJSON *current_patch = NULL; + int status = 0; + + if (!cJSON_IsArray(patches)) + { + /* malformed patches. */ + return 1; + } + + if (patches != NULL) + { + current_patch = patches->child; + } + + while (current_patch != NULL) + { + status = apply_patch(object, current_patch, false); + if (status != 0) + { + return status; + } + current_patch = current_patch->next; + } + + return 0; +} + +CJSON_PUBLIC(int) cJSONUtils_ApplyPatchesCaseSensitive(cJSON * const object, const cJSON * const patches) +{ + const cJSON *current_patch = NULL; + int status = 0; + + if (!cJSON_IsArray(patches)) + { + /* malformed patches. */ + return 1; + } + + if (patches != NULL) + { + current_patch = patches->child; + } + + while (current_patch != NULL) + { + status = apply_patch(object, current_patch, true); + if (status != 0) + { + return status; + } + current_patch = current_patch->next; + } + + return 0; +} + +static void compose_patch(cJSON * const patches, const unsigned char * const operation, const unsigned char * const path, const unsigned char *suffix, const cJSON * const value) +{ + cJSON *patch = NULL; + + if ((patches == NULL) || (operation == NULL) || (path == NULL)) + { + return; + } + + patch = cJSON_CreateObject(); + if (patch == NULL) + { + return; + } + cJSON_AddItemToObject(patch, "op", cJSON_CreateString((const char*)operation)); + + if (suffix == NULL) + { + cJSON_AddItemToObject(patch, "path", cJSON_CreateString((const char*)path)); + } + else + { + size_t suffix_length = pointer_encoded_length(suffix); + size_t path_length = strlen((const char*)path); + unsigned char *full_path = (unsigned char*)cJSON_malloc(path_length + suffix_length + sizeof("/")); + + sprintf((char*)full_path, "%s/", (const char*)path); + encode_string_as_pointer(full_path + path_length + 1, suffix); + + cJSON_AddItemToObject(patch, "path", cJSON_CreateString((const char*)full_path)); + cJSON_free(full_path); + } + + if (value != NULL) + { + cJSON_AddItemToObject(patch, "value", cJSON_Duplicate(value, 1)); + } + cJSON_AddItemToArray(patches, patch); +} + +CJSON_PUBLIC(void) cJSONUtils_AddPatchToArray(cJSON * const array, const char * const operation, const char * const path, const cJSON * const value) +{ + compose_patch(array, (const unsigned char*)operation, (const unsigned char*)path, NULL, value); +} + +static void create_patches(cJSON * const patches, const unsigned char * const path, cJSON * const from, cJSON * const to, const cJSON_bool case_sensitive) +{ + if ((from == NULL) || (to == NULL)) + { + return; + } + + if ((from->type & 0xFF) != (to->type & 0xFF)) + { + compose_patch(patches, (const unsigned char*)"replace", path, 0, to); + return; + } + + switch (from->type & 0xFF) + { + case cJSON_Number: + if ((from->valueint != to->valueint) || (from->valuedouble != to->valuedouble)) + { + compose_patch(patches, (const unsigned char*)"replace", path, NULL, to); + } + return; + + case cJSON_String: + if (strcmp(from->valuestring, to->valuestring) != 0) + { + compose_patch(patches, (const unsigned char*)"replace", path, NULL, to); + } + return; + + case cJSON_Array: + { + size_t index = 0; + cJSON *from_child = from->child; + cJSON *to_child = to->child; + unsigned char *new_path = (unsigned char*)cJSON_malloc(strlen((const char*)path) + 20 + sizeof("/")); /* Allow space for 64bit int. log10(2^64) = 20 */ + + /* generate patches for all array elements that exist in both "from" and "to" */ + for (index = 0; (from_child != NULL) && (to_child != NULL); (void)(from_child = from_child->next), (void)(to_child = to_child->next), index++) + { + /* check if conversion to unsigned long is valid + * This should be eliminated at compile time by dead code elimination + * if size_t is an alias of unsigned long, or if it is bigger */ + if (index > ULONG_MAX) + { + cJSON_free(new_path); + return; + } + sprintf((char*)new_path, "%s/%lu", path, (unsigned long)index); /* path of the current array element */ + create_patches(patches, new_path, from_child, to_child, case_sensitive); + } + + /* remove leftover elements from 'from' that are not in 'to' */ + for (; (from_child != NULL); (void)(from_child = from_child->next)) + { + /* check if conversion to unsigned long is valid + * This should be eliminated at compile time by dead code elimination + * if size_t is an alias of unsigned long, or if it is bigger */ + if (index > ULONG_MAX) + { + cJSON_free(new_path); + return; + } + sprintf((char*)new_path, "%lu", (unsigned long)index); + compose_patch(patches, (const unsigned char*)"remove", path, new_path, NULL); + } + /* add new elements in 'to' that were not in 'from' */ + for (; (to_child != NULL); (void)(to_child = to_child->next), index++) + { + compose_patch(patches, (const unsigned char*)"add", path, (const unsigned char*)"-", to_child); + } + cJSON_free(new_path); + return; + } + + case cJSON_Object: + { + cJSON *from_child = NULL; + cJSON *to_child = NULL; + sort_object(from, case_sensitive); + sort_object(to, case_sensitive); + + from_child = from->child; + to_child = to->child; + /* for all object values in the object with more of them */ + while ((from_child != NULL) || (to_child != NULL)) + { + int diff; + if (from_child == NULL) + { + diff = 1; + } + else if (to_child == NULL) + { + diff = -1; + } + else + { + diff = compare_strings((unsigned char*)from_child->string, (unsigned char*)to_child->string, case_sensitive); + } + + if (diff == 0) + { + /* both object keys are the same */ + size_t path_length = strlen((const char*)path); + size_t from_child_name_length = pointer_encoded_length((unsigned char*)from_child->string); + unsigned char *new_path = (unsigned char*)cJSON_malloc(path_length + from_child_name_length + sizeof("/")); + + sprintf((char*)new_path, "%s/", path); + encode_string_as_pointer(new_path + path_length + 1, (unsigned char*)from_child->string); + + /* create a patch for the element */ + create_patches(patches, new_path, from_child, to_child, case_sensitive); + cJSON_free(new_path); + + from_child = from_child->next; + to_child = to_child->next; + } + else if (diff < 0) + { + /* object element doesn't exist in 'to' --> remove it */ + compose_patch(patches, (const unsigned char*)"remove", path, (unsigned char*)from_child->string, NULL); + + from_child = from_child->next; + } + else + { + /* object element doesn't exist in 'from' --> add it */ + compose_patch(patches, (const unsigned char*)"add", path, (unsigned char*)to_child->string, to_child); + + to_child = to_child->next; + } + } + return; + } + + default: + break; + } +} + +CJSON_PUBLIC(cJSON *) cJSONUtils_GeneratePatches(cJSON * const from, cJSON * const to) +{ + cJSON *patches = NULL; + + if ((from == NULL) || (to == NULL)) + { + return NULL; + } + + patches = cJSON_CreateArray(); + create_patches(patches, (const unsigned char*)"", from, to, false); + + return patches; +} + +CJSON_PUBLIC(cJSON *) cJSONUtils_GeneratePatchesCaseSensitive(cJSON * const from, cJSON * const to) +{ + cJSON *patches = NULL; + + if ((from == NULL) || (to == NULL)) + { + return NULL; + } + + patches = cJSON_CreateArray(); + create_patches(patches, (const unsigned char*)"", from, to, true); + + return patches; +} + +CJSON_PUBLIC(void) cJSONUtils_SortObject(cJSON * const object) +{ + sort_object(object, false); +} + +CJSON_PUBLIC(void) cJSONUtils_SortObjectCaseSensitive(cJSON * const object) +{ + sort_object(object, true); +} + +static cJSON *merge_patch(cJSON *target, const cJSON * const patch, const cJSON_bool case_sensitive) +{ + cJSON *patch_child = NULL; + + if (!cJSON_IsObject(patch)) + { + /* scalar value, array or NULL, just duplicate */ + cJSON_Delete(target); + return cJSON_Duplicate(patch, 1); + } + + if (!cJSON_IsObject(target)) + { + cJSON_Delete(target); + target = cJSON_CreateObject(); + } + + patch_child = patch->child; + while (patch_child != NULL) + { + if (cJSON_IsNull(patch_child)) + { + /* NULL is the indicator to remove a value, see RFC7396 */ + if (case_sensitive) + { + cJSON_DeleteItemFromObjectCaseSensitive(target, patch_child->string); + } + else + { + cJSON_DeleteItemFromObject(target, patch_child->string); + } + } + else + { + cJSON *replace_me = NULL; + cJSON *replacement = NULL; + + if (case_sensitive) + { + replace_me = cJSON_DetachItemFromObjectCaseSensitive(target, patch_child->string); + } + else + { + replace_me = cJSON_DetachItemFromObject(target, patch_child->string); + } + + replacement = merge_patch(replace_me, patch_child, case_sensitive); + if (replacement == NULL) + { + return NULL; + } + + cJSON_AddItemToObject(target, patch_child->string, replacement); + } + patch_child = patch_child->next; + } + return target; +} + +CJSON_PUBLIC(cJSON *) cJSONUtils_MergePatch(cJSON *target, const cJSON * const patch) +{ + return merge_patch(target, patch, false); +} + +CJSON_PUBLIC(cJSON *) cJSONUtils_MergePatchCaseSensitive(cJSON *target, const cJSON * const patch) +{ + return merge_patch(target, patch, true); +} + +static cJSON *generate_merge_patch(cJSON * const from, cJSON * const to, const cJSON_bool case_sensitive) +{ + cJSON *from_child = NULL; + cJSON *to_child = NULL; + cJSON *patch = NULL; + if (to == NULL) + { + /* patch to delete everything */ + return cJSON_CreateNull(); + } + if (!cJSON_IsObject(to) || !cJSON_IsObject(from)) + { + return cJSON_Duplicate(to, 1); + } + + sort_object(from, case_sensitive); + sort_object(to, case_sensitive); + + from_child = from->child; + to_child = to->child; + patch = cJSON_CreateObject(); + while (from_child || to_child) + { + int diff; + if (from_child != NULL) + { + if (to_child != NULL) + { + diff = strcmp(from_child->string, to_child->string); + } + else + { + diff = -1; + } + } + else + { + diff = 1; + } + + if (diff < 0) + { + /* from has a value that to doesn't have -> remove */ + cJSON_AddItemToObject(patch, from_child->string, cJSON_CreateNull()); + + from_child = from_child->next; + } + else if (diff > 0) + { + /* to has a value that from doesn't have -> add to patch */ + cJSON_AddItemToObject(patch, to_child->string, cJSON_Duplicate(to_child, 1)); + + to_child = to_child->next; + } + else + { + /* object key exists in both objects */ + if (!compare_json(from_child, to_child, case_sensitive)) + { + /* not identical --> generate a patch */ + cJSON_AddItemToObject(patch, to_child->string, cJSONUtils_GenerateMergePatch(from_child, to_child)); + } + + /* next key in the object */ + from_child = from_child->next; + to_child = to_child->next; + } + } + if (patch->child == NULL) + { + /* no patch generated */ + cJSON_Delete(patch); + return NULL; + } + + return patch; +} + +CJSON_PUBLIC(cJSON *) cJSONUtils_GenerateMergePatch(cJSON * const from, cJSON * const to) +{ + return generate_merge_patch(from, to, false); +} + +CJSON_PUBLIC(cJSON *) cJSONUtils_GenerateMergePatchCaseSensitive(cJSON * const from, cJSON * const to) +{ + return generate_merge_patch(from, to, true); +} diff --git a/components/json/port/include/cJSON_Utils.h b/components/json/port/include/cJSON_Utils.h index b129b06252..03ec10c9e2 100644 --- a/components/json/port/include/cJSON_Utils.h +++ b/components/json/port/include/cJSON_Utils.h @@ -1,30 +1,74 @@ +/* + Copyright (c) 2009-2017 Dave Gamble and cJSON contributors + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + #include "cJSON.h" -/* Implement RFC6901 (https://tools.ietf.org/html/rfc6901) JSON Pointer spec. */ -cJSON *cJSONUtils_GetPointer(cJSON *object,const char *pointer); +/* Implement RFC6901 (https://tools.ietf.org/html/rfc6901) JSON Pointer spec. */ +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. */ -cJSON* cJSONUtils_GeneratePatches(cJSON *from,cJSON *to); -void cJSONUtils_AddPatchToArray(cJSON *array,const char *op,const char *path,cJSON *val); /* Utility for generating patch array entries. */ -int cJSONUtils_ApplyPatches(cJSON *object,cJSON *patches); /* Returns 0 for success. */ +/* Implement RFC6902 (https://tools.ietf.org/html/rfc6902) JSON Patch spec. */ +/* NOTE: This modifies objects in 'from' and 'to' by sorting the elements by their key */ +CJSON_PUBLIC(cJSON *) cJSONUtils_GeneratePatches(cJSON * const from, cJSON * const to); +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: //int cJSONUtils_AtomicApplyPatches(cJSON **object, cJSON *patches) //{ -// cJSON *modme=cJSON_Duplicate(*object,1); -// int error=cJSONUtils_ApplyPatches(modme,patches); -// if (!error) {cJSON_Delete(*object);*object=modme;} -// else cJSON_Delete(modme); -// return error; +// cJSON *modme = cJSON_Duplicate(*object, 1); +// int error = cJSONUtils_ApplyPatches(modme, patches); +// if (!error) +// { +// cJSON_Delete(*object); +// *object = modme; +// } +// else +// { +// cJSON_Delete(modme); +// } +// +// return error; //} // Code not added to library since this strategy is a LOT slower. */ /* 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. */ -cJSON *cJSONUtils_GenerateMergePatch(cJSON *from,cJSON *to); /* generates a patch to move from -> to */ +/* target will be modified by patch. return value is new ptr for target. */ +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); From df6adbd5bf7ec20d107fd41cf32d99e61a7d5a9d Mon Sep 17 00:00:00 2001 From: Darian Leung Date: Thu, 23 Nov 2017 22:35:54 +0800 Subject: [PATCH 06/56] freertos/fix SMP bug with Idle task clean up This commit backports vTaskDelete() behavior from FreeRTOS v9.0.0 which allows for the immediate freeing of task memory if the task being deleted is not currently running and not pinned to the other core. This commit also fixes a bug in prvCheckTasksWaitingTermination which prevented the Idle Task from cleaning up all tasks awaiting deletion. Each iteration of the Idle Task should traverse the xTasksWaitingTermination list and clean up all tasks not pinned to the other core. The previous implementation would cause prvCheckTasksWaitingTermination to return when encountering a task pinned to the other core whilst traversing the xTasksWaitingTermination list. The test case for vTaskDelete() has been updated to test for the bugfix and backported deletion behavior. --- components/freertos/tasks.c | 156 ++++++++++++------ .../freertos/test/test_freertos_task_delete.c | 64 +++++-- docs/api-guides/freertos-smp.rst | 119 +++++++++---- tools/unit-test-app/main/app_main.c | 2 +- 4 files changed, 247 insertions(+), 94 deletions(-) diff --git a/components/freertos/tasks.c b/components/freertos/tasks.c index ff549183a1..ff5c46bb40 100644 --- a/components/freertos/tasks.c +++ b/components/freertos/tasks.c @@ -541,6 +541,12 @@ static portTASK_FUNCTION_PROTO( prvIdleTask, pvParameters ); #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 * 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 ) + 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; + int core = xPortGetCoreID(); //Current core + UBaseType_t free_now; //Flag to indicate if task memory can be freed immediately + taskENTER_CRITICAL(&xTaskQueueMutex); { /* If null is passed in here then it is the calling task that is being deleted. */ pxTCB = prvGetTCBFromHandle( xTaskToDelete ); - /* Remove task from the ready list and place in the termination 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. */ + /* Remove task from the ready list. */ if( uxListRemove( &( pxTCB->xGenericListItem ) ) == ( UBaseType_t ) 0 ) { taskRESET_READY_PRIORITY( pxTCB->uxPriority ); @@ -1233,29 +1245,67 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB, TaskFunction_t pxTaskCode mtCOVERAGE_TEST_MARKER(); } - vListInsertEnd( &xTasksWaitingTermination, &( pxTCB->xGenericListItem ) ); - - /* Increment the ucTasksDeleted variable so the idle task knows - there is a task that has been deleted and that it should therefore - check the xTasksWaitingTermination list. */ - ++uxTasksDeleted; - - /* Increment the uxTaskNumberVariable also so kernel aware debuggers - can detect that the task lists need re-generating. */ + /* 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 ) ); + + /* Increment the ucTasksDeleted variable so the idle task knows + there is a task that has been deleted and that it should therefore + check the xTasksWaitingTermination list. */ + ++uxTasksDeleted; + + /* The pre-delete hook is primarily for the Windows simulator, + in which Windows specific clean up operations are performed, + 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 ); } 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 been deleted. */ if( xSchedulerRunning != pdFALSE ) { //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, 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()] ); 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 */ - vPortYieldOtherCore( !xPortGetCoreID() ); + vPortYieldOtherCore( !core ); } else { - /* Reset the next expected unblock time in case it referred to - the task that has just been deleted. */ - taskENTER_CRITICAL(&xTaskQueueMutex); - { - prvResetNextTaskUnblockTime(); - } - taskEXIT_CRITICAL(&xTaskQueueMutex); + mtCOVERAGE_TEST_MARKER(); } } } @@ -3583,52 +3627,48 @@ static void prvCheckTasksWaitingTermination( void ) #if ( INCLUDE_vTaskDelete == 1 ) { BaseType_t xListIsEmpty; + int core = xPortGetCoreID(); /* ucTasksDeleted is used to prevent vTaskSuspendAll() being called too often in the idle task. */ while(uxTasksDeleted > ( UBaseType_t ) 0U ) { TCB_t *pxTCB = NULL; + taskENTER_CRITICAL(&xTaskQueueMutex); { 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 - be called on the core the process is pinned on, if any */ - if( pxTCB->xCoreID == tskNO_AFFINITY || pxTCB->xCoreID == xPortGetCoreID()) { - ( void ) uxListRemove( &( pxTCB->xGenericListItem ) ); + be called on the core the process is pinned on, if any */ + ListItem_t *target = listGET_HEAD_ENTRY(&xTasksWaitingTermination); + for( ; target != listGET_END_MARKER(&xTasksWaitingTermination); target = listGET_NEXT(target) ){ + int coreid = (( TCB_t * )listGET_LIST_ITEM_OWNER(target))->xCoreID; + if(coreid == core || coreid == tskNO_AFFINITY){ //Find first item not pinned to other core + pxTCB = ( TCB_t * )listGET_LIST_ITEM_OWNER(target); + break; + } + } + if(pxTCB != NULL){ + ( void ) uxListRemove( target ); //Remove list item from list --uxCurrentNumberOfTasks; --uxTasksDeleted; - } else { - /* Need to wait until the idle task on the other processor kills that task first. */ - taskEXIT_CRITICAL(&xTaskQueueMutex); - break; } } } - taskEXIT_CRITICAL(&xTaskQueueMutex); + taskEXIT_CRITICAL(&xTaskQueueMutex); //Need to call deletion callbacks outside critical section - if (pxTCB != NULL) { - #if ( configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 ) && ( configTHREAD_LOCAL_STORAGE_DELETE_CALLBACKS ) - int x; - 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 - prvDeleteTCB( pxTCB ); + if (pxTCB != NULL) { //Call deletion callbacks and free TCB memory + #if ( configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 ) && ( configTHREAD_LOCAL_STORAGE_DELETE_CALLBACKS ) + prvDeleteTLS( pxTCB ); + #endif + prvDeleteTCB( pxTCB ); } else { mtCOVERAGE_TEST_MARKER(); + break; //No TCB found that could be freed by this core, break out of loop } } } @@ -3831,7 +3871,6 @@ BaseType_t xTaskGetAffinity( TaskHandle_t xTask ) #if ( INCLUDE_vTaskDelete == 1 ) - static void prvDeleteTCB( TCB_t *pxTCB ) { /* Free up the memory allocated by the scheduler for the task. It is up @@ -3886,6 +3925,23 @@ BaseType_t xTaskGetAffinity( TaskHandle_t xTask ) #endif /* INCLUDE_vTaskDelete */ /*-----------------------------------------------------------*/ +#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 ) { TCB_t *pxTCB; diff --git a/components/freertos/test/test_freertos_task_delete.c b/components/freertos/test/test_freertos_task_delete.c index 68a6683fcb..72f5cc85ad 100644 --- a/components/freertos/test/test_freertos_task_delete.c +++ b/components/freertos/test/test_freertos_task_delete.c @@ -1,26 +1,66 @@ +/* + * Test backported deletion behavior by creating tasks of various affinities and + * check if the task memory is freed immediately under the correct conditions. + * + * The behavior of vTaskDelete() has been backported form FreeRTOS v9.0.0. This + * results in the immediate freeing of task memory and the immediate execution + * of deletion callbacks under the following conditions... + * - When deleting a task that is not currently running on either core + * - When deleting a task that is pinned to the same core (with respect to + * the core that calls vTaskDelete() + * + * If the two conditions are not met, freeing of task memory and execution of + * deletion callbacks will still be carried out by the Idle Task. + */ #include #include "freertos/FreeRTOS.h" #include "freertos/task.h" -#include "freertos/semphr.h" -#include "freertos/queue.h" -#include "freertos/event_groups.h" +#include "esp_heap_caps.h" + #include "unity.h" -static void task_delete_self(void *param) +#define NO_OF_TSKS 3 +#define DELAY_TICKS 2 +#define HEAP_CAPS (MALLOC_CAP_INTERNAL|MALLOC_CAP_DEFAULT) + + +static void tsk_self_del(void *param) { - printf("Task %p running on core %d. Deleting shortly...\n", xTaskGetCurrentTaskHandle(), xPortGetCoreID()); - vTaskDelay(5); - vTaskDelete(NULL); + vTaskDelete(NULL); //Deleting self means deleting currently running task +} + +static void tsk_extern_del(void *param) +{ + vTaskDelay(portMAX_DELAY); //Await external deletion } TEST_CASE("FreeRTOS Delete Tasks", "[freertos]") { +/* -------------- Test vTaskDelete() on currently running tasks ----------------*/ uint32_t before_count = uxTaskGetNumberOfTasks(); - - xTaskCreatePinnedToCore(task_delete_self, "tsk_self_a", 4096, NULL, configMAX_PRIORITIES - 1, NULL, 0); - xTaskCreatePinnedToCore(task_delete_self, "tsk_self_a", 4096, NULL, configMAX_PRIORITIES - 1, NULL, 0); - TEST_ASSERT_EQUAL(before_count + 2, uxTaskGetNumberOfTasks()); - vTaskDelay(200 / portTICK_PERIOD_MS); + uint32_t before_heap = heap_caps_get_free_size(HEAP_CAPS); + for(int i = 0; i < portNUM_PROCESSORS; i++){ + for(int j = 0; j < NO_OF_TSKS; j++){ + TEST_ASSERT_EQUAL(pdTRUE, xTaskCreatePinnedToCore(tsk_self_del, "tsk_self", 1024, NULL, configMAX_PRIORITIES - 1, NULL, i)); + } + } + 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)); + } diff --git a/docs/api-guides/freertos-smp.rst b/docs/api-guides/freertos-smp.rst index 6d0c29bc4f..16d4c139b4 100644 --- a/docs/api-guides/freertos-smp.rst +++ b/docs/api-guides/freertos-smp.rst @@ -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 the calling core has released the mutex by exiting the critical section. -:ref:`deletion-callbacks`: ESP-IDF FreeRTOS has -backported the Thread Local Storage Pointers feature. However they have the -extra feature of deletion callbacks. Deletion callbacks are used to -automatically free memory used by Thread Local Storage Pointers during the task -deletion. Call ``vTaskSetThreadLocalStoragePointerAndDelCallback()`` -to set Thread Local Storage Pointers and deletion callbacks. +:ref:`floating-points`: The ESP32 supports hardware acceleration of single +precision floating point arithmetic (`float`). However the use of hardware +acceleration leads to some behavioral restrictions in ESP-IDF FreeRTOS. +Therefore, tasks that utilize `float` will automatically be pinned to a core if +not done so already. Furthermore, `float` cannot be used in interrupt service +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 `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 +``vTaskSetThreadLocalStoragePointerAndDelCallback()`` to set TLSP and Deletion +Callbacks. :ref:`FreeRTOS Hooks`: Vanilla FreeRTOS Hooks were not designed for SMP. ESP-IDF provides its own Idle and Tick Hooks in addition to the Vanilla FreeRTOS @@ -375,39 +388,83 @@ mutex is provided upon entering and exiting, the type of call should not 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 `vTaskDelete()`) if the task being deleted is not currently +running or is not pinned to the other core (with respect to the core +`vTaskDelete()` is called on). TLSP deletion callbacks will also run immediately +if the same conditions are met. + +However, calling `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: Thread Local Storage Pointers & Deletion Callbacks -------------------------------------------------- -Thread Local Storage Pointers are pointers stored directly in the TCB which -allows each task to have a pointer to a data structure containing that is -specific to that task. However vanilla FreeRTOS provides no functionality to -free the memory pointed to by the Thread Local Storage Pointers. Therefore if -the memory pointed to by the Thread Local Storage Pointers is not explicitly -freed by the user before a task is deleted, memory leak will occur. +Thread Local Storage Pointers (TLSP) are pointers stored directly in the TCB. +TLSP allow each task to have its own unique set of pointers to data structures. +However task deletion behavior in vanilla FreeRTOS does not automatically +free the memory pointed to by TLSP. Therefore if the memory pointed to by +TLSP is not explicitly freed by the user before task deletion, memory leak will +occur. -ESP-IDF FreeRTOS provides the added feature of deletion callbacks. These -deletion callbacks are used to automatically free the memory pointed to by the -Thread Local Storage Pointers when a task is deleted. Each Thread Local Storage -Pointer can have its own call back, and these call backs are called when the -Idle tasks cleans up a deleted tasks. +ESP-IDF FreeRTOS provides the added feature of Deletion Callbacks. Deletion +Callbacks are called automatically during task deletion to free memory pointed +to by TLSP. Each TLSP can have its own Deletion Callback. Note that due to the +to :ref:`task-deletion` behavior, there can be instances where Deletion +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 -``vTaskSetThreadLocalStoragePointer()`` whereas ESP-IDF FreeRTOS sets a Thread -Local Storage Pointers and Deletion Callbacks using -``vTaskSetThreadLocalStoragePointerAndDelCallback()`` which accepts a pointer -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. +Deletion callbacks are of type +``void (*TlsDeleteCallbackFunction_t)( int, void * )`` where the first parameter +is the index number of the associated TLSP, and the second parameter is the +TLSP itself. -In IDF the FreeRTOS thread local storage at index 0 is reserved and is used to implement -the pthreads API thread local storage (pthread_getspecific() & pthread_setspecific()). -Other indexes can be used for any purpose, provided -:ref:`CONFIG_FREERTOS_THREAD_LOCAL_STORAGE_POINTERS` is set to a high enough value. +Deletion callbacks are set alongside TLSP by calling +``vTaskSetThreadLocalStoragePointerAndDelCallback()``. Calling the vanilla +FreeRTOS function ``vTaskSetThreadLocalStoragePointer()`` will simply set the +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. + +: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 :component_file:`freertos/include/freertos/task.h` diff --git a/tools/unit-test-app/main/app_main.c b/tools/unit-test-app/main/app_main.c index b3d249fc50..a7a7e87542 100644 --- a/tools/unit-test-app/main/app_main.c +++ b/tools/unit-test-app/main/app_main.c @@ -6,7 +6,7 @@ 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 */ } From 66fe94f816098e6cc01de8447c3cf0c8c855f721 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Fri, 1 Dec 2017 12:50:45 +0800 Subject: [PATCH 07/56] docs: add FreeRTOS API docs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Use `code` tags instead of a mix of `
` and
  `@verbatim .. @endverbatim`
- Remove manually added function prototypes from comment blocks
- Remove of grouping (`\defgroup`) — some extra work is needed
  to make groups compatible with the way we auto-generate API
  reference from Doxygen XML files. It's pretty easy to add the
  grouping directives back if/when we implement support for
  Doxygen groups in the later stages of documentation build
  process.
- Hide private APIs under `@cond .. @endcond`
- Convert some comments into Doxygen-compatible ones
- Fix various documentation issues: missing documentation for
  some parameters, mismatch between parameter names in comment
  block and in function prototype.
- Add doxygen comments for functions which didn't have them
  (thread local storage).
- Add [out] param tags where necessary
- Redefine `xTaskCreate` and `xTaskCreateStatic` as inline
  functions instead of macros.
---
 .../freertos/include/freertos/event_groups.h  |  617 +++----
 components/freertos/include/freertos/queue.h  | 1518 +++++++---------
 .../freertos/include/freertos/ringbuf.h       |  254 +--
 components/freertos/include/freertos/semphr.h |  876 +++++----
 components/freertos/include/freertos/task.h   | 1577 +++++++++--------
 components/freertos/include/freertos/timers.h |  125 +-
 docs/Doxyfile                                 |   23 +-
 docs/api-reference/system/freertos.rst        |   42 +
 docs/api-reference/system/hooks.rst           |    4 +-
 docs/api-reference/system/index.rst           |   11 +-
 10 files changed, 2420 insertions(+), 2627 deletions(-)
 create mode 100644 docs/api-reference/system/freertos.rst

diff --git a/components/freertos/include/freertos/event_groups.h b/components/freertos/include/freertos/event_groups.h
index a2d70a43f3..6b83e04e18 100644
--- a/components/freertos/include/freertos/event_groups.h
+++ b/components/freertos/include/freertos/event_groups.h
@@ -104,7 +104,6 @@ extern "C" {
  * used to create a synchronisation point between multiple tasks (a
  * 'rendezvous').
  *
- * \defgroup EventGroup
  */
 
 
@@ -116,7 +115,6 @@ extern "C" {
  * xEventGroupCreate() returns an EventGroupHandle_t variable that can then
  * be used as a parameter to other event group functions.
  *
- * \defgroup EventGroupHandle_t EventGroupHandle_t
  * \ingroup EventGroup
  */
 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,
  * 32 bits if set to 0. 
  *
- * \defgroup EventBits_t EventBits_t
  * \ingroup EventGroup
  */
 typedef TickType_t EventBits_t;
 
 /**
- * event_groups.h
- *
- EventGroupHandle_t xEventGroupCreate( void );
- 
- * * Create a new event group. * * 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 * * Example usage: -
-	// Declare a variable to hold the created event group.
-	EventGroupHandle_t xCreatedEventGroup;
-
-	// Attempt to create the event group.
-	xCreatedEventGroup = xEventGroupCreate();
-
-	// Was the event group created successfully?
-	if( xCreatedEventGroup == NULL )
-	{
-		// The event group was not created because there was insufficient
-		// FreeRTOS heap available.
-	}
-	else
-	{
-		// The event group was created.
-	}
-   
- * \defgroup xEventGroupCreate xEventGroupCreate + * @code{c} + * // Declare a variable to hold the created event group. + * EventGroupHandle_t xCreatedEventGroup; + * + * // Attempt to create the event group. + * xCreatedEventGroup = xEventGroupCreate(); + * + * // Was the event group created successfully? + * if( xCreatedEventGroup == NULL ) + * { + * // The event group was not created because there was insufficient + * // FreeRTOS heap available. + * } + * else + * { + * // The event group was created. + * } + * @endcode * \ingroup EventGroup */ #if( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) @@ -188,11 +179,6 @@ typedef TickType_t EventBits_t; #endif /** - * event_groups.h - *
- EventGroupHandle_t xEventGroupCreateStatic( EventGroupHandle_t * pxEventGroupBuffer );
- 
- * * Create a new event group. * * 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. * * Example usage: -
-	// StaticEventGroup_t is a publicly accessible structure that has the same
-	// 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
-	// group (which is dependent on the architecture and configuration file
-	// settings) without breaking the strict data hiding policy by exposing the
-	// real event group internals.  This StaticEventGroup_t variable is passed
-	// into the xSemaphoreCreateEventGroupStatic() function and is used to store
-	// the event group's data structures
-	StaticEventGroup_t xEventGroupBuffer;
-
-	// Create the event group without dynamically allocating any memory.
-	xEventGroup = xEventGroupCreateStatic( &xEventGroupBuffer );
-   
+ * @code{c} + * // StaticEventGroup_t is a publicly accessible structure that has the same + * // 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 + * // group (which is dependent on the architecture and configuration file + * // settings) without breaking the strict data hiding policy by exposing the + * // real event group internals. This StaticEventGroup_t variable is passed + * // into the xSemaphoreCreateEventGroupStatic() function and is used to store + * // the event group's data structures + * StaticEventGroup_t xEventGroupBuffer; + * + * // Create the event group without dynamically allocating any memory. + * xEventGroup = xEventGroupCreateStatic( &xEventGroupBuffer ); + * @endcode */ #if( configSUPPORT_STATIC_ALLOCATION == 1 ) EventGroupHandle_t xEventGroupCreateStatic( StaticEventGroup_t *pxEventGroupBuffer ) PRIVILEGED_FUNCTION; #endif /** - * event_groups.h - *
-	EventBits_t xEventGroupWaitBits( 	EventGroupHandle_t xEventGroup,
-										const EventBits_t uxBitsToWaitFor,
-										const BaseType_t xClearOnExit,
-										const BaseType_t xWaitForAllBits,
-										const TickType_t xTicksToWait );
- 
- * * [Potentially] block to wait for one or more bits to be set within a * previously created event group. * @@ -292,54 +269,48 @@ typedef TickType_t EventBits_t; * pdTRUE. * * Example usage: -
-   #define BIT_0	( 1 << 0 )
-   #define BIT_4	( 1 << 4 )
-
-   void aFunction( EventGroupHandle_t xEventGroup )
-   {
-   EventBits_t uxBits;
-   const TickType_t xTicksToWait = 100 / portTICK_PERIOD_MS;
-
-		// Wait a maximum of 100ms for either bit 0 or bit 4 to be set within
-		// the event group.  Clear the bits before exiting.
-		uxBits = xEventGroupWaitBits(
-					xEventGroup,	// The event group being tested.
-					BIT_0 | BIT_4,	// The bits within the event group to wait for.
-					pdTRUE,			// BIT_0 and BIT_4 should be cleared before returning.
-					pdFALSE,		// Don't wait for both bits, either bit will do.
-					xTicksToWait );	// Wait a maximum of 100ms for either bit to be set.
-
-		if( ( uxBits & ( BIT_0 | BIT_4 ) ) == ( BIT_0 | BIT_4 ) )
-		{
-			// xEventGroupWaitBits() returned because both bits were set.
-		}
-		else if( ( uxBits & BIT_0 ) != 0 )
-		{
-			// xEventGroupWaitBits() returned because just BIT_0 was set.
-		}
-		else if( ( uxBits & BIT_4 ) != 0 )
-		{
-			// xEventGroupWaitBits() returned because just BIT_4 was set.
-		}
-		else
-		{
-			// xEventGroupWaitBits() returned because xTicksToWait ticks passed
-			// without either BIT_0 or BIT_4 becoming set.
-		}
-   }
-   
- * \defgroup xEventGroupWaitBits xEventGroupWaitBits + * @code{c} + * #define BIT_0 ( 1 << 0 ) + * #define BIT_4 ( 1 << 4 ) + * + * void aFunction( EventGroupHandle_t xEventGroup ) + * { + * EventBits_t uxBits; + * const TickType_t xTicksToWait = 100 / portTICK_PERIOD_MS; + * + * // Wait a maximum of 100ms for either bit 0 or bit 4 to be set within + * // the event group. Clear the bits before exiting. + * uxBits = xEventGroupWaitBits( + * xEventGroup, // The event group being tested. + * BIT_0 | BIT_4, // The bits within the event group to wait for. + * pdTRUE, // BIT_0 and BIT_4 should be cleared before returning. + * pdFALSE, // Don't wait for both bits, either bit will do. + * xTicksToWait ); // Wait a maximum of 100ms for either bit to be set. + * + * if( ( uxBits & ( BIT_0 | BIT_4 ) ) == ( BIT_0 | BIT_4 ) ) + * { + * // xEventGroupWaitBits() returned because both bits were set. + * } + * else if( ( uxBits & BIT_0 ) != 0 ) + * { + * // xEventGroupWaitBits() returned because just BIT_0 was set. + * } + * else if( ( uxBits & BIT_4 ) != 0 ) + * { + * // xEventGroupWaitBits() returned because just BIT_4 was set. + * } + * else + * { + * // xEventGroupWaitBits() returned because xTicksToWait ticks passed + * // without either BIT_0 or BIT_4 becoming set. + * } + * } + * @endcode{c} * \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; /** - * event_groups.h - *
-	EventBits_t xEventGroupClearBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToClear );
- 
- * * Clear bits within an event group. This function cannot be called from an * 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. * * Example usage: -
-   #define BIT_0	( 1 << 0 )
-   #define BIT_4	( 1 << 4 )
-
-   void aFunction( EventGroupHandle_t xEventGroup )
-   {
-   EventBits_t uxBits;
-
-		// Clear bit 0 and bit 4 in xEventGroup.
-		uxBits = xEventGroupClearBits(
-								xEventGroup,	// The event group being updated.
-								BIT_0 | BIT_4 );// The bits being cleared.
-
-		if( ( uxBits & ( BIT_0 | BIT_4 ) ) == ( BIT_0 | BIT_4 ) )
-		{
-			// Both bit 0 and bit 4 were set before xEventGroupClearBits() was
-			// called.  Both will now be clear (not set).
-		}
-		else if( ( uxBits & BIT_0 ) != 0 )
-		{
-			// Bit 0 was set before xEventGroupClearBits() was called.  It will
-			// now be clear.
-		}
-		else if( ( uxBits & BIT_4 ) != 0 )
-		{
-			// Bit 4 was set before xEventGroupClearBits() was called.  It will
-			// now be clear.
-		}
-		else
-		{
-			// Neither bit 0 nor bit 4 were set in the first place.
-		}
-   }
-   
- * \defgroup xEventGroupClearBits xEventGroupClearBits + * @code{c} + * #define BIT_0 ( 1 << 0 ) + * #define BIT_4 ( 1 << 4 ) + * + * void aFunction( EventGroupHandle_t xEventGroup ) + * { + * EventBits_t uxBits; + * + * // Clear bit 0 and bit 4 in xEventGroup. + * uxBits = xEventGroupClearBits( + * xEventGroup, // The event group being updated. + * BIT_0 | BIT_4 );// The bits being cleared. + * + * if( ( uxBits & ( BIT_0 | BIT_4 ) ) == ( BIT_0 | BIT_4 ) ) + * { + * // Both bit 0 and bit 4 were set before xEventGroupClearBits() was + * // called. Both will now be clear (not set). + * } + * else if( ( uxBits & BIT_0 ) != 0 ) + * { + * // Bit 0 was set before xEventGroupClearBits() was called. It will + * // now be clear. + * } + * else if( ( uxBits & BIT_4 ) != 0 ) + * { + * // Bit 4 was set before xEventGroupClearBits() was called. It will + * // now be clear. + * } + * else + * { + * // Neither bit 0 nor bit 4 were set in the first place. + * } + * } + * @endcode * \ingroup EventGroup */ EventBits_t xEventGroupClearBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToClear ) PRIVILEGED_FUNCTION; /** - * event_groups.h - *
-	BaseType_t xEventGroupClearBitsFromISR( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet );
- 
- * * A version of xEventGroupClearBits() that can be called from an interrupt. * * 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. * * Example usage: -
-   #define BIT_0	( 1 << 0 )
-   #define BIT_4	( 1 << 4 )
-
-   // An event group which it is assumed has already been created by a call to
-   // xEventGroupCreate().
-   EventGroupHandle_t xEventGroup;
-
-   void anInterruptHandler( void )
-   {
-		// Clear bit 0 and bit 4 in xEventGroup.
-		xResult = xEventGroupClearBitsFromISR(
-							xEventGroup,	 // The event group being updated.
-							BIT_0 | BIT_4 ); // The bits being set.
-
-		if( xResult == pdPASS )
-		{
-			// The message was posted successfully.
-		}
-  }
-   
- * \defgroup xEventGroupSetBitsFromISR xEventGroupSetBitsFromISR + * @code{c} + * #define BIT_0 ( 1 << 0 ) + * #define BIT_4 ( 1 << 4 ) + * + * // An event group which it is assumed has already been created by a call to + * // xEventGroupCreate(). + * EventGroupHandle_t xEventGroup; + * + * void anInterruptHandler( void ) + * { + * // Clear bit 0 and bit 4 in xEventGroup. + * xResult = xEventGroupClearBitsFromISR( + * xEventGroup, // The event group being updated. + * BIT_0 | BIT_4 ); // The bits being set. + * + * if( xResult == pdPASS ) + * { + * // The message was posted successfully. + * } + * } + * @endcode * \ingroup EventGroup */ #if( configUSE_TRACE_FACILITY == 1 ) @@ -451,11 +415,6 @@ EventBits_t xEventGroupClearBits( EventGroupHandle_t xEventGroup, const EventBit #endif /** - * event_groups.h - *
-	EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet );
- 
- * * Set bits within an event group. * This function cannot be called from an interrupt. xEventGroupSetBitsFromISR() * 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. * * Example usage: -
-   #define BIT_0	( 1 << 0 )
-   #define BIT_4	( 1 << 4 )
-
-   void aFunction( EventGroupHandle_t xEventGroup )
-   {
-   EventBits_t uxBits;
-
-		// Set bit 0 and bit 4 in xEventGroup.
-		uxBits = xEventGroupSetBits(
-							xEventGroup,	// The event group being updated.
-							BIT_0 | BIT_4 );// The bits being set.
-
-		if( ( uxBits & ( BIT_0 | BIT_4 ) ) == ( BIT_0 | BIT_4 ) )
-		{
-			// Both bit 0 and bit 4 remained set when the function returned.
-		}
-		else if( ( uxBits & BIT_0 ) != 0 )
-		{
-			// Bit 0 remained set when the function returned, but bit 4 was
-			// cleared.  It might be that bit 4 was cleared automatically as a
-			// task that was waiting for bit 4 was removed from the Blocked
-			// state.
-		}
-		else if( ( uxBits & BIT_4 ) != 0 )
-		{
-			// Bit 4 remained set when the function returned, but bit 0 was
-			// cleared.  It might be that bit 0 was cleared automatically as a
-			// task that was waiting for bit 0 was removed from the Blocked
-			// state.
-		}
-		else
-		{
-			// 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
-			// cleared as the task left the Blocked state.
-		}
-   }
-   
- * \defgroup xEventGroupSetBits xEventGroupSetBits + * @code{c} + * #define BIT_0 ( 1 << 0 ) + * #define BIT_4 ( 1 << 4 ) + * + * void aFunction( EventGroupHandle_t xEventGroup ) + * { + * EventBits_t uxBits; + * + * // Set bit 0 and bit 4 in xEventGroup. + * uxBits = xEventGroupSetBits( + * xEventGroup, // The event group being updated. + * BIT_0 | BIT_4 );// The bits being set. + * + * if( ( uxBits & ( BIT_0 | BIT_4 ) ) == ( BIT_0 | BIT_4 ) ) + * { + * // Both bit 0 and bit 4 remained set when the function returned. + * } + * else if( ( uxBits & BIT_0 ) != 0 ) + * { + * // Bit 0 remained set when the function returned, but bit 4 was + * // cleared. It might be that bit 4 was cleared automatically as a + * // task that was waiting for bit 4 was removed from the Blocked + * // state. + * } + * else if( ( uxBits & BIT_4 ) != 0 ) + * { + * // Bit 4 remained set when the function returned, but bit 0 was + * // cleared. It might be that bit 0 was cleared automatically as a + * // task that was waiting for bit 0 was removed from the Blocked + * // state. + * } + * else + * { + * // 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 + * // cleared as the task left the Blocked state. + * } + * } + * @endcode{c} * \ingroup EventGroup */ EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet ) PRIVILEGED_FUNCTION; /** - * event_groups.h - *
-	BaseType_t xEventGroupSetBitsFromISR( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet, BaseType_t *pxHigherPriorityTaskWoken );
- 
- * * A version of xEventGroupSetBits() that can be called from an interrupt. * * 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. * * Example usage: -
-   #define BIT_0	( 1 << 0 )
-   #define BIT_4	( 1 << 4 )
-
-   // An event group which it is assumed has already been created by a call to
-   // xEventGroupCreate().
-   EventGroupHandle_t xEventGroup;
-
-   void anInterruptHandler( void )
-   {
-   BaseType_t xHigherPriorityTaskWoken, xResult;
-
-		// xHigherPriorityTaskWoken must be initialised to pdFALSE.
-		xHigherPriorityTaskWoken = pdFALSE;
-
-		// Set bit 0 and bit 4 in xEventGroup.
-		xResult = xEventGroupSetBitsFromISR(
-							xEventGroup,	// The event group being updated.
-							BIT_0 | BIT_4   // The bits being set.
-							&xHigherPriorityTaskWoken );
-
-		// Was the message posted successfully?
-		if( xResult == pdPASS )
-		{
-			// If xHigherPriorityTaskWoken is now set to pdTRUE then a context
-			// switch should be requested.  The macro used is port specific and 
-			// will be either portYIELD_FROM_ISR() or portEND_SWITCHING_ISR() - 
-			// refer to the documentation page for the port being used.
-			portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
-		}
-  }
-   
- * \defgroup xEventGroupSetBitsFromISR xEventGroupSetBitsFromISR + * @code{c} + * #define BIT_0 ( 1 << 0 ) + * #define BIT_4 ( 1 << 4 ) + * + * // An event group which it is assumed has already been created by a call to + * // xEventGroupCreate(). + * EventGroupHandle_t xEventGroup; + * + * void anInterruptHandler( void ) + * { + * BaseType_t xHigherPriorityTaskWoken, xResult; + * + * // xHigherPriorityTaskWoken must be initialised to pdFALSE. + * xHigherPriorityTaskWoken = pdFALSE; + * + * // Set bit 0 and bit 4 in xEventGroup. + * xResult = xEventGroupSetBitsFromISR( + * xEventGroup, // The event group being updated. + * BIT_0 | BIT_4 // The bits being set. + * &xHigherPriorityTaskWoken ); + * + * // Was the message posted successfully? + * if( xResult == pdPASS ) + * { + * // If xHigherPriorityTaskWoken is now set to pdTRUE then a context + * // switch should be requested. The macro used is port specific and + * // will be either portYIELD_FROM_ISR() or portEND_SWITCHING_ISR() - + * // refer to the documentation page for the port being used. + * portYIELD_FROM_ISR( xHigherPriorityTaskWoken ); + * } + * } + * @endcode * \ingroup EventGroup */ #if( configUSE_TRACE_FACILITY == 1 ) @@ -603,14 +555,6 @@ EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup, const EventBits_ #endif /** - * event_groups.h - *
-	EventBits_t xEventGroupSync(	EventGroupHandle_t xEventGroup,
-									const EventBits_t uxBitsToSet,
-									const EventBits_t uxBitsToWaitFor,
-									TickType_t xTicksToWait );
- 
- * * 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 * 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. * * Example usage: -
- // Bits used by the three tasks.
- #define TASK_0_BIT		( 1 << 0 )
- #define TASK_1_BIT		( 1 << 1 )
- #define TASK_2_BIT		( 1 << 2 )
-
- #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
- // group has already been created elsewhere.
- EventGroupHandle_t xEventBits;
-
- void vTask0( void *pvParameters )
- {
- EventBits_t uxReturn;
- TickType_t xTicksToWait = 100 / portTICK_PERIOD_MS;
-
-	 for( ;; )
-	 {
-		// Perform task functionality here.
-
-		// 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
-		// 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
-		// for this to happen.
-		uxReturn = xEventGroupSync( xEventBits, TASK_0_BIT, ALL_SYNC_BITS, xTicksToWait );
-
-		if( ( uxReturn & ALL_SYNC_BITS ) == ALL_SYNC_BITS )
-		{
-			// All three tasks reached the synchronisation point before the call
-			// to xEventGroupSync() timed out.
-		}
-	}
- }
-
- void vTask1( void *pvParameters )
- {
-	 for( ;; )
-	 {
-		// Perform task functionality here.
-
-		// 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
-		// bits defined by ALL_SYNC_BITS.  All three tasks have reached the
-		// synchronisation point when all the ALL_SYNC_BITS are set.  Wait
-		// indefinitely for this to happen.
-		xEventGroupSync( xEventBits, TASK_1_BIT, ALL_SYNC_BITS, portMAX_DELAY );
-
-		// xEventGroupSync() was called with an indefinite block time, so
-		// 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.
-	 }
- }
-
- void vTask2( void *pvParameters )
- {
-	 for( ;; )
-	 {
-		// Perform task functionality here.
-
-		// 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
-		// bits defined by ALL_SYNC_BITS.  All three tasks have reached the
-		// synchronisation point when all the ALL_SYNC_BITS are set.  Wait
-		// indefinitely for this to happen.
-		xEventGroupSync( xEventBits, TASK_2_BIT, ALL_SYNC_BITS, portMAX_DELAY );
-
-		// xEventGroupSync() was called with an indefinite block time, so
-		// 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.
-	}
- }
-
- 
- * \defgroup xEventGroupSync xEventGroupSync + * @code{c} + * // Bits used by the three tasks. + * #define TASK_0_BIT ( 1 << 0 ) + * #define TASK_1_BIT ( 1 << 1 ) + * #define TASK_2_BIT ( 1 << 2 ) + * + * #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 + * // group has already been created elsewhere. + * EventGroupHandle_t xEventBits; + * + * void vTask0( void *pvParameters ) + * { + * EventBits_t uxReturn; + * TickType_t xTicksToWait = 100 / portTICK_PERIOD_MS; + * + * for( ;; ) + * { + * // Perform task functionality here. + * + * // 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 + * // 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 + * // for this to happen. + * uxReturn = xEventGroupSync( xEventBits, TASK_0_BIT, ALL_SYNC_BITS, xTicksToWait ); + * + * if( ( uxReturn & ALL_SYNC_BITS ) == ALL_SYNC_BITS ) + * { + * // All three tasks reached the synchronisation point before the call + * // to xEventGroupSync() timed out. + * } + * } + * } + * + * void vTask1( void *pvParameters ) + * { + * for( ;; ) + * { + * // Perform task functionality here. + * + * // 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 + * // bits defined by ALL_SYNC_BITS. All three tasks have reached the + * // synchronisation point when all the ALL_SYNC_BITS are set. Wait + * // indefinitely for this to happen. + * xEventGroupSync( xEventBits, TASK_1_BIT, ALL_SYNC_BITS, portMAX_DELAY ); + * + * // xEventGroupSync() was called with an indefinite block time, so + * // 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. + * } + * } + * + * void vTask2( void *pvParameters ) + * { + * for( ;; ) + * { + * // Perform task functionality here. + * + * // 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 + * // bits defined by ALL_SYNC_BITS. All three tasks have reached the + * // synchronisation point when all the ALL_SYNC_BITS are set. Wait + * // indefinitely for this to happen. + * xEventGroupSync( xEventBits, TASK_2_BIT, ALL_SYNC_BITS, portMAX_DELAY ); + * + * // xEventGroupSync() was called with an indefinite block time, so + * // 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. + * } + * } + * + * @endcode * \ingroup EventGroup */ EventBits_t xEventGroupSync( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet, const EventBits_t uxBitsToWaitFor, TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; /** - * event_groups.h - *
-	EventBits_t xEventGroupGetBits( EventGroupHandle_t xEventGroup );
- 
- * * Returns the current value of the bits in an event group. This function * 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. * - * \defgroup xEventGroupGetBits xEventGroupGetBits * \ingroup EventGroup */ #define xEventGroupGetBits( xEventGroup ) xEventGroupClearBits( xEventGroup, 0 ) /** - * event_groups.h - *
-	EventBits_t xEventGroupGetBitsFromISR( EventGroupHandle_t xEventGroup );
- 
- * * A version of xEventGroupGetBits() that can be called from an ISR. * * @param xEventGroup The event group being queried. * * @return The event group bits at the time xEventGroupGetBitsFromISR() was called. * - * \defgroup xEventGroupGetBitsFromISR xEventGroupGetBitsFromISR * \ingroup EventGroup */ EventBits_t xEventGroupGetBitsFromISR( EventGroupHandle_t xEventGroup ); /** - * event_groups.h - *
-	void xEventGroupDelete( EventGroupHandle_t xEventGroup );
- 
* * Delete an event group that was previously created by a call to * 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 ); +/** @cond */ + /* For internal use only. */ void vEventGroupSetBitsCallback( void *pvEventGroup, const uint32_t ulBitsToSet ); 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 ); #endif +/** @endcond */ + #ifdef __cplusplus } #endif diff --git a/components/freertos/include/freertos/queue.h b/components/freertos/include/freertos/queue.h index 638157765a..e15152ee97 100644 --- a/components/freertos/include/freertos/queue.h +++ b/components/freertos/include/freertos/queue.h @@ -101,6 +101,7 @@ typedef void * QueueSetHandle_t; */ typedef void * QueueSetMemberHandle_t; +/** @cond */ /* For internal use only. */ #define queueSEND_TO_BACK ( ( BaseType_t ) 0 ) #define queueSEND_TO_FRONT ( ( BaseType_t ) 1 ) @@ -114,15 +115,9 @@ typedef void * QueueSetMemberHandle_t; #define queueQUEUE_TYPE_BINARY_SEMAPHORE ( ( uint8_t ) 3U ) #define queueQUEUE_TYPE_RECURSIVE_MUTEX ( ( uint8_t ) 4U ) +/** @endcond */ + /** - * queue. h - *
- QueueHandle_t xQueueCreate(
-							  UBaseType_t uxQueueLength,
-							  UBaseType_t uxItemSize
-						  );
- * 
- * * Creates a new queue instance. This allocates the storage required by the * new queue and returns a handle for the queue. * @@ -138,36 +133,35 @@ typedef void * QueueSetMemberHandle_t; * returned. * * Example usage: -
- struct AMessage
- {
-	char ucMessageID;
-	char ucData[ 20 ];
- };
-
- void vATask( void *pvParameters )
- {
- QueueHandle_t xQueue1, xQueue2;
-
-	// Create a queue capable of containing 10 uint32_t values.
-	xQueue1 = xQueueCreate( 10, sizeof( uint32_t ) );
-	if( xQueue1 == 0 )
-	{
-		// Queue was not created and must not be used.
-	}
-
-	// Create a queue capable of containing 10 pointers to AMessage structures.
-	// These should be passed by pointer as they contain a lot of data.
-	xQueue2 = xQueueCreate( 10, sizeof( struct AMessage * ) );
-	if( xQueue2 == 0 )
-	{
-		// Queue was not created and must not be used.
-	}
-
-	// ... Rest of task code.
- }
- 
- * \defgroup xQueueCreate xQueueCreate + * @code{c} + * struct AMessage + * { + * char ucMessageID; + * char ucData[ 20 ]; + * }; + * + * void vATask( void *pvParameters ) + * { + * QueueHandle_t xQueue1, xQueue2; + * + * // Create a queue capable of containing 10 uint32_t values. + * xQueue1 = xQueueCreate( 10, sizeof( uint32_t ) ); + * if( xQueue1 == 0 ) + * { + * // Queue was not created and must not be used. + * } + * + * // Create a queue capable of containing 10 pointers to AMessage structures. + * // These should be passed by pointer as they contain a lot of data. + * xQueue2 = xQueueCreate( 10, sizeof( struct AMessage * ) ); + * if( xQueue2 == 0 ) + * { + * // Queue was not created and must not be used. + * } + * + * // ... Rest of task code. + * } + * @endcode * \ingroup QueueManagement */ #if( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) @@ -175,16 +169,6 @@ typedef void * QueueSetMemberHandle_t; #endif /** - * queue. h - *
- QueueHandle_t xQueueCreateStatic(
-							  UBaseType_t uxQueueLength,
-							  UBaseType_t uxItemSize,
-							  uint8_t *pucQueueStorageBuffer,
-							  StaticQueue_t *pxQueueBuffer
-						  );
- * 
- * * Creates a new queue instance, and returns a handle by which the new queue * can be referenced. * @@ -207,7 +191,7 @@ typedef void * QueueSetMemberHandle_t; * that will be copied for each posted item. Each item on the queue must be * the same size. * - * @param pucQueueStorageBuffer If uxItemSize is not zero then + * @param pucQueueStorage If uxItemSize is not zero then * pucQueueStorageBuffer must point to a uint8_t array that is at least large * enough to hold the maximum number of items that can be in the queue at any * one time - which is ( uxQueueLength * uxItemsSize ) bytes. If uxItemSize is @@ -220,40 +204,39 @@ typedef void * QueueSetMemberHandle_t; * returned. If pxQueueBuffer is NULL then NULL is returned. * * Example usage: -
- struct AMessage
- {
-	char ucMessageID;
-	char ucData[ 20 ];
- };
-
- #define QUEUE_LENGTH 10
- #define ITEM_SIZE sizeof( uint32_t )
-
- // xQueueBuffer will hold the queue structure.
- StaticQueue_t xQueueBuffer;
-
- // ucQueueStorage will hold the items posted to the queue.  Must be at least
- // [(queue length) * ( queue item size)] bytes long.
- uint8_t ucQueueStorage[ QUEUE_LENGTH * ITEM_SIZE ];
-
- void vATask( void *pvParameters )
- {
- QueueHandle_t xQueue1;
-
-	// Create a queue capable of containing 10 uint32_t values.
-	xQueue1 = xQueueCreate( QUEUE_LENGTH, // The number of items the queue can hold.
-							ITEM_SIZE	  // The size of each item in the queue
-							&( ucQueueStorage[ 0 ] ), // The buffer that will hold the items in the queue.
-							&xQueueBuffer ); // The buffer that will hold the queue structure.
-
-	// The queue is guaranteed to be created successfully as no dynamic memory
-	// allocation is used.  Therefore xQueue1 is now a handle to a valid queue.
-
-	// ... Rest of task code.
- }
- 
- * \defgroup xQueueCreateStatic xQueueCreateStatic + * @code{c} + * struct AMessage + * { + * char ucMessageID; + * char ucData[ 20 ]; + * }; + * + * #define QUEUE_LENGTH 10 + * #define ITEM_SIZE sizeof( uint32_t ) + * + * // xQueueBuffer will hold the queue structure. + * StaticQueue_t xQueueBuffer; + * + * // ucQueueStorage will hold the items posted to the queue. Must be at least + * // [(queue length) * ( queue item size)] bytes long. + * uint8_t ucQueueStorage[ QUEUE_LENGTH * ITEM_SIZE ]; + * + * void vATask( void *pvParameters ) + * { + * QueueHandle_t xQueue1; + * + * // Create a queue capable of containing 10 uint32_t values. + * xQueue1 = xQueueCreate( QUEUE_LENGTH, // The number of items the queue can hold. + * ITEM_SIZE // The size of each item in the queue + * &( ucQueueStorage[ 0 ] ), // The buffer that will hold the items in the queue. + * &xQueueBuffer ); // The buffer that will hold the queue structure. + * + * // The queue is guaranteed to be created successfully as no dynamic memory + * // allocation is used. Therefore xQueue1 is now a handle to a valid queue. + * + * // ... Rest of task code. + * } + * @endcode * \ingroup QueueManagement */ #if( configSUPPORT_STATIC_ALLOCATION == 1 ) @@ -261,15 +244,6 @@ typedef void * QueueSetMemberHandle_t; #endif /* configSUPPORT_STATIC_ALLOCATION */ /** - * queue. h - *
- BaseType_t xQueueSendToToFront(
-								   QueueHandle_t	xQueue,
-								   const void		*pvItemToQueue,
-								   TickType_t		xTicksToWait
-							   );
- * 
- * * This is a macro that calls xQueueGenericSend(). * * Post an item to the front of a queue. The item is queued by copy, not by @@ -293,65 +267,55 @@ typedef void * QueueSetMemberHandle_t; * @return pdTRUE if the item was successfully posted, otherwise errQUEUE_FULL. * * Example usage: -
- struct AMessage
- {
-	char ucMessageID;
-	char ucData[ 20 ];
- } xMessage;
-
- uint32_t ulVar = 10UL;
-
- void vATask( void *pvParameters )
- {
- QueueHandle_t xQueue1, xQueue2;
- struct AMessage *pxMessage;
-
-	// Create a queue capable of containing 10 uint32_t values.
-	xQueue1 = xQueueCreate( 10, sizeof( uint32_t ) );
-
-	// Create a queue capable of containing 10 pointers to AMessage structures.
-	// These should be passed by pointer as they contain a lot of data.
-	xQueue2 = xQueueCreate( 10, sizeof( struct AMessage * ) );
-
-	// ...
-
-	if( xQueue1 != 0 )
-	{
-		// Send an uint32_t.  Wait for 10 ticks for space to become
-		// available if necessary.
-		if( xQueueSendToFront( xQueue1, ( void * ) &ulVar, ( TickType_t ) 10 ) != pdPASS )
-		{
-			// Failed to post the message, even after 10 ticks.
-		}
-	}
-
-	if( xQueue2 != 0 )
-	{
-		// Send a pointer to a struct AMessage object.  Don't block if the
-		// queue is already full.
-		pxMessage = & xMessage;
-		xQueueSendToFront( xQueue2, ( void * ) &pxMessage, ( TickType_t ) 0 );
-	}
-
-	// ... Rest of task code.
- }
- 
- * \defgroup xQueueSend xQueueSend + * @code{c} + * struct AMessage + * { + * char ucMessageID; + * char ucData[ 20 ]; + * } xMessage; + * + * uint32_t ulVar = 10UL; + * + * void vATask( void *pvParameters ) + * { + * QueueHandle_t xQueue1, xQueue2; + * struct AMessage *pxMessage; + * + * // Create a queue capable of containing 10 uint32_t values. + * xQueue1 = xQueueCreate( 10, sizeof( uint32_t ) ); + * + * // Create a queue capable of containing 10 pointers to AMessage structures. + * // These should be passed by pointer as they contain a lot of data. + * xQueue2 = xQueueCreate( 10, sizeof( struct AMessage * ) ); + * + * // ... + * + * if( xQueue1 != 0 ) + * { + * // Send an uint32_t. Wait for 10 ticks for space to become + * // available if necessary. + * if( xQueueSendToFront( xQueue1, ( void * ) &ulVar, ( TickType_t ) 10 ) != pdPASS ) + * { + * // Failed to post the message, even after 10 ticks. + * } + * } + * + * if( xQueue2 != 0 ) + * { + * // Send a pointer to a struct AMessage object. Don't block if the + * // queue is already full. + * pxMessage = & xMessage; + * xQueueSendToFront( xQueue2, ( void * ) &pxMessage, ( TickType_t ) 0 ); + * } + * + * // ... Rest of task code. + * } + * @endcode * \ingroup QueueManagement */ #define xQueueSendToFront( xQueue, pvItemToQueue, xTicksToWait ) xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), ( xTicksToWait ), queueSEND_TO_FRONT ) /** - * queue. h - *
- BaseType_t xQueueSendToBack(
-								   QueueHandle_t	xQueue,
-								   const void		*pvItemToQueue,
-								   TickType_t		xTicksToWait
-							   );
- * 
- * * This is a macro that calls xQueueGenericSend(). * * Post an item to the back of a queue. The item is queued by copy, not by @@ -375,65 +339,55 @@ typedef void * QueueSetMemberHandle_t; * @return pdTRUE if the item was successfully posted, otherwise errQUEUE_FULL. * * Example usage: -
- struct AMessage
- {
-	char ucMessageID;
-	char ucData[ 20 ];
- } xMessage;
-
- uint32_t ulVar = 10UL;
-
- void vATask( void *pvParameters )
- {
- QueueHandle_t xQueue1, xQueue2;
- struct AMessage *pxMessage;
-
-	// Create a queue capable of containing 10 uint32_t values.
-	xQueue1 = xQueueCreate( 10, sizeof( uint32_t ) );
-
-	// Create a queue capable of containing 10 pointers to AMessage structures.
-	// These should be passed by pointer as they contain a lot of data.
-	xQueue2 = xQueueCreate( 10, sizeof( struct AMessage * ) );
-
-	// ...
-
-	if( xQueue1 != 0 )
-	{
-		// Send an uint32_t.  Wait for 10 ticks for space to become
-		// available if necessary.
-		if( xQueueSendToBack( xQueue1, ( void * ) &ulVar, ( TickType_t ) 10 ) != pdPASS )
-		{
-			// Failed to post the message, even after 10 ticks.
-		}
-	}
-
-	if( xQueue2 != 0 )
-	{
-		// Send a pointer to a struct AMessage object.  Don't block if the
-		// queue is already full.
-		pxMessage = & xMessage;
-		xQueueSendToBack( xQueue2, ( void * ) &pxMessage, ( TickType_t ) 0 );
-	}
-
-	// ... Rest of task code.
- }
- 
- * \defgroup xQueueSend xQueueSend + * @code{c} + * struct AMessage + * { + * char ucMessageID; + * char ucData[ 20 ]; + * } xMessage; + * + * uint32_t ulVar = 10UL; + * + * void vATask( void *pvParameters ) + * { + * QueueHandle_t xQueue1, xQueue2; + * struct AMessage *pxMessage; + * + * // Create a queue capable of containing 10 uint32_t values. + * xQueue1 = xQueueCreate( 10, sizeof( uint32_t ) ); + * + * // Create a queue capable of containing 10 pointers to AMessage structures. + * // These should be passed by pointer as they contain a lot of data. + * xQueue2 = xQueueCreate( 10, sizeof( struct AMessage * ) ); + * + * // ... + * + * if( xQueue1 != 0 ) + * { + * // Send an uint32_t. Wait for 10 ticks for space to become + * // available if necessary. + * if( xQueueSendToBack( xQueue1, ( void * ) &ulVar, ( TickType_t ) 10 ) != pdPASS ) + * { + * // Failed to post the message, even after 10 ticks. + * } + * } + * + * if( xQueue2 != 0 ) + * { + * // Send a pointer to a struct AMessage object. Don't block if the + * // queue is already full. + * pxMessage = & xMessage; + * xQueueSendToBack( xQueue2, ( void * ) &pxMessage, ( TickType_t ) 0 ); + * } + * + * // ... Rest of task code. + * } + * @endcode * \ingroup QueueManagement */ #define xQueueSendToBack( xQueue, pvItemToQueue, xTicksToWait ) xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), ( xTicksToWait ), queueSEND_TO_BACK ) /** - * queue. h - *
- BaseType_t xQueueSend(
-							  QueueHandle_t xQueue,
-							  const void * pvItemToQueue,
-							  TickType_t xTicksToWait
-						 );
- * 
- * * This is a macro that calls xQueueGenericSend(). It is included for * backward compatibility with versions of FreeRTOS.org that did not * include the xQueueSendToFront() and xQueueSendToBack() macros. It is @@ -459,64 +413,55 @@ typedef void * QueueSetMemberHandle_t; * @return pdTRUE if the item was successfully posted, otherwise errQUEUE_FULL. * * Example usage: -
- struct AMessage
- {
-	char ucMessageID;
-	char ucData[ 20 ];
- } xMessage;
-
- uint32_t ulVar = 10UL;
-
- void vATask( void *pvParameters )
- {
- QueueHandle_t xQueue1, xQueue2;
- struct AMessage *pxMessage;
-
-	// Create a queue capable of containing 10 uint32_t values.
-	xQueue1 = xQueueCreate( 10, sizeof( uint32_t ) );
-
-	// Create a queue capable of containing 10 pointers to AMessage structures.
-	// These should be passed by pointer as they contain a lot of data.
-	xQueue2 = xQueueCreate( 10, sizeof( struct AMessage * ) );
-
-	// ...
-
-	if( xQueue1 != 0 )
-	{
-		// Send an uint32_t.  Wait for 10 ticks for space to become
-		// available if necessary.
-		if( xQueueSend( xQueue1, ( void * ) &ulVar, ( TickType_t ) 10 ) != pdPASS )
-		{
-			// Failed to post the message, even after 10 ticks.
-		}
-	}
-
-	if( xQueue2 != 0 )
-	{
-		// Send a pointer to a struct AMessage object.  Don't block if the
-		// queue is already full.
-		pxMessage = & xMessage;
-		xQueueSend( xQueue2, ( void * ) &pxMessage, ( TickType_t ) 0 );
-	}
-
-	// ... Rest of task code.
- }
- 
- * \defgroup xQueueSend xQueueSend + * @code{c} + * struct AMessage + * { + * char ucMessageID; + * char ucData[ 20 ]; + * } xMessage; + * + * uint32_t ulVar = 10UL; + * + * void vATask( void *pvParameters ) + * { + * QueueHandle_t xQueue1, xQueue2; + * struct AMessage *pxMessage; + * + * // Create a queue capable of containing 10 uint32_t values. + * xQueue1 = xQueueCreate( 10, sizeof( uint32_t ) ); + * + * // Create a queue capable of containing 10 pointers to AMessage structures. + * // These should be passed by pointer as they contain a lot of data. + * xQueue2 = xQueueCreate( 10, sizeof( struct AMessage * ) ); + * + * // ... + * + * if( xQueue1 != 0 ) + * { + * // Send an uint32_t. Wait for 10 ticks for space to become + * // available if necessary. + * if( xQueueSend( xQueue1, ( void * ) &ulVar, ( TickType_t ) 10 ) != pdPASS ) + * { + * // Failed to post the message, even after 10 ticks. + * } + * } + * + * if( xQueue2 != 0 ) + * { + * // Send a pointer to a struct AMessage object. Don't block if the + * // queue is already full. + * pxMessage = & xMessage; + * xQueueSend( xQueue2, ( void * ) &pxMessage, ( TickType_t ) 0 ); + * } + * + * // ... Rest of task code. + * } + * @endcode * \ingroup QueueManagement */ #define xQueueSend( xQueue, pvItemToQueue, xTicksToWait ) xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), ( xTicksToWait ), queueSEND_TO_BACK ) /** - * queue. h - *
- BaseType_t xQueueOverwrite(
-							  QueueHandle_t xQueue,
-							  const void * pvItemToQueue
-						 );
- * 
- * * Only for use with queues that have a length of one - so the queue is either * empty or full. * @@ -539,70 +484,59 @@ typedef void * QueueSetMemberHandle_t; * to the queue even when the queue is already full. * * Example usage: -
-
- void vFunction( void *pvParameters )
- {
- QueueHandle_t xQueue;
- uint32_t ulVarToSend, ulValReceived;
-
-	// Create a queue to hold one uint32_t value.  It is strongly
-	// recommended *not* to use xQueueOverwrite() on queues that can
-	// contain more than one value, and doing so will trigger an assertion
-	// if configASSERT() is defined.
-	xQueue = xQueueCreate( 1, sizeof( uint32_t ) );
-
-	// Write the value 10 to the queue using xQueueOverwrite().
-	ulVarToSend = 10;
-	xQueueOverwrite( xQueue, &ulVarToSend );
-
-	// Peeking the queue should now return 10, but leave the value 10 in
-	// the queue.  A block time of zero is used as it is known that the
-	// queue holds a value.
-	ulValReceived = 0;
-	xQueuePeek( xQueue, &ulValReceived, 0 );
-
-	if( ulValReceived != 10 )
-	{
-		// Error unless the item was removed by a different task.
-	}
-
-	// The queue is still full.  Use xQueueOverwrite() to overwrite the
-	// value held in the queue with 100.
-	ulVarToSend = 100;
-	xQueueOverwrite( xQueue, &ulVarToSend );
-
-	// This time read from the queue, leaving the queue empty once more.
-	// A block time of 0 is used again.
-	xQueueReceive( xQueue, &ulValReceived, 0 );
-
-	// The value read should be the last value written, even though the
-	// queue was already full when the value was written.
-	if( ulValReceived != 100 )
-	{
-		// Error!
-	}
-
-	// ...
-}
- 
- * \defgroup xQueueOverwrite xQueueOverwrite + * @code{c} + * + * void vFunction( void *pvParameters ) + * { + * QueueHandle_t xQueue; + * uint32_t ulVarToSend, ulValReceived; + * + * // Create a queue to hold one uint32_t value. It is strongly + * // recommended *not* to use xQueueOverwrite() on queues that can + * // contain more than one value, and doing so will trigger an assertion + * // if configASSERT() is defined. + * xQueue = xQueueCreate( 1, sizeof( uint32_t ) ); + * + * // Write the value 10 to the queue using xQueueOverwrite(). + * ulVarToSend = 10; + * xQueueOverwrite( xQueue, &ulVarToSend ); + * + * // Peeking the queue should now return 10, but leave the value 10 in + * // the queue. A block time of zero is used as it is known that the + * // queue holds a value. + * ulValReceived = 0; + * xQueuePeek( xQueue, &ulValReceived, 0 ); + * + * if( ulValReceived != 10 ) + * { + * // Error unless the item was removed by a different task. + * } + * + * // The queue is still full. Use xQueueOverwrite() to overwrite the + * // value held in the queue with 100. + * ulVarToSend = 100; + * xQueueOverwrite( xQueue, &ulVarToSend ); + * + * // This time read from the queue, leaving the queue empty once more. + * // A block time of 0 is used again. + * xQueueReceive( xQueue, &ulValReceived, 0 ); + * + * // The value read should be the last value written, even though the + * // queue was already full when the value was written. + * if( ulValReceived != 100 ) + * { + * // Error! + * } + * + * // ... + * } + * @endcode * \ingroup QueueManagement */ #define xQueueOverwrite( xQueue, pvItemToQueue ) xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), 0, queueOVERWRITE ) /** - * queue. h - *
- BaseType_t xQueueGenericSend(
-									QueueHandle_t xQueue,
-									const void * pvItemToQueue,
-									TickType_t xTicksToWait
-									BaseType_t xCopyPosition
-								);
- * 
- * * It is preferred that the macros xQueueSend(), xQueueSendToFront() and * xQueueSendToBack() are used in place of calling this function directly. * @@ -630,64 +564,55 @@ typedef void * QueueSetMemberHandle_t; * @return pdTRUE if the item was successfully posted, otherwise errQUEUE_FULL. * * Example usage: -
- struct AMessage
- {
-	char ucMessageID;
-	char ucData[ 20 ];
- } xMessage;
-
- uint32_t ulVar = 10UL;
-
- void vATask( void *pvParameters )
- {
- QueueHandle_t xQueue1, xQueue2;
- struct AMessage *pxMessage;
-
-	// Create a queue capable of containing 10 uint32_t values.
-	xQueue1 = xQueueCreate( 10, sizeof( uint32_t ) );
-
-	// Create a queue capable of containing 10 pointers to AMessage structures.
-	// These should be passed by pointer as they contain a lot of data.
-	xQueue2 = xQueueCreate( 10, sizeof( struct AMessage * ) );
-
-	// ...
-
-	if( xQueue1 != 0 )
-	{
-		// Send an uint32_t.  Wait for 10 ticks for space to become
-		// available if necessary.
-		if( xQueueGenericSend( xQueue1, ( void * ) &ulVar, ( TickType_t ) 10, queueSEND_TO_BACK ) != pdPASS )
-		{
-			// Failed to post the message, even after 10 ticks.
-		}
-	}
-
-	if( xQueue2 != 0 )
-	{
-		// Send a pointer to a struct AMessage object.  Don't block if the
-		// queue is already full.
-		pxMessage = & xMessage;
-		xQueueGenericSend( xQueue2, ( void * ) &pxMessage, ( TickType_t ) 0, queueSEND_TO_BACK );
-	}
-
-	// ... Rest of task code.
- }
- 
- * \defgroup xQueueSend xQueueSend + * @code{c} + * struct AMessage + * { + * char ucMessageID; + * char ucData[ 20 ]; + * } xMessage; + * + * uint32_t ulVar = 10UL; + * + * void vATask( void *pvParameters ) + * { + * QueueHandle_t xQueue1, xQueue2; + * struct AMessage *pxMessage; + * + * // Create a queue capable of containing 10 uint32_t values. + * xQueue1 = xQueueCreate( 10, sizeof( uint32_t ) ); + * + * // Create a queue capable of containing 10 pointers to AMessage structures. + * // These should be passed by pointer as they contain a lot of data. + * xQueue2 = xQueueCreate( 10, sizeof( struct AMessage * ) ); + * + * // ... + * + * if( xQueue1 != 0 ) + * { + * // Send an uint32_t. Wait for 10 ticks for space to become + * // available if necessary. + * if( xQueueGenericSend( xQueue1, ( void * ) &ulVar, ( TickType_t ) 10, queueSEND_TO_BACK ) != pdPASS ) + * { + * // Failed to post the message, even after 10 ticks. + * } + * } + * + * if( xQueue2 != 0 ) + * { + * // Send a pointer to a struct AMessage object. Don't block if the + * // queue is already full. + * pxMessage = & xMessage; + * xQueueGenericSend( xQueue2, ( void * ) &pxMessage, ( TickType_t ) 0, queueSEND_TO_BACK ); + * } + * + * // ... Rest of task code. + * } + * @endcode * \ingroup QueueManagement */ BaseType_t xQueueGenericSend( QueueHandle_t xQueue, const void * const pvItemToQueue, TickType_t xTicksToWait, const BaseType_t xCopyPosition ) PRIVILEGED_FUNCTION; /** - * queue. h - *
- BaseType_t xQueuePeek(
-							 QueueHandle_t xQueue,
-							 void *pvBuffer,
-							 TickType_t xTicksToWait
-						 );
- * * This is a macro that calls the xQueueGenericReceive() function. * * Receive an item from a queue without removing the item from the queue. @@ -719,70 +644,62 @@ BaseType_t xQueueGenericSend( QueueHandle_t xQueue, const void * const pvItemToQ * otherwise pdFALSE. * * Example usage: -
- struct AMessage
- {
-	char ucMessageID;
-	char ucData[ 20 ];
- } xMessage;
-
- QueueHandle_t xQueue;
-
- // Task to create a queue and post a value.
- void vATask( void *pvParameters )
- {
- struct AMessage *pxMessage;
-
-	// Create a queue capable of containing 10 pointers to AMessage structures.
-	// These should be passed by pointer as they contain a lot of data.
-	xQueue = xQueueCreate( 10, sizeof( struct AMessage * ) );
-	if( xQueue == 0 )
-	{
-		// Failed to create the queue.
-	}
-
-	// ...
-
-	// Send a pointer to a struct AMessage object.  Don't block if the
-	// queue is already full.
-	pxMessage = & xMessage;
-	xQueueSend( xQueue, ( void * ) &pxMessage, ( TickType_t ) 0 );
-
-	// ... Rest of task code.
- }
-
- // Task to peek the data from the queue.
- void vADifferentTask( void *pvParameters )
- {
- struct AMessage *pxRxedMessage;
-
-	if( xQueue != 0 )
-	{
-		// Peek a message on the created queue.  Block for 10 ticks if a
-		// message is not immediately available.
-		if( xQueuePeek( xQueue, &( pxRxedMessage ), ( TickType_t ) 10 ) )
-		{
-			// pcRxedMessage now points to the struct AMessage variable posted
-			// by vATask, but the item still remains on the queue.
-		}
-	}
-
-	// ... Rest of task code.
- }
- 
- * \defgroup xQueueReceive xQueueReceive + * @code{c} + * struct AMessage + * { + * char ucMessageID; + * char ucData[ 20 ]; + * } xMessage; + * + * QueueHandle_t xQueue; + * + * // Task to create a queue and post a value. + * void vATask( void *pvParameters ) + * { + * struct AMessage *pxMessage; + * + * // Create a queue capable of containing 10 pointers to AMessage structures. + * // These should be passed by pointer as they contain a lot of data. + * xQueue = xQueueCreate( 10, sizeof( struct AMessage * ) ); + * if( xQueue == 0 ) + * { + * // Failed to create the queue. + * } + * + * // ... + * + * // Send a pointer to a struct AMessage object. Don't block if the + * // queue is already full. + * pxMessage = & xMessage; + * xQueueSend( xQueue, ( void * ) &pxMessage, ( TickType_t ) 0 ); + * + * // ... Rest of task code. + * } + * + * // Task to peek the data from the queue. + * void vADifferentTask( void *pvParameters ) + * { + * struct AMessage *pxRxedMessage; + * + * if( xQueue != 0 ) + * { + * // Peek a message on the created queue. Block for 10 ticks if a + * // message is not immediately available. + * if( xQueuePeek( xQueue, &( pxRxedMessage ), ( TickType_t ) 10 ) ) + * { + * // pcRxedMessage now points to the struct AMessage variable posted + * // by vATask, but the item still remains on the queue. + * } + * } + * + * // ... Rest of task code. + * } + * @endcode * \ingroup QueueManagement */ #define xQueuePeek( xQueue, pvBuffer, xTicksToWait ) xQueueGenericReceive( ( xQueue ), ( pvBuffer ), ( xTicksToWait ), pdTRUE ) /** - * queue. h - *
- BaseType_t xQueuePeekFromISR(
-									QueueHandle_t xQueue,
-									void *pvBuffer,
-								);
- * * A version of xQueuePeek() that can be called from an interrupt service * routine (ISR). * @@ -803,7 +720,6 @@ BaseType_t xQueueGenericSend( QueueHandle_t xQueue, const void * const pvItemToQ * @return pdTRUE if an item was successfully received from the queue, * otherwise pdFALSE. * - * \defgroup xQueuePeekFromISR xQueuePeekFromISR * \ingroup QueueManagement */ BaseType_t xQueuePeekFromISR( QueueHandle_t xQueue, void * const pvBuffer ) PRIVILEGED_FUNCTION; @@ -845,73 +761,63 @@ BaseType_t xQueuePeekFromISR( QueueHandle_t xQueue, void * const pvBuffer ) PRIV * otherwise pdFALSE. * * Example usage: -
- struct AMessage
- {
-	char ucMessageID;
-	char ucData[ 20 ];
- } xMessage;
-
- QueueHandle_t xQueue;
-
- // Task to create a queue and post a value.
- void vATask( void *pvParameters )
- {
- struct AMessage *pxMessage;
-
-	// Create a queue capable of containing 10 pointers to AMessage structures.
-	// These should be passed by pointer as they contain a lot of data.
-	xQueue = xQueueCreate( 10, sizeof( struct AMessage * ) );
-	if( xQueue == 0 )
-	{
-		// Failed to create the queue.
-	}
-
-	// ...
-
-	// Send a pointer to a struct AMessage object.  Don't block if the
-	// queue is already full.
-	pxMessage = & xMessage;
-	xQueueSend( xQueue, ( void * ) &pxMessage, ( TickType_t ) 0 );
-
-	// ... Rest of task code.
- }
-
- // Task to receive from the queue.
- void vADifferentTask( void *pvParameters )
- {
- struct AMessage *pxRxedMessage;
-
-	if( xQueue != 0 )
-	{
-		// Receive a message on the created queue.  Block for 10 ticks if a
-		// message is not immediately available.
-		if( xQueueReceive( xQueue, &( pxRxedMessage ), ( TickType_t ) 10 ) )
-		{
-			// pcRxedMessage now points to the struct AMessage variable posted
-			// by vATask.
-		}
-	}
-
-	// ... Rest of task code.
- }
- 
- * \defgroup xQueueReceive xQueueReceive + * @code{c} + * struct AMessage + * { + * char ucMessageID; + * char ucData[ 20 ]; + * } xMessage; + * + * QueueHandle_t xQueue; + * + * // Task to create a queue and post a value. + * void vATask( void *pvParameters ) + * { + * struct AMessage *pxMessage; + * + * // Create a queue capable of containing 10 pointers to AMessage structures. + * // These should be passed by pointer as they contain a lot of data. + * xQueue = xQueueCreate( 10, sizeof( struct AMessage * ) ); + * if( xQueue == 0 ) + * { + * // Failed to create the queue. + * } + * + * // ... + * + * // Send a pointer to a struct AMessage object. Don't block if the + * // queue is already full. + * pxMessage = & xMessage; + * xQueueSend( xQueue, ( void * ) &pxMessage, ( TickType_t ) 0 ); + * + * // ... Rest of task code. + * } + * + * // Task to receive from the queue. + * void vADifferentTask( void *pvParameters ) + * { + * struct AMessage *pxRxedMessage; + * + * if( xQueue != 0 ) + * { + * // Receive a message on the created queue. Block for 10 ticks if a + * // message is not immediately available. + * if( xQueueReceive( xQueue, &( pxRxedMessage ), ( TickType_t ) 10 ) ) + * { + * // pcRxedMessage now points to the struct AMessage variable posted + * // by vATask. + * } + * } + * + * // ... Rest of task code. + * } + * @endcode * \ingroup QueueManagement */ #define xQueueReceive( xQueue, pvBuffer, xTicksToWait ) xQueueGenericReceive( ( xQueue ), ( pvBuffer ), ( xTicksToWait ), pdFALSE ) /** - * queue. h - *
- BaseType_t xQueueGenericReceive(
-									   QueueHandle_t	xQueue,
-									   void	*pvBuffer,
-									   TickType_t	xTicksToWait
-									   BaseType_t	xJustPeek
-									);
- * * It is preferred that the macro xQueueReceive() be used rather than calling * this function directly. * @@ -944,81 +850,73 @@ BaseType_t xQueuePeekFromISR( QueueHandle_t xQueue, void * const pvBuffer ) PRIV * otherwise pdFALSE. * * Example usage: -
- struct AMessage
- {
-	char ucMessageID;
-	char ucData[ 20 ];
- } xMessage;
-
- QueueHandle_t xQueue;
-
- // Task to create a queue and post a value.
- void vATask( void *pvParameters )
- {
- struct AMessage *pxMessage;
-
-	// Create a queue capable of containing 10 pointers to AMessage structures.
-	// These should be passed by pointer as they contain a lot of data.
-	xQueue = xQueueCreate( 10, sizeof( struct AMessage * ) );
-	if( xQueue == 0 )
-	{
-		// Failed to create the queue.
-	}
-
-	// ...
-
-	// Send a pointer to a struct AMessage object.  Don't block if the
-	// queue is already full.
-	pxMessage = & xMessage;
-	xQueueSend( xQueue, ( void * ) &pxMessage, ( TickType_t ) 0 );
-
-	// ... Rest of task code.
- }
-
- // Task to receive from the queue.
- void vADifferentTask( void *pvParameters )
- {
- struct AMessage *pxRxedMessage;
-
-	if( xQueue != 0 )
-	{
-		// Receive a message on the created queue.  Block for 10 ticks if a
-		// message is not immediately available.
-		if( xQueueGenericReceive( xQueue, &( pxRxedMessage ), ( TickType_t ) 10 ) )
-		{
-			// pcRxedMessage now points to the struct AMessage variable posted
-			// by vATask.
-		}
-	}
-
-	// ... Rest of task code.
- }
- 
- * \defgroup xQueueReceive xQueueReceive + * @code{c} + * struct AMessage + * { + * char ucMessageID; + * char ucData[ 20 ]; + * } xMessage; + * + * QueueHandle_t xQueue; + * + * // Task to create a queue and post a value. + * void vATask( void *pvParameters ) + * { + * struct AMessage *pxMessage; + * + * // Create a queue capable of containing 10 pointers to AMessage structures. + * // These should be passed by pointer as they contain a lot of data. + * xQueue = xQueueCreate( 10, sizeof( struct AMessage * ) ); + * if( xQueue == 0 ) + * { + * // Failed to create the queue. + * } + * + * // ... + * + * // Send a pointer to a struct AMessage object. Don't block if the + * // queue is already full. + * pxMessage = & xMessage; + * xQueueSend( xQueue, ( void * ) &pxMessage, ( TickType_t ) 0 ); + * + * // ... Rest of task code. + * } + * + * // Task to receive from the queue. + * void vADifferentTask( void *pvParameters ) + * { + * struct AMessage *pxRxedMessage; + * + * if( xQueue != 0 ) + * { + * // Receive a message on the created queue. Block for 10 ticks if a + * // message is not immediately available. + * if( xQueueGenericReceive( xQueue, &( pxRxedMessage ), ( TickType_t ) 10 ) ) + * { + * // pcRxedMessage now points to the struct AMessage variable posted + * // by vATask. + * } + * } + * + * // ... Rest of task code. + * } + * @endcode * \ingroup QueueManagement */ BaseType_t xQueueGenericReceive( QueueHandle_t xQueue, void * const pvBuffer, TickType_t xTicksToWait, const BaseType_t xJustPeek ) PRIVILEGED_FUNCTION; /** - * queue. h - *
UBaseType_t uxQueueMessagesWaiting( const QueueHandle_t xQueue );
- * * Return the number of messages stored in a queue. * * @param xQueue A handle to the queue being queried. * * @return The number of messages available in the queue. * - * \defgroup uxQueueMessagesWaiting uxQueueMessagesWaiting * \ingroup QueueManagement */ UBaseType_t uxQueueMessagesWaiting( const QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; /** - * queue. h - *
UBaseType_t uxQueueSpacesAvailable( const QueueHandle_t xQueue );
- * * Return the number of free spaces available in a queue. This is equal to the * number of items that can be sent to the queue before the queue becomes full * if no items are removed. @@ -1027,35 +925,21 @@ UBaseType_t uxQueueMessagesWaiting( const QueueHandle_t xQueue ) PRIVILEGED_FUNC * * @return The number of spaces available in the queue. * - * \defgroup uxQueueMessagesWaiting uxQueueMessagesWaiting * \ingroup QueueManagement */ UBaseType_t uxQueueSpacesAvailable( const QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; /** - * queue. h - *
void vQueueDelete( QueueHandle_t xQueue );
- * * Delete a queue - freeing all the memory allocated for storing of items * placed on the queue. * * @param xQueue A handle to the queue to be deleted. * - * \defgroup vQueueDelete vQueueDelete * \ingroup QueueManagement */ void vQueueDelete( QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; /** - * queue. h - *
- BaseType_t xQueueSendToFrontFromISR(
-										 QueueHandle_t xQueue,
-										 const void *pvItemToQueue,
-										 BaseType_t *pxHigherPriorityTaskWoken
-									  );
- 
- * * This is a macro that calls xQueueGenericSendFromISR(). * * Post an item to the front of a queue. It is safe to use this macro from @@ -1072,7 +956,7 @@ void vQueueDelete( QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; * queue was created, so this many bytes will be copied from pvItemToQueue * into the queue storage area. * - * @param pxHigherPriorityTaskWoken xQueueSendToFrontFromISR() will set + * @param[out] pxHigherPriorityTaskWoken xQueueSendToFrontFromISR() will set * *pxHigherPriorityTaskWoken to pdTRUE if sending to the queue caused a task * to unblock, and the unblocked task has a priority higher than the currently * running task. If xQueueSendToFromFromISR() sets this value to pdTRUE then @@ -1083,50 +967,39 @@ void vQueueDelete( QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; * * Example usage for buffered IO (where the ISR can obtain more than one value * per call): -
- void vBufferISR( void )
- {
- char cIn;
- BaseType_t xHigherPrioritTaskWoken;
-
-	// We have not woken a task at the start of the ISR.
-	xHigherPriorityTaskWoken = pdFALSE;
-
-	// Loop until the buffer is empty.
-	do
-	{
-		// Obtain a byte from the buffer.
-		cIn = portINPUT_BYTE( RX_REGISTER_ADDRESS );
-
-		// Post the byte.
-		xQueueSendToFrontFromISR( xRxQueue, &cIn, &xHigherPriorityTaskWoken );
-
-	} while( portINPUT_BYTE( BUFFER_COUNT ) );
-
-	// Now the buffer is empty we can switch context if necessary.
-	if( xHigherPriorityTaskWoken )
-	{
-		portYIELD_FROM_ISR ();
-	}
- }
- 
+ * @code{c} + * void vBufferISR( void ) + * { + * char cIn; + * BaseType_t xHigherPrioritTaskWoken; * - * \defgroup xQueueSendFromISR xQueueSendFromISR + * // We have not woken a task at the start of the ISR. + * xHigherPriorityTaskWoken = pdFALSE; + * + * // Loop until the buffer is empty. + * do + * { + * // Obtain a byte from the buffer. + * cIn = portINPUT_BYTE( RX_REGISTER_ADDRESS ); + * + * // Post the byte. + * xQueueSendToFrontFromISR( xRxQueue, &cIn, &xHigherPriorityTaskWoken ); + * + * } while( portINPUT_BYTE( BUFFER_COUNT ) ); + * + * // Now the buffer is empty we can switch context if necessary. + * if( xHigherPriorityTaskWoken ) + * { + * portYIELD_FROM_ISR (); + * } + * } + * @endcode * \ingroup QueueManagement */ #define xQueueSendToFrontFromISR( xQueue, pvItemToQueue, pxHigherPriorityTaskWoken ) xQueueGenericSendFromISR( ( xQueue ), ( pvItemToQueue ), ( pxHigherPriorityTaskWoken ), queueSEND_TO_FRONT ) /** - * queue. h - *
- BaseType_t xQueueSendToBackFromISR(
-										 QueueHandle_t xQueue,
-										 const void *pvItemToQueue,
-										 BaseType_t *pxHigherPriorityTaskWoken
-									  );
- 
- * * This is a macro that calls xQueueGenericSendFromISR(). * * Post an item to the back of a queue. It is safe to use this macro from @@ -1143,7 +1016,7 @@ void vQueueDelete( QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; * queue was created, so this many bytes will be copied from pvItemToQueue * into the queue storage area. * - * @param pxHigherPriorityTaskWoken xQueueSendToBackFromISR() will set + * @param[out] pxHigherPriorityTaskWoken xQueueSendToBackFromISR() will set * *pxHigherPriorityTaskWoken to pdTRUE if sending to the queue caused a task * to unblock, and the unblocked task has a priority higher than the currently * running task. If xQueueSendToBackFromISR() sets this value to pdTRUE then @@ -1154,49 +1027,38 @@ void vQueueDelete( QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; * * Example usage for buffered IO (where the ISR can obtain more than one value * per call): -
- void vBufferISR( void )
- {
- char cIn;
- BaseType_t xHigherPriorityTaskWoken;
-
-	// We have not woken a task at the start of the ISR.
-	xHigherPriorityTaskWoken = pdFALSE;
-
-	// Loop until the buffer is empty.
-	do
-	{
-		// Obtain a byte from the buffer.
-		cIn = portINPUT_BYTE( RX_REGISTER_ADDRESS );
-
-		// Post the byte.
-		xQueueSendToBackFromISR( xRxQueue, &cIn, &xHigherPriorityTaskWoken );
-
-	} while( portINPUT_BYTE( BUFFER_COUNT ) );
-
-	// Now the buffer is empty we can switch context if necessary.
-	if( xHigherPriorityTaskWoken )
-	{
-		portYIELD_FROM_ISR ();
-	}
- }
- 
+ * @code{c} + * void vBufferISR( void ) + * { + * char cIn; + * BaseType_t xHigherPriorityTaskWoken; * - * \defgroup xQueueSendFromISR xQueueSendFromISR + * // We have not woken a task at the start of the ISR. + * xHigherPriorityTaskWoken = pdFALSE; + * + * // Loop until the buffer is empty. + * do + * { + * // Obtain a byte from the buffer. + * cIn = portINPUT_BYTE( RX_REGISTER_ADDRESS ); + * + * // Post the byte. + * xQueueSendToBackFromISR( xRxQueue, &cIn, &xHigherPriorityTaskWoken ); + * + * } while( portINPUT_BYTE( BUFFER_COUNT ) ); + * + * // Now the buffer is empty we can switch context if necessary. + * if( xHigherPriorityTaskWoken ) + * { + * portYIELD_FROM_ISR (); + * } + * } + * @endcode * \ingroup QueueManagement */ #define xQueueSendToBackFromISR( xQueue, pvItemToQueue, pxHigherPriorityTaskWoken ) xQueueGenericSendFromISR( ( xQueue ), ( pvItemToQueue ), ( pxHigherPriorityTaskWoken ), queueSEND_TO_BACK ) /** - * queue. h - *
- BaseType_t xQueueOverwriteFromISR(
-							  QueueHandle_t xQueue,
-							  const void * pvItemToQueue,
-							  BaseType_t *pxHigherPriorityTaskWoken
-						 );
- * 
- * * A version of xQueueOverwrite() that can be used in an interrupt service * routine (ISR). * @@ -1213,7 +1075,7 @@ void vQueueDelete( QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; * queue was created, so this many bytes will be copied from pvItemToQueue * into the queue storage area. * - * @param pxHigherPriorityTaskWoken xQueueOverwriteFromISR() will set + * @param[out] pxHigherPriorityTaskWoken xQueueOverwriteFromISR() will set * *pxHigherPriorityTaskWoken to pdTRUE if sending to the queue caused a task * to unblock, and the unblocked task has a priority higher than the currently * running task. If xQueueOverwriteFromISR() sets this value to pdTRUE then @@ -1226,64 +1088,53 @@ void vQueueDelete( QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; * the queue is already full. * * Example usage: -
-
- QueueHandle_t xQueue;
-
- void vFunction( void *pvParameters )
- {
- 	// Create a queue to hold one uint32_t value.  It is strongly
-	// recommended *not* to use xQueueOverwriteFromISR() on queues that can
-	// contain more than one value, and doing so will trigger an assertion
-	// if configASSERT() is defined.
-	xQueue = xQueueCreate( 1, sizeof( uint32_t ) );
-}
-
-void vAnInterruptHandler( void )
-{
-// xHigherPriorityTaskWoken must be set to pdFALSE before it is used.
-BaseType_t xHigherPriorityTaskWoken = pdFALSE;
-uint32_t ulVarToSend, ulValReceived;
-
-	// Write the value 10 to the queue using xQueueOverwriteFromISR().
-	ulVarToSend = 10;
-	xQueueOverwriteFromISR( xQueue, &ulVarToSend, &xHigherPriorityTaskWoken );
-
-	// The queue is full, but calling xQueueOverwriteFromISR() again will still
-	// pass because the value held in the queue will be overwritten with the
-	// new value.
-	ulVarToSend = 100;
-	xQueueOverwriteFromISR( xQueue, &ulVarToSend, &xHigherPriorityTaskWoken );
-
-	// Reading from the queue will now return 100.
-
-	// ...
-
-	if( xHigherPrioritytaskWoken == pdTRUE )
-	{
-		// Writing to the queue caused a task to unblock and the unblocked task
-		// has a priority higher than or equal to the priority of the currently
-		// executing task (the task this interrupt interrupted).  Perform a context
-		// switch so this interrupt returns directly to the unblocked task.
-		portYIELD_FROM_ISR(); // or portEND_SWITCHING_ISR() depending on the port.
-	}
-}
- 
- * \defgroup xQueueOverwriteFromISR xQueueOverwriteFromISR + * @code{c} + * QueueHandle_t xQueue; + * + * void vFunction( void *pvParameters ) + * { + * // Create a queue to hold one uint32_t value. It is strongly + * // recommended *not* to use xQueueOverwriteFromISR() on queues that can + * // contain more than one value, and doing so will trigger an assertion + * // if configASSERT() is defined. + * xQueue = xQueueCreate( 1, sizeof( uint32_t ) ); + * } + * + * void vAnInterruptHandler( void ) + * { + * // xHigherPriorityTaskWoken must be set to pdFALSE before it is used. + * BaseType_t xHigherPriorityTaskWoken = pdFALSE; + * uint32_t ulVarToSend, ulValReceived; + * + * // Write the value 10 to the queue using xQueueOverwriteFromISR(). + * ulVarToSend = 10; + * xQueueOverwriteFromISR( xQueue, &ulVarToSend, &xHigherPriorityTaskWoken ); + * + * // The queue is full, but calling xQueueOverwriteFromISR() again will still + * // pass because the value held in the queue will be overwritten with the + * // new value. + * ulVarToSend = 100; + * xQueueOverwriteFromISR( xQueue, &ulVarToSend, &xHigherPriorityTaskWoken ); + * + * // Reading from the queue will now return 100. + * + * // ... + * + * if( xHigherPrioritytaskWoken == pdTRUE ) + * { + * // Writing to the queue caused a task to unblock and the unblocked task + * // has a priority higher than or equal to the priority of the currently + * // executing task (the task this interrupt interrupted). Perform a context + * // switch so this interrupt returns directly to the unblocked task. + * portYIELD_FROM_ISR(); // or portEND_SWITCHING_ISR() depending on the port. + * } + * } + * @endcode * \ingroup QueueManagement */ #define xQueueOverwriteFromISR( xQueue, pvItemToQueue, pxHigherPriorityTaskWoken ) xQueueGenericSendFromISR( ( xQueue ), ( pvItemToQueue ), ( pxHigherPriorityTaskWoken ), queueOVERWRITE ) /** - * queue. h - *
- BaseType_t xQueueSendFromISR(
-									 QueueHandle_t xQueue,
-									 const void *pvItemToQueue,
-									 BaseType_t *pxHigherPriorityTaskWoken
-								);
- 
- * * This is a macro that calls xQueueGenericSendFromISR(). It is included * for backward compatibility with versions of FreeRTOS.org that did not * include the xQueueSendToBackFromISR() and xQueueSendToFrontFromISR() @@ -1303,7 +1154,7 @@ uint32_t ulVarToSend, ulValReceived; * queue was created, so this many bytes will be copied from pvItemToQueue * into the queue storage area. * - * @param pxHigherPriorityTaskWoken xQueueSendFromISR() will set + * @param[out] pxHigherPriorityTaskWoken xQueueSendFromISR() will set * *pxHigherPriorityTaskWoken to pdTRUE if sending to the queue caused a task * to unblock, and the unblocked task has a priority higher than the currently * running task. If xQueueSendFromISR() sets this value to pdTRUE then @@ -1314,51 +1165,41 @@ uint32_t ulVarToSend, ulValReceived; * * Example usage for buffered IO (where the ISR can obtain more than one value * per call): -
- void vBufferISR( void )
- {
- char cIn;
- BaseType_t xHigherPriorityTaskWoken;
-
-	// We have not woken a task at the start of the ISR.
-	xHigherPriorityTaskWoken = pdFALSE;
-
-	// Loop until the buffer is empty.
-	do
-	{
-		// Obtain a byte from the buffer.
-		cIn = portINPUT_BYTE( RX_REGISTER_ADDRESS );
-
-		// Post the byte.
-		xQueueSendFromISR( xRxQueue, &cIn, &xHigherPriorityTaskWoken );
-
-	} while( portINPUT_BYTE( BUFFER_COUNT ) );
-
-	// Now the buffer is empty we can switch context if necessary.
-	if( xHigherPriorityTaskWoken )
-	{
-		// Actual macro used here is port specific.
-		portYIELD_FROM_ISR ();
-	}
- }
- 
+ * @code{c} + * void vBufferISR( void ) + * { + * char cIn; + * BaseType_t xHigherPriorityTaskWoken; + * + * // We have not woken a task at the start of the ISR. + * xHigherPriorityTaskWoken = pdFALSE; + * + * // Loop until the buffer is empty. + * do + * { + * // Obtain a byte from the buffer. + * cIn = portINPUT_BYTE( RX_REGISTER_ADDRESS ); + * + * // Post the byte. + * xQueueSendFromISR( xRxQueue, &cIn, &xHigherPriorityTaskWoken ); + * + * } while( portINPUT_BYTE( BUFFER_COUNT ) ); + * + * // Now the buffer is empty we can switch context if necessary. + * if( xHigherPriorityTaskWoken ) + * { + * // Actual macro used here is port specific. + * portYIELD_FROM_ISR (); + * } + * } + * @endcode * - * \defgroup xQueueSendFromISR xQueueSendFromISR * \ingroup QueueManagement */ #define xQueueSendFromISR( xQueue, pvItemToQueue, pxHigherPriorityTaskWoken ) xQueueGenericSendFromISR( ( xQueue ), ( pvItemToQueue ), ( pxHigherPriorityTaskWoken ), queueSEND_TO_BACK ) +/**@{*/ /** - * queue. h - *
- BaseType_t xQueueGenericSendFromISR(
-										   QueueHandle_t		xQueue,
-										   const	void	*pvItemToQueue,
-										   BaseType_t	*pxHigherPriorityTaskWoken,
-										   BaseType_t	xCopyPosition
-									   );
- 
- * * It is preferred that the macros xQueueSendFromISR(), * xQueueSendToFrontFromISR() and xQueueSendToBackFromISR() be used in place * of calling this function directly. xQueueGiveFromISR() is an @@ -1378,7 +1219,7 @@ uint32_t ulVarToSend, ulValReceived; * queue was created, so this many bytes will be copied from pvItemToQueue * into the queue storage area. * - * @param pxHigherPriorityTaskWoken xQueueGenericSendFromISR() will set + * @param[out] pxHigherPriorityTaskWoken xQueueGenericSendFromISR() will set * *pxHigherPriorityTaskWoken to pdTRUE if sending to the queue caused a task * to unblock, and the unblocked task has a priority higher than the currently * running task. If xQueueGenericSendFromISR() sets this value to pdTRUE then @@ -1393,51 +1234,41 @@ uint32_t ulVarToSend, ulValReceived; * * Example usage for buffered IO (where the ISR can obtain more than one value * per call): -
- void vBufferISR( void )
- {
- char cIn;
- BaseType_t xHigherPriorityTaskWokenByPost;
-
-	// We have not woken a task at the start of the ISR.
-	xHigherPriorityTaskWokenByPost = pdFALSE;
-
-	// Loop until the buffer is empty.
-	do
-	{
-		// Obtain a byte from the buffer.
-		cIn = portINPUT_BYTE( RX_REGISTER_ADDRESS );
-
-		// Post each byte.
-		xQueueGenericSendFromISR( xRxQueue, &cIn, &xHigherPriorityTaskWokenByPost, queueSEND_TO_BACK );
-
-	} while( portINPUT_BYTE( BUFFER_COUNT ) );
-
-	// Now the buffer is empty we can switch context if necessary.  Note that the
-	// name of the yield function required is port specific.
-	if( xHigherPriorityTaskWokenByPost )
-	{
-		taskYIELD_YIELD_FROM_ISR();
-	}
- }
- 
+ * @code{c} + * void vBufferISR( void ) + * { + * char cIn; + * BaseType_t xHigherPriorityTaskWokenByPost; * - * \defgroup xQueueSendFromISR xQueueSendFromISR + * // We have not woken a task at the start of the ISR. + * xHigherPriorityTaskWokenByPost = pdFALSE; + * + * // Loop until the buffer is empty. + * do + * { + * // Obtain a byte from the buffer. + * cIn = portINPUT_BYTE( RX_REGISTER_ADDRESS ); + * + * // Post each byte. + * xQueueGenericSendFromISR( xRxQueue, &cIn, &xHigherPriorityTaskWokenByPost, queueSEND_TO_BACK ); + * + * } while( portINPUT_BYTE( BUFFER_COUNT ) ); + * + * // Now the buffer is empty we can switch context if necessary. Note that the + * // name of the yield function required is port specific. + * if( xHigherPriorityTaskWokenByPost ) + * { + * taskYIELD_YIELD_FROM_ISR(); + * } + * } + * @endcode * \ingroup QueueManagement */ BaseType_t xQueueGenericSendFromISR( QueueHandle_t xQueue, const void * const pvItemToQueue, BaseType_t * const pxHigherPriorityTaskWoken, const BaseType_t xCopyPosition ) PRIVILEGED_FUNCTION; BaseType_t xQueueGiveFromISR( QueueHandle_t xQueue, BaseType_t * const pxHigherPriorityTaskWoken ) PRIVILEGED_FUNCTION; +/**@}*/ /** - * queue. h - *
- BaseType_t xQueueReceiveFromISR(
-									   QueueHandle_t	xQueue,
-									   void	*pvBuffer,
-									   BaseType_t *pxTaskWoken
-								   );
- * 
- * * Receive an item from a queue. It is safe to use this function from within an * interrupt service routine. * @@ -1447,7 +1278,7 @@ BaseType_t xQueueGiveFromISR( QueueHandle_t xQueue, BaseType_t * const pxHigherP * @param pvBuffer Pointer to the buffer into which the received item will * be copied. * - * @param pxTaskWoken A task may be blocked waiting for space to become + * @param[out] pxHigherPriorityTaskWoken A task may be blocked waiting for space to become * available on the queue. If xQueueReceiveFromISR causes such a task to * unblock *pxTaskWoken will get set to pdTRUE, otherwise *pxTaskWoken will * remain unchanged. @@ -1456,77 +1287,77 @@ BaseType_t xQueueGiveFromISR( QueueHandle_t xQueue, BaseType_t * const pxHigherP * otherwise pdFALSE. * * Example usage: -
-
- QueueHandle_t xQueue;
-
- // Function to create a queue and post some values.
- void vAFunction( void *pvParameters )
- {
- char cValueToPost;
- const TickType_t xTicksToWait = ( TickType_t )0xff;
-
-	// Create a queue capable of containing 10 characters.
-	xQueue = xQueueCreate( 10, sizeof( char ) );
-	if( xQueue == 0 )
-	{
-		// Failed to create the queue.
-	}
-
-	// ...
-
-	// Post some characters that will be used within an ISR.  If the queue
-	// is full then this task will block for xTicksToWait ticks.
-	cValueToPost = 'a';
-	xQueueSend( xQueue, ( void * ) &cValueToPost, xTicksToWait );
-	cValueToPost = 'b';
-	xQueueSend( xQueue, ( void * ) &cValueToPost, xTicksToWait );
-
-	// ... keep posting characters ... this task may block when the queue
-	// becomes full.
-
-	cValueToPost = 'c';
-	xQueueSend( xQueue, ( void * ) &cValueToPost, xTicksToWait );
- }
-
- // ISR that outputs all the characters received on the queue.
- void vISR_Routine( void )
- {
- BaseType_t xTaskWokenByReceive = pdFALSE;
- char cRxedChar;
-
-	while( xQueueReceiveFromISR( xQueue, ( void * ) &cRxedChar, &xTaskWokenByReceive) )
-	{
-		// A character was received.  Output the character now.
-		vOutputCharacter( cRxedChar );
-
-		// If removing the character from the queue woke the task that was
-		// posting onto the queue cTaskWokenByReceive will have been set to
-		// pdTRUE.  No matter how many times this loop iterates only one
-		// task will be woken.
-	}
-
-	if( cTaskWokenByPost != ( char ) pdFALSE;
-	{
-		taskYIELD ();
-	}
- }
- 
- * \defgroup xQueueReceiveFromISR xQueueReceiveFromISR + * @code{c} + * QueueHandle_t xQueue; + * + * // Function to create a queue and post some values. + * void vAFunction( void *pvParameters ) + * { + * char cValueToPost; + * const TickType_t xTicksToWait = ( TickType_t )0xff; + * + * // Create a queue capable of containing 10 characters. + * xQueue = xQueueCreate( 10, sizeof( char ) ); + * if( xQueue == 0 ) + * { + * // Failed to create the queue. + * } + * + * // ... + * + * // Post some characters that will be used within an ISR. If the queue + * // is full then this task will block for xTicksToWait ticks. + * cValueToPost = 'a'; + * xQueueSend( xQueue, ( void * ) &cValueToPost, xTicksToWait ); + * cValueToPost = 'b'; + * xQueueSend( xQueue, ( void * ) &cValueToPost, xTicksToWait ); + * + * // ... keep posting characters ... this task may block when the queue + * // becomes full. + * + * cValueToPost = 'c'; + * xQueueSend( xQueue, ( void * ) &cValueToPost, xTicksToWait ); + * } + * + * // ISR that outputs all the characters received on the queue. + * void vISR_Routine( void ) + * { + * BaseType_t xTaskWokenByReceive = pdFALSE; + * char cRxedChar; + * + * while( xQueueReceiveFromISR( xQueue, ( void * ) &cRxedChar, &xTaskWokenByReceive) ) + * { + * // A character was received. Output the character now. + * vOutputCharacter( cRxedChar ); + * + * // If removing the character from the queue woke the task that was + * // posting onto the queue cTaskWokenByReceive will have been set to + * // pdTRUE. No matter how many times this loop iterates only one + * // task will be woken. + * } + * + * if( cTaskWokenByPost != ( char ) pdFALSE; + * { + * taskYIELD (); + * } + * } + * @endcode * \ingroup QueueManagement */ BaseType_t xQueueReceiveFromISR( QueueHandle_t xQueue, void * const pvBuffer, BaseType_t * const pxHigherPriorityTaskWoken ) PRIVILEGED_FUNCTION; -/* +/**@{*/ +/** * Utilities to query queues that are safe to use from an ISR. These utilities * should be used only from witin an ISR, or within a critical section. */ BaseType_t xQueueIsQueueEmptyFromISR( const QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; BaseType_t xQueueIsQueueFullFromISR( const QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; UBaseType_t uxQueueMessagesWaitingFromISR( const QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; +/**@}*/ - -/* +/** @cond */ +/** * xQueueAltGenericSend() is an alternative version of xQueueGenericSend(). * Likewise xQueueAltGenericReceive() is an alternative version of * xQueueGenericReceive(). @@ -1578,16 +1409,20 @@ void* xQueueGetMutexHolder( QueueHandle_t xSemaphore ) PRIVILEGED_FUNCTION; */ BaseType_t xQueueTakeMutexRecursive( QueueHandle_t xMutex, TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; BaseType_t xQueueGiveMutexRecursive( QueueHandle_t pxMutex ) PRIVILEGED_FUNCTION; +/** @endcond */ -/* +/** * Reset a queue back to its original empty state. pdPASS is returned if the * queue is successfully reset. pdFAIL is returned if the queue could not be * reset because there are tasks blocked on the queue waiting to either * receive from the queue or send to the queue. + * + * @param xQueue The queue to reset + * @return always returns pdPASS */ #define xQueueReset( xQueue ) xQueueGenericReset( xQueue, pdFALSE ) -/* +/** * The registry is provided as a means for kernel aware debuggers to * locate queues, semaphores and mutexes. Call vQueueAddToRegistry() add * a queue, semaphore or mutex handle to the registry if you want the handle @@ -1613,7 +1448,7 @@ BaseType_t xQueueGiveMutexRecursive( QueueHandle_t pxMutex ) PRIVILEGED_FUNCTION void vQueueAddToRegistry( QueueHandle_t xQueue, const char *pcName ) PRIVILEGED_FUNCTION; /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ #endif -/* +/** * The registry is provided as a means for kernel aware debuggers to * locate queues, semaphores and mutexes. Call vQueueAddToRegistry() add * a queue, semaphore or mutex handle to the registry if you want the handle @@ -1627,7 +1462,7 @@ BaseType_t xQueueGiveMutexRecursive( QueueHandle_t pxMutex ) PRIVILEGED_FUNCTION void vQueueUnregisterQueue( QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; #endif -/* +/** * @note This function has been back ported from FreeRTOS v9.0.0 * * The queue registry is provided as a means for kernel aware debuggers to @@ -1641,10 +1476,10 @@ BaseType_t xQueueGiveMutexRecursive( QueueHandle_t pxMutex ) PRIVILEGED_FUNCTION * returned. */ #if( configQUEUE_REGISTRY_SIZE > 0 ) - const char *pcQueueGetName( QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + const char *pcQueueGetName( QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ #endif -/* +/** * Generic version of the function used to creaet a queue using dynamic memory * allocation. This is called by other functions and macros that create other * RTOS objects that use the queue structure as their base. @@ -1653,7 +1488,7 @@ BaseType_t xQueueGiveMutexRecursive( QueueHandle_t pxMutex ) PRIVILEGED_FUNCTION QueueHandle_t xQueueGenericCreate( const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, const uint8_t ucQueueType ) PRIVILEGED_FUNCTION; #endif -/* +/** * Generic version of the function used to creaet a queue using dynamic memory * allocation. This is called by other functions and macros that create other * RTOS objects that use the queue structure as their base. @@ -1662,7 +1497,7 @@ BaseType_t xQueueGiveMutexRecursive( QueueHandle_t pxMutex ) PRIVILEGED_FUNCTION QueueHandle_t xQueueGenericCreateStatic( const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, uint8_t *pucQueueStorage, StaticQueue_t *pxStaticQueue, const uint8_t ucQueueType ) PRIVILEGED_FUNCTION; #endif -/* +/** * Queue sets provide a mechanism to allow a task to block (pend) on a read * operation from multiple queues or semaphores simultaneously. * @@ -1712,7 +1547,7 @@ BaseType_t xQueueGiveMutexRecursive( QueueHandle_t pxMutex ) PRIVILEGED_FUNCTION */ QueueSetHandle_t xQueueCreateSet( const UBaseType_t uxEventQueueLength ) PRIVILEGED_FUNCTION; -/* +/** * Adds a queue or semaphore to a queue set that was previously created by a * call to xQueueCreateSet(). * @@ -1736,7 +1571,7 @@ QueueSetHandle_t xQueueCreateSet( const UBaseType_t uxEventQueueLength ) PRIVILE */ BaseType_t xQueueAddToSet( QueueSetMemberHandle_t xQueueOrSemaphore, QueueSetHandle_t xQueueSet ) PRIVILEGED_FUNCTION; -/* +/** * Removes a queue or semaphore from a queue set. A queue or semaphore can only * be removed from a set if the queue or semaphore is empty. * @@ -1755,7 +1590,7 @@ BaseType_t xQueueAddToSet( QueueSetMemberHandle_t xQueueOrSemaphore, QueueSetHan */ BaseType_t xQueueRemoveFromSet( QueueSetMemberHandle_t xQueueOrSemaphore, QueueSetHandle_t xQueueSet ) PRIVILEGED_FUNCTION; -/* +/** * xQueueSelectFromSet() selects from the members of a queue set a queue or * semaphore that either contains data (in the case of a queue) or is available * to take (in the case of a semaphore). xQueueSelectFromSet() effectively @@ -1791,18 +1626,19 @@ BaseType_t xQueueRemoveFromSet( QueueSetMemberHandle_t xQueueOrSemaphore, QueueS */ QueueSetMemberHandle_t xQueueSelectFromSet( QueueSetHandle_t xQueueSet, const TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; -/* +/** * A version of xQueueSelectFromSet() that can be used from an ISR. */ QueueSetMemberHandle_t xQueueSelectFromSetFromISR( QueueSetHandle_t xQueueSet ) PRIVILEGED_FUNCTION; +/** @cond */ /* Not public API functions. */ void vQueueWaitForMessageRestricted( QueueHandle_t xQueue, TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; BaseType_t xQueueGenericReset( QueueHandle_t xQueue, BaseType_t xNewQueue ) PRIVILEGED_FUNCTION; void vQueueSetQueueNumber( QueueHandle_t xQueue, UBaseType_t uxQueueNumber ) PRIVILEGED_FUNCTION; UBaseType_t uxQueueGetQueueNumber( QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; uint8_t ucQueueGetQueueType( QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; - +/** @endcond */ #ifdef __cplusplus } diff --git a/components/freertos/include/freertos/ringbuf.h b/components/freertos/include/freertos/ringbuf.h index 23ea868680..88e570aa59 100644 --- a/components/freertos/include/freertos/ringbuf.h +++ b/components/freertos/include/freertos/ringbuf.h @@ -9,50 +9,58 @@ extern "C" { #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 //An opaque handle for a ringbuff object. 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 { + /** 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, + /** 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, + /** 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_t; @@ -60,22 +68,19 @@ typedef enum { /** * @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 - * that at the moment is equal to sizeof(size_t). - * @param allow_split_items : pdTRUE if it is acceptable that item data is inserted as two - * items instead of one. + * @param buf_length Length of circular buffer, in bytes. Each entry will + * take up its own length, plus a header that at the moment + * is equal to sizeof(size_t). + * @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. */ RingbufHandle_t xRingbufferCreate(size_t buf_length, ringbuf_type_t type); - /** * @brief Delete a ring buffer * - * @param ringbuf - Ring buffer to delete - * - * @return void + * @param ringbuf Ring buffer to delete */ void vRingbufferDelete(RingbufHandle_t ringbuf); @@ -83,7 +88,7 @@ void vRingbufferDelete(RingbufHandle_t ringbuf); /** * @brief Get maximum size of an item that can be placed in the ring buffer * - * @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. */ @@ -93,13 +98,15 @@ size_t xRingbufferGetMaxItemSize(RingbufHandle_t ringbuf); /** * @brief Insert an item into the ring buffer * - * @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_size - Size of data to insert. A value of 0 is allowed. - * @param xTicksToWait - Ticks to wait for room in the ringbuffer. + * @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_size Size of data to insert. A value of 0 is allowed. + * @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 - * than indicated by xRingbufferGetMaxItemSize(ringbuf). + * @return + * - 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); @@ -107,11 +114,11 @@ BaseType_t xRingbufferSend(RingbufHandle_t ringbuf, void *data, size_t data_size /** * @brief Insert an item into the ring buffer from an ISR * - * @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_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 - * priority task. + * @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_size Size of data to insert. A value of 0 is allowed. + * @param[out] higher_prio_task_awoken Value pointed to will be set to pdTRUE + * if the push woke up a higher priority task. * * @return pdTRUE if succeeded, pdFALSE when the ring buffer does not have space. */ @@ -120,14 +127,18 @@ BaseType_t xRingbufferSendFromISR(RingbufHandle_t ringbuf, void *data, size_t da /** * @brief Retrieve an item from the ring buffer * - * @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 item_size - Pointer to a variable to which the size of the retrieved item will be written. - * @param xTicksToWait - Ticks to wait for items in the ringbuffer. + * @param ringbuf Ring buffer to retrieve the item from + * @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. * - * @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. + * @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 *xRingbufferReceive(RingbufHandle_t ringbuf, size_t *item_size, TickType_t ticks_to_wait); @@ -135,44 +146,58 @@ void *xRingbufferReceive(RingbufHandle_t ringbuf, size_t *item_size, TickType_t /** * @brief Retrieve an item from the ring buffer from an ISR * - * @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 item_size - Pointer to a variable to which the size of the retrieved item will be written. + * @param ringbuf Ring buffer to retrieve the item from + * @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 - * item. NULL when the ringbuffer is empty, *item_size is untouched in that case. + * @return + * - 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); /** - * @brief Retrieve bytes from a ByteBuf type of ring buffer, specifying the maximum amount of bytes - * to return - - * @note A call to vRingbufferReturnItem() is required after this to free up the data received. + * @brief Retrieve bytes from a ByteBuf type of ring buffer, + * specifying the maximum amount of bytes to return * - * @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 xTicksToWait - Ticks to wait for items in the ringbuffer. + * @note A call to vRingbufferReturnItem() is required after this to free up + * the data received. * - * @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. + * @param ringbuf Ring buffer to retrieve the item from + * @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); /** - * @brief Retrieve bytes from a ByteBuf type of ring buffer, specifying the maximum amount of bytes - * to return. Call this from an ISR. + * @brief Retrieve bytes from a ByteBuf type of ring buffer, + * 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 item_size - Pointer to a variable to which the size of the retrieved item will be written. + * @param ringbuf Ring buffer to retrieve the item from + * @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 - * item. NULL when the ringbuffer is empty, *item_size is untouched in that case. + * @return + * - 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); @@ -181,10 +206,8 @@ void *xRingbufferReceiveUpToFromISR(RingbufHandle_t ringbuf, size_t *item_size, /** * @brief Return a previously-retrieved item to the ringbuffer * - * @param ringbuf - Ring buffer the item was retrieved from - * @param item - Item that was received earlier - * - * @return void + * @param ringbuf Ring buffer the item was retrieved from + * @param item Item that was received earlier */ void vRingbufferReturnItem(RingbufHandle_t ringbuf, void *item); @@ -193,34 +216,37 @@ void vRingbufferReturnItem(RingbufHandle_t ringbuf, void *item); /** * @brief Return a previously-retrieved item to the ringbuffer from an ISR * - * @param ringbuf - Ring buffer the item was retrieved from - * @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 - * priority task. - * - * @return void + * @param ringbuf Ring buffer the item was retrieved from + * @param item Item that was received earlier + * @param[out] higher_prio_task_awoken Value pointed to will be set to pdTRUE + * if the push woke up a higher priority task. */ 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 - * more space has become available in the ringbuffer. + * @brief Add the ringbuffer to a queue set. * - * @param ringbuf - Ring buffer to add to the queue set - * @param xQueueSet - Queue set to add the ringbuffer to + * This specifically adds the semaphore that indicates more space + * 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); /** - * @brief Add the ringbuffer to a queue set. This specifically adds the semaphore that indicates - * something has been written into the ringbuffer. + * @brief Add the ringbuffer to a queue set. * - * @param ringbuf - Ring buffer to add to the queue set - * @param xQueueSet - Queue set to add the ringbuffer to + * This specifically adds the semaphore that indicates something has been + * 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 */ @@ -228,11 +254,13 @@ BaseType_t xRingbufferAddToQueueSetWrite(RingbufHandle_t ringbuf, QueueSetHandle /** - * @brief Remove the ringbuffer from a queue set. This specifically removes the semaphore that indicates - * more space has become available in the ringbuffer. + * @brief Remove the ringbuffer from a queue set. * - * @param ringbuf - Ring buffer to remove from the queue set - * @param xQueueSet - Queue set to remove the ringbuffer from + * This specifically removes the semaphore that indicates more space + * 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 */ @@ -240,11 +268,13 @@ BaseType_t xRingbufferRemoveFromQueueSetRead(RingbufHandle_t ringbuf, QueueSetHa /** - * @brief Remove the ringbuffer from a queue set. This specifically removes the semaphore that indicates - * something has been written to the ringbuffer. + * @brief Remove the ringbuffer from a queue set. * - * @param ringbuf - Ring buffer to remove from the queue set - * @param xQueueSet - Queue set to remove the ringbuffer from + * This specifically removes the semaphore that indicates something + * 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 */ @@ -254,9 +284,7 @@ BaseType_t xRingbufferRemoveFromQueueSetWrite(RingbufHandle_t ringbuf, QueueSetH /** * @brief Debugging function to print the internal pointers in the ring buffer * - * @param ringbuf - Ring buffer to show - * - * @return void + * @param ringbuf Ring buffer to show */ void xRingbufferPrintInfo(RingbufHandle_t ringbuf); diff --git a/components/freertos/include/freertos/semphr.h b/components/freertos/include/freertos/semphr.h index 049b994e53..abe3819f8f 100644 --- a/components/freertos/include/freertos/semphr.h +++ b/components/freertos/include/freertos/semphr.h @@ -82,11 +82,8 @@ typedef QueueHandle_t SemaphoreHandle_t; #define semSEMAPHORE_QUEUE_ITEM_LENGTH ( ( uint8_t ) 0U ) #define semGIVE_BLOCK_TIME ( ( TickType_t ) 0U ) - +/** @cond */ /** - * semphr. h - *
vSemaphoreCreateBinary( SemaphoreHandle_t xSemaphore )
- * * This old vSemaphoreCreateBinary() macro is now deprecated in favour of the * xSemaphoreCreateBinary() function. Note that binary semaphores created using * the vSemaphoreCreateBinary() macro are created in a state such that the @@ -109,23 +106,22 @@ typedef QueueHandle_t SemaphoreHandle_t; * @param xSemaphore Handle to the created semaphore. Should be of type SemaphoreHandle_t. * * Example usage: -
- SemaphoreHandle_t xSemaphore = NULL;
-
- void vATask( void * pvParameters )
- {
-    // Semaphore cannot be used before a call to vSemaphoreCreateBinary ().
-    // This is a macro so pass the variable in directly.
-    vSemaphoreCreateBinary( xSemaphore );
-
-    if( xSemaphore != NULL )
-    {
-        // The semaphore was created successfully.
-        // The semaphore can now be used.
-    }
- }
- 
- * \defgroup vSemaphoreCreateBinary vSemaphoreCreateBinary + * @code{c} + * SemaphoreHandle_t xSemaphore = NULL; + * + * void vATask( void * pvParameters ) + * { + * // Semaphore cannot be used before a call to vSemaphoreCreateBinary (). + * // This is a macro so pass the variable in directly. + * vSemaphoreCreateBinary( xSemaphore ); + * + * if( xSemaphore != NULL ) + * { + * // The semaphore was created successfully. + * // The semaphore can now be used. + * } + * } + * @endcode * \ingroup Semaphores */ #if( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) @@ -138,11 +134,9 @@ typedef QueueHandle_t SemaphoreHandle_t; } \ } #endif +/** @endcond */ /** - * semphr. h - *
SemaphoreHandle_t xSemaphoreCreateBinary( void )
- * * Creates a new binary semaphore instance, and returns a handle by which the * new semaphore can be referenced. * @@ -181,23 +175,22 @@ typedef QueueHandle_t SemaphoreHandle_t; * @return Handle to the created semaphore. * * Example usage: -
- SemaphoreHandle_t xSemaphore = NULL;
-
- void vATask( void * pvParameters )
- {
-    // Semaphore cannot be used before a call to vSemaphoreCreateBinary ().
-    // This is a macro so pass the variable in directly.
-    xSemaphore = xSemaphoreCreateBinary();
-
-    if( xSemaphore != NULL )
-    {
-        // The semaphore was created successfully.
-        // The semaphore can now be used.
-    }
- }
- 
- * \defgroup vSemaphoreCreateBinary vSemaphoreCreateBinary + * @code{c} + * SemaphoreHandle_t xSemaphore = NULL; + * + * void vATask( void * pvParameters ) + * { + * // Semaphore cannot be used before a call to vSemaphoreCreateBinary (). + * // This is a macro so pass the variable in directly. + * xSemaphore = xSemaphoreCreateBinary(); + * + * if( xSemaphore != NULL ) + * { + * // The semaphore was created successfully. + * // The semaphore can now be used. + * } + * } + * @endcode * \ingroup Semaphores */ #if( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) @@ -205,9 +198,6 @@ typedef QueueHandle_t SemaphoreHandle_t; #endif /** - * semphr. h - *
SemaphoreHandle_t xSemaphoreCreateBinaryStatic( StaticSemaphore_t *pxSemaphoreBuffer )
- * * Creates a new binary semaphore instance, and returns a handle by which the * new semaphore can be referenced. * @@ -231,7 +221,7 @@ typedef QueueHandle_t SemaphoreHandle_t; * semaphore does not use a priority inheritance mechanism. For an alternative * that does use priority inheritance see xSemaphoreCreateMutex(). * - * @param pxSemaphoreBuffer Must point to a variable of type StaticSemaphore_t, + * @param pxStaticSemaphore Must point to a variable of type StaticSemaphore_t, * which will then be used to hold the semaphore's data structure, removing the * need for the memory to be allocated dynamically. * @@ -239,24 +229,23 @@ typedef QueueHandle_t SemaphoreHandle_t; * returned. If pxSemaphoreBuffer is NULL then NULL is returned. * * Example usage: -
- SemaphoreHandle_t xSemaphore = NULL;
- StaticSemaphore_t xSemaphoreBuffer;
-
- void vATask( void * pvParameters )
- {
-    // Semaphore cannot be used before a call to xSemaphoreCreateBinary().
-    // The semaphore's data structures will be placed in the xSemaphoreBuffer
-    // variable, the address of which is passed into the function.  The
-    // function's parameter is not NULL, so the function will not attempt any
-    // dynamic memory allocation, and therefore the function will not return
-    // return NULL.
-    xSemaphore = xSemaphoreCreateBinary( &xSemaphoreBuffer );
-
-    // Rest of task code goes here.
- }
- 
- * \defgroup xSemaphoreCreateBinaryStatic xSemaphoreCreateBinaryStatic + * @code{c} + * SemaphoreHandle_t xSemaphore = NULL; + * StaticSemaphore_t xSemaphoreBuffer; + * + * void vATask( void * pvParameters ) + * { + * // Semaphore cannot be used before a call to xSemaphoreCreateBinary(). + * // The semaphore's data structures will be placed in the xSemaphoreBuffer + * // variable, the address of which is passed into the function. The + * // function's parameter is not NULL, so the function will not attempt any + * // dynamic memory allocation, and therefore the function will not return + * // return NULL. + * xSemaphore = xSemaphoreCreateBinary( &xSemaphoreBuffer ); + * + * // Rest of task code goes here. + * } + * @endcode * \ingroup Semaphores */ #if( configSUPPORT_STATIC_ALLOCATION == 1 ) @@ -264,12 +253,6 @@ typedef QueueHandle_t SemaphoreHandle_t; #endif /* configSUPPORT_STATIC_ALLOCATION */ /** - * semphr. h - *
xSemaphoreTake(
- *                   SemaphoreHandle_t xSemaphore,
- *                   TickType_t xBlockTime
- *               )
- * * Macro to obtain a semaphore. The semaphore must have previously been * created with a call to vSemaphoreCreateBinary(), xSemaphoreCreateMutex() or * xSemaphoreCreateCounting(). @@ -287,56 +270,49 @@ typedef QueueHandle_t SemaphoreHandle_t; * if xBlockTime expired without the semaphore becoming available. * * Example usage: -
- SemaphoreHandle_t xSemaphore = NULL;
-
- // A task that creates a semaphore.
- void vATask( void * pvParameters )
- {
-    // Create the semaphore to guard a shared resource.
-    vSemaphoreCreateBinary( xSemaphore );
- }
-
- // A task that uses the semaphore.
- void vAnotherTask( void * pvParameters )
- {
-    // ... Do other things.
-
-    if( xSemaphore != NULL )
-    {
-        // See if we can obtain the semaphore.  If the semaphore is not available
-        // wait 10 ticks to see if it becomes free.
-        if( xSemaphoreTake( xSemaphore, ( TickType_t ) 10 ) == pdTRUE )
-        {
-            // We were able to obtain the semaphore and can now access the
-            // shared resource.
-
-            // ...
-
-            // We have finished accessing the shared resource.  Release the
-            // semaphore.
-            xSemaphoreGive( xSemaphore );
-        }
-        else
-        {
-            // We could not obtain the semaphore and can therefore not access
-            // the shared resource safely.
-        }
-    }
- }
- 
- * \defgroup xSemaphoreTake xSemaphoreTake + * @code{c} + * SemaphoreHandle_t xSemaphore = NULL; + * + * // A task that creates a semaphore. + * void vATask( void * pvParameters ) + * { + * // Create the semaphore to guard a shared resource. + * vSemaphoreCreateBinary( xSemaphore ); + * } + * + * // A task that uses the semaphore. + * void vAnotherTask( void * pvParameters ) + * { + * // ... Do other things. + * + * if( xSemaphore != NULL ) + * { + * // See if we can obtain the semaphore. If the semaphore is not available + * // wait 10 ticks to see if it becomes free. + * if( xSemaphoreTake( xSemaphore, ( TickType_t ) 10 ) == pdTRUE ) + * { + * // We were able to obtain the semaphore and can now access the + * // shared resource. + * + * // ... + * + * // We have finished accessing the shared resource. Release the + * // semaphore. + * xSemaphoreGive( xSemaphore ); + * } + * else + * { + * // We could not obtain the semaphore and can therefore not access + * // the shared resource safely. + * } + * } + * } + * @endcode * \ingroup Semaphores */ #define xSemaphoreTake( xSemaphore, xBlockTime ) xQueueGenericReceive( ( QueueHandle_t ) ( xSemaphore ), NULL, ( xBlockTime ), pdFALSE ) /** - * semphr. h - * xSemaphoreTakeRecursive( - * SemaphoreHandle_t xMutex, - * TickType_t xBlockTime - * ) - * * Macro to recursively obtain, or 'take', a mutex type semaphore. * The mutex must have previously been created using a call to * xSemaphoreCreateRecursiveMutex(); @@ -366,64 +342,63 @@ typedef QueueHandle_t SemaphoreHandle_t; * expired without the semaphore becoming available. * * Example usage: -
- SemaphoreHandle_t xMutex = NULL;
-
- // A task that creates a mutex.
- void vATask( void * pvParameters )
- {
-    // Create the mutex to guard a shared resource.
-    xMutex = xSemaphoreCreateRecursiveMutex();
- }
-
- // A task that uses the mutex.
- void vAnotherTask( void * pvParameters )
- {
-    // ... Do other things.
-
-    if( xMutex != NULL )
-    {
-        // See if we can obtain the mutex.  If the mutex is not available
-        // wait 10 ticks to see if it becomes free.
-        if( xSemaphoreTakeRecursive( xSemaphore, ( TickType_t ) 10 ) == pdTRUE )
-        {
-            // We were able to obtain the mutex and can now access the
-            // shared resource.
-
-            // ...
-            // For some reason due to the nature of the code further calls to
-			// xSemaphoreTakeRecursive() are made on the same mutex.  In real
-			// code these would not be just sequential calls as this would make
-			// no sense.  Instead the calls are likely to be buried inside
-			// a more complex call structure.
-            xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 );
-            xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 );
-
-            // The mutex has now been 'taken' three times, so will not be
-			// available to another task until it has also been given back
-			// three times.  Again it is unlikely that real code would have
-			// these calls sequentially, but instead buried in a more complex
-			// call structure.  This is just for illustrative purposes.
-            xSemaphoreGiveRecursive( xMutex );
-			xSemaphoreGiveRecursive( xMutex );
-			xSemaphoreGiveRecursive( xMutex );
-
-			// Now the mutex can be taken by other tasks.
-        }
-        else
-        {
-            // We could not obtain the mutex and can therefore not access
-            // the shared resource safely.
-        }
-    }
- }
- 
- * \defgroup xSemaphoreTakeRecursive xSemaphoreTakeRecursive + * @code{c} + * SemaphoreHandle_t xMutex = NULL; + * + * // A task that creates a mutex. + * void vATask( void * pvParameters ) + * { + * // Create the mutex to guard a shared resource. + * xMutex = xSemaphoreCreateRecursiveMutex(); + * } + * + * // A task that uses the mutex. + * void vAnotherTask( void * pvParameters ) + * { + * // ... Do other things. + * + * if( xMutex != NULL ) + * { + * // See if we can obtain the mutex. If the mutex is not available + * // wait 10 ticks to see if it becomes free. + * if( xSemaphoreTakeRecursive( xSemaphore, ( TickType_t ) 10 ) == pdTRUE ) + * { + * // We were able to obtain the mutex and can now access the + * // shared resource. + * + * // ... + * // For some reason due to the nature of the code further calls to + * // xSemaphoreTakeRecursive() are made on the same mutex. In real + * // code these would not be just sequential calls as this would make + * // no sense. Instead the calls are likely to be buried inside + * // a more complex call structure. + * xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 ); + * xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 ); + * + * // The mutex has now been 'taken' three times, so will not be + * // available to another task until it has also been given back + * // three times. Again it is unlikely that real code would have + * // these calls sequentially, but instead buried in a more complex + * // call structure. This is just for illustrative purposes. + * xSemaphoreGiveRecursive( xMutex ); + * xSemaphoreGiveRecursive( xMutex ); + * xSemaphoreGiveRecursive( xMutex ); + * + * // Now the mutex can be taken by other tasks. + * } + * else + * { + * // We could not obtain the mutex and can therefore not access + * // the shared resource safely. + * } + * } + * } + * @endcode * \ingroup Semaphores */ #define xSemaphoreTakeRecursive( xMutex, xBlockTime ) xQueueTakeMutexRecursive( ( xMutex ), ( xBlockTime ) ) - +/** @cond */ /* * xSemaphoreAltTake() is an alternative version of xSemaphoreTake(). * @@ -437,11 +412,9 @@ typedef QueueHandle_t SemaphoreHandle_t; * sacrifices execution speed to ensure better interrupt responsiveness. */ #define xSemaphoreAltTake( xSemaphore, xBlockTime ) xQueueAltGenericReceive( ( QueueHandle_t ) ( xSemaphore ), NULL, ( xBlockTime ), pdFALSE ) +/** @endcond */ /** - * semphr. h - *
xSemaphoreGive( SemaphoreHandle_t xSemaphore )
- * * Macro to release a semaphore. The semaphore must have previously been * created with a call to vSemaphoreCreateBinary(), xSemaphoreCreateMutex() or * xSemaphoreCreateCounting(). and obtained using sSemaphoreTake(). @@ -461,50 +434,46 @@ typedef QueueHandle_t SemaphoreHandle_t; * semaphore was not first obtained correctly. * * Example usage: -
- SemaphoreHandle_t xSemaphore = NULL;
-
- void vATask( void * pvParameters )
- {
-    // Create the semaphore to guard a shared resource.
-    vSemaphoreCreateBinary( xSemaphore );
-
-    if( xSemaphore != NULL )
-    {
-        if( xSemaphoreGive( xSemaphore ) != pdTRUE )
-        {
-            // We would expect this call to fail because we cannot give
-            // a semaphore without first "taking" it!
-        }
-
-        // Obtain the semaphore - don't block if the semaphore is not
-        // immediately available.
-        if( xSemaphoreTake( xSemaphore, ( TickType_t ) 0 ) )
-        {
-            // We now have the semaphore and can access the shared resource.
-
-            // ...
-
-            // We have finished accessing the shared resource so can free the
-            // semaphore.
-            if( xSemaphoreGive( xSemaphore ) != pdTRUE )
-            {
-                // We would not expect this call to fail because we must have
-                // obtained the semaphore to get here.
-            }
-        }
-    }
- }
- 
- * \defgroup xSemaphoreGive xSemaphoreGive + * @code{c} + * SemaphoreHandle_t xSemaphore = NULL; + * + * void vATask( void * pvParameters ) + * { + * // Create the semaphore to guard a shared resource. + * vSemaphoreCreateBinary( xSemaphore ); + * + * if( xSemaphore != NULL ) + * { + * if( xSemaphoreGive( xSemaphore ) != pdTRUE ) + * { + * // We would expect this call to fail because we cannot give + * // a semaphore without first "taking" it! + * } + * + * // Obtain the semaphore - don't block if the semaphore is not + * // immediately available. + * if( xSemaphoreTake( xSemaphore, ( TickType_t ) 0 ) ) + * { + * // We now have the semaphore and can access the shared resource. + * + * // ... + * + * // We have finished accessing the shared resource so can free the + * // semaphore. + * if( xSemaphoreGive( xSemaphore ) != pdTRUE ) + * { + * // We would not expect this call to fail because we must have + * // obtained the semaphore to get here. + * } + * } + * } + * } + * @endcode * \ingroup Semaphores */ #define xSemaphoreGive( xSemaphore ) xQueueGenericSend( ( QueueHandle_t ) ( xSemaphore ), NULL, semGIVE_BLOCK_TIME, queueSEND_TO_BACK ) /** - * semphr. h - *
xSemaphoreGiveRecursive( SemaphoreHandle_t xMutex )
- * * Macro to recursively release, or 'give', a mutex type semaphore. * The mutex must have previously been created using a call to * xSemaphoreCreateRecursiveMutex(); @@ -527,64 +496,64 @@ typedef QueueHandle_t SemaphoreHandle_t; * @return pdTRUE if the semaphore was given. * * Example usage: -
- SemaphoreHandle_t xMutex = NULL;
-
- // A task that creates a mutex.
- void vATask( void * pvParameters )
- {
-    // Create the mutex to guard a shared resource.
-    xMutex = xSemaphoreCreateRecursiveMutex();
- }
-
- // A task that uses the mutex.
- void vAnotherTask( void * pvParameters )
- {
-    // ... Do other things.
-
-    if( xMutex != NULL )
-    {
-        // See if we can obtain the mutex.  If the mutex is not available
-        // wait 10 ticks to see if it becomes free.
-        if( xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 ) == pdTRUE )
-        {
-            // We were able to obtain the mutex and can now access the
-            // shared resource.
-
-            // ...
-            // For some reason due to the nature of the code further calls to
-			// xSemaphoreTakeRecursive() are made on the same mutex.  In real
-			// code these would not be just sequential calls as this would make
-			// no sense.  Instead the calls are likely to be buried inside
-			// a more complex call structure.
-            xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 );
-            xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 );
-
-            // The mutex has now been 'taken' three times, so will not be
-			// available to another task until it has also been given back
-			// three times.  Again it is unlikely that real code would have
-			// these calls sequentially, it would be more likely that the calls
-			// to xSemaphoreGiveRecursive() would be called as a call stack
-			// unwound.  This is just for demonstrative purposes.
-            xSemaphoreGiveRecursive( xMutex );
-			xSemaphoreGiveRecursive( xMutex );
-			xSemaphoreGiveRecursive( xMutex );
-
-			// Now the mutex can be taken by other tasks.
-        }
-        else
-        {
-            // We could not obtain the mutex and can therefore not access
-            // the shared resource safely.
-        }
-    }
- }
- 
- * \defgroup xSemaphoreGiveRecursive xSemaphoreGiveRecursive + * @code{c} + * SemaphoreHandle_t xMutex = NULL; + * + * // A task that creates a mutex. + * void vATask( void * pvParameters ) + * { + * // Create the mutex to guard a shared resource. + * xMutex = xSemaphoreCreateRecursiveMutex(); + * } + * + * // A task that uses the mutex. + * void vAnotherTask( void * pvParameters ) + * { + * // ... Do other things. + * + * if( xMutex != NULL ) + * { + * // See if we can obtain the mutex. If the mutex is not available + * // wait 10 ticks to see if it becomes free. + * if( xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 ) == pdTRUE ) + * { + * // We were able to obtain the mutex and can now access the + * // shared resource. + * + * // ... + * // For some reason due to the nature of the code further calls to + * // xSemaphoreTakeRecursive() are made on the same mutex. In real + * // code these would not be just sequential calls as this would make + * // no sense. Instead the calls are likely to be buried inside + * // a more complex call structure. + * xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 ); + * xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 ); + * + * // The mutex has now been 'taken' three times, so will not be + * // available to another task until it has also been given back + * // three times. Again it is unlikely that real code would have + * // these calls sequentially, it would be more likely that the calls + * // to xSemaphoreGiveRecursive() would be called as a call stack + * // unwound. This is just for demonstrative purposes. + * xSemaphoreGiveRecursive( xMutex ); + * xSemaphoreGiveRecursive( xMutex ); + * xSemaphoreGiveRecursive( xMutex ); + * + * // Now the mutex can be taken by other tasks. + * } + * else + * { + * // We could not obtain the mutex and can therefore not access + * // the shared resource safely. + * } + * } + * } + * @endcode * \ingroup Semaphores */ #define xSemaphoreGiveRecursive( xMutex ) xQueueGiveMutexRecursive( ( xMutex ) ) +/** @cond */ /* * xSemaphoreAltGive() is an alternative version of xSemaphoreGive(). * @@ -599,14 +568,9 @@ typedef QueueHandle_t SemaphoreHandle_t; */ #define xSemaphoreAltGive( xSemaphore ) xQueueAltGenericSend( ( QueueHandle_t ) ( xSemaphore ), NULL, semGIVE_BLOCK_TIME, queueSEND_TO_BACK ) +/** @endcond */ + /** - * semphr. h - *
- xSemaphoreGiveFromISR(
-                          SemaphoreHandle_t xSemaphore,
-                          BaseType_t *pxHigherPriorityTaskWoken
-                      )
- * * Macro to release a semaphore. The semaphore must have previously been * created with a call to vSemaphoreCreateBinary() or xSemaphoreCreateCounting(). * @@ -618,7 +582,7 @@ typedef QueueHandle_t SemaphoreHandle_t; * @param xSemaphore A handle to the semaphore being released. This is the * handle returned when the semaphore was created. * - * @param pxHigherPriorityTaskWoken xSemaphoreGiveFromISR() will set + * @param[out] pxHigherPriorityTaskWoken xSemaphoreGiveFromISR() will set * *pxHigherPriorityTaskWoken to pdTRUE if giving the semaphore caused a task * to unblock, and the unblocked task has a priority higher than the currently * running task. If xSemaphoreGiveFromISR() sets this value to pdTRUE then @@ -627,77 +591,69 @@ typedef QueueHandle_t SemaphoreHandle_t; * @return pdTRUE if the semaphore was successfully given, otherwise errQUEUE_FULL. * * Example usage: -
- \#define LONG_TIME 0xffff
- \#define TICKS_TO_WAIT	10
- SemaphoreHandle_t xSemaphore = NULL;
-
- // Repetitive task.
- void vATask( void * pvParameters )
- {
-    for( ;; )
-    {
-        // We want this task to run every 10 ticks of a timer.  The semaphore
-        // was created before this task was started.
-
-        // Block waiting for the semaphore to become available.
-        if( xSemaphoreTake( xSemaphore, LONG_TIME ) == pdTRUE )
-        {
-            // It is time to execute.
-
-            // ...
-
-            // We have finished our task.  Return to the top of the loop where
-            // we will block on the semaphore until it is time to execute
-            // again.  Note when using the semaphore for synchronisation with an
-			// ISR in this manner there is no need to 'give' the semaphore back.
-        }
-    }
- }
-
- // Timer ISR
- void vTimerISR( void * pvParameters )
- {
- static uint8_t ucLocalTickCount = 0;
- static BaseType_t xHigherPriorityTaskWoken;
-
-    // A timer tick has occurred.
-
-    // ... Do other time functions.
-
-    // Is it time for vATask () to run?
-	xHigherPriorityTaskWoken = pdFALSE;
-    ucLocalTickCount++;
-    if( ucLocalTickCount >= TICKS_TO_WAIT )
-    {
-        // Unblock the task by releasing the semaphore.
-        xSemaphoreGiveFromISR( xSemaphore, &xHigherPriorityTaskWoken );
-
-        // Reset the count so we release the semaphore again in 10 ticks time.
-        ucLocalTickCount = 0;
-    }
-
-    if( xHigherPriorityTaskWoken != pdFALSE )
-    {
-        // We can force a context switch here.  Context switching from an
-        // ISR uses port specific syntax.  Check the demo task for your port
-        // to find the syntax required.
-    }
- }
- 
- * \defgroup xSemaphoreGiveFromISR xSemaphoreGiveFromISR + * @code{c} + * \#define LONG_TIME 0xffff + * \#define TICKS_TO_WAIT 10 + * SemaphoreHandle_t xSemaphore = NULL; + * + * // Repetitive task. + * void vATask( void * pvParameters ) + * { + * for( ;; ) + * { + * // We want this task to run every 10 ticks of a timer. The semaphore + * // was created before this task was started. + * + * // Block waiting for the semaphore to become available. + * if( xSemaphoreTake( xSemaphore, LONG_TIME ) == pdTRUE ) + * { + * // It is time to execute. + * + * // ... + * + * // We have finished our task. Return to the top of the loop where + * // we will block on the semaphore until it is time to execute + * // again. Note when using the semaphore for synchronisation with an + * // ISR in this manner there is no need to 'give' the semaphore back. + * } + * } + * } + * + * // Timer ISR + * void vTimerISR( void * pvParameters ) + * { + * static uint8_t ucLocalTickCount = 0; + * static BaseType_t xHigherPriorityTaskWoken; + * + * // A timer tick has occurred. + * + * // ... Do other time functions. + * + * // Is it time for vATask () to run? + * xHigherPriorityTaskWoken = pdFALSE; + * ucLocalTickCount++; + * if( ucLocalTickCount >= TICKS_TO_WAIT ) + * { + * // Unblock the task by releasing the semaphore. + * xSemaphoreGiveFromISR( xSemaphore, &xHigherPriorityTaskWoken ); + * + * // Reset the count so we release the semaphore again in 10 ticks time. + * ucLocalTickCount = 0; + * } + * + * if( xHigherPriorityTaskWoken != pdFALSE ) + * { + * // We can force a context switch here. Context switching from an + * // ISR uses port specific syntax. Check the demo task for your port + * // to find the syntax required. + * } + * } + * @endcode * \ingroup Semaphores */ #define xSemaphoreGiveFromISR( xSemaphore, pxHigherPriorityTaskWoken ) xQueueGiveFromISR( ( QueueHandle_t ) ( xSemaphore ), ( pxHigherPriorityTaskWoken ) ) /** - * semphr. h - *
- xSemaphoreTakeFromISR(
-                          SemaphoreHandle_t xSemaphore,
-                          BaseType_t *pxHigherPriorityTaskWoken
-                      )
- * * Macro to take a semaphore from an ISR. The semaphore must have * previously been created with a call to vSemaphoreCreateBinary() or * xSemaphoreCreateCounting(). @@ -713,7 +669,7 @@ typedef QueueHandle_t SemaphoreHandle_t; * @param xSemaphore A handle to the semaphore being taken. This is the * handle returned when the semaphore was created. * - * @param pxHigherPriorityTaskWoken xSemaphoreTakeFromISR() will set + * @param[out] pxHigherPriorityTaskWoken xSemaphoreTakeFromISR() will set * *pxHigherPriorityTaskWoken to pdTRUE if taking the semaphore caused a task * to unblock, and the unblocked task has a priority higher than the currently * running task. If xSemaphoreTakeFromISR() sets this value to pdTRUE then @@ -725,9 +681,6 @@ typedef QueueHandle_t SemaphoreHandle_t; #define xSemaphoreTakeFromISR( xSemaphore, pxHigherPriorityTaskWoken ) xQueueReceiveFromISR( ( QueueHandle_t ) ( xSemaphore ), NULL, ( pxHigherPriorityTaskWoken ) ) /** - * semphr. h - *
SemaphoreHandle_t xSemaphoreCreateMutex( void )
- * * Macro that implements a mutex semaphore by using the existing queue * mechanism. * @@ -760,23 +713,22 @@ typedef QueueHandle_t SemaphoreHandle_t; * data structures then NULL is returned. * * Example usage: -
- SemaphoreHandle_t xSemaphore;
-
- void vATask( void * pvParameters )
- {
-    // Semaphore cannot be used before a call to xSemaphoreCreateMutex().
-    // This is a macro so pass the variable in directly.
-    xSemaphore = xSemaphoreCreateMutex();
-
-    if( xSemaphore != NULL )
-    {
-        // The semaphore was created successfully.
-        // The semaphore can now be used.
-    }
- }
- 
- * \defgroup vSemaphoreCreateMutex vSemaphoreCreateMutex + * @code{c} + * SemaphoreHandle_t xSemaphore; + * + * void vATask( void * pvParameters ) + * { + * // Semaphore cannot be used before a call to xSemaphoreCreateMutex(). + * // This is a macro so pass the variable in directly. + * xSemaphore = xSemaphoreCreateMutex(); + * + * if( xSemaphore != NULL ) + * { + * // The semaphore was created successfully. + * // The semaphore can now be used. + * } + * } + * @endcode * \ingroup Semaphores */ #if( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) @@ -784,9 +736,6 @@ typedef QueueHandle_t SemaphoreHandle_t; #endif /** - * semphr. h - *
SemaphoreHandle_t xSemaphoreCreateMutexStatic( StaticSemaphore_t *pxMutexBuffer )
- * * Creates a new mutex type semaphore instance, and returns a handle by which * the new mutex can be referenced. * @@ -822,22 +771,21 @@ typedef QueueHandle_t SemaphoreHandle_t; * mutex is returned. If pxMutexBuffer was NULL then NULL is returned. * * Example usage: -
- SemaphoreHandle_t xSemaphore;
- StaticSemaphore_t xMutexBuffer;
-
- void vATask( void * pvParameters )
- {
-    // A mutex cannot be used before it has been created.  xMutexBuffer is
-    // into xSemaphoreCreateMutexStatic() so no dynamic memory allocation is
-    // attempted.
-    xSemaphore = xSemaphoreCreateMutexStatic( &xMutexBuffer );
-
-    // As no dynamic memory allocation was performed, xSemaphore cannot be NULL,
-    // so there is no need to check it.
- }
- 
- * \defgroup xSemaphoreCreateMutexStatic xSemaphoreCreateMutexStatic + * @code + * SemaphoreHandle_t xSemaphore; + * StaticSemaphore_t xMutexBuffer; + * + * void vATask( void * pvParameters ) + * { + * // A mutex cannot be used before it has been created. xMutexBuffer is + * // into xSemaphoreCreateMutexStatic() so no dynamic memory allocation is + * // attempted. + * xSemaphore = xSemaphoreCreateMutexStatic( &xMutexBuffer ); + * + * // As no dynamic memory allocation was performed, xSemaphore cannot be NULL, + * // so there is no need to check it. + * } + * @endcode * \ingroup Semaphores */ #if( configSUPPORT_STATIC_ALLOCATION == 1 ) @@ -846,9 +794,6 @@ typedef QueueHandle_t SemaphoreHandle_t; /** - * semphr. h - *
SemaphoreHandle_t xSemaphoreCreateRecursiveMutex( void )
- * * Creates a new recursive mutex type semaphore instance, and returns a handle * by which the new recursive mutex can be referenced. * @@ -889,23 +834,22 @@ typedef QueueHandle_t SemaphoreHandle_t; * SemaphoreHandle_t. * * Example usage: -
- SemaphoreHandle_t xSemaphore;
-
- void vATask( void * pvParameters )
- {
-    // Semaphore cannot be used before a call to xSemaphoreCreateMutex().
-    // This is a macro so pass the variable in directly.
-    xSemaphore = xSemaphoreCreateRecursiveMutex();
-
-    if( xSemaphore != NULL )
-    {
-        // The semaphore was created successfully.
-        // The semaphore can now be used.
-    }
- }
- 
- * \defgroup vSemaphoreCreateMutex vSemaphoreCreateMutex + * @code{c} + * SemaphoreHandle_t xSemaphore; + * + * void vATask( void * pvParameters ) + * { + * // Semaphore cannot be used before a call to xSemaphoreCreateMutex(). + * // This is a macro so pass the variable in directly. + * xSemaphore = xSemaphoreCreateRecursiveMutex(); + * + * if( xSemaphore != NULL ) + * { + * // The semaphore was created successfully. + * // The semaphore can now be used. + * } + * } + * @endcode * \ingroup Semaphores */ #if( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configUSE_RECURSIVE_MUTEXES == 1 ) ) @@ -913,9 +857,6 @@ typedef QueueHandle_t SemaphoreHandle_t; #endif /** - * semphr. h - *
SemaphoreHandle_t xSemaphoreCreateRecursiveMutexStatic( StaticSemaphore_t *pxMutexBuffer )
- * * Creates a new recursive mutex type semaphore instance, and returns a handle * by which the new recursive mutex can be referenced. * @@ -952,7 +893,7 @@ typedef QueueHandle_t SemaphoreHandle_t; * semaphore and another always 'takes' the semaphore) and from within interrupt * service routines. * - * @param pxMutexBuffer Must point to a variable of type StaticSemaphore_t, + * @param pxStaticSemaphore Must point to a variable of type StaticSemaphore_t, * which will then be used to hold the recursive mutex's data structure, * removing the need for the memory to be allocated dynamically. * @@ -961,24 +902,23 @@ typedef QueueHandle_t SemaphoreHandle_t; * returned. * * Example usage: -
- SemaphoreHandle_t xSemaphore;
- StaticSemaphore_t xMutexBuffer;
-
- void vATask( void * pvParameters )
- {
-    // A recursive semaphore cannot be used before it is created.  Here a
-    // recursive mutex is created using xSemaphoreCreateRecursiveMutexStatic().
-    // The address of xMutexBuffer is passed into the function, and will hold
-    // the mutexes data structures - so no dynamic memory allocation will be
-    // attempted.
-    xSemaphore = xSemaphoreCreateRecursiveMutexStatic( &xMutexBuffer );
-
-    // As no dynamic memory allocation was performed, xSemaphore cannot be NULL,
-    // so there is no need to check it.
- }
- 
- * \defgroup xSemaphoreCreateRecursiveMutexStatic xSemaphoreCreateRecursiveMutexStatic + * @code + * SemaphoreHandle_t xSemaphore; + * StaticSemaphore_t xMutexBuffer; + * + * void vATask( void * pvParameters ) + * { + * // A recursive semaphore cannot be used before it is created. Here a + * // recursive mutex is created using xSemaphoreCreateRecursiveMutexStatic(). + * // The address of xMutexBuffer is passed into the function, and will hold + * // the mutexes data structures - so no dynamic memory allocation will be + * // attempted. + * xSemaphore = xSemaphoreCreateRecursiveMutexStatic( &xMutexBuffer ); + * + * // As no dynamic memory allocation was performed, xSemaphore cannot be NULL, + * // so there is no need to check it. + * } + * @endcode * \ingroup Semaphores */ #if( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configUSE_RECURSIVE_MUTEXES == 1 ) ) @@ -986,9 +926,6 @@ typedef QueueHandle_t SemaphoreHandle_t; #endif /* configSUPPORT_STATIC_ALLOCATION */ /** - * semphr. h - *
SemaphoreHandle_t xSemaphoreCreateCounting( UBaseType_t uxMaxCount, UBaseType_t uxInitialCount )
- * * Creates a new counting semaphore instance, and returns a handle by which the * new counting semaphore can be referenced. * @@ -1039,26 +976,25 @@ typedef QueueHandle_t SemaphoreHandle_t; * created. * * Example usage: -
- SemaphoreHandle_t xSemaphore;
-
- void vATask( void * pvParameters )
- {
- SemaphoreHandle_t xSemaphore = NULL;
-
-    // Semaphore cannot be used before a call to xSemaphoreCreateCounting().
-    // The max value to which the semaphore can count should be 10, and the
-    // initial value assigned to the count should be 0.
-    xSemaphore = xSemaphoreCreateCounting( 10, 0 );
-
-    if( xSemaphore != NULL )
-    {
-        // The semaphore was created successfully.
-        // The semaphore can now be used.
-    }
- }
- 
- * \defgroup xSemaphoreCreateCounting xSemaphoreCreateCounting + * @code{c} + * SemaphoreHandle_t xSemaphore; + * + * void vATask( void * pvParameters ) + * { + * SemaphoreHandle_t xSemaphore = NULL; + * + * // Semaphore cannot be used before a call to xSemaphoreCreateCounting(). + * // The max value to which the semaphore can count should be 10, and the + * // initial value assigned to the count should be 0. + * xSemaphore = xSemaphoreCreateCounting( 10, 0 ); + * + * if( xSemaphore != NULL ) + * { + * // The semaphore was created successfully. + * // The semaphore can now be used. + * } + * } + * @endcode * \ingroup Semaphores */ #if( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) @@ -1066,9 +1002,6 @@ typedef QueueHandle_t SemaphoreHandle_t; #endif /** - * semphr. h - *
SemaphoreHandle_t xSemaphoreCreateCountingStatic( UBaseType_t uxMaxCount, UBaseType_t uxInitialCount, StaticSemaphore_t *pxSemaphoreBuffer )
- * * Creates a new counting semaphore instance, and returns a handle by which the * new counting semaphore can be referenced. * @@ -1123,27 +1056,26 @@ typedef QueueHandle_t SemaphoreHandle_t; * then NULL is returned. * * Example usage: -
- SemaphoreHandle_t xSemaphore;
- StaticSemaphore_t xSemaphoreBuffer;
-
- void vATask( void * pvParameters )
- {
- SemaphoreHandle_t xSemaphore = NULL;
-
-    // Counting semaphore cannot be used before they have been created.  Create
-    // a counting semaphore using xSemaphoreCreateCountingStatic().  The max
-    // value to which the semaphore can count is 10, and the initial value
-    // assigned to the count will be 0.  The address of xSemaphoreBuffer is
-    // passed in and will be used to hold the semaphore structure, so no dynamic
-    // memory allocation will be used.
-    xSemaphore = xSemaphoreCreateCounting( 10, 0, &xSemaphoreBuffer );
-
-    // No memory allocation was attempted so xSemaphore cannot be NULL, so there
-    // is no need to check its value.
- }
- 
- * \defgroup xSemaphoreCreateCountingStatic xSemaphoreCreateCountingStatic + * @code{c} + * SemaphoreHandle_t xSemaphore; + * StaticSemaphore_t xSemaphoreBuffer; + * + * void vATask( void * pvParameters ) + * { + * SemaphoreHandle_t xSemaphore = NULL; + * + * // Counting semaphore cannot be used before they have been created. Create + * // a counting semaphore using xSemaphoreCreateCountingStatic(). The max + * // value to which the semaphore can count is 10, and the initial value + * // assigned to the count will be 0. The address of xSemaphoreBuffer is + * // passed in and will be used to hold the semaphore structure, so no dynamic + * // memory allocation will be used. + * xSemaphore = xSemaphoreCreateCounting( 10, 0, &xSemaphoreBuffer ); + * + * // No memory allocation was attempted so xSemaphore cannot be NULL, so there + * // is no need to check its value. + * } + * @endcode * \ingroup Semaphores */ #if( configSUPPORT_STATIC_ALLOCATION == 1 ) @@ -1151,23 +1083,16 @@ typedef QueueHandle_t SemaphoreHandle_t; #endif /* configSUPPORT_STATIC_ALLOCATION */ /** - * semphr. h - *
void vSemaphoreDelete( SemaphoreHandle_t xSemaphore );
- * * Delete a semaphore. This function must be used with care. For example, * do not delete a mutex type semaphore if the mutex is held by a task. * * @param xSemaphore A handle to the semaphore to be deleted. * - * \defgroup vSemaphoreDelete vSemaphoreDelete * \ingroup Semaphores */ #define vSemaphoreDelete( xSemaphore ) vQueueDelete( ( QueueHandle_t ) ( xSemaphore ) ) /** - * semphr.h - *
TaskHandle_t xSemaphoreGetMutexHolder( SemaphoreHandle_t xMutex );
- * * If xMutex is indeed a mutex type semaphore, return the current mutex holder. * If xMutex is not a mutex type semaphore, or the mutex is available (not held * by a task), return NULL. @@ -1180,9 +1105,6 @@ typedef QueueHandle_t SemaphoreHandle_t; #define xSemaphoreGetMutexHolder( xSemaphore ) xQueueGetMutexHolder( ( xSemaphore ) ) /** - * semphr.h - *
UBaseType_t uxSemaphoreGetCount( SemaphoreHandle_t xSemaphore );
- * * If the semaphore is a counting semaphore then uxSemaphoreGetCount() returns * its current count value. If the semaphore is a binary semaphore then * uxSemaphoreGetCount() returns 1 if the semaphore is available, and 0 if the diff --git a/components/freertos/include/freertos/task.h b/components/freertos/include/freertos/task.h index 073267445f..ab45f1c43e 100644 --- a/components/freertos/include/freertos/task.h +++ b/components/freertos/include/freertos/task.h @@ -102,38 +102,38 @@ extern "C" { * returns (via a pointer parameter) an TaskHandle_t variable that can then * be used as a parameter to vTaskDelete to delete the task. * - * \defgroup TaskHandle_t TaskHandle_t * \ingroup Tasks */ typedef void * TaskHandle_t; -/* +/** * Defines the prototype to which the application task hook function must * conform. */ typedef BaseType_t (*TaskHookFunction_t)( void * ); -/* Task states returned by eTaskGetState. */ +/** Task states returned by eTaskGetState. */ typedef enum { - eRunning = 0, /* A task is querying the state of itself, so must be running. */ - eReady, /* The task being queried is in a read or pending ready list. */ - eBlocked, /* The task being queried is in the Blocked state. */ - eSuspended, /* The task being queried is in the Suspended state, or is in the Blocked state with an infinite time out. */ - eDeleted /* The task being queried has been deleted, but its TCB has not yet been freed. */ + eRunning = 0, /*!< A task is querying the state of itself, so must be running. */ + eReady, /*!< The task being queried is in a read or pending ready list. */ + eBlocked, /*!< The task being queried is in the Blocked state. */ + eSuspended, /*!< The task being queried is in the Suspended state, or is in the Blocked state with an infinite time out. */ + eDeleted /*!< The task being queried has been deleted, but its TCB has not yet been freed. */ } eTaskState; -/* Actions that can be performed when vTaskNotify() is called. */ +/** Actions that can be performed when vTaskNotify() is called. */ typedef enum { - eNoAction = 0, /* Notify the task without updating its notify value. */ - eSetBits, /* Set bits in the task's notification value. */ - eIncrement, /* Increment the task's notification value. */ - eSetValueWithOverwrite, /* Set the task's notification value to a specific value even if the previous value has not yet been read by the task. */ - eSetValueWithoutOverwrite /* Set the task's notification value if the previous value has been read by the task. */ + eNoAction = 0, /*!< Notify the task without updating its notify value. */ + eSetBits, /*!< Set bits in the task's notification value. */ + eIncrement, /*!< Increment the task's notification value. */ + eSetValueWithOverwrite, /*!< Set the task's notification value to a specific value even if the previous value has not yet been read by the task. */ + eSetValueWithoutOverwrite /*!< Set the task's notification value if the previous value has been read by the task. */ } eNotifyAction; -/* +/** @cond */ +/** * Used internally only. */ typedef struct xTIME_OUT @@ -142,7 +142,7 @@ typedef struct xTIME_OUT TickType_t xTimeOnEntering; } TimeOut_t; -/* +/** * Defines the memory ranges allocated to the task when an MPU is used. */ typedef struct xMEMORY_REGION @@ -152,7 +152,7 @@ typedef struct xMEMORY_REGION uint32_t ulParameters; } MemoryRegion_t; -/* +/** * Parameters required to create an MPU protected task. */ typedef struct xTASK_PARAMETERS @@ -165,40 +165,44 @@ typedef struct xTASK_PARAMETERS StackType_t *puxStackBuffer; MemoryRegion_t xRegions[ portNUM_CONFIGURABLE_REGIONS ]; } TaskParameters_t; +/** @endcond */ -/* Used with the uxTaskGetSystemState() function to return the state of each task -in the system. */ +/** + * Used with the uxTaskGetSystemState() function to return the state of each task in the system. +*/ typedef struct xTASK_STATUS { - TaskHandle_t xHandle; /* The handle of the task to which the rest of the information in the structure relates. */ - const char *pcTaskName; /* A pointer to the task's name. This value will be invalid if the task was deleted since the structure was populated! */ /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ - UBaseType_t xTaskNumber; /* A number unique to the task. */ - eTaskState eCurrentState; /* The state in which the task existed when the structure was populated. */ - UBaseType_t uxCurrentPriority; /* The priority at which the task was running (may be inherited) when the structure was populated. */ - UBaseType_t uxBasePriority; /* The priority to which the task will return if the task's current priority has been inherited to avoid unbounded priority inversion when obtaining a mutex. Only valid if configUSE_MUTEXES is defined as 1 in FreeRTOSConfig.h. */ - uint32_t ulRunTimeCounter; /* The total run time allocated to the task so far, as defined by the run time stats clock. See http://www.freertos.org/rtos-run-time-stats.html. Only valid when configGENERATE_RUN_TIME_STATS is defined as 1 in FreeRTOSConfig.h. */ - StackType_t *pxStackBase; /* Points to the lowest address of the task's stack area. */ - uint32_t usStackHighWaterMark; /* The minimum amount of stack space that has remained for the task since the task was created. The closer this value is to zero the closer the task has come to overflowing its stack. */ + TaskHandle_t xHandle; /*!< The handle of the task to which the rest of the information in the structure relates. */ + const char *pcTaskName; /*!< A pointer to the task's name. This value will be invalid if the task was deleted since the structure was populated! */ /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + UBaseType_t xTaskNumber; /*!< A number unique to the task. */ + eTaskState eCurrentState; /*!< The state in which the task existed when the structure was populated. */ + UBaseType_t uxCurrentPriority; /*!< The priority at which the task was running (may be inherited) when the structure was populated. */ + UBaseType_t uxBasePriority; /*!< The priority to which the task will return if the task's current priority has been inherited to avoid unbounded priority inversion when obtaining a mutex. Only valid if configUSE_MUTEXES is defined as 1 in FreeRTOSConfig.h. */ + uint32_t ulRunTimeCounter; /*!< The total run time allocated to the task so far, as defined by the run time stats clock. See http://www.freertos.org/rtos-run-time-stats.html. Only valid when configGENERATE_RUN_TIME_STATS is defined as 1 in FreeRTOSConfig.h. */ + StackType_t *pxStackBase; /*!< Points to the lowest address of the task's stack area. */ + uint32_t usStackHighWaterMark; /*!< The minimum amount of stack space that has remained for the task since the task was created. The closer this value is to zero the closer the task has come to overflowing its stack. */ } TaskStatus_t; -/* +/** * Used with the uxTaskGetSnapshotAll() function to save memory snapshot of each task in the system. * We need this struct because TCB_t is defined (hidden) in tasks.c. */ typedef struct xTASK_SNAPSHOT { - void *pxTCB; /* Address of task control block. */ - StackType_t *pxTopOfStack; /* Points to the location of the last item placed on the tasks stack. */ - StackType_t *pxEndOfStack; /* Points to the end of the stack. pxTopOfStack < pxEndOfStack, stack grows hi2lo - pxTopOfStack > pxEndOfStack, stack grows lo2hi*/ + void *pxTCB; /*!< Address of task control block. */ + StackType_t *pxTopOfStack; /*!< Points to the location of the last item placed on the tasks stack. */ + StackType_t *pxEndOfStack; /*!< Points to the end of the stack. pxTopOfStack < pxEndOfStack, stack grows hi2lo + pxTopOfStack > pxEndOfStack, stack grows lo2hi*/ } TaskSnapshot_t; -/* Possible return values for eTaskConfirmSleepModeStatus(). */ +/** + * Possible return values for eTaskConfirmSleepModeStatus(). + */ typedef enum { - eAbortSleep = 0, /* A task has been made ready or a context switch pended since portSUPPORESS_TICKS_AND_SLEEP() was called - abort entering a sleep mode. */ - eStandardSleep, /* Enter a sleep mode that will not last any longer than the expected idle time. */ - eNoTasksWaitingTimeout /* No tasks are waiting for a timeout so it is safe to enter a sleep mode that can only be exited by an external interrupt. */ + eAbortSleep = 0, /*!< A task has been made ready or a context switch pended since portSUPPORESS_TICKS_AND_SLEEP() was called - abort entering a sleep mode. */ + eStandardSleep, /*!< Enter a sleep mode that will not last any longer than the expected idle time. */ + eNoTasksWaitingTimeout /*!< No tasks are waiting for a timeout so it is safe to enter a sleep mode that can only be exited by an external interrupt. */ } eSleepModeStatus; @@ -214,7 +218,6 @@ typedef enum * * Macro for forcing a context switch. * - * \defgroup taskYIELD taskYIELD * \ingroup SchedulerControl */ #define taskYIELD() portYIELD() @@ -225,10 +228,9 @@ typedef enum * Macro to mark the start of a critical code region. Preemptive context * switches cannot occur when in a critical region. * - * NOTE: This may alter the stack (depending on the portable implementation) + * @note This may alter the stack (depending on the portable implementation) * so must be used with care! * - * \defgroup taskENTER_CRITICAL taskENTER_CRITICAL * \ingroup SchedulerControl */ #define taskENTER_CRITICAL(mux) portENTER_CRITICAL(mux) @@ -240,10 +242,9 @@ typedef enum * Macro to mark the end of a critical code region. Preemptive context * switches cannot occur when in a critical region. * - * NOTE: This may alter the stack (depending on the portable implementation) + * @note This may alter the stack (depending on the portable implementation) * so must be used with care! * - * \defgroup taskEXIT_CRITICAL taskEXIT_CRITICAL * \ingroup SchedulerControl */ #define taskEXIT_CRITICAL(mux) portEXIT_CRITICAL(mux) @@ -254,7 +255,6 @@ typedef enum * * Macro to disable all maskable interrupts. * - * \defgroup taskDISABLE_INTERRUPTS taskDISABLE_INTERRUPTS * \ingroup SchedulerControl */ #define taskDISABLE_INTERRUPTS() portDISABLE_INTERRUPTS() @@ -264,7 +264,6 @@ typedef enum * * Macro to enable microcontroller interrupts. * - * \defgroup taskENABLE_INTERRUPTS taskENABLE_INTERRUPTS * \ingroup SchedulerControl */ #define taskENABLE_INTERRUPTS() portENABLE_INTERRUPTS() @@ -282,17 +281,58 @@ is used in assert() statements. */ *----------------------------------------------------------*/ /** - * task. h - *
- BaseType_t xTaskCreate(
-							  TaskFunction_t pvTaskCode,
-							  const char * const pcName,
-							  uint32_t usStackDepth,
-							  void *pvParameters,
-							  UBaseType_t uxPriority,
-							  TaskHandle_t *pvCreatedTask
-						  );
+ * Create a new task with a specified affinity. * + * This function is similar to xTaskCreate, but allows setting task affinity + * in SMP system. + * + * @param pvTaskCode Pointer to the task entry function. Tasks + * must be implemented to never return (i.e. continuous loop). + * + * @param pcName A descriptive name for the task. This is mainly used to + * facilitate debugging. Max length defined by configMAX_TASK_NAME_LEN - default + * is 16. + * + * @param usStackDepth The size of the task stack specified as the number of + * variables the stack can hold - not the number of bytes. For example, if + * the stack is 16 bits wide and usStackDepth is defined as 100, 200 bytes + * will be allocated for stack storage. + * + * @param pvParameters Pointer that will be used as the parameter for the task + * being created. + * + * @param uxPriority The priority at which the task should run. Systems that + * include MPU support can optionally create tasks in a privileged (system) + * mode by setting bit portPRIVILEGE_BIT of the priority parameter. For + * example, to create a privileged task at priority 2 the uxPriority parameter + * should be set to ( 2 | portPRIVILEGE_BIT ). + * + * @param pvCreatedTask Used to pass back a handle by which the created task + * can be referenced. + * + * @param xCoreID If the value is tskNO_AFFINITY, the created task is not + * pinned to any CPU, and the scheduler can run it on any core available. + * Other values indicate the index number of the CPU which the task should + * be pinned to. Specifying values larger than (portNUM_PROCESSORS - 1) will + * cause the function to fail. + * + * @return pdPASS if the task was successfully created and added to a ready + * list, otherwise an error code defined in the file projdefs.h + * + * \ingroup Tasks + */ +#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) + BaseType_t xTaskCreatePinnedToCore( TaskFunction_t pvTaskCode, + const char * const pcName, + const uint32_t usStackDepth, + void * const pvParameters, + UBaseType_t uxPriority, + TaskHandle_t * const pvCreatedTask, + const BaseType_t xCoreID); + +#endif + +/** * Create a new task and add it to the list of tasks that are ready to run. * * Internally, within the FreeRTOS implementation, tasks use two blocks of @@ -341,64 +381,113 @@ is used in assert() statements. */ * list, otherwise an error code defined in the file projdefs.h * * Example usage: -
- // Task to be created.
- void vTaskCode( void * pvParameters )
- {
-	 for( ;; )
-	 {
-		 // Task code goes here.
-	 }
- }
-
- // Function that creates a task.
- void vOtherFunction( void )
- {
- static uint8_t ucParameterToPass;
- TaskHandle_t xHandle = NULL;
-
-	 // Create the task, storing the handle.  Note that the passed parameter ucParameterToPass
-	 // must exist for the lifetime of the task, so in this case is declared static.  If it was just an
-	 // an automatic stack variable it might no longer exist, or at least have been corrupted, by the time
-	 // the new task attempts to access it.
-	 xTaskCreate( vTaskCode, "NAME", STACK_SIZE, &ucParameterToPass, tskIDLE_PRIORITY, &xHandle );
-     configASSERT( xHandle );
-
-	 // Use the handle to delete the task.
-     if( xHandle != NULL )
-     {
-	     vTaskDelete( xHandle );
-     }
- }
-   
- * \defgroup xTaskCreate xTaskCreate + * @code{c} + * // Task to be created. + * void vTaskCode( void * pvParameters ) + * { + * for( ;; ) + * { + * // Task code goes here. + * } + * } + * + * // Function that creates a task. + * void vOtherFunction( void ) + * { + * static uint8_t ucParameterToPass; + * TaskHandle_t xHandle = NULL; + * + * // Create the task, storing the handle. Note that the passed parameter ucParameterToPass + * // must exist for the lifetime of the task, so in this case is declared static. If it was just an + * // an automatic stack variable it might no longer exist, or at least have been corrupted, by the time + * // the new task attempts to access it. + * xTaskCreate( vTaskCode, "NAME", STACK_SIZE, &ucParameterToPass, tskIDLE_PRIORITY, &xHandle ); + * configASSERT( xHandle ); + * + * // Use the handle to delete the task. + * if( xHandle != NULL ) + * { + * vTaskDelete( xHandle ); + * } + * } + * @endcode * \ingroup Tasks */ -#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) - BaseType_t xTaskCreatePinnedToCore( TaskFunction_t pxTaskCode, - const char * const pcName, - const uint32_t usStackDepth, - void * const pvParameters, - UBaseType_t uxPriority, - TaskHandle_t * const pxCreatedTask, - const BaseType_t xCoreID); -#define xTaskCreate( pvTaskCode, pcName, usStackDepth, pvParameters, uxPriority, pxCreatedTask ) xTaskCreatePinnedToCore( ( pvTaskCode ), ( pcName ), ( usStackDepth ), ( pvParameters ), ( uxPriority ), ( pxCreatedTask ), tskNO_AFFINITY ) +#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) + + static inline IRAM_ATTR BaseType_t xTaskCreate( + TaskFunction_t pvTaskCode, + const char * const pcName, + const uint32_t usStackDepth, + void * const pvParameters, + UBaseType_t uxPriority, + TaskHandle_t * const pvCreatedTask) + { + return xTaskCreatePinnedToCore( pvTaskCode, pcName, usStackDepth, pvParameters, uxPriority, pvCreatedTask, tskNO_AFFINITY ); + } + #endif -/** - * task. h - *
- TaskHandle_t xTaskCreateStatic( TaskFunction_t pvTaskCode,
-								 const char * const pcName,
-								 uint32_t ulStackDepth,
-								 void *pvParameters,
-								 UBaseType_t uxPriority,
-								 StackType_t *pxStackBuffer,
-								 StaticTask_t *pxTaskBuffer,
-                                 const BaseType_t xCoreID );
+ + +/** + * Create a new task with a specified affinity. * + * This function is similar to xTaskCreateStatic, but allows specifying + * task affinity in an SMP system. + * + * @param pvTaskCode Pointer to the task entry function. Tasks + * must be implemented to never return (i.e. continuous loop). + * + * @param pcName A descriptive name for the task. This is mainly used to + * facilitate debugging. The maximum length of the string is defined by + * configMAX_TASK_NAME_LEN in FreeRTOSConfig.h. + * + * @param ulStackDepth The size of the task stack specified as the number of + * variables the stack can hold - not the number of bytes. For example, if + * the stack is 32-bits wide and ulStackDepth is defined as 100 then 400 bytes + * will be allocated for stack storage. + * + * @param pvParameters Pointer that will be used as the parameter for the task + * being created. + * + * @param uxPriority The priority at which the task will run. + * + * @param pxStackBuffer Must point to a StackType_t array that has at least + * ulStackDepth indexes - the array will then be used as the task's stack, + * removing the need for the stack to be allocated dynamically. + * + * @param pxTaskBuffer Must point to a variable of type StaticTask_t, which will + * then be used to hold the task's data structures, removing the need for the + * memory to be allocated dynamically. + * + * @param xCoreID If the value is tskNO_AFFINITY, the created task is not + * pinned to any CPU, and the scheduler can run it on any core available. + * Other values indicate the index number of the CPU which the task should + * be pinned to. Specifying values larger than (portNUM_PROCESSORS - 1) will + * cause the function to fail. + * + * @return If neither pxStackBuffer or pxTaskBuffer are NULL, then the task will + * be created and pdPASS is returned. If either pxStackBuffer or pxTaskBuffer + * are NULL then the task will not be created and + * errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY is returned. + * + * \ingroup Tasks + */ +#if( configSUPPORT_STATIC_ALLOCATION == 1 ) + TaskHandle_t xTaskCreateStaticPinnedToCore( TaskFunction_t pvTaskCode, + const char * const pcName, + const uint32_t ulStackDepth, + void * const pvParameters, + UBaseType_t uxPriority, + StackType_t * const pxStackBuffer, + StaticTask_t * const pxTaskBuffer, + const BaseType_t xCoreID ); +#endif /* configSUPPORT_STATIC_ALLOCATION */ + +/** * Create a new task and add it to the list of tasks that are ready to run. * * Internally, within the FreeRTOS implementation, tasks use two blocks of @@ -442,77 +531,75 @@ is used in assert() statements. */ * errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY is returned. * * Example usage: -
-
-    // Dimensions the buffer that the task being created will use as its stack.
-    // NOTE:  This is the number of words the stack will hold, not the number of
-    // bytes.  For example, if each stack item is 32-bits, and this is set to 100,
-    // then 400 bytes (100 * 32-bits) will be allocated.
-    #define STACK_SIZE 200
-
-    // Structure that will hold the TCB of the task being created.
-    StaticTask_t xTaskBuffer;
-
-    // Buffer that the task being created will use as its stack.  Note this is
-    // an array of StackType_t variables.  The size of StackType_t is dependent on
-    // the RTOS port.
-    StackType_t xStack[ STACK_SIZE ];
-
-    // Function that implements the task being created.
-    void vTaskCode( void * pvParameters )
-    {
-        // The parameter value is expected to be 1 as 1 is passed in the
-        // pvParameters value in the call to xTaskCreateStatic().
-        configASSERT( ( uint32_t ) pvParameters == 1UL );
-
-        for( ;; )
-        {
-            // Task code goes here.
-        }
-    }
-
-    // Function that creates a task.
-    void vOtherFunction( void )
-    {
-        TaskHandle_t xHandle = NULL;
-
-        // Create the task without using any dynamic memory allocation.
-        xHandle = xTaskCreateStatic(
-                      vTaskCode,       // Function that implements the task.
-                      "NAME",          // Text name for the task.
-                      STACK_SIZE,      // Stack size in words, not bytes.
-                      ( void * ) 1,    // Parameter passed into the task.
-                      tskIDLE_PRIORITY,// Priority at which the task is created.
-                      xStack,          // Array to use as the task's stack.
-                      &xTaskBuffer );  // Variable to hold the task's data structure.
-
-        // puxStackBuffer and pxTaskBuffer were not NULL, so the task will have
-        // been created, and xHandle will be the task's handle.  Use the handle
-        // to suspend the task.
-        vTaskSuspend( xHandle );
-    }
-   
- * \defgroup xTaskCreateStatic xTaskCreateStatic + * @code{c} + * + * // Dimensions the buffer that the task being created will use as its stack. + * // NOTE: This is the number of words the stack will hold, not the number of + * // bytes. For example, if each stack item is 32-bits, and this is set to 100, + * // then 400 bytes (100 * 32-bits) will be allocated. + * #define STACK_SIZE 200 + * + * // Structure that will hold the TCB of the task being created. + * StaticTask_t xTaskBuffer; + * + * // Buffer that the task being created will use as its stack. Note this is + * // an array of StackType_t variables. The size of StackType_t is dependent on + * // the RTOS port. + * StackType_t xStack[ STACK_SIZE ]; + * + * // Function that implements the task being created. + * void vTaskCode( void * pvParameters ) + * { + * // The parameter value is expected to be 1 as 1 is passed in the + * // pvParameters value in the call to xTaskCreateStatic(). + * configASSERT( ( uint32_t ) pvParameters == 1UL ); + * + * for( ;; ) + * { + * // Task code goes here. + * } + * } + * + * // Function that creates a task. + * void vOtherFunction( void ) + * { + * TaskHandle_t xHandle = NULL; + * + * // Create the task without using any dynamic memory allocation. + * xHandle = xTaskCreateStatic( + * vTaskCode, // Function that implements the task. + * "NAME", // Text name for the task. + * STACK_SIZE, // Stack size in words, not bytes. + * ( void * ) 1, // Parameter passed into the task. + * tskIDLE_PRIORITY,// Priority at which the task is created. + * xStack, // Array to use as the task's stack. + * &xTaskBuffer ); // Variable to hold the task's data structure. + * + * // puxStackBuffer and pxTaskBuffer were not NULL, so the task will have + * // been created, and xHandle will be the task's handle. Use the handle + * // to suspend the task. + * vTaskSuspend( xHandle ); + * } + * @endcode * \ingroup Tasks */ -#if( configSUPPORT_STATIC_ALLOCATION == 1 ) - TaskHandle_t xTaskCreateStaticPinnedToCore( TaskFunction_t pxTaskCode, - const char * const pcName, - const uint32_t ulStackDepth, - void * const pvParameters, - UBaseType_t uxPriority, - StackType_t * const puxStackBuffer, - StaticTask_t * const pxTaskBuffer, - const BaseType_t xCoreID ); -#define xTaskCreateStatic( pvTaskCode, pcName, usStackDepth, pvParameters, uxPriority, pxStackBuffer, pxTaskBuffer ) xTaskCreateStaticPinnedToCore( ( pvTaskCode ), ( pcName ), ( usStackDepth ), ( pvParameters ), ( uxPriority ), ( pxStackBuffer ), ( pxTaskBuffer ), tskNO_AFFINITY ) +#if( configSUPPORT_STATIC_ALLOCATION == 1 ) + static inline IRAM_ATTR TaskHandle_t xTaskCreateStatic( + TaskFunction_t pvTaskCode, + const char * const pcName, + const uint32_t ulStackDepth, + void * const pvParameters, + UBaseType_t uxPriority, + StackType_t * const pxStackBuffer, + StaticTask_t * const pxTaskBuffer) + { + return xTaskCreateStaticPinnedToCore( pvTaskCode, pcName, ulStackDepth, pvParameters, uxPriority, pxStackBuffer, pxTaskBuffer, tskNO_AFFINITY ); + } #endif /* configSUPPORT_STATIC_ALLOCATION */ +/** @cond */ /** - * task. h - *
- BaseType_t xTaskCreateRestricted( TaskParameters_t *pxTaskDefinition, TaskHandle_t *pxCreatedTask );
- * * xTaskCreateRestricted() should only be used in systems that include an MPU * implementation. * @@ -532,58 +619,54 @@ is used in assert() statements. */ * list, otherwise an error code defined in the file projdefs.h * * Example usage: -
-// Create an TaskParameters_t structure that defines the task to be created.
-static const TaskParameters_t xCheckTaskParameters =
-{
-	vATask,		// pvTaskCode - the function that implements the task.
-	"ATask",	// pcName - just a text name for the task to assist debugging.
-	100,		// usStackDepth	- the stack size DEFINED IN WORDS.
-	NULL,		// pvParameters - passed into the task function as the function parameters.
-	( 1UL | portPRIVILEGE_BIT ),// uxPriority - task priority, set the portPRIVILEGE_BIT if the task should run in a privileged state.
-	cStackBuffer,// puxStackBuffer - the buffer to be used as the task stack.
-
-	// xRegions - Allocate up to three separate memory regions for access by
-	// the task, with appropriate access permissions.  Different processors have
-	// different memory alignment requirements - refer to the FreeRTOS documentation
-	// for full information.
-	{
-		// Base address					Length	Parameters
-        { cReadWriteArray,				32,		portMPU_REGION_READ_WRITE },
-        { cReadOnlyArray,				32,		portMPU_REGION_READ_ONLY },
-        { cPrivilegedOnlyAccessArray,	128,	portMPU_REGION_PRIVILEGED_READ_WRITE }
-	}
-};
-
-int main( void )
-{
-TaskHandle_t xHandle;
-
-	// Create a task from the const structure defined above.  The task handle
-	// is requested (the second parameter is not NULL) but in this case just for
-	// demonstration purposes as its not actually used.
-	xTaskCreateRestricted( &xRegTest1Parameters, &xHandle );
-
-	// Start the scheduler.
-	vTaskStartScheduler();
-
-	// Will only get here if there was insufficient memory to create the idle
-	// and/or timer task.
-	for( ;; );
-}
-   
- * \defgroup xTaskCreateRestricted xTaskCreateRestricted + * @code{c} + * // Create an TaskParameters_t structure that defines the task to be created. + * static const TaskParameters_t xCheckTaskParameters = + * { + * vATask, // pvTaskCode - the function that implements the task. + * "ATask", // pcName - just a text name for the task to assist debugging. + * 100, // usStackDepth - the stack size DEFINED IN WORDS. + * NULL, // pvParameters - passed into the task function as the function parameters. + * ( 1UL | portPRIVILEGE_BIT ),// uxPriority - task priority, set the portPRIVILEGE_BIT if the task should run in a privileged state. + * cStackBuffer,// puxStackBuffer - the buffer to be used as the task stack. + * + * // xRegions - Allocate up to three separate memory regions for access by + * // the task, with appropriate access permissions. Different processors have + * // different memory alignment requirements - refer to the FreeRTOS documentation + * // for full information. + * { + * // Base address Length Parameters + * { cReadWriteArray, 32, portMPU_REGION_READ_WRITE }, + * { cReadOnlyArray, 32, portMPU_REGION_READ_ONLY }, + * { cPrivilegedOnlyAccessArray, 128, portMPU_REGION_PRIVILEGED_READ_WRITE } + * } + * }; + * + * int main( void ) + * { + * TaskHandle_t xHandle; + * + * // Create a task from the const structure defined above. The task handle + * // is requested (the second parameter is not NULL) but in this case just for + * // demonstration purposes as its not actually used. + * xTaskCreateRestricted( &xRegTest1Parameters, &xHandle ); + * + * // Start the scheduler. + * vTaskStartScheduler(); + * + * // Will only get here if there was insufficient memory to create the idle + * // and/or timer task. + * for( ;; ); + * } + * @endcode * \ingroup Tasks */ #if( portUSING_MPU_WRAPPERS == 1 ) BaseType_t xTaskCreateRestricted( const TaskParameters_t * const pxTaskDefinition, TaskHandle_t *pxCreatedTask ) PRIVILEGED_FUNCTION; #endif + /** - * task. h - *
- void vTaskAllocateMPURegions( TaskHandle_t xTask, const MemoryRegion_t * const pxRegions );
- * * Memory regions are assigned to a restricted task when the task is created by * a call to xTaskCreateRestricted(). These regions can be redefined using * vTaskAllocateMPURegions(). @@ -594,50 +677,51 @@ TaskHandle_t xHandle; * new memory region definitions. * * Example usage: -
-// Define an array of MemoryRegion_t structures that configures an MPU region
-// allowing read/write access for 1024 bytes starting at the beginning of the
-// ucOneKByte array.  The other two of the maximum 3 definable regions are
-// unused so set to zero.
-static const MemoryRegion_t xAltRegions[ portNUM_CONFIGURABLE_REGIONS ] =
-{
-	// Base address		Length		Parameters
-	{ ucOneKByte,		1024,		portMPU_REGION_READ_WRITE },
-	{ 0,				0,			0 },
-	{ 0,				0,			0 }
-};
-
-void vATask( void *pvParameters )
-{
-	// This task was created such that it has access to certain regions of
-	// memory as defined by the MPU configuration.  At some point it is
-	// desired that these MPU regions are replaced with that defined in the
-	// xAltRegions const struct above.  Use a call to vTaskAllocateMPURegions()
-	// for this purpose.  NULL is used as the task handle to indicate that this
-	// function should modify the MPU regions of the calling task.
-	vTaskAllocateMPURegions( NULL, xAltRegions );
-
-	// Now the task can continue its function, but from this point on can only
-	// access its stack and the ucOneKByte array (unless any other statically
-	// defined or shared regions have been declared elsewhere).
-}
-   
- * \defgroup xTaskCreateRestricted xTaskCreateRestricted + * + * @code{c} + * // Define an array of MemoryRegion_t structures that configures an MPU region + * // allowing read/write access for 1024 bytes starting at the beginning of the + * // ucOneKByte array. The other two of the maximum 3 definable regions are + * // unused so set to zero. + * static const MemoryRegion_t xAltRegions[ portNUM_CONFIGURABLE_REGIONS ] = + * { + * // Base address Length Parameters + * { ucOneKByte, 1024, portMPU_REGION_READ_WRITE }, + * { 0, 0, 0 }, + * { 0, 0, 0 } + * }; + * + * void vATask( void *pvParameters ) + * { + * // This task was created such that it has access to certain regions of + * // memory as defined by the MPU configuration. At some point it is + * // desired that these MPU regions are replaced with that defined in the + * // xAltRegions const struct above. Use a call to vTaskAllocateMPURegions() + * // for this purpose. NULL is used as the task handle to indicate that this + * // function should modify the MPU regions of the calling task. + * vTaskAllocateMPURegions( NULL, xAltRegions ); + * + * // Now the task can continue its function, but from this point on can only + * // access its stack and the ucOneKByte array (unless any other statically + * // defined or shared regions have been declared elsewhere). + * } + * @endcode * \ingroup Tasks */ void vTaskAllocateMPURegions( TaskHandle_t xTask, const MemoryRegion_t * const pxRegions ) PRIVILEGED_FUNCTION; +/** @endcond */ + /** - * task. h - *
void vTaskDelete( TaskHandle_t xTask );
+ * Remove a task from the RTOS real time kernel's management. + * + * The task being deleted will be removed from all ready, blocked, suspended + * and event lists. * * INCLUDE_vTaskDelete must be defined as 1 for this function to be available. * See the configuration section for more information. * - * Remove a task from the RTOS real time kernel's management. The task being - * deleted will be removed from all ready, blocked, suspended and event lists. - * - * NOTE: The idle task is responsible for freeing the kernel allocated + * @note The idle task is responsible for freeing the kernel allocated * memory from tasks that have been deleted. It is therefore important that * the idle task is not starved of microcontroller processing time if your * application makes any calls to vTaskDelete (). Memory allocated by the @@ -647,23 +731,22 @@ void vTaskAllocateMPURegions( TaskHandle_t xTask, const MemoryRegion_t * const p * See the demo application file death.c for sample code that utilises * vTaskDelete (). * - * @param xTask The handle of the task to be deleted. Passing NULL will + * @param xTaskToDelete The handle of the task to be deleted. Passing NULL will * cause the calling task to be deleted. * * Example usage: -
- void vOtherFunction( void )
- {
- TaskHandle_t xHandle;
-
-	 // Create the task, storing the handle.
-	 xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle );
-
-	 // Use the handle to delete the task.
-	 vTaskDelete( xHandle );
- }
-   
- * \defgroup vTaskDelete vTaskDelete + * @code{c} + * void vOtherFunction( void ) + * { + * TaskHandle_t xHandle; + * + * // Create the task, storing the handle. + * xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle ); + * + * // Use the handle to delete the task. + * vTaskDelete( xHandle ); + * } + * @endcode * \ingroup Tasks */ void vTaskDelete( TaskHandle_t xTaskToDelete ) PRIVILEGED_FUNCTION; @@ -673,18 +756,15 @@ void vTaskDelete( TaskHandle_t xTaskToDelete ) PRIVILEGED_FUNCTION; *----------------------------------------------------------*/ /** - * task. h - *
void vTaskDelay( const TickType_t xTicksToDelay );
+ * Delay a task for a given number of ticks. * - * Delay a task for a given number of ticks. The actual time that the - * task remains blocked depends on the tick rate. The constant - * portTICK_PERIOD_MS can be used to calculate real time from the tick - * rate - with the resolution of one tick period. + * The actual time that the task remains blocked depends on the tick rate. + * The constant portTICK_PERIOD_MS can be used to calculate real time from + * the tick rate - with the resolution of one tick period. * * INCLUDE_vTaskDelay must be defined as 1 for this function to be available. * See the configuration section for more information. * - * * vTaskDelay() specifies a time at which the task wishes to unblock relative to * the time at which vTaskDelay() is called. For example, specifying a block * period of 100 ticks will cause the task to unblock 100 ticks after @@ -701,34 +781,31 @@ void vTaskDelete( TaskHandle_t xTaskToDelete ) PRIVILEGED_FUNCTION; * the calling task should block. * * Example usage: - - void vTaskFunction( void * pvParameters ) - { - // Block for 500ms. - const TickType_t xDelay = 500 / portTICK_PERIOD_MS; - - for( ;; ) - { - // Simply toggle the LED every 500ms, blocking between each toggle. - vToggleLED(); - vTaskDelay( xDelay ); - } - } - - * \defgroup vTaskDelay vTaskDelay + * @code{c} + * void vTaskFunction( void * pvParameters ) + * { + * // Block for 500ms. + * const TickType_t xDelay = 500 / portTICK_PERIOD_MS; + * + * for( ;; ) + * { + * // Simply toggle the LED every 500ms, blocking between each toggle. + * vToggleLED(); + * vTaskDelay( xDelay ); + * } + * } + * @endcode * \ingroup TaskCtrl */ void vTaskDelay( const TickType_t xTicksToDelay ) PRIVILEGED_FUNCTION; /** - * task. h - *
void vTaskDelayUntil( TickType_t *pxPreviousWakeTime, const TickType_t xTimeIncrement );
+ * Delay a task until a specified time. * * INCLUDE_vTaskDelayUntil must be defined as 1 for this function to be available. * See the configuration section for more information. * - * Delay a task until a specified time. This function can be used by periodic - * tasks to ensure a constant execution frequency. + * This function can be used by periodic tasks to ensure a constant execution frequency. * * This function differs from vTaskDelay () in one important aspect: vTaskDelay () will * cause a task to block for the specified number of ticks from the time vTaskDelay () is @@ -756,94 +833,90 @@ void vTaskDelay( const TickType_t xTicksToDelay ) PRIVILEGED_FUNCTION; * a fixed interface period. * * Example usage: -
- // Perform an action every 10 ticks.
- void vTaskFunction( void * pvParameters )
- {
- TickType_t xLastWakeTime;
- const TickType_t xFrequency = 10;
-
-	 // Initialise the xLastWakeTime variable with the current time.
-	 xLastWakeTime = xTaskGetTickCount ();
-	 for( ;; )
-	 {
-		 // Wait for the next cycle.
-		 vTaskDelayUntil( &xLastWakeTime, xFrequency );
-
-		 // Perform action here.
-	 }
- }
-   
- * \defgroup vTaskDelayUntil vTaskDelayUntil + * @code{c} + * // Perform an action every 10 ticks. + * void vTaskFunction( void * pvParameters ) + * { + * TickType_t xLastWakeTime; + * const TickType_t xFrequency = 10; + * + * // Initialise the xLastWakeTime variable with the current time. + * xLastWakeTime = xTaskGetTickCount (); + * for( ;; ) + * { + * // Wait for the next cycle. + * vTaskDelayUntil( &xLastWakeTime, xFrequency ); + * + * // Perform action here. + * } + * } + * @endcode * \ingroup TaskCtrl */ void vTaskDelayUntil( TickType_t * const pxPreviousWakeTime, const TickType_t xTimeIncrement ) PRIVILEGED_FUNCTION; /** - * task. h - *
UBaseType_t uxTaskPriorityGet( TaskHandle_t xTask );
+ * Obtain the priority of any task. * * INCLUDE_uxTaskPriorityGet must be defined as 1 for this function to be available. * See the configuration section for more information. * - * Obtain the priority of any task. - * * @param xTask Handle of the task to be queried. Passing a NULL * handle results in the priority of the calling task being returned. * * @return The priority of xTask. * * Example usage: -
- void vAFunction( void )
- {
- TaskHandle_t xHandle;
-
-	 // Create a task, storing the handle.
-	 xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle );
-
-	 // ...
-
-	 // Use the handle to obtain the priority of the created task.
-	 // It was created with tskIDLE_PRIORITY, but may have changed
-	 // it itself.
-	 if( uxTaskPriorityGet( xHandle ) != tskIDLE_PRIORITY )
-	 {
-		 // The task has changed it's priority.
-	 }
-
-	 // ...
-
-	 // Is our priority higher than the created task?
-	 if( uxTaskPriorityGet( xHandle ) < uxTaskPriorityGet( NULL ) )
-	 {
-		 // Our priority (obtained using NULL handle) is higher.
-	 }
- }
-   
- * \defgroup uxTaskPriorityGet uxTaskPriorityGet + * @code{c} + * void vAFunction( void ) + * { + * TaskHandle_t xHandle; + * + * // Create a task, storing the handle. + * xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle ); + * + * // ... + * + * // Use the handle to obtain the priority of the created task. + * // It was created with tskIDLE_PRIORITY, but may have changed + * // it itself. + * if( uxTaskPriorityGet( xHandle ) != tskIDLE_PRIORITY ) + * { + * // The task has changed it's priority. + * } + * + * // ... + * + * // Is our priority higher than the created task? + * if( uxTaskPriorityGet( xHandle ) < uxTaskPriorityGet( NULL ) ) + * { + * // Our priority (obtained using NULL handle) is higher. + * } + * } + * @endcode * \ingroup TaskCtrl */ UBaseType_t uxTaskPriorityGet( TaskHandle_t xTask ) PRIVILEGED_FUNCTION; /** - * task. h - *
UBaseType_t uxTaskPriorityGetFromISR( TaskHandle_t xTask );
- * * A version of uxTaskPriorityGet() that can be used from an ISR. + * + * @param xTask Handle of the task to be queried. Passing a NULL + * handle results in the priority of the calling task being returned. + * + * @return The priority of xTask. + * */ UBaseType_t uxTaskPriorityGetFromISR( TaskHandle_t xTask ) PRIVILEGED_FUNCTION; /** - * task. h - *
eTaskState eTaskGetState( TaskHandle_t xTask );
+ * Obtain the state of any task. + * + * States are encoded by the eTaskState enumerated type. * * INCLUDE_eTaskGetState must be defined as 1 for this function to be available. * See the configuration section for more information. * - * Obtain the state of any task. States are encoded by the eTaskState - * enumerated type. - * * @param xTask Handle of the task to be queried. * * @return The state of xTask at the time the function was called. Note the @@ -853,14 +926,11 @@ UBaseType_t uxTaskPriorityGetFromISR( TaskHandle_t xTask ) PRIVILEGED_FUNCTION; eTaskState eTaskGetState( TaskHandle_t xTask ) PRIVILEGED_FUNCTION; /** - * task. h - *
void vTaskPrioritySet( TaskHandle_t xTask, UBaseType_t uxNewPriority );
+ * Set the priority of any task. * * INCLUDE_vTaskPrioritySet must be defined as 1 for this function to be available. * See the configuration section for more information. * - * Set the priority of any task. - * * A context switch will occur before the function returns if the priority * being set is higher than the currently executing task. * @@ -870,39 +940,37 @@ eTaskState eTaskGetState( TaskHandle_t xTask ) PRIVILEGED_FUNCTION; * @param uxNewPriority The priority to which the task will be set. * * Example usage: -
- void vAFunction( void )
- {
- TaskHandle_t xHandle;
-
-	 // Create a task, storing the handle.
-	 xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle );
-
-	 // ...
-
-	 // Use the handle to raise the priority of the created task.
-	 vTaskPrioritySet( xHandle, tskIDLE_PRIORITY + 1 );
-
-	 // ...
-
-	 // Use a NULL handle to raise our priority to the same value.
-	 vTaskPrioritySet( NULL, tskIDLE_PRIORITY + 1 );
- }
-   
- * \defgroup vTaskPrioritySet vTaskPrioritySet + * @code{c} + * void vAFunction( void ) + * { + * TaskHandle_t xHandle; + * + * // Create a task, storing the handle. + * xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle ); + * + * // ... + * + * // Use the handle to raise the priority of the created task. + * vTaskPrioritySet( xHandle, tskIDLE_PRIORITY + 1 ); + * + * // ... + * + * // Use a NULL handle to raise our priority to the same value. + * vTaskPrioritySet( NULL, tskIDLE_PRIORITY + 1 ); + * } + * @endcode * \ingroup TaskCtrl */ void vTaskPrioritySet( TaskHandle_t xTask, UBaseType_t uxNewPriority ) PRIVILEGED_FUNCTION; /** - * task. h - *
void vTaskSuspend( TaskHandle_t xTaskToSuspend );
+ * Suspend a task. * * INCLUDE_vTaskSuspend must be defined as 1 for this function to be available. * See the configuration section for more information. * - * Suspend any task. When suspended a task will never get any microcontroller - * processing time, no matter what its priority. + * When suspended, a task will never get any microcontroller processing time, + * no matter what its priority. * * Calls to vTaskSuspend are not accumulative - * i.e. calling vTaskSuspend () twice on the same task still only requires one @@ -912,48 +980,44 @@ void vTaskPrioritySet( TaskHandle_t xTask, UBaseType_t uxNewPriority ) PRIVILEGE * handle will cause the calling task to be suspended. * * Example usage: -
- void vAFunction( void )
- {
- TaskHandle_t xHandle;
-
-	 // Create a task, storing the handle.
-	 xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle );
-
-	 // ...
-
-	 // Use the handle to suspend the created task.
-	 vTaskSuspend( xHandle );
-
-	 // ...
-
-	 // The created task will not run during this period, unless
-	 // another task calls vTaskResume( xHandle ).
-
-	 //...
-
-
-	 // Suspend ourselves.
-	 vTaskSuspend( NULL );
-
-	 // We cannot get here unless another task calls vTaskResume
-	 // with our handle as the parameter.
- }
-   
- * \defgroup vTaskSuspend vTaskSuspend + * @code{c} + * void vAFunction( void ) + * { + * TaskHandle_t xHandle; + * + * // Create a task, storing the handle. + * xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle ); + * + * // ... + * + * // Use the handle to suspend the created task. + * vTaskSuspend( xHandle ); + * + * // ... + * + * // The created task will not run during this period, unless + * // another task calls vTaskResume( xHandle ). + * + * //... + * + * + * // Suspend ourselves. + * vTaskSuspend( NULL ); + * + * // We cannot get here unless another task calls vTaskResume + * // with our handle as the parameter. + * } + * @endcode * \ingroup TaskCtrl */ void vTaskSuspend( TaskHandle_t xTaskToSuspend ) PRIVILEGED_FUNCTION; /** - * task. h - *
void vTaskResume( TaskHandle_t xTaskToResume );
+ * Resumes a suspended task. * * INCLUDE_vTaskSuspend must be defined as 1 for this function to be available. * See the configuration section for more information. * - * Resumes a suspended task. - * * A task that has been suspended by one or more calls to vTaskSuspend () * will be made available for running again by a single call to * vTaskResume (). @@ -961,48 +1025,44 @@ void vTaskSuspend( TaskHandle_t xTaskToSuspend ) PRIVILEGED_FUNCTION; * @param xTaskToResume Handle to the task being readied. * * Example usage: -
- void vAFunction( void )
- {
- TaskHandle_t xHandle;
-
-	 // Create a task, storing the handle.
-	 xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle );
-
-	 // ...
-
-	 // Use the handle to suspend the created task.
-	 vTaskSuspend( xHandle );
-
-	 // ...
-
-	 // The created task will not run during this period, unless
-	 // another task calls vTaskResume( xHandle ).
-
-	 //...
-
-
-	 // Resume the suspended task ourselves.
-	 vTaskResume( xHandle );
-
-	 // The created task will once again get microcontroller processing
-	 // time in accordance with its priority within the system.
- }
-   
- * \defgroup vTaskResume vTaskResume + * @code{c} + * void vAFunction( void ) + * { + * TaskHandle_t xHandle; + * + * // Create a task, storing the handle. + * xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle ); + * + * // ... + * + * // Use the handle to suspend the created task. + * vTaskSuspend( xHandle ); + * + * // ... + * + * // The created task will not run during this period, unless + * // another task calls vTaskResume( xHandle ). + * + * //... + * + * + * // Resume the suspended task ourselves. + * vTaskResume( xHandle ); + * + * // The created task will once again get microcontroller processing + * // time in accordance with its priority within the system. + * } + * @endcode * \ingroup TaskCtrl */ void vTaskResume( TaskHandle_t xTaskToResume ) PRIVILEGED_FUNCTION; /** - * task. h - *
void xTaskResumeFromISR( TaskHandle_t xTaskToResume );
+ * An implementation of vTaskResume() that can be called from within an ISR. * * INCLUDE_xTaskResumeFromISR must be defined as 1 for this function to be * available. See the configuration section for more information. * - * An implementation of vTaskResume() that can be called from within an ISR. - * * A task that has been suspended by one or more calls to vTaskSuspend () * will be made available for running again by a single call to * xTaskResumeFromISR (). @@ -1018,7 +1078,6 @@ void vTaskResume( TaskHandle_t xTaskToResume ) PRIVILEGED_FUNCTION; * otherwise pdFALSE. This is used by the ISR to determine if a context switch * may be required following the ISR. * - * \defgroup vTaskResumeFromISR vTaskResumeFromISR * \ingroup TaskCtrl */ BaseType_t xTaskResumeFromISR( TaskHandle_t xTaskToResume ) PRIVILEGED_FUNCTION; @@ -1026,46 +1085,42 @@ BaseType_t xTaskResumeFromISR( TaskHandle_t xTaskToResume ) PRIVILEGED_FUNCTION; /*----------------------------------------------------------- * SCHEDULER CONTROL *----------------------------------------------------------*/ - +/** @cond */ /** - * task. h - *
void vTaskStartScheduler( void );
+ * Starts the real time kernel tick processing. * - * Starts the real time kernel tick processing. After calling the kernel - * has control over which tasks are executed and when. + * After calling the kernel has control over which tasks are executed and when. * * See the demo application file main.c for an example of creating * tasks and starting the kernel. * * Example usage: -
- void vAFunction( void )
- {
-	 // Create at least one task before starting the kernel.
-	 xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, NULL );
-
-	 // Start the real time kernel with preemption.
-	 vTaskStartScheduler ();
-
-	 // Will not get here unless a task calls vTaskEndScheduler ()
- }
-   
+ * @code{c} + * void vAFunction( void ) + * { + * // Create at least one task before starting the kernel. + * xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, NULL ); + * + * // Start the real time kernel with preemption. + * vTaskStartScheduler (); + * + * // Will not get here unless a task calls vTaskEndScheduler () + * } + * @endcode * - * \defgroup vTaskStartScheduler vTaskStartScheduler * \ingroup SchedulerControl */ void vTaskStartScheduler( void ) PRIVILEGED_FUNCTION; /** - * task. h - *
void vTaskEndScheduler( void );
+ * Stops the real time kernel tick. * - * NOTE: At the time of writing only the x86 real mode port, which runs on a PC + * @note At the time of writing only the x86 real mode port, which runs on a PC * in place of DOS, implements this function. * - * Stops the real time kernel tick. All created tasks will be automatically - * deleted and multitasking (either preemptive or cooperative) will - * stop. Execution then resumes from the point where vTaskStartScheduler () + * All created tasks will be automatically deleted and multitasking + * (either preemptive or cooperative) will stop. + * Execution then resumes from the point where vTaskStartScheduler () * was called, as if vTaskStartScheduler () had just returned. * * See the demo application file main. c in the demo/PC directory for an @@ -1080,44 +1135,42 @@ void vTaskStartScheduler( void ) PRIVILEGED_FUNCTION; * tasks. * * Example usage: -
- void vTaskCode( void * pvParameters )
- {
-	 for( ;; )
-	 {
-		 // Task code goes here.
-
-		 // At some point we want to end the real time kernel processing
-		 // so call ...
-		 vTaskEndScheduler ();
-	 }
- }
-
- void vAFunction( void )
- {
-	 // Create at least one task before starting the kernel.
-	 xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, NULL );
-
-	 // Start the real time kernel with preemption.
-	 vTaskStartScheduler ();
-
-	 // Will only get here when the vTaskCode () task has called
-	 // vTaskEndScheduler ().  When we get here we are back to single task
-	 // execution.
- }
-   
+ * @code{c} + * void vTaskCode( void * pvParameters ) + * { + * for( ;; ) + * { + * // Task code goes here. * - * \defgroup vTaskEndScheduler vTaskEndScheduler + * // At some point we want to end the real time kernel processing + * // so call ... + * vTaskEndScheduler (); + * } + * } + * + * void vAFunction( void ) + * { + * // Create at least one task before starting the kernel. + * xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, NULL ); + * + * // Start the real time kernel with preemption. + * vTaskStartScheduler (); + * + * // Will only get here when the vTaskCode () task has called + * // vTaskEndScheduler (). When we get here we are back to single task + * // execution. + * } + * @endcode * \ingroup SchedulerControl */ void vTaskEndScheduler( void ) PRIVILEGED_FUNCTION; +/** @endcond */ + /** - * task. h - *
void vTaskSuspendAll( void );
+ * Suspends the scheduler without disabling interrupts. * - * Suspends the scheduler without disabling interrupts. Context switches will - * not occur while the scheduler is suspended. + * Context switches will not occur while the scheduler is suspended. * * After calling vTaskSuspendAll () the calling task will continue to execute * without risk of being swapped out until a call to xTaskResumeAll () has been @@ -1128,45 +1181,41 @@ void vTaskEndScheduler( void ) PRIVILEGED_FUNCTION; * is suspended. * * Example usage: -
- void vTask1( void * pvParameters )
- {
-	 for( ;; )
-	 {
-		 // Task code goes here.
-
-		 // ...
-
-		 // At some point the task wants to perform a long operation during
-		 // which it does not want to get swapped out.  It cannot use
-		 // taskENTER_CRITICAL ()/taskEXIT_CRITICAL () as the length of the
-		 // operation may cause interrupts to be missed - including the
-		 // ticks.
-
-		 // Prevent the real time kernel swapping out the task.
-		 vTaskSuspendAll ();
-
-		 // Perform the operation here.  There is no need to use critical
-		 // sections as we have all the microcontroller processing time.
-		 // During this time interrupts will still operate and the kernel
-		 // tick count will be maintained.
-
-		 // ...
-
-		 // The operation is complete.  Restart the kernel.
-		 xTaskResumeAll ();
-	 }
- }
-   
- * \defgroup vTaskSuspendAll vTaskSuspendAll + * @code{c} + * void vTask1( void * pvParameters ) + * { + * for( ;; ) + * { + * // Task code goes here. + * + * // ... + * + * // At some point the task wants to perform a long operation during + * // which it does not want to get swapped out. It cannot use + * // taskENTER_CRITICAL ()/taskEXIT_CRITICAL () as the length of the + * // operation may cause interrupts to be missed - including the + * // ticks. + * + * // Prevent the real time kernel swapping out the task. + * vTaskSuspendAll (); + * + * // Perform the operation here. There is no need to use critical + * // sections as we have all the microcontroller processing time. + * // During this time interrupts will still operate and the kernel + * // tick count will be maintained. + * + * // ... + * + * // The operation is complete. Restart the kernel. + * xTaskResumeAll (); + * } + * } + * @endcode * \ingroup SchedulerControl */ void vTaskSuspendAll( void ) PRIVILEGED_FUNCTION; /** - * task. h - *
BaseType_t xTaskResumeAll( void );
- * * Resumes scheduler activity after it was suspended by a call to * vTaskSuspendAll(). * @@ -1177,42 +1226,41 @@ void vTaskSuspendAll( void ) PRIVILEGED_FUNCTION; * returned, otherwise pdFALSE is returned. * * Example usage: -
- void vTask1( void * pvParameters )
- {
-	 for( ;; )
-	 {
-		 // Task code goes here.
-
-		 // ...
-
-		 // At some point the task wants to perform a long operation during
-		 // which it does not want to get swapped out.  It cannot use
-		 // taskENTER_CRITICAL ()/taskEXIT_CRITICAL () as the length of the
-		 // operation may cause interrupts to be missed - including the
-		 // ticks.
-
-		 // Prevent the real time kernel swapping out the task.
-		 vTaskSuspendAll ();
-
-		 // Perform the operation here.  There is no need to use critical
-		 // sections as we have all the microcontroller processing time.
-		 // During this time interrupts will still operate and the real
-		 // time kernel tick count will be maintained.
-
-		 // ...
-
-		 // The operation is complete.  Restart the kernel.  We want to force
-		 // a context switch - but there is no point if resuming the scheduler
-		 // caused a context switch already.
-		 if( !xTaskResumeAll () )
-		 {
-			  taskYIELD ();
-		 }
-	 }
- }
-   
- * \defgroup xTaskResumeAll xTaskResumeAll + * @code{c} + * void vTask1( void * pvParameters ) + * { + * for( ;; ) + * { + * // Task code goes here. + * + * // ... + * + * // At some point the task wants to perform a long operation during + * // which it does not want to get swapped out. It cannot use + * // taskENTER_CRITICAL ()/taskEXIT_CRITICAL () as the length of the + * // operation may cause interrupts to be missed - including the + * // ticks. + * + * // Prevent the real time kernel swapping out the task. + * vTaskSuspendAll (); + * + * // Perform the operation here. There is no need to use critical + * // sections as we have all the microcontroller processing time. + * // During this time interrupts will still operate and the real + * // time kernel tick count will be maintained. + * + * // ... + * + * // The operation is complete. Restart the kernel. We want to force + * // a context switch - but there is no point if resuming the scheduler + * // caused a context switch already. + * if( !xTaskResumeAll () ) + * { + * taskYIELD (); + * } + * } + * } + * @endcode * \ingroup SchedulerControl */ BaseType_t xTaskResumeAll( void ) PRIVILEGED_FUNCTION; @@ -1222,19 +1270,16 @@ BaseType_t xTaskResumeAll( void ) PRIVILEGED_FUNCTION; *----------------------------------------------------------*/ /** - * task. h - *
TickType_t xTaskGetTickCount( void );
+ * Get tick count * * @return The count of ticks since vTaskStartScheduler was called. * - * \defgroup xTaskGetTickCount xTaskGetTickCount * \ingroup TaskUtils */ TickType_t xTaskGetTickCount( void ) PRIVILEGED_FUNCTION; /** - * task. h - *
TickType_t xTaskGetTickCountFromISR( void );
+ * Get tick count from ISR * * @return The count of ticks since vTaskStartScheduler was called. * @@ -1243,50 +1288,43 @@ TickType_t xTaskGetTickCount( void ) PRIVILEGED_FUNCTION; * microcontroller being used or interrupt nesting is either not supported or * not being used. * - * \defgroup xTaskGetTickCountFromISR xTaskGetTickCountFromISR * \ingroup TaskUtils */ TickType_t xTaskGetTickCountFromISR( void ) PRIVILEGED_FUNCTION; /** - * task. h - *
UBaseType_t uxTaskGetNumberOfTasks( void );
+ * Get current number of tasks * * @return The number of tasks that the real time kernel is currently managing. * This includes all ready, blocked and suspended tasks. A task that * has been deleted but not yet freed by the idle task will also be * included in the count. * - * \defgroup uxTaskGetNumberOfTasks uxTaskGetNumberOfTasks * \ingroup TaskUtils */ UBaseType_t uxTaskGetNumberOfTasks( void ) PRIVILEGED_FUNCTION; /** - * task. h - *
char *pcTaskGetTaskName( TaskHandle_t xTaskToQuery );
+ * Get task name * * @return The text (human readable) name of the task referenced by the handle * xTaskToQuery. A task can query its own name by either passing in its own * handle, or by setting xTaskToQuery to NULL. INCLUDE_pcTaskGetTaskName must be * set to 1 in FreeRTOSConfig.h for pcTaskGetTaskName() to be available. * - * \defgroup pcTaskGetTaskName pcTaskGetTaskName * \ingroup TaskUtils */ char *pcTaskGetTaskName( TaskHandle_t xTaskToQuery ) PRIVILEGED_FUNCTION; /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ /** - * task.h - *
UBaseType_t uxTaskGetStackHighWaterMark( TaskHandle_t xTask );
+ * Returns the high water mark of the stack associated with xTask. * * INCLUDE_uxTaskGetStackHighWaterMark must be set to 1 in FreeRTOSConfig.h for * this function to be available. * - * Returns the high water mark of the stack associated with xTask. That is, - * the minimum free stack space there has been (in words, so on a 32 bit machine - * a value of 1 means 4 bytes) since the task started. The smaller the returned - * number the closer the task has come to overflowing its stack. + * High water mark is the minimum free stack space there has been (in words, + * so on a 32 bit machine a value of 1 means 4 bytes) since the task started. + * The smaller the returned number the closer the task has come to overflowing its stack. * * @param xTask Handle of the task associated with the stack to be checked. * Set xTask to NULL to check the stack of the calling task. @@ -1298,14 +1336,12 @@ char *pcTaskGetTaskName( TaskHandle_t xTaskToQuery ) PRIVILEGED_FUNCTION; /*lint UBaseType_t uxTaskGetStackHighWaterMark( TaskHandle_t xTask ) PRIVILEGED_FUNCTION; /** - * task.h - *
uint8_t* pxTaskGetStackStart( TaskHandle_t xTask);
+ * Returns the start of the stack associated with xTask. * * INCLUDE_pxTaskGetStackStart must be set to 1 in FreeRTOSConfig.h for * this function to be available. * - * Returns the start of the stack associated with xTask. That is, - * the highest stack memory address on architectures where the stack grows down + * Returns the highest stack memory address on architectures where the stack grows down * from high memory, and the lowest memory address on architectures where the * stack grows up from low memory. * @@ -1325,73 +1361,130 @@ constant. */ #ifdef configUSE_APPLICATION_TASK_TAG #if configUSE_APPLICATION_TASK_TAG == 1 /** - * task.h - *
void vTaskSetApplicationTaskTag( TaskHandle_t xTask, TaskHookFunction_t pxHookFunction );
- * * Sets pxHookFunction to be the task hook function used by the task xTask. - * Passing xTask as NULL has the effect of setting the calling tasks hook - * function. + * @param xTask Handle of the task to set the hook function for + * Passing xTask as NULL has the effect of setting the calling + * tasks hook function. + * @param pxHookFunction Pointer to the hook function. */ void vTaskSetApplicationTaskTag( TaskHandle_t xTask, TaskHookFunction_t pxHookFunction ) PRIVILEGED_FUNCTION; /** - * task.h - *
void xTaskGetApplicationTaskTag( TaskHandle_t xTask );
- * - * Returns the pxHookFunction value assigned to the task xTask. + * Get the hook function assigned to given task. + * @param xTask Handle of the task to get the hook function for + * Passing xTask as NULL has the effect of getting the calling + * tasks hook function. + * @return The pxHookFunction value assigned to the task xTask. */ TaskHookFunction_t xTaskGetApplicationTaskTag( TaskHandle_t xTask ) PRIVILEGED_FUNCTION; #endif /* configUSE_APPLICATION_TASK_TAG ==1 */ #endif /* ifdef configUSE_APPLICATION_TASK_TAG */ #if( configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 ) - /* Each task contains an array of pointers that is dimensioned by the - configNUM_THREAD_LOCAL_STORAGE_POINTERS setting in FreeRTOSConfig.h. The - kernel does not use the pointers itself, so the application writer can use - the pointers for any purpose they wish. The following two functions are - used to set and query a pointer respectively. */ + /** + * Set local storage pointer specific to the given task. + * + * Each task contains an array of pointers that is dimensioned by the + * configNUM_THREAD_LOCAL_STORAGE_POINTERS setting in FreeRTOSConfig.h. + * The kernel does not use the pointers itself, so the application writer + * can use the pointers for any purpose they wish. + * + * @param xTaskToSet Task to set thread local storage pointer for + * @param xIndex The index of the pointer to set, from 0 to + * configNUM_THREAD_LOCAL_STORAGE_POINTERS - 1. + * @param pvValue Pointer value to set. + */ void vTaskSetThreadLocalStoragePointer( TaskHandle_t xTaskToSet, BaseType_t xIndex, void *pvValue ) PRIVILEGED_FUNCTION; + + + /** + * Get local storage pointer specific to the given task. + * + * Each task contains an array of pointers that is dimensioned by the + * configNUM_THREAD_LOCAL_STORAGE_POINTERS setting in FreeRTOSConfig.h. + * The kernel does not use the pointers itself, so the application writer + * can use the pointers for any purpose they wish. + * + * @param xTaskToQuery Task to get thread local storage pointer for + * @param xIndex The index of the pointer to get, from 0 to + * configNUM_THREAD_LOCAL_STORAGE_POINTERS - 1. + * @return Pointer value + */ void *pvTaskGetThreadLocalStoragePointer( TaskHandle_t xTaskToQuery, BaseType_t xIndex ) PRIVILEGED_FUNCTION; - #if ( configTHREAD_LOCAL_STORAGE_DELETE_CALLBACKS ) - typedef void (*TlsDeleteCallbackFunction_t)( int, void * ); - void vTaskSetThreadLocalStoragePointerAndDelCallback( TaskHandle_t xTaskToSet, BaseType_t xIndex, void *pvValue , TlsDeleteCallbackFunction_t xDelCallback); + #if ( configTHREAD_LOCAL_STORAGE_DELETE_CALLBACKS ) + + /** + * Prototype of local storage pointer deletion callback. + */ + typedef void (*TlsDeleteCallbackFunction_t)( int, void * ); + + /** + * Set local storage pointer and deletion callback. + * + * Each task contains an array of pointers that is dimensioned by the + * configNUM_THREAD_LOCAL_STORAGE_POINTERS setting in FreeRTOSConfig.h. + * The kernel does not use the pointers itself, so the application writer + * can use the pointers for any purpose they wish. + * + * Local storage pointers set for a task can reference dynamically + * allocated resources. This function is similar to + * vTaskSetThreadLocalStoragePointer, but provides a way to release + * these resources when the task gets deleted. For each pointer, + * a callback function can be set. This function will be called + * when task is deleted, with the local storage pointer index + * and value as arguments. + * + * @param xTaskToSet Task to set thread local storage pointer for + * @param xIndex The index of the pointer to set, from 0 to + * configNUM_THREAD_LOCAL_STORAGE_POINTERS - 1. + * @param pvValue Pointer value to set. + * @param pvDelCallback Function to call to dispose of the local + * storage pointer when the task is deleted. + */ + void vTaskSetThreadLocalStoragePointerAndDelCallback( TaskHandle_t xTaskToSet, BaseType_t xIndex, void *pvValue, TlsDeleteCallbackFunction_t pvDelCallback); #endif #endif /** - * task.h - *
BaseType_t xTaskCallApplicationTaskHook( TaskHandle_t xTask, void *pvParameter );
- * - * Calls the hook function associated with xTask. Passing xTask as NULL has + * Calls the hook function associated with xTask. Passing xTask as NULL has * the effect of calling the Running tasks (the calling task) hook function. * - * pvParameter is passed to the hook function for the task to interpret as it + * @param xTask Handle of the task to call the hook for. + * @param pvParameter Parameter passed to the hook function for the task to interpret as it * wants. The return value is the value returned by the task hook function * registered by the user. */ BaseType_t xTaskCallApplicationTaskHook( TaskHandle_t xTask, void *pvParameter ) PRIVILEGED_FUNCTION; /** + * Get the handle of idle task for the current CPU. + * * xTaskGetIdleTaskHandle() is only available if * INCLUDE_xTaskGetIdleTaskHandle is set to 1 in FreeRTOSConfig.h. * - * Simply returns the handle of the idle task. It is not valid to call + * @return The handle of the idle task. It is not valid to call * xTaskGetIdleTaskHandle() before the scheduler has been started. */ TaskHandle_t xTaskGetIdleTaskHandle( void ); /** + * Get the handle of idle task for the given CPU. + * * xTaskGetIdleTaskHandleForCPU() is only available if * INCLUDE_xTaskGetIdleTaskHandle is set to 1 in FreeRTOSConfig.h. * - * Simply returns the idle task handle of a given cpu. It is not valid to call + * @param cpuid The CPU to get the handle for + * + * @return Idle task handle of a given cpu. It is not valid to call * xTaskGetIdleTaskHandleForCPU() before the scheduler has been started. */ TaskHandle_t xTaskGetIdleTaskHandleForCPU( UBaseType_t cpuid ); /** + * Get the state of tasks in the system. + * * configUSE_TRACE_FACILITY must be defined as 1 in FreeRTOSConfig.h for * uxTaskGetSystemState() to be available. * @@ -1401,7 +1494,7 @@ TaskHandle_t xTaskGetIdleTaskHandleForCPU( UBaseType_t cpuid ); * of run time consumed by the task. See the TaskStatus_t structure * definition in this file for the full member list. * - * NOTE: This function is intended for debugging use only as its use results in + * @note This function is intended for debugging use only as its use results in * the scheduler remaining suspended for an extended period. * * @param pxTaskStatusArray A pointer to an array of TaskStatus_t structures. @@ -1426,79 +1519,78 @@ TaskHandle_t xTaskGetIdleTaskHandleForCPU( UBaseType_t cpuid ); * in the uxArraySize parameter was too small. * * Example usage: -
-    // This example demonstrates how a human readable table of run time stats
-	// information is generated from raw data provided by uxTaskGetSystemState().
-	// The human readable table is written to pcWriteBuffer
-	void vTaskGetRunTimeStats( char *pcWriteBuffer )
-	{
-	TaskStatus_t *pxTaskStatusArray;
-	volatile UBaseType_t uxArraySize, x;
-	uint32_t ulTotalRunTime, ulStatsAsPercentage;
-
-		// Make sure the write buffer does not contain a string.
-		*pcWriteBuffer = 0x00;
-
-		// Take a snapshot of the number of tasks in case it changes while this
-		// function is executing.
-		uxArraySize = uxTaskGetNumberOfTasks();
-
-		// Allocate a TaskStatus_t structure for each task.  An array could be
-		// allocated statically at compile time.
-		pxTaskStatusArray = pvPortMalloc( uxArraySize * sizeof( TaskStatus_t ) );
-
-		if( pxTaskStatusArray != NULL )
-		{
-			// Generate raw status information about each task.
-			uxArraySize = uxTaskGetSystemState( pxTaskStatusArray, uxArraySize, &ulTotalRunTime );
-
-			// For percentage calculations.
-			ulTotalRunTime /= 100UL;
-
-			// Avoid divide by zero errors.
-			if( ulTotalRunTime > 0 )
-			{
-				// For each populated position in the pxTaskStatusArray array,
-				// format the raw data as human readable ASCII data
-				for( x = 0; x < uxArraySize; x++ )
-				{
-					// What percentage of the total run time has the task used?
-					// This will always be rounded down to the nearest integer.
-					// ulTotalRunTimeDiv100 has already been divided by 100.
-					ulStatsAsPercentage = pxTaskStatusArray[ x ].ulRunTimeCounter / ulTotalRunTime;
-
-					if( ulStatsAsPercentage > 0UL )
-					{
-						sprintf( pcWriteBuffer, "%s\t\t%lu\t\t%lu%%\r\n", pxTaskStatusArray[ x ].pcTaskName, pxTaskStatusArray[ x ].ulRunTimeCounter, ulStatsAsPercentage );
-					}
-					else
-					{
-						// If the percentage is zero here then the task has
-						// consumed less than 1% of the total run time.
-						sprintf( pcWriteBuffer, "%s\t\t%lu\t\t<1%%\r\n", pxTaskStatusArray[ x ].pcTaskName, pxTaskStatusArray[ x ].ulRunTimeCounter );
-					}
-
-					pcWriteBuffer += strlen( ( char * ) pcWriteBuffer );
-				}
-			}
-
-			// The array is no longer needed, free the memory it consumes.
-			vPortFree( pxTaskStatusArray );
-		}
-	}
-	
+ * @code{c} + * // This example demonstrates how a human readable table of run time stats + * // information is generated from raw data provided by uxTaskGetSystemState(). + * // The human readable table is written to pcWriteBuffer + * void vTaskGetRunTimeStats( char *pcWriteBuffer ) + * { + * TaskStatus_t *pxTaskStatusArray; + * volatile UBaseType_t uxArraySize, x; + * uint32_t ulTotalRunTime, ulStatsAsPercentage; + * + * // Make sure the write buffer does not contain a string. + * *pcWriteBuffer = 0x00; + * + * // Take a snapshot of the number of tasks in case it changes while this + * // function is executing. + * uxArraySize = uxTaskGetNumberOfTasks(); + * + * // Allocate a TaskStatus_t structure for each task. An array could be + * // allocated statically at compile time. + * pxTaskStatusArray = pvPortMalloc( uxArraySize * sizeof( TaskStatus_t ) ); + * + * if( pxTaskStatusArray != NULL ) + * { + * // Generate raw status information about each task. + * uxArraySize = uxTaskGetSystemState( pxTaskStatusArray, uxArraySize, &ulTotalRunTime ); + * + * // For percentage calculations. + * ulTotalRunTime /= 100UL; + * + * // Avoid divide by zero errors. + * if( ulTotalRunTime > 0 ) + * { + * // For each populated position in the pxTaskStatusArray array, + * // format the raw data as human readable ASCII data + * for( x = 0; x < uxArraySize; x++ ) + * { + * // What percentage of the total run time has the task used? + * // This will always be rounded down to the nearest integer. + * // ulTotalRunTimeDiv100 has already been divided by 100. + * ulStatsAsPercentage = pxTaskStatusArray[ x ].ulRunTimeCounter / ulTotalRunTime; + * + * if( ulStatsAsPercentage > 0UL ) + * { + * sprintf( pcWriteBuffer, "%s\t\t%lu\t\t%lu%%\r\n", pxTaskStatusArray[ x ].pcTaskName, pxTaskStatusArray[ x ].ulRunTimeCounter, ulStatsAsPercentage ); + * } + * else + * { + * // If the percentage is zero here then the task has + * // consumed less than 1% of the total run time. + * sprintf( pcWriteBuffer, "%s\t\t%lu\t\t<1%%\r\n", pxTaskStatusArray[ x ].pcTaskName, pxTaskStatusArray[ x ].ulRunTimeCounter ); + * } + * + * pcWriteBuffer += strlen( ( char * ) pcWriteBuffer ); + * } + * } + * + * // The array is no longer needed, free the memory it consumes. + * vPortFree( pxTaskStatusArray ); + * } + * } + * @endcode */ UBaseType_t uxTaskGetSystemState( TaskStatus_t * const pxTaskStatusArray, const UBaseType_t uxArraySize, uint32_t * const pulTotalRunTime ); /** - * task. h - *
void vTaskList( char *pcWriteBuffer );
+ * List all the current tasks. * * configUSE_TRACE_FACILITY and configUSE_STATS_FORMATTING_FUNCTIONS must * both be defined as 1 for this function to be available. See the * configuration section of the FreeRTOS.org website for more information. * - * NOTE 1: This function will disable interrupts for its duration. It is + * @note This function will disable interrupts for its duration. It is * not intended for normal application runtime use but as a debug aid. * * Lists all the current tasks, along with their current state and stack @@ -1507,9 +1599,7 @@ UBaseType_t uxTaskGetSystemState( TaskStatus_t * const pxTaskStatusArray, const * Tasks are reported as blocked ('B'), ready ('R'), deleted ('D') or * suspended ('S'). * - * PLEASE NOTE: - * - * This function is provided for convenience only, and is used by many of the + * @note This function is provided for convenience only, and is used by many of the * demo applications. Do not consider it to be part of the scheduler. * * vTaskList() calls uxTaskGetSystemState(), then formats part of the @@ -1532,14 +1622,12 @@ UBaseType_t uxTaskGetSystemState( TaskStatus_t * const pxTaskStatusArray, const * enough to contain the generated report. Approximately 40 bytes per * task should be sufficient. * - * \defgroup vTaskList vTaskList * \ingroup TaskUtils */ void vTaskList( char * pcWriteBuffer ) PRIVILEGED_FUNCTION; /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ /** - * task. h - *
void vTaskGetRunTimeStats( char *pcWriteBuffer );
+ * Get the state of running tasks as a string * * configGENERATE_RUN_TIME_STATS and configUSE_STATS_FORMATTING_FUNCTIONS * must both be defined as 1 for this function to be available. The application @@ -1549,7 +1637,7 @@ void vTaskList( char * pcWriteBuffer ) PRIVILEGED_FUNCTION; /*lint !e971 Unquali * value respectively. The counter should be at least 10 times the frequency of * the tick count. * - * NOTE 1: This function will disable interrupts for its duration. It is + * @note This function will disable interrupts for its duration. It is * not intended for normal application runtime use but as a debug aid. * * Setting configGENERATE_RUN_TIME_STATS to 1 will result in a total @@ -1560,9 +1648,7 @@ void vTaskList( char * pcWriteBuffer ) PRIVILEGED_FUNCTION; /*lint !e971 Unquali * task into a buffer, both as an absolute count value and as a percentage * of the total system execution time. * - * NOTE 2: - * - * This function is provided for convenience only, and is used by many of the + * @note This function is provided for convenience only, and is used by many of the * demo applications. Do not consider it to be part of the scheduler. * * vTaskGetRunTimeStats() calls uxTaskGetSystemState(), then formats part of the @@ -1586,14 +1672,12 @@ void vTaskList( char * pcWriteBuffer ) PRIVILEGED_FUNCTION; /*lint !e971 Unquali * contain the generated report. Approximately 40 bytes per task should * be sufficient. * - * \defgroup vTaskGetRunTimeStats vTaskGetRunTimeStats * \ingroup TaskUtils */ void vTaskGetRunTimeStats( char *pcWriteBuffer ) PRIVILEGED_FUNCTION; /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ /** - * task. h - *
BaseType_t xTaskNotify( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction );
+ * Send task notification. * * configUSE_TASK_NOTIFICATIONS must be undefined or defined as 1 for this * function to be available. @@ -1634,43 +1718,40 @@ void vTaskGetRunTimeStats( char *pcWriteBuffer ) PRIVILEGED_FUNCTION; /*lint !e9 * * @param eAction Specifies how the notification updates the task's notification * value, if at all. Valid values for eAction are as follows: + * - eSetBits: + * The task's notification value is bitwise ORed with ulValue. xTaskNofify() + * always returns pdPASS in this case. * - * eSetBits - - * The task's notification value is bitwise ORed with ulValue. xTaskNofify() - * always returns pdPASS in this case. + * - eIncrement: + * The task's notification value is incremented. ulValue is not used and + * xTaskNotify() always returns pdPASS in this case. * - * eIncrement - - * The task's notification value is incremented. ulValue is not used and - * xTaskNotify() always returns pdPASS in this case. + * - eSetValueWithOverwrite: + * The task's notification value is set to the value of ulValue, even if the + * task being notified had not yet processed the previous notification (the + * task already had a notification pending). xTaskNotify() always returns + * pdPASS in this case. * - * eSetValueWithOverwrite - - * The task's notification value is set to the value of ulValue, even if the - * task being notified had not yet processed the previous notification (the - * task already had a notification pending). xTaskNotify() always returns - * pdPASS in this case. + * - eSetValueWithoutOverwrite: + * If the task being notified did not already have a notification pending then + * the task's notification value is set to ulValue and xTaskNotify() will + * return pdPASS. If the task being notified already had a notification + * pending then no action is performed and pdFAIL is returned. * - * eSetValueWithoutOverwrite - - * If the task being notified did not already have a notification pending then - * the task's notification value is set to ulValue and xTaskNotify() will - * return pdPASS. If the task being notified already had a notification - * pending then no action is performed and pdFAIL is returned. - * - * eNoAction - - * The task receives a notification without its notification value being - * updated. ulValue is not used and xTaskNotify() always returns pdPASS in - * this case. + * - eNoAction: + * The task receives a notification without its notification value being + *   updated. ulValue is not used and xTaskNotify() always returns pdPASS in + * this case. * * @return Dependent on the value of eAction. See the description of the * eAction parameter. * - * \defgroup xTaskNotify xTaskNotify * \ingroup TaskNotifications */ BaseType_t xTaskNotify( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction ); /** - * task. h - *
BaseType_t xTaskNotifyFromISR( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction, BaseType_t *pxHigherPriorityTaskWoken );
+ * Send task notification from an ISR. * * configUSE_TASK_NOTIFICATIONS must be undefined or defined as 1 for this * function to be available. @@ -1714,31 +1795,30 @@ BaseType_t xTaskNotify( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAct * * @param eAction Specifies how the notification updates the task's notification * value, if at all. Valid values for eAction are as follows: + * - eSetBits: + * The task's notification value is bitwise ORed with ulValue. xTaskNofify() + * always returns pdPASS in this case. * - * eSetBits - - * The task's notification value is bitwise ORed with ulValue. xTaskNofify() - * always returns pdPASS in this case. + * - eIncrement: + * The task's notification value is incremented. ulValue is not used and + * xTaskNotify() always returns pdPASS in this case. * - * eIncrement - - * The task's notification value is incremented. ulValue is not used and - * xTaskNotify() always returns pdPASS in this case. + * - eSetValueWithOverwrite: + * The task's notification value is set to the value of ulValue, even if the + * task being notified had not yet processed the previous notification (the + * task already had a notification pending). xTaskNotify() always returns + * pdPASS in this case. * - * eSetValueWithOverwrite - - * The task's notification value is set to the value of ulValue, even if the - * task being notified had not yet processed the previous notification (the - * task already had a notification pending). xTaskNotify() always returns - * pdPASS in this case. + * - eSetValueWithoutOverwrite: + * If the task being notified did not already have a notification pending then + * the task's notification value is set to ulValue and xTaskNotify() will + * return pdPASS. If the task being notified already had a notification + * pending then no action is performed and pdFAIL is returned. * - * eSetValueWithoutOverwrite - - * If the task being notified did not already have a notification pending then - * the task's notification value is set to ulValue and xTaskNotify() will - * return pdPASS. If the task being notified already had a notification - * pending then no action is performed and pdFAIL is returned. - * - * eNoAction - - * The task receives a notification without its notification value being - * updated. ulValue is not used and xTaskNotify() always returns pdPASS in - * this case. + * - eNoAction: + * The task receives a notification without its notification value being + * updated. ulValue is not used and xTaskNotify() always returns pdPASS in + * this case. * * @param pxHigherPriorityTaskWoken xTaskNotifyFromISR() will set * *pxHigherPriorityTaskWoken to pdTRUE if sending the notification caused the @@ -1752,14 +1832,12 @@ BaseType_t xTaskNotify( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAct * @return Dependent on the value of eAction. See the description of the * eAction parameter. * - * \defgroup xTaskNotify xTaskNotify * \ingroup TaskNotifications */ BaseType_t xTaskNotifyFromISR( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction, BaseType_t *pxHigherPriorityTaskWoken ); /** - * task. h - *
BaseType_t xTaskNotifyWait( uint32_t ulBitsToClearOnEntry, uint32_t ulBitsToClearOnExit, uint32_t *pulNotificationValue, TickType_t xTicksToWait );
+ * Wait for task notification * * configUSE_TASK_NOTIFICATIONS must be undefined or defined as 1 for this * function to be available. @@ -1827,14 +1905,12 @@ BaseType_t xTaskNotifyFromISR( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNo * already pending when xTaskNotifyWait was called) then pdPASS is * returned. Otherwise pdFAIL is returned. * - * \defgroup xTaskNotifyWait xTaskNotifyWait * \ingroup TaskNotifications */ BaseType_t xTaskNotifyWait( uint32_t ulBitsToClearOnEntry, uint32_t ulBitsToClearOnExit, uint32_t *pulNotificationValue, TickType_t xTicksToWait ); /** - * task. h - *
BaseType_t xTaskNotifyGive( TaskHandle_t xTaskToNotify );
+ * Simplified macro for sending task notification. * * configUSE_TASK_NOTIFICATIONS must be undefined or defined as 1 for this macro * to be available. @@ -1873,14 +1949,12 @@ BaseType_t xTaskNotifyWait( uint32_t ulBitsToClearOnEntry, uint32_t ulBitsToClea * @return xTaskNotifyGive() is a macro that calls xTaskNotify() with the * eAction parameter set to eIncrement - so pdPASS is always returned. * - * \defgroup xTaskNotifyGive xTaskNotifyGive * \ingroup TaskNotifications */ #define xTaskNotifyGive( xTaskToNotify ) xTaskNotify( ( xTaskToNotify ), 0, eIncrement ); /** - * task. h - *
void vTaskNotifyGiveFromISR( TaskHandle_t xTaskHandle, BaseType_t *pxHigherPriorityTaskWoken );
+ * Simplified macro for sending task notification from ISR.
  *
  * configUSE_TASK_NOTIFICATIONS must be undefined or defined as 1 for this macro
  * to be available.
@@ -1928,14 +2002,12 @@ BaseType_t xTaskNotifyWait( uint32_t ulBitsToClearOnEntry, uint32_t ulBitsToClea
  * requested from an ISR is dependent on the port - see the documentation page
  * for the port in use.
  *
- * \defgroup xTaskNotifyWait xTaskNotifyWait
  * \ingroup TaskNotifications
  */
 void vTaskNotifyGiveFromISR( TaskHandle_t xTaskToNotify, BaseType_t *pxHigherPriorityTaskWoken );
 
 /**
- * task. h
- * 
uint32_t ulTaskNotifyTake( BaseType_t xClearCountOnExit, TickType_t xTicksToWait );
+ * Simplified macro for receiving task notification. * * configUSE_TASK_NOTIFICATIONS must be undefined or defined as 1 for this * function to be available. @@ -1997,7 +2069,6 @@ void vTaskNotifyGiveFromISR( TaskHandle_t xTaskToNotify, BaseType_t *pxHigherPri * @return The task's notification count before it is either cleared to zero or * decremented (see the xClearCountOnExit parameter). * - * \defgroup ulTaskNotifyTake ulTaskNotifyTake * \ingroup TaskNotifications */ uint32_t ulTaskNotifyTake( BaseType_t xClearCountOnExit, TickType_t xTicksToWait ); @@ -2005,7 +2076,7 @@ uint32_t ulTaskNotifyTake( BaseType_t xClearCountOnExit, TickType_t xTicksToWait /*----------------------------------------------------------- * SCHEDULER INTERNALS AVAILABLE FOR PORTING PURPOSES *----------------------------------------------------------*/ - +/** @cond */ /* * THIS FUNCTION MUST NOT BE USED FROM APPLICATION CODE. IT IS ONLY * INTENDED FOR USE WHEN IMPLEMENTING A PORT OF THE SCHEDULER AND IS @@ -2224,6 +2295,8 @@ void *pvTaskIncrementMutexHeldCount( void ); */ UBaseType_t uxTaskGetSnapshotAll( TaskSnapshot_t * const pxTaskSnapshotArray, const UBaseType_t uxArraySize, UBaseType_t * const pxTcbSz ); +/** @endcond */ + #ifdef __cplusplus } #endif diff --git a/components/freertos/include/freertos/timers.h b/components/freertos/include/freertos/timers.h index 8656d069d7..17492e64c6 100644 --- a/components/freertos/include/freertos/timers.h +++ b/components/freertos/include/freertos/timers.h @@ -117,24 +117,18 @@ or interrupt version of the queue send function should be used. */ */ typedef void * TimerHandle_t; -/* +/** * Defines the prototype to which timer callback functions must conform. */ typedef void (*TimerCallbackFunction_t)( TimerHandle_t xTimer ); -/* +/** * Defines the prototype to which functions used with the * xTimerPendFunctionCallFromISR() function must conform. */ 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 * 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. * * Example usage: - * @verbatim + * @code{c} * #define NUM_TIMERS 5 * * // An array to hold handles to the created timers. @@ -263,7 +257,7 @@ typedef void (*PendedFunction_t)( void *, uint32_t ); * // Should not reach here. * for( ;; ); * } - * @endverbatim + * @endcode */ #if( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) TimerHandle_t xTimerCreate( const char * const pcTimerName, @@ -274,13 +268,6 @@ typedef void (*PendedFunction_t)( void *, uint32_t ); #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 * 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. * * Example usage: - * @verbatim + * @code{c} * * // The buffer used to hold the software timer's data structure. * static StaticTimer_t xTimerBuffer; @@ -393,20 +380,18 @@ typedef void (*PendedFunction_t)( void *, uint32_t ); * // Should not reach here. * for( ;; ); * } - * @endverbatim + * @endcode */ #if( configSUPPORT_STATIC_ALLOCATION == 1 ) - TimerHandle_t xTimerCreateStatic( const char * const pcTimerName, - const TickType_t xTimerPeriodInTicks, - const UBaseType_t uxAutoReload, - void * const pvTimerID, - TimerCallbackFunction_t pxCallbackFunction, - StaticTimer_t *pxTimerBuffer ) PRIVILEGED_FUNCTION; /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + TimerHandle_t xTimerCreateStatic( const char * const pcTimerName, + const TickType_t xTimerPeriodInTicks, + const UBaseType_t uxAutoReload, + void * const pvTimerID, + TimerCallbackFunction_t pxCallbackFunction, + StaticTimer_t *pxTimerBuffer ) PRIVILEGED_FUNCTION; /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ #endif /* configSUPPORT_STATIC_ALLOCATION */ /** - * void *pvTimerGetTimerID( TimerHandle_t xTimer ); - * * Returns the ID assigned to the timer. * * 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 vTimerSetTimerID( TimerHandle_t xTimer, void *pvNewID ); - * * Sets the ID assigned to the timer. * * 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; /** - * BaseType_t xTimerIsTimerActive( TimerHandle_t xTimer ); - * * Queries a timer to see if it is active or dormant. * * A timer will be dormant if: + * * 1) It has been created but not started, or + * * 2) It is an expired one-shot timer that has not been restarted. * * 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. * * Example usage: - * @verbatim + * @code{c} * // This function assumes xTimer has already been created. * 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. * } * } - * @endverbatim + * @endcode */ BaseType_t xTimerIsTimerActive( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION; /** - * TaskHandle_t xTimerGetTimerDaemonTaskHandle( void ); - * * xTimerGetTimerDaemonTaskHandle() is only available if * 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 ); /** - * TickType_t xTimerGetPeriod( TimerHandle_t xTimer ); - * * Returns the period of a timer. * * @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 xTimerGetExpiryTime( TimerHandle_t xTimer ); - * * 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 * current time. @@ -522,8 +499,6 @@ TickType_t xTimerGetPeriod( 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 * 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 @@ -574,8 +549,6 @@ TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION; #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 * 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 @@ -616,10 +589,6 @@ TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION; #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 * 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 @@ -661,7 +630,7 @@ TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION; * configTIMER_TASK_PRIORITY configuration constant. * * Example usage: - * @verbatim + * @code{c} * // This function assumes xTimer has already been created. If 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 @@ -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 ) ) /** - * BaseType_t xTimerDelete( TimerHandle_t xTimer, TickType_t xTicksToWait ); - * * 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 * 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 ) ) /** - * BaseType_t xTimerReset( TimerHandle_t xTimer, TickType_t xTicksToWait ); - * * 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 * 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. * * Example usage: - * @verbatim + * @code{c} * // 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 * // 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. * for( ;; ); * } - * @endverbatim + * @endcode */ #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 * routine. * @@ -888,7 +850,7 @@ TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION; * configuration constant. * * Example usage: - * @verbatim + * @code{c} * // This scenario assumes xBacklightTimer has already been created. 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 @@ -939,14 +901,11 @@ TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION; * // depends on the FreeRTOS port being used). * } * } - * @endverbatim + * @endcode */ #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 * routine. * @@ -972,7 +931,7 @@ TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION; * priority is set by the configTIMER_TASK_PRIORITY configuration constant. * * Example usage: - * @verbatim + * @code{c} * // This scenario assumes xTimer has already been created and started. When * // 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). * } * } - * @endverbatim + * @endcode */ #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 * service routine. * @@ -1045,7 +1000,7 @@ TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION; * priority is set by the configTIMER_TASK_PRIORITY configuration constant. * * Example usage: - * @verbatim + * @code{c} * // This scenario assumes xTimer has already been created and started. When * // 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). * } * } - * @endverbatim + * @endcode */ #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 * routine. * @@ -1110,7 +1062,7 @@ TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION; * task priority is set by the configTIMER_TASK_PRIORITY configuration constant. * * Example usage: - * @verbatim + * @code{c} * // This scenario assumes xBacklightTimer has already been created. 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 @@ -1161,18 +1113,12 @@ TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION; * // depends on the FreeRTOS port being used). * } * } - * @endverbatim + * @endcode */ #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 * function to the RTOS daemon task (the timer service task, hence this function * 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. * * Example usage: - * @verbatim + * @code{c} * * // The callback function that will execute in the context of the daemon task. * // 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 ); * * } - * @endverbatim + * @endcode */ 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 * service task, hence this function is implemented in timers.c and is prefixed * 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 ); /** - * const char * const pcTimerGetTimerName( TimerHandle_t xTimer ); - * * Returns the name that was assigned to a timer when the timer was created. * * @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. */ +/** @cond */ /* * Functions beyond this part are not part of the public API and are intended * 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 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 } #endif diff --git a/docs/Doxyfile b/docs/Doxyfile index 950f914214..713b6a49dd 100644 --- a/docs/Doxyfile +++ b/docs/Doxyfile @@ -153,7 +153,17 @@ INPUT = \ ../components/esp32/include/esp_pm.h \ ../components/esp32/include/esp32/pm.h \ ### 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 @@ -165,7 +175,16 @@ WARN_NO_PARAMDOC = YES ENABLE_PREPROCESSING = YES MACRO_EXPANSION = 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 ## diff --git a/docs/api-reference/system/freertos.rst b/docs/api-reference/system/freertos.rst new file mode 100644 index 0000000000..2b0b0929fb --- /dev/null +++ b/docs/api-reference/system/freertos.rst @@ -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 + diff --git a/docs/api-reference/system/hooks.rst b/docs/api-reference/system/hooks.rst index 78ac708a23..5a65b29a16 100644 --- a/docs/api-reference/system/hooks.rst +++ b/docs/api-reference/system/hooks.rst @@ -1,7 +1,7 @@ .. _hooks_api_reference: -ESP-IDF FreeRTOS Hooks -====================== +FreeRTOS Hooks +============== Overview -------- diff --git a/docs/api-reference/system/index.rst b/docs/api-reference/system/index.rst index c895ebd8b5..593fcc3d69 100644 --- a/docs/api-reference/system/index.rst +++ b/docs/api-reference/system/index.rst @@ -4,19 +4,20 @@ System API .. toctree:: :maxdepth: 1 + FreeRTOS + FreeRTOS Hooks Heap Memory Allocation Heap Memory Debugging Interrupt Allocation Watchdogs - Hooks Inter-Processor Call High Resolution Timer - Over The Air Updates (OTA) - Sleep Modes - Power Management Logging - Base MAC address Application Level Tracing + Power Management + Sleep Modes + Base MAC address + Over The Air Updates (OTA) Example code for this API section is provided in :example:`system` directory of ESP-IDF examples. From 6f90393f2218560ff3fbd4bb4980ea03b4173193 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Mon, 4 Dec 2017 20:09:12 +0800 Subject: [PATCH 08/56] docs: link to FreeRTOS APIs from SMP changes documentation --- docs/api-guides/freertos-smp.rst | 122 +++++++++++++++---------------- 1 file changed, 61 insertions(+), 61 deletions(-) diff --git a/docs/api-guides/freertos-smp.rst b/docs/api-guides/freertos-smp.rst index 16d4c139b4..e1763bf593 100644 --- a/docs/api-guides/freertos-smp.rst +++ b/docs/api-guides/freertos-smp.rst @@ -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 to ESP-IDF. -:ref:`tasks-and-task-creation`: Use ``xTaskCreatePinnedToCore()`` or -``xTaskCreateStaticPinnedToCore()`` to create tasks in ESP-IDF FreeRTOS. The +:ref:`tasks-and-task-creation`: Use :cpp:func:`xTaskCreatePinnedToCore` or +:cpp:func:`xTaskCreateStaticPinnedToCore` to create tasks in ESP-IDF FreeRTOS. The 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**, ``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 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 access protection. :ref:`tick-interrupt-synchronicity`: Tick interrupts of **PRO_CPU** and **APP_CPU** -are not synchronized. Do not expect to use ``vTaskDelay`` or -``vTaskDelayUntil`` as an accurate method of synchronizing task execution +are not synchronized. Do not expect to use :cpp:func:`vTaskDelay` or +:cpp:func:`vTaskDelayUntil` as an accurate method of synchronizing task execution between the two cores. Use a counting semaphore instead as their context switches are not tied to tick interrupts due to preemption. @@ -51,15 +51,15 @@ 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. :ref:`floating-points`: The ESP32 supports hardware acceleration of single -precision floating point arithmetic (`float`). However the use of hardware +precision floating point arithmetic (``float``). However the use of hardware acceleration leads to some behavioral restrictions in ESP-IDF FreeRTOS. -Therefore, tasks that utilize `float` will automatically be pinned to a core if -not done so already. Furthermore, `float` cannot be used in interrupt service +Therefore, tasks that utilize ``float`` will automatically be pinned to a core if +not done so already. Furthermore, ``float`` cannot be used in interrupt service 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 `vTaskDelete()` is called to delete a task that is not currently running +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. @@ -67,7 +67,7 @@ be delegated to the Idle Task. 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 -``vTaskSetThreadLocalStoragePointerAndDelCallback()`` to set TLSP and Deletion +:cpp:func:`vTaskSetThreadLocalStoragePointerAndDelCallback()` to set TLSP and Deletion Callbacks. :ref:`FreeRTOS Hooks`: Vanilla FreeRTOS Hooks were not designed for SMP. @@ -94,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 following functions can be called... - - ``xTaskCreateStatic()`` See :ref:`backporting-notes` below - - ``xQueueCreateStatic()`` - - ``xSemaphoreCreateBinaryStatic()`` - - ``xSemaphoreCreateCountingStatic()`` - - ``xSemaphoreCreateMutexStatic()`` - - ``xSemaphoreCreateRecursiveMutexStatic()`` - - ``xTimerCreateStatic()`` See :ref:`backporting-notes` below - - ``xEventGroupCreateStatic()`` + - :cpp:func:`xTaskCreateStatic` (see :ref:`backporting-notes` below) + - :c:macro:`xQueueCreateStatic` + - :c:macro:`xSemaphoreCreateBinaryStatic` + - :c:macro:`xSemaphoreCreateCountingStatic` + - :c:macro:`xSemaphoreCreateMutexStatic` + - :c:macro:`xSemaphoreCreateRecursiveMutexStatic` + - :cpp:func:`xTimerCreateStatic` (see :ref:`backporting-notes` below) + - :cpp:func:`xEventGroupCreateStatic` Other Features ^^^^^^^^^^^^^^ - - ``vTaskSetThreadLocalStoragePointer()`` See :ref:`backporting-notes` below - - ``pvTaskGetThreadLocalStoragePointer()`` See :ref:`backporting-notes` below - - ``vTimerSetTimerID()`` - - ``xTimerGetPeriod()`` - - ``xTimerGetExpiryTime()`` - - ``pcQueueGetName()`` - - ``uxSemaphoreGetCount()`` + - :cpp:func:`vTaskSetThreadLocalStoragePointer` (see :ref:`backporting-notes` below) + - :cpp:func:`pvTaskGetThreadLocalStoragePointer` (see :ref:`backporting-notes` below) + - :cpp:func:`vTimerSetTimerID` + - :cpp:func:`xTimerGetPeriod` + - :cpp:func:`xTimerGetExpiryTime` + - :cpp:func:`pcQueueGetName` + - :c:macro:`uxSemaphoreGetCount` .. _backporting-notes: Backporting Notes ^^^^^^^^^^^^^^^^^ -**1)** ``xTaskCreateStatic`` has been made SMP compatible in a similar -fashion to ``xTaskCreate`` (see :ref:`tasks-and-task-creation`). Therefore -``xTaskCreateStaticPinnedToCore()`` can also be called. +**1)** :cpp:func:`xTaskCreateStatic` has been made SMP compatible in a similar +fashion to :cpp:func:`xTaskCreate` (see :ref:`tasks-and-task-creation`). Therefore +:cpp:func:`xTaskCreateStaticPinnedToCore` can also be called. **2)** Although vanilla FreeRTOS allows the Timer feature's daemon task to be statically allocated, the daemon task is always dynamically allocated in @@ -130,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 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. @@ -142,9 +142,9 @@ Tasks and Task Creation 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 appending ``PinnedToCore`` to the names of the task creation functions in -vanilla FreeRTOS. The vanilla FreeRTOS functions of ``xTaskCreate()`` -and ``xTaskCreateStatic()`` have led to the addition of -``xTaskCreatePinnedToCore()`` and ``xTaskCreateStaticPinnedToCore()`` in +vanilla FreeRTOS. The vanilla FreeRTOS functions of :cpp:func:`xTaskCreate` +and :cpp:func:`xTaskCreateStatic` have led to the addition of +:cpp:func:`xTaskCreatePinnedToCore` and :cpp:func:`xTaskCreateStaticPinnedToCore` in ESP-IDF FreeRTOS (see :ref:`backported-features`). For more details see :component_file:`freertos/task.c` @@ -164,9 +164,9 @@ of 1000 bytes. It should be noted that the ``uxStackDepth`` parameter in vanilla FreeRTOS specifies a task’s stack depth in terms of the number of words, whereas ESP-IDF FreeRTOS specifies the stack depth in terms of bytes. -Note that the vanilla FreeRTOS functions ``xTaskCreate`` and -``xTaskCreateStatic`` have been macro defined in ESP-IDF FreeRTOS to call -``xTaskCreatePinnedToCore()`` and ``xTaskCreateStaticPinnedToCore()`` +Note that the vanilla FreeRTOS functions :cpp:func:`xTaskCreate` and +:cpp:func:`xTaskCreateStatic` have been defined in ESP-IDF FreeRTOS as inline functions which call +:cpp:func:`xTaskCreatePinnedToCore` and :cpp:func:`xTaskCreateStaticPinnedToCore` respectively with ``tskNO_AFFINITY`` as the ``xCoreID`` value. Each Task Control Block (TCB) in ESP-IDF stores the ``xCoreID`` as a member. @@ -283,18 +283,18 @@ different cores. Scheduler Suspension ^^^^^^^^^^^^^^^^^^^^ -In vanilla FreeRTOS, suspending the scheduler via ``vTaskSuspendAll()`` will -prevent calls of ``vTaskSwitchContext()`` from context switching until the -scheduler has been resumed with ``vTaskResumeAll()``. However servicing ISRs +In vanilla FreeRTOS, suspending the scheduler via :cpp:func:`vTaskSuspendAll` will +prevent calls of ``vTaskSwitchContext`` from context switching until the +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 current running task or ISRSs will not be executed until the scheduler is resumed. Scheduler suspension in vanilla FreeRTOS is a common protection method against simultaneous access of data shared between tasks, whilst still allowing 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 -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 pinned to different cores, scheduler suspension is **NOT** a valid method of protection against simultaneous access. Consider using critical sections @@ -302,7 +302,7 @@ protection against simultaneous access. Consider using critical sections protecting shared resources in ESP-IDF FreeRTOS. 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: @@ -316,8 +316,8 @@ each core being independent, and the tick interrupts to each core being unsynchronized. In vanilla FreeRTOS the tick interrupt triggers a call to -``xTaskIncrementTick()`` which is responsible for incrementing the tick -counter, checking if tasks which have called ``vTaskDelay()`` have fulfilled +:cpp:func:`xTaskIncrementTick` which is responsible for incrementing the tick +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 Ready Task List. The tick interrupt will then call the scheduler if a context switch is necessary. @@ -372,11 +372,11 @@ The ESP-IDF FreeRTOS critical section functions have been modified as follows… - ``taskENTER_CRITICAL(mux)``, ``taskENTER_CRITICAL_ISR(mux)``, ``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)``, ``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` and :component_file:`freertos/task.c` @@ -394,23 +394,23 @@ Floating Point Aritmetic ------------------------ The ESP32 supports hardware acceleration of single precision floating point -arithmetic (`float`) via Floating Point Units (FPU, also known as coprocessors) +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 +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`. +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`. +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: @@ -420,12 +420,12 @@ 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 `vTaskDelete()`) if the task being deleted is not currently +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 -`vTaskDelete()` is called on). TLSP deletion callbacks will also run immediately +:cpp:func:`vTaskDelete` is called on). TLSP deletion callbacks will also run immediately if the same conditions are met. -However, calling `vTaskDelete()` to delete a task that is either currently +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. @@ -456,8 +456,8 @@ is the index number of the associated TLSP, and the second parameter is the TLSP itself. Deletion callbacks are set alongside TLSP by calling -``vTaskSetThreadLocalStoragePointerAndDelCallback()``. Calling the vanilla -FreeRTOS function ``vTaskSetThreadLocalStoragePointer()`` will simply set the +:cpp:func:`vTaskSetThreadLocalStoragePointerAndDelCallback`. Calling the vanilla +FreeRTOS function :cpp:func:`vTaskSetThreadLocalStoragePointer` will simply set the 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 @@ -466,7 +466,7 @@ task deletion in order to avoid memory leak. :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 :component_file:`freertos/include/freertos/task.h` +For more details see :doc:`FreeRTOS API reference<../api-reference/system/freertos>`. .. _esp-idf-freertos-configuration: @@ -491,9 +491,9 @@ number of Thread Local Storage Pointers each task will have in ESP-IDF FreeRTOS. :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 particular functions in ESP-IDF FreeRTOS which have not been fully tested in an SMP context. - + From 58accf05cfed6a5f321e5241555a8d0b6da3c5db Mon Sep 17 00:00:00 2001 From: Mahavir Jain Date: Wed, 6 Dec 2017 12:05:11 +0530 Subject: [PATCH 09/56] docs: fix i2s code snippet for interrupt flags setting Signed-off-by: Mahavir Jain --- components/esp32/include/esp_intr_alloc.h | 4 ++-- docs/api-reference/peripherals/i2s.rst | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/components/esp32/include/esp_intr_alloc.h b/components/esp32/include/esp_intr_alloc.h index ad121abb39..02ddac57ef 100644 --- a/components/esp32/include/esp_intr_alloc.h +++ b/components/esp32/include/esp_intr_alloc.h @@ -37,13 +37,13 @@ extern "C" { */ //Keep the LEVELx values as they are here; they match up with (1< Date: Thu, 7 Dec 2017 18:19:40 +0530 Subject: [PATCH 10/56] heap_trace: fix bug in realloc for copying trace record Closes https://github.com/espressif/esp-idf/issues/1354 Signed-off-by: Mahavir Jain --- components/heap/heap_trace.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/heap/heap_trace.c b/components/heap/heap_trace.c index b1da4415e0..b7ef48514e 100644 --- a/components/heap/heap_trace.c +++ b/components/heap/heap_trace.c @@ -373,11 +373,11 @@ static IRAM_ATTR __attribute__((noinline)) void *trace_realloc(void *p, size_t s record_free(p, callers); } heap_trace_record_t rec = { - .address = p, + .address = r, .ccount = ccount, .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); } return r; From 3085eb7ec6ca1ffff4cce1f16aae284cf4cd49f5 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Fri, 8 Dec 2017 13:00:11 +0800 Subject: [PATCH 11/56] openssl: add feature check for MBEDTLS_SSL_ALPN Fixes https://github.com/espressif/esp-idf/issues/1342 --- components/openssl/platform/ssl_pm.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/components/openssl/platform/ssl_pm.c b/components/openssl/platform/ssl_pm.c index 5545f958e4..cd9960da12 100755 --- a/components/openssl/platform/ssl_pm.c +++ b/components/openssl/platform/ssl_pm.c @@ -154,7 +154,11 @@ int ssl_pm_new(SSL *ssl) } if (ssl->ctx->ssl_alpn.alpn_status == ALPN_ENABLE) { - mbedtls_ssl_conf_alpn_protocols( &ssl_pm->conf, ssl->ctx->ssl_alpn.alpn_list ); +#ifdef MBEDTLS_SSL_ALPN + 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); From 21912b95f4b2bba9446349477a9f5ddd85611460 Mon Sep 17 00:00:00 2001 From: robotrovsky Date: Thu, 23 Nov 2017 20:51:17 +0100 Subject: [PATCH 12/56] Bugfix I_DELAY macro When compiling > const ulp_insn_t program[] = { > I_DELAY(1) > }; with the xtensa-esp32-elf-g++ compiler i always got the error: > sorry, unimplemented: non-trivial designated initializers not supported > > }; This was due to the different order in the macro and the struct. The struct has another order of the fields (opcode, unused, cycles) vs (cycles, unused, opcode): > struct { > uint32_t cycles : 16; /*!< Number of cycles to sleep */ > uint32_t unused : 12; /*!< Unused */ > uint32_t opcode : 4; /*!< Opcode (OPCODE_DELAY) */ > } delay; /*!< Format of DELAY instruction */ After updating the order in the macro it is possible to compile with the g++ compiler. Merges https://github.com/espressif/esp-idf/pull/1310 --- components/ulp/include/esp32/ulp.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/ulp/include/esp32/ulp.h b/components/ulp/include/esp32/ulp.h index 302a47a0ca..ae539d84da 100644 --- a/components/ulp/include/esp32/ulp.h +++ b/components/ulp/include/esp32/ulp.h @@ -266,9 +266,9 @@ _Static_assert(sizeof(ulp_insn_t) == 4, "ULP coprocessor instruction size should * Delay (nop) for a given number of cycles */ #define I_DELAY(cycles_) { .delay = {\ - .opcode = OPCODE_DELAY, \ + .cycles = cycles_, \ .unused = 0, \ - .cycles = cycles_ } } + .opcode = OPCODE_DELAY } } /** * Halt the coprocessor. From 5960e7419d7e7e4f2b7adb5125b18b4b6c037a27 Mon Sep 17 00:00:00 2001 From: Paul Reimer Date: Sat, 25 Nov 2017 16:28:34 -0800 Subject: [PATCH 13/56] build system: Add *.cc files to list of file extensions compiled by default Merges https://github.com/espressif/esp-idf/pull/1318 --- docs/api-guides/build-system.rst | 2 +- make/component_wrapper.mk | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/docs/api-guides/build-system.rst b/docs/api-guides/build-system.rst index f3d6153cab..2ef47a4689 100644 --- a/docs/api-guides/build-system.rst +++ b/docs/api-guides/build-system.rst @@ -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: -- 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. - The component library will be linked into the project app. diff --git a/make/component_wrapper.mk b/make/component_wrapper.mk index 84edc0a835..cc0afaa05d 100644 --- a/make/component_wrapper.mk +++ b/make/component_wrapper.mk @@ -85,11 +85,12 @@ include $(COMPONENT_MAKEFILE) ifndef COMPONENT_CONFIG_ONLY # Skip steps 3-5 if COMPONENT_CONFIG_ONLY is set # 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 # 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 %.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))) # Make relative by removing COMPONENT_PATH from all found object paths COMPONENT_OBJS := $(patsubst $(COMPONENT_PATH)/%,%,$(COMPONENT_OBJS)) @@ -221,6 +222,11 @@ $(1)/%.o: $$(COMPONENT_PATH)/$(1)/%.cpp $(COMMON_MAKEFILES) $(COMPONENT_MAKEFILE $$(CXX) $$(CXXFLAGS) $$(CPPFLAGS) $$(addprefix -I,$$(COMPONENT_INCLUDES)) $$(addprefix -I,$$(COMPONENT_EXTRA_INCLUDES)) -I$(1) -c $$< -o $$@ $(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) $$(summary) AS $$(patsubst $$(PWD)/%,%,$$(CURDIR))/$$@ $$(CC) $$(CPPFLAGS) $$(DEBUG_FLAGS) $$(addprefix -I ,$$(COMPONENT_INCLUDES)) $$(addprefix -I ,$$(COMPONENT_EXTRA_INCLUDES)) -I$(1) -c $$< -o $$@ From 8388e1be54eaa188552e465d29f3e183b2034380 Mon Sep 17 00:00:00 2001 From: Paul Reimer Date: Wed, 6 Dec 2017 09:09:40 -0800 Subject: [PATCH 14/56] Add #include guards and __cplusplus guards to esp_debug.h Merges https://github.com/espressif/esp-idf/pull/1358 --- components/mbedtls/port/include/mbedtls/esp_debug.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/components/mbedtls/port/include/mbedtls/esp_debug.h b/components/mbedtls/port/include/mbedtls/esp_debug.h index bf39cc73ba..8e23a5ea32 100644 --- a/components/mbedtls/port/include/mbedtls/esp_debug.h +++ b/components/mbedtls/port/include/mbedtls/esp_debug.h @@ -11,6 +11,12 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. +#ifndef _ESP_DEBUG_H_ +#define _ESP_DEBUG_H_ + +#ifdef __cplusplus +extern "C" { +#endif #include "sdkconfig.h" #ifdef CONFIG_MBEDTLS_DEBUG @@ -43,3 +49,9 @@ void mbedtls_esp_disable_debug_log(mbedtls_ssl_config *conf); #endif + +#ifdef __cplusplus +} +#endif + +#endif /* __ESP_DEBUG_H__ */ From cb9be8c0c49c44a6a85c03c56049ed64d6d494f5 Mon Sep 17 00:00:00 2001 From: panfeng Date: Fri, 1 Dec 2017 18:48:12 +0800 Subject: [PATCH 15/56] bugfix: io setting useless when io_num > 32 --- examples/peripherals/gpio/main/gpio_example_main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/peripherals/gpio/main/gpio_example_main.c b/examples/peripherals/gpio/main/gpio_example_main.c index 3b306ac14c..b6e71339ec 100644 --- a/examples/peripherals/gpio/main/gpio_example_main.c +++ b/examples/peripherals/gpio/main/gpio_example_main.c @@ -33,10 +33,10 @@ #define GPIO_OUTPUT_IO_0 18 #define GPIO_OUTPUT_IO_1 19 -#define GPIO_OUTPUT_PIN_SEL ((1< Date: Thu, 7 Dec 2017 22:45:39 +0100 Subject: [PATCH 16/56] 1. Following https://esp32.com/viewtopic.php?f=14&t=3834 and https://github.com/espressif/esp-idf/issues/1351 updated information regarding selection of the main XTAL frequency. 2. Removed obsolete note about ': not a valid identifier...', as it does not show up anymore with the latests MSYS2 installation. --- docs/get-started/index.rst | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/docs/get-started/index.rst b/docs/get-started/index.rst index de1caa84c2..68c8f2ff0a 100644 --- a/docs/get-started/index.rst +++ b/docs/get-started/index.rst @@ -127,10 +127,6 @@ ESP-IDF will be downloaded into ``~/esp/esp-idf``. cd ~/esp/esp-idf 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 `_ but the git clone still succeeds without any problems. - .. _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``. -.. 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: @@ -283,7 +276,18 @@ Several lines below, after start up and diagnostic log, you should see "Hello wo Restarting in 8 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 ` 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���)(Xn@�y.!��(�PW+)��Hn9a؅/9�!�t5��P�~�k��e�ea�5�jA + ~zY��Y(1�,1�� e���)(Xn@�y.!Dr�zY(�jpi�|�+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 `, change :ref:`CONFIG_ESP32_XTAL_FREQ_SEL` to 26MHz, then :ref:`build and flash ` the application again. + +To execute ``make flash`` and ``make monitor`` in one go, type ``make flash monitor``. Check section :doc:`IDF Monitor ` for handy shortcuts and more details on using this application. That's all what you need to get started with ESP32! From b4c1bdb11bc1088c01a8b864ef2b25c64198a219 Mon Sep 17 00:00:00 2001 From: luc lebosse Date: Sat, 14 Oct 2017 14:52:40 +0200 Subject: [PATCH 17/56] Unify the time file creation for SPIFFS and SD --- components/fatfs/src/vfs_fat.c | 5 +++++ components/spiffs/esp_spiffs.c | 22 ++++++++++++++++++++++ components/spiffs/include/spiffs_config.h | 2 +- 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/components/fatfs/src/vfs_fat.c b/components/fatfs/src/vfs_fat.c index a5a12d5b65..b1192b602c 100644 --- a/components/fatfs/src/vfs_fat.c +++ b/components/fatfs/src/vfs_fat.c @@ -389,6 +389,9 @@ static int vfs_fat_fstat(void* ctx, int fd, struct stat * st) FIL* file = &fat_ctx->files[fd]; st->st_size = f_size(file); 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; } @@ -422,6 +425,8 @@ static int vfs_fat_stat(void* ctx, const char * path, struct stat * st) ftime >>= 6; tm.tm_hour = (ftime & 0x1f); st->st_mtime = mktime(&tm); + st->st_atime = 0; + st->st_ctime = 0; return 0; } diff --git a/components/spiffs/esp_spiffs.c b/components/spiffs/esp_spiffs.c index 3f81b151d5..4520c81fa6 100644 --- a/components/spiffs/esp_spiffs.c +++ b/components/spiffs/esp_spiffs.c @@ -513,6 +513,16 @@ static int vfs_spiffs_open(void* ctx, const char * path, int flags, int mode) SPIFFS_clearerr(efs->fs); return -1; } +#if SPIFFS_OBJ_META_LEN > 0 + if (!(spiffs_mode_conv(flags) & SPIFFS_O_RDONLY)) + { + time_t t = time(NULL); + struct tm tmr; + localtime_r(&t, &tmr); + time_t meta = mktime(&tmr); + SPIFFS_fupdate_meta(efs->fs, fd, &meta); + } +#endif return fd; } @@ -578,6 +588,9 @@ static int vfs_spiffs_fstat(void* ctx, int fd, struct stat * st) } st->st_size = s.size; st->st_mode = S_IRWXU | S_IRWXG | S_IRWXO | S_IFREG; + st->st_mtime = 0; + st->st_atime = 0; + st->st_ctime = 0; return res; } @@ -597,6 +610,15 @@ static int vfs_spiffs_stat(void* ctx, const char * path, struct stat * st) st->st_size = s.size; st->st_mode = S_IRWXU | S_IRWXG | S_IRWXO; st->st_mode |= (s.type == SPIFFS_TYPE_DIR)?S_IFDIR:S_IFREG; +#if SPIFFS_OBJ_META_LEN > 0 + time_t t =0; + memcpy(&t, s.meta, sizeof(time_t)); + st->st_mtime = t; +#else + st->st_mtime = 0; +#endif + st->st_atime = 0; + st->st_ctime = 0; return res; } diff --git a/components/spiffs/include/spiffs_config.h b/components/spiffs/include/spiffs_config.h index e412bfd0cf..c96a3412d3 100755 --- a/components/spiffs/include/spiffs_config.h +++ b/components/spiffs/include/spiffs_config.h @@ -158,7 +158,7 @@ extern void spiffs_api_unlock(struct spiffs_t *fs); // This is derived from following: // logical_page_size - (SPIFFS_OBJ_NAME_LEN + sizeof(spiffs_page_header) + // spiffs_object_ix_header fields + at least some LUT entries) -#define SPIFFS_OBJ_META_LEN (0) +#define SPIFFS_OBJ_META_LEN (4) // Size of buffer allocated on stack used when copying data. // Lower value generates more read/writes. No meaning having it bigger From 033124be1406465ab0fb10c2217af8211bef6468 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Wed, 22 Nov 2017 00:17:11 +0800 Subject: [PATCH 18/56] spiffs: make OBJ_META_LEN configurable, make mtime support optional - SPIFFS_OBJ_META_LEN can be set in sdkconfig - mtime support can be enabled in sdkconfig, if META_LENGTH is sufficient - add test for mtime updates --- components/spiffs/Kconfig | 19 +++++++ components/spiffs/esp_spiffs.c | 61 +++++++++++++++-------- components/spiffs/include/spiffs_config.h | 2 +- components/spiffs/test/test_spiffs.c | 42 ++++++++++++++++ 4 files changed, 103 insertions(+), 21 deletions(-) diff --git a/components/spiffs/Kconfig b/components/spiffs/Kconfig index d82ceec73e..1f7a196d72 100644 --- a/components/spiffs/Kconfig +++ b/components/spiffs/Kconfig @@ -88,6 +88,25 @@ config SPIFFS_USE_MAGIC_LENGTH configured and formatted for 4 megabytes will not be accepted 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" config SPIFFS_DBG diff --git a/components/spiffs/esp_spiffs.c b/components/spiffs/esp_spiffs.c index 4520c81fa6..2c454e9dd3 100644 --- a/components/spiffs/esp_spiffs.c +++ b/components/spiffs/esp_spiffs.c @@ -33,6 +33,10 @@ 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 */ @@ -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 int vfs_spiffs_mkdir(void* ctx, const char* name, mode_t mode); 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]; @@ -507,22 +513,16 @@ static int vfs_spiffs_open(void* ctx, const char * path, int flags, int mode) { assert(path); 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) { errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs)); SPIFFS_clearerr(efs->fs); return -1; } -#if SPIFFS_OBJ_META_LEN > 0 - if (!(spiffs_mode_conv(flags) & SPIFFS_O_RDONLY)) - { - time_t t = time(NULL); - struct tm tmr; - localtime_r(&t, &tmr); - time_t meta = mktime(&tmr); - SPIFFS_fupdate_meta(efs->fs, fd, &meta); - } -#endif + if (!(spiffs_flags & SPIFFS_RDONLY)) { + vfs_spiffs_update_mtime(efs->fs, fd); + } return fd; } @@ -572,7 +572,6 @@ static off_t vfs_spiffs_lseek(void* ctx, int fd, off_t offset, int mode) return -1; } return res; - } static int vfs_spiffs_fstat(void* ctx, int fd, struct stat * st) @@ -588,7 +587,7 @@ static int vfs_spiffs_fstat(void* ctx, int fd, struct stat * st) } st->st_size = s.size; st->st_mode = S_IRWXU | S_IRWXG | S_IRWXO | S_IFREG; - st->st_mtime = 0; + st->st_mtime = vfs_spiffs_get_mtime(&s); st->st_atime = 0; st->st_ctime = 0; return res; @@ -610,13 +609,7 @@ static int vfs_spiffs_stat(void* ctx, const char * path, struct stat * st) st->st_size = s.size; st->st_mode = S_IRWXU | S_IRWXG | S_IRWXO; st->st_mode |= (s.type == SPIFFS_TYPE_DIR)?S_IFDIR:S_IFREG; -#if SPIFFS_OBJ_META_LEN > 0 - time_t t =0; - memcpy(&t, s.meta, sizeof(time_t)); - st->st_mtime = t; -#else - st->st_mtime = 0; -#endif + st->st_mtime = vfs_spiffs_get_mtime(&s); st->st_atime = 0; st->st_ctime = 0; return res; @@ -786,3 +779,31 @@ static int vfs_spiffs_link(void* ctx, const char* n1, const char* n2) errno = ENOTSUP; 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; +} diff --git a/components/spiffs/include/spiffs_config.h b/components/spiffs/include/spiffs_config.h index c96a3412d3..28414facf9 100755 --- a/components/spiffs/include/spiffs_config.h +++ b/components/spiffs/include/spiffs_config.h @@ -158,7 +158,7 @@ extern void spiffs_api_unlock(struct spiffs_t *fs); // This is derived from following: // logical_page_size - (SPIFFS_OBJ_NAME_LEN + sizeof(spiffs_page_header) + // spiffs_object_ix_header fields + at least some LUT entries) -#define SPIFFS_OBJ_META_LEN (4) +#define SPIFFS_OBJ_META_LEN (CONFIG_SPIFFS_META_LENGTH) // Size of buffer allocated on stack used when copying data. // Lower value generates more read/writes. No meaning having it bigger diff --git a/components/spiffs/test/test_spiffs.c b/components/spiffs/test/test_spiffs.c index daf11ddc94..b428de4ceb 100644 --- a/components/spiffs/test/test_spiffs.c +++ b/components/spiffs/test/test_spiffs.c @@ -505,3 +505,45 @@ TEST_CASE("multiple tasks can use same volume", "[spiffs]") test_spiffs_concurrent("/spiffs/f"); 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 From 96be8f2efa2d41e839265dc65806788d384575f0 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Fri, 8 Dec 2017 19:58:39 +0800 Subject: [PATCH 19/56] vfs/fatfs: fix stat call failing when called for mount point FATFS does not support f_stat call for drive root. When handling stat for drive root, don't call f_stat and just return struct st with S_IFDIR flag set. Closes #984 --- components/fatfs/src/vfs_fat.c | 20 ++++++++++++++++++-- components/fatfs/test/test_fatfs_common.c | 19 ++++++++++++------- components/fatfs/test/test_fatfs_common.h | 2 +- components/fatfs/test/test_fatfs_sdmmc.c | 2 +- components/fatfs/test/test_fatfs_spiflash.c | 2 +- 5 files changed, 33 insertions(+), 12 deletions(-) diff --git a/components/fatfs/src/vfs_fat.c b/components/fatfs/src/vfs_fat.c index b1192b602c..62a0f54343 100644 --- a/components/fatfs/src/vfs_fat.c +++ b/components/fatfs/src/vfs_fat.c @@ -395,8 +395,23 @@ static int vfs_fat_fstat(void* ctx, int fd, struct stat * st) 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) { + 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; _lock_acquire(&fat_ctx->lock); prepend_drive_to_path(fat_ctx, &path, NULL); @@ -408,9 +423,10 @@ static int vfs_fat_stat(void* ctx, const char * path, struct stat * st) errno = fresult_to_errno(res); return -1; } + + memset(st, 0, sizeof(*st)); st->st_size = info.fsize; - st->st_mode = S_IRWXU | S_IRWXG | S_IRWXO | - ((info.fattrib & AM_DIR) ? S_IFDIR : S_IFREG); + st->st_mode = get_stat_mode((info.fattrib & AM_DIR) != 0); struct tm tm; uint16_t fdate = info.fdate; tm.tm_mday = fdate & 0x1f; diff --git a/components/fatfs/test/test_fatfs_common.c b/components/fatfs/test/test_fatfs_common.c index ca27cdd4ba..799d36f1b7 100644 --- a/components/fatfs/test/test_fatfs_common.c +++ b/components/fatfs/test/test_fatfs_common.c @@ -125,15 +125,15 @@ void test_fatfs_lseek(const char* filename) 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; - tm.tm_year = 2016 - 1900; - tm.tm_mon = 0; - tm.tm_mday = 10; - tm.tm_hour = 16; - tm.tm_min = 30; - tm.tm_sec = 0; + tm.tm_year = 2017 - 1900; + tm.tm_mon = 11; + tm.tm_mday = 8; + tm.tm_hour = 19; + tm.tm_min = 51; + tm.tm_sec = 10; time_t t = mktime(&tm); printf("Setting time: %s", asctime(&tm)); 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_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) diff --git a/components/fatfs/test/test_fatfs_common.h b/components/fatfs/test/test_fatfs_common.h index dcc31e37d4..36e7ca62b6 100644 --- a/components/fatfs/test/test_fatfs_common.h +++ b/components/fatfs/test/test_fatfs_common.h @@ -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_stat(const char* filename); +void test_fatfs_stat(const char* filename, const char* root_dir); void test_fatfs_unlink(const char* filename); diff --git a/components/fatfs/test/test_fatfs_sdmmc.c b/components/fatfs/test/test_fatfs_sdmmc.c index 6935fc7083..3610511a91 100644 --- a/components/fatfs/test/test_fatfs_sdmmc.c +++ b/components/fatfs/test/test_fatfs_sdmmc.c @@ -105,7 +105,7 @@ TEST_CASE("(SD) can lseek", "[fatfs][sdcard][ignore]") TEST_CASE("(SD) stat returns correct values", "[fatfs][ignore]") { test_setup(); - test_fatfs_stat("/sdcard/stat.txt"); + test_fatfs_stat("/sdcard/stat.txt", "/sdcard"); test_teardown(); } diff --git a/components/fatfs/test/test_fatfs_spiflash.c b/components/fatfs/test/test_fatfs_spiflash.c index bcc396b93c..47116cd8d5 100644 --- a/components/fatfs/test/test_fatfs_spiflash.c +++ b/components/fatfs/test/test_fatfs_spiflash.c @@ -100,7 +100,7 @@ TEST_CASE("(WL) can lseek", "[fatfs][wear_levelling]") TEST_CASE("(WL) stat returns correct values", "[fatfs][wear_levelling]") { test_setup(); - test_fatfs_stat("/spiflash/stat.txt"); + test_fatfs_stat("/spiflash/stat.txt", "/spiflash"); test_teardown(); } From f4554c81fc741e89b92c20788c347902467389f0 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Fri, 8 Dec 2017 20:03:24 +0800 Subject: [PATCH 20/56] vfs/fatfs: use structures with bit fields for FAT date/time Replace explicit masks and shifts with bit fields when working with FATFS date and time representations. Also zero-initialize remaining members of struct tm. Fixes https://github.com/espressif/esp-idf/issues/1369. --- components/fatfs/src/vfs_fat.c | 42 +++++++++++++++++++++++----------- 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/components/fatfs/src/vfs_fat.c b/components/fatfs/src/vfs_fat.c index 62a0f54343..556492e3ed 100644 --- a/components/fatfs/src/vfs_fat.c +++ b/components/fatfs/src/vfs_fat.c @@ -43,6 +43,25 @@ typedef struct { struct dirent cur_dirent; } 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 ssize_t vfs_fat_write(void* p, int fd, const void * data, size_t size); @@ -427,19 +446,16 @@ static int vfs_fat_stat(void* ctx, const char * path, struct stat * st) memset(st, 0, sizeof(*st)); st->st_size = info.fsize; st->st_mode = get_stat_mode((info.fattrib & AM_DIR) != 0); - struct tm tm; - uint16_t fdate = info.fdate; - tm.tm_mday = fdate & 0x1f; - fdate >>= 5; - tm.tm_mon = (fdate & 0xf) - 1; - fdate >>=4; - tm.tm_year = fdate + 80; - uint16_t ftime = info.ftime; - tm.tm_sec = (ftime & 0x1f) * 2; - ftime >>= 5; - tm.tm_min = (ftime & 0x3f); - ftime >>= 6; - tm.tm_hour = (ftime & 0x1f); + fat_date_t fdate = { .as_int = info.fdate }; + fat_time_t ftime = { .as_int = info.ftime }; + struct tm tm = { + .tm_mday = fdate.mday, + .tm_mon = fdate.mon - 1, /* unlike tm_mday, tm_mon is zero-based */ + .tm_year = fdate.year + 80, + .tm_sec = ftime.sec * 2, + .tm_min = ftime.min, + .tm_hour = ftime.hour + }; st->st_mtime = mktime(&tm); st->st_atime = 0; st->st_ctime = 0; From 50b710d2675ba88355b4d0787f4e35223faad781 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Fri, 8 Dec 2017 16:06:11 +0800 Subject: [PATCH 21/56] newlib/time: fix compilation error when only RTC is used as clock source Fixes https://github.com/espressif/esp-idf/issues/1245 --- components/newlib/time.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/components/newlib/time.c b/components/newlib/time.c index edb9a59fe0..8aeb5b37ef 100644 --- a/components/newlib/time.c +++ b/components/newlib/time.c @@ -74,13 +74,15 @@ static uint64_t get_rtc_time_us() // when RTC is used to persist time, two RTC_STORE registers are used to store boot time #elif defined(WITH_FRC1) static uint64_t s_boot_time; -#endif +#endif // WITH_RTC #if defined(WITH_RTC) || defined(WITH_FRC1) static _lock_t s_boot_time_lock; #endif -#ifdef WITH_RTC +// Offset between FRC timer and the RTC. +// Initialized after reset or light sleep. +#if defined(WITH_RTC) && defined(WITH_FRC1) uint64_t s_microseconds_offset; #endif @@ -171,10 +173,14 @@ static uint64_t get_time_since_boot() { uint64_t microseconds = 0; #ifdef WITH_FRC1 +#ifdef WITH_RTC microseconds = s_microseconds_offset + esp_timer_get_time(); +#else + microseconds = esp_timer_get_time(); +#endif // WITH_RTC #elif defined(WITH_RTC) microseconds = get_rtc_time_us(); -#endif +#endif // WITH_FRC1 return microseconds; } #endif // defined( WITH_FRC1 ) || defined( WITH_RTC ) From e8fbd6e288519821c53187b2f4a7eb8430574ca9 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Fri, 8 Dec 2017 16:14:20 +0800 Subject: [PATCH 22/56] time: rename time source option from FRC to "high-resolution timer" libc time function now rely on esp_timer_get_time as the source of high-resolution time, rather than FRC1 timer. Internally, on the ESP32 esp_timer implementation uses FRC2 timer. - Change help text and labels in Kconfig to use "high-resolution timer" instead of FRC1. Keep existing Kconfig option name to be backwards compatible. - Change references to "FRC1" in the source code to "FRC". --- components/esp32/Kconfig | 20 ++++++++++++-------- components/newlib/time.c | 34 +++++++++++++++++----------------- 2 files changed, 29 insertions(+), 25 deletions(-) diff --git a/components/esp32/Kconfig b/components/esp32/Kconfig index f1d31fd199..426045ad2f 100644 --- a/components/esp32/Kconfig +++ b/components/esp32/Kconfig @@ -631,6 +631,10 @@ config BROWNOUT_DET_LVL 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 prompt "Timers used for gettimeofday function" default ESP32_TIME_SYSCALL_USE_RTC_FRC1 @@ -638,12 +642,12 @@ choice ESP32_TIME_SYSCALL This setting defines which hardware timers are used to implement 'gettimeofday' and 'time' functions in C library. - - If only FRC1 timer is used, gettimeofday will provide time at - microsecond resolution. Time will not be preserved when going - into deep sleep mode. - - If both FRC1 and RTC timers are used, timekeeping will + - If both high-resolution and RTC timers are used, timekeeping will 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 deep sleep, but time will be measured at 6.(6) microsecond 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 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 bool "RTC" -config ESP32_TIME_SYSCALL_USE_RTC_FRC1 - bool "RTC and FRC1" config ESP32_TIME_SYSCALL_USE_FRC1 - bool "FRC1" + bool "High-resolution timer" config ESP32_TIME_SYSCALL_USE_NONE bool "None" endchoice diff --git a/components/newlib/time.c b/components/newlib/time.c index 8aeb5b37ef..8443d2b54b 100644 --- a/components/newlib/time.c +++ b/components/newlib/time.c @@ -42,7 +42,7 @@ #endif #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 #ifdef WITH_RTC @@ -72,21 +72,21 @@ static uint64_t get_rtc_time_us() // s_boot_time: time from Epoch to the first boot time #ifdef WITH_RTC // 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; #endif // WITH_RTC -#if defined(WITH_RTC) || defined(WITH_FRC1) +#if defined(WITH_RTC) || defined(WITH_FRC) static _lock_t s_boot_time_lock; #endif // Offset between FRC timer and the RTC. // Initialized after reset or light sleep. -#if defined(WITH_RTC) && defined(WITH_FRC1) +#if defined(WITH_RTC) && defined(WITH_FRC) uint64_t s_microseconds_offset; #endif -#if defined(WITH_RTC) || defined(WITH_FRC1) +#if defined(WITH_RTC) || defined(WITH_FRC) static void set_boot_time(uint64_t time_us) { _lock_acquire(&s_boot_time_lock); @@ -111,7 +111,7 @@ static uint64_t get_boot_time() _lock_release(&s_boot_time_lock); 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) @@ -141,10 +141,10 @@ uint32_t esp_clk_slowclk_cal_get() 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 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) @@ -168,11 +168,11 @@ clock_t IRAM_ATTR _times_r(struct _reent *r, struct tms *ptms) 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() { uint64_t microseconds = 0; -#ifdef WITH_FRC1 +#ifdef WITH_FRC #ifdef WITH_RTC microseconds = s_microseconds_offset + esp_timer_get_time(); #else @@ -180,15 +180,15 @@ static uint64_t get_time_since_boot() #endif // WITH_RTC #elif defined(WITH_RTC) microseconds = get_rtc_time_us(); -#endif // WITH_FRC1 +#endif // WITH_FRC 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) { (void) tz; -#if defined( WITH_FRC1 ) || defined( WITH_RTC ) +#if defined( WITH_FRC ) || defined( WITH_RTC ) if (tv) { uint64_t microseconds = get_boot_time() + get_time_since_boot(); tv->tv_sec = microseconds / 1000000; @@ -198,13 +198,13 @@ int IRAM_ATTR _gettimeofday_r(struct _reent *r, struct timeval *tv, void *tz) #else __errno_r(r) = ENOSYS; 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) { (void) tz; -#if defined( WITH_FRC1 ) || defined( WITH_RTC ) +#if defined( WITH_FRC ) || defined( WITH_RTC ) if (tv) { uint64_t now = ((uint64_t) tv->tv_sec) * 1000000LL + tv->tv_usec; uint64_t since_boot = get_time_since_boot(); @@ -239,7 +239,7 @@ unsigned int sleep(unsigned int seconds) 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(); #else return 0; @@ -250,7 +250,7 @@ uint32_t system_get_current_time(void) __attribute__((alias("system_get_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; #else return 0; From d3400889930b6805d5c22786b50180d2dca79274 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Thu, 7 Dec 2017 11:50:39 +0800 Subject: [PATCH 23/56] docs: add description of ULP I2C instructions --- docs/api-guides/ulp_instruction_set.rst | 39 +++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/docs/api-guides/ulp_instruction_set.rst b/docs/api-guides/ulp_instruction_set.rst index fa280b8b81..c4d21947cc 100755 --- a/docs/api-guides/ulp_instruction_set.rst +++ b/docs/api-guides/ulp_instruction_set.rst @@ -667,6 +667,45 @@ 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 +**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. + +**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. + +**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 ------------------------------------------ From 103559153f914a8167b699b6ee377b94a7be1e61 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Thu, 7 Dec 2017 14:41:17 +0800 Subject: [PATCH 24/56] ulp: mention that instructions array must be declared in local scope Closes https://github.com/espressif/esp-idf/issues/1327 --- components/ulp/README.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/components/ulp/README.rst b/components/ulp/README.rst index af1a748f89..f4c027cd70 100644 --- a/components/ulp/README.rst +++ b/components/ulp/README.rst @@ -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. +.. 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). 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. From 5c1506f796edd0c8760afc55e7780b8965a327e0 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Thu, 7 Dec 2017 17:11:24 +0800 Subject: [PATCH 25/56] ulp: document the need to wait for RTC to be ready for wakeup --- components/soc/esp32/include/soc/rtc_cntl_reg.h | 11 ++++++++++- docs/api-guides/ulp_instruction_set.rst | 12 +++++++++--- examples/system/ulp/main/ulp/pulse_cnt.S | 5 +++++ examples/system/ulp_adc/main/ulp/adc.S | 2 +- 4 files changed, 25 insertions(+), 5 deletions(-) diff --git a/components/soc/esp32/include/soc/rtc_cntl_reg.h b/components/soc/esp32/include/soc/rtc_cntl_reg.h index ffcbb3c033..d54a7dde7c 100644 --- a/components/soc/esp32/include/soc/rtc_cntl_reg.h +++ b/components/soc/esp32/include/soc/rtc_cntl_reg.h @@ -1830,7 +1830,16 @@ #define RTC_CNTL_SCRATCH7_V 0xFFFFFFFF #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 ; */ /*description: */ #define RTC_CNTL_LOW_POWER_DIAG0 0xFFFFFFFF diff --git a/docs/api-guides/ulp_instruction_set.rst b/docs/api-guides/ulp_instruction_set.rst index c4d21947cc..62cbe3bd5a 100755 --- a/docs/api-guides/ulp_instruction_set.rst +++ b/docs/api-guides/ulp_instruction_set.rst @@ -573,11 +573,17 @@ 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. + 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**:: - 1: WAKE // Trigger wake up - REG_WR 0x006, 24, 24, 0 // Stop ULP timer (clear RTC_CNTL_ULP_CP_SLP_TIMER_EN) - HALT // Stop the ULP program + 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) + HALT // Stop the ULP program // After these instructions, SoC will wake up, // and ULP will not run again until started by the main program. diff --git a/examples/system/ulp/main/ulp/pulse_cnt.S b/examples/system/ulp/main/ulp/pulse_cnt.S index ba7b453442..e573e3244f 100644 --- a/examples/system/ulp/main/ulp/pulse_cnt.S +++ b/examples/system/ulp/main/ulp/pulse_cnt.S @@ -130,6 +130,11 @@ edge_detected: .global 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 halt diff --git a/examples/system/ulp_adc/main/ulp/adc.S b/examples/system/ulp_adc/main/ulp/adc.S index 1f0f6d5ac9..70d0439c81 100644 --- a/examples/system/ulp_adc/main/ulp/adc.S +++ b/examples/system/ulp_adc/main/ulp/adc.S @@ -105,7 +105,7 @@ exit: .global wake_up wake_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 jump exit, eq From 242f8ea7432493bb60369de5c8da708e0aafd330 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Mon, 11 Dec 2017 12:11:24 +0800 Subject: [PATCH 26/56] docs: add information about execution time of ULP instructions --- .gitlab-ci.yml | 7 ++ components/soc/esp32/test/test_rtc_clk.c | 9 ++ docs/api-guides/ulp_instruction_set.rst | 128 +++++++++++++++++++---- 3 files changed, 125 insertions(+), 19 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index b327068c2a..6e4c2eccaf 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -751,6 +751,13 @@ UT_003_08: - UT_T1_1 - UT_single_core +UT_003_09: + <<: *unit_test_template + tags: + - ESP32_IDF + - UT_T1_1 + - UT_single_core + UT_004_01: <<: *unit_test_template tags: diff --git a/components/soc/esp32/test/test_rtc_clk.c b/components/soc/esp32/test/test_rtc_clk.c index 00fb0040ff..3773f9401e 100644 --- a/components/soc/esp32/test/test_rtc_clk.c +++ b/components/soc/esp32/test/test_rtc_clk.c @@ -112,6 +112,15 @@ static void test_clock_switching(void (*switch_func)(rtc_cpu_freq_t)) 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_clock_switching(rtc_clk_cpu_freq_set); diff --git a/docs/api-guides/ulp_instruction_set.rst b/docs/api-guides/ulp_instruction_set.rst index 62cbe3bd5a..893fcc56e1 100755 --- a/docs/api-guides/ulp_instruction_set.rst +++ b/docs/api-guides/ulp_instruction_set.rst @@ -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, // 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 ---------------------- -**Syntax:** +**Syntax** **NOP** -**Operands:** +**Operands** None -**Description:** +**Cycles** + 2 (fetch) + 1 (execute) +**Description** No operation is performed. Only the PC is incremented. **Example**:: @@ -80,20 +98,22 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol **ADD** - Add to register ------------------------- -**Syntax:** +**Syntax** **ADD** *Rdst, Rsrc1, Rsrc2* **ADD** *Rdst, Rsrc1, imm* -**Operands:** +**Operands** - *Rdst* - Register R[0..3] - *Rsrc1* - Register R[0..3] - *Rsrc2* - Register R[0..3] - *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. **Examples**:: @@ -115,21 +135,24 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol **SUB** - Subtract from register -------------------------------- -**Syntax:** +**Syntax** **SUB** *Rdst, Rsrc1, Rsrc2* **SUB** *Rdst, Rsrc1, imm* -**Operands:** +**Operands** - *Rdst* - Register R[0..3] - *Rsrc1* - Register R[0..3] - *Rsrc2* - Register R[0..3] - *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. -**Examples:**:: +**Examples**:: 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 ------------------------------------- -**Syntax:** +**Syntax** **AND** *Rdst, Rsrc1, Rsrc2* **AND** *Rdst, Rsrc1, imm* -**Operands:** +**Operands** - *Rdst* - Register R[0..3] - *Rsrc1* - Register R[0..3] - *Rsrc2* - Register R[0..3] - *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. -**Example**:: +**Examples**:: 1: AND R1, R2, R3 //R1 = R2 & R3 @@ -183,12 +209,14 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol **OR** *Rdst, Rsrc1, imm* - **Operands** - *Rdst* - Register R[0..3] - *Rsrc1* - Register R[0..3] - *Rsrc2* - Register R[0..3] - *Imm* - 16-bit signed value + +**Cycles** + 2 (fetch) + 2 (execute) **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. @@ -223,6 +251,9 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol - *Rsrc2* - Register R[0..3] - *Imm* - 16-bit signed value +**Cycles** + 2 (fetch) + 2 (execute) + **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. @@ -255,6 +286,9 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol *Rsrc2* - Register R[0..3] *Imm* - 16-bit signed value +**Cycles** + 2 (fetch) + 2 (execute) + **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. @@ -286,6 +320,9 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol - *Rsrc* – Register R[0..3] - *Imm* – 16-bit signed value +**Cycles** + 2 (fetch) + 2 (execute) + **Description** 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 - *Offset* – 10-bit signed value, offset in bytes +**Cycles** + 2 (fetch) + 4 (execute) + **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:: @@ -352,6 +392,9 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol *Offset* – 10-bit signed value, offset in bytes +**Cycles** + 2 (fetch) + 4 (execute) + **Description** 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 - OV – jump if last ALU has set overflow flag +**Cycles** + 2 (fetch) + 2 (execute) **Description** 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 +**Cycles** + 2 (fetch) + 2 (execute) + **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. @@ -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 - *GT* (greater than) – jump if value in stage_cnt > threshold +**Cycles** + 2 (fetch) + 2 (execute) + **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. @@ -487,6 +538,9 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol **Description** The instruction sets the stage count register to 0 +**Cycles** + 2 (fetch) + 2 (execute) + **Examples**:: 1: STAGE_RST // Reset stage count register @@ -502,6 +556,9 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol **Operands** - *Value* – 8 bits value +**Cycles** + 2 (fetch) + 2 (execute) + **Description** 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** - *Value* – 8 bits value +**Cycles** + 2 (fetch) + 2 (execute) + **Description** 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** No operands + +**Cycles** + 2 (fetch) + 2 (execute) + **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**:: - 1: HALT // Move chip to powerdown + 1: HALT // Halt the coprocessor -**WAKE** – wakeup the chip --------------------------- +**WAKE** – Wake up the chip +--------------------------- **Syntax** **WAKE** @@ -566,6 +630,9 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol **Operands** No operands +**Cycles** + 2 (fetch) + 2 (execute) + **Description** The instruction sends an interrupt from ULP to RTC controller. @@ -598,6 +665,9 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol **Operands** - *sleep_reg* – 0..4, selects one of ``SENS_ULP_CP_SLEEP_CYCx_REG`` registers. +**Cycles** + 2 (fetch) + 2 (execute) + **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. @@ -618,6 +688,9 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol **Operands** - *Cycles* – number of cycles for wait +**Cycles** + 2 (fetch) + *Cycles* (execute) + **Description** The instruction delays for given number of cycles. @@ -641,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 - *Wait_Delay* – number of cycles used to perform the measurement +**Cycles** + 2 (fetch) + *Wait_Delay* + 3 * TSENS_CLK **Description** The instruction performs measurement using TSENS and stores the result into a general purpose register. @@ -666,6 +741,9 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol - *Sar_sel* – Select ADC: 0 = SARADC1, 1 = SARADC2 - *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** The instruction makes measurements from ADC. @@ -684,6 +762,9 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol - *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. @@ -705,6 +786,9 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol - *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``. @@ -724,6 +808,9 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol - *High* – High part of R0 - *Low* – Low part of R0 +**Cycles** + 2 (fetch) + 6 (execute) + **Description** The instruction reads up to 16 bits from a peripheral register into a general purpose register: ``R0 = REG[Addr][High:Low]``. @@ -749,6 +836,9 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol - *Low* – Low part of R0 - *Data* – value to write, 8 bits +**Cycles** + 2 (fetch) + 10 (execute) + **Description** The instruction writes up to 8 bits from a general purpose register into a peripheral register. ``REG[Addr][High:Low] = data`` From 28e4162dd28848f5bc7696a97d58d434a3b428a7 Mon Sep 17 00:00:00 2001 From: krzychb Date: Mon, 11 Dec 2017 22:53:39 +0100 Subject: [PATCH 27/56] Resolves: Warning '-s option given but default rule can be matched'. Closes https://github.com/espressif/esp-idf/issues/1338 --- tools/kconfig/zconf.l | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/kconfig/zconf.l b/tools/kconfig/zconf.l index f0b65608f0..8fd75491c3 100644 --- a/tools/kconfig/zconf.l +++ b/tools/kconfig/zconf.l @@ -1,5 +1,5 @@ %option nostdinit noyywrap never-interactive full ecs -%option 8bit nodefault perf-report perf-report +%option 8bit perf-report perf-report %option noinput %x COMMAND HELP STRING PARAM %{ From ae30d1bc7b2c69cc75b76d1798de0ea195fc3e31 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Fri, 8 Dec 2017 12:17:58 +0800 Subject: [PATCH 28/56] fatfs: fix deinit not called for SDSPI host Closes https://github.com/espressif/esp-idf/issues/1362 --- components/fatfs/src/vfs_fat_sdmmc.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/components/fatfs/src/vfs_fat_sdmmc.c b/components/fatfs/src/vfs_fat_sdmmc.c index a712fa9733..68751cb50f 100644 --- a/components/fatfs/src/vfs_fat_sdmmc.c +++ b/components/fatfs/src/vfs_fat_sdmmc.c @@ -139,7 +139,7 @@ esp_err_t esp_vfs_fat_sdmmc_mount(const char* base_path, return ESP_OK; fail: - sdmmc_host_deinit(); + host_config->deinit(); free(workbuf); if (fs) { f_mount(NULL, drv, 0); @@ -160,10 +160,11 @@ esp_err_t esp_vfs_fat_sdmmc_unmount() char drv[3] = {(char)('0' + s_pdrv), ':', 0}; f_mount(0, drv, 0); // release SD driver + esp_err_t (*host_deinit)() = s_card->host.deinit; ff_diskio_unregister(s_pdrv); free(s_card); s_card = NULL; - sdmmc_host_deinit(); + (*host_deinit)(); esp_err_t err = esp_vfs_fat_unregister_path(s_base_path); free(s_base_path); s_base_path = NULL; From a0776b2f21b02ecfd111c2775cb537ccaed11ae7 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Fri, 8 Dec 2017 12:23:54 +0800 Subject: [PATCH 29/56] sdspi: use response timeout passed from upper layer Previously SDSPI host driver would rely on retry count when waiting for the card to read or write data. This caused different timeout times depending on CPU frequency and card clock frequency. In practice, card performance does not depend on these two factors. This change uses timeout_ms field of sdmmc_command_t introduced previously for SDMMC host. Fixes https://esp32.com/viewtopic.php?f=2&t=3440&p=16037 and similar issues related to SDSPI timeouts. --- components/driver/sdspi_host.c | 70 +++++++++++++++------------ components/driver/sdspi_private.h | 4 +- components/driver/sdspi_transaction.c | 5 +- 3 files changed, 44 insertions(+), 35 deletions(-) diff --git a/components/driver/sdspi_host.c b/components/driver/sdspi_host.c index 57cf3f9935..74cc24c244 100644 --- a/components/driver/sdspi_host.c +++ b/components/driver/sdspi_host.c @@ -25,12 +25,12 @@ #include "driver/sdspi_host.h" #include "sdspi_private.h" #include "sdspi_crc.h" +#include "esp_timer.h" + /// Max number of transactions in flight (used in start_command_write_blocks) #define SDSPI_TRANSACTION_COUNT 4 #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 /// Size of the buffer returned by get_block_buf #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 -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; *t = (spi_transaction_t) { @@ -436,7 +436,9 @@ static esp_err_t poll_busy(int slot, spi_transaction_t* t) }; 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_data[0] = 0; 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; } if (t->rx_data[0] != 0) { - if (i < SDSPI_RETRY_COUNT - 2) { - i = SDSPI_RETRY_COUNT - 2; + if (++nonzero_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 -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; *t = (spi_transaction_t) { @@ -462,8 +465,8 @@ static esp_err_t poll_response_token(int slot, spi_transaction_t* t) .length = 8, }; 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 { t_rx = SDSPI_MOSI_IDLE_VAL; t->rx_data[0] = 0; 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; } 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) { 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) { return ESP_ERR_INVALID_RESPONSE; } - if (retry == SDSPI_RETRY_COUNT - 1) { - return ESP_ERR_TIMEOUT; - } - } + } while (esp_timer_get_time() < t_end); - return ESP_OK; + ESP_LOGD(TAG, "%s: timeout", __func__); + return ESP_ERR_TIMEOUT; } // Wait for data token, reading 8 bytes at a time. // If the token is found, write all subsequent bytes to extra_ptr, // and store the number of bytes written to extra_size. 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]; *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, }; 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)); ret = spi_device_transmit(spi_handle(slot), t); if (ret != ESP_OK) { @@ -522,13 +524,11 @@ static esp_err_t poll_data_token(int slot, spi_transaction_t* t, } } if (found) { - break; + return ESP_OK; } - if (retry == SDSPI_RETRY_COUNT - 1) { - return ESP_ERR_TIMEOUT; - } - } - 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) { // Wait for data to be ready 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) { extra_data_ptr = cmd_u8 + SDSPI_CMD_R1_SIZE; } - release_transaction(slot); } // 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 // card to process it 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); if (ret != ESP_OK) { return ret; } 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); + if (ret != ESP_OK) { + return ret; + } } 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 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); if (ret != ESP_OK) { 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 t_poll = get_transaction(slot); - ret = poll_busy(slot, t_poll); + ret = poll_busy(slot, t_poll, cmd->timeout_ms); release_transaction(slot); if (ret != ESP_OK) { 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); 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); if (ret != ESP_OK) { return ret; diff --git a/components/driver/sdspi_private.h b/components/driver/sdspi_private.h index d28cfcf88c..e14dd19bb8 100644 --- a/components/driver/sdspi_private.h +++ b/components/driver/sdspi_private.h @@ -73,6 +73,8 @@ typedef struct { uint8_t r1; /// Up to 16 bytes of response. Luckily, this is aligned on 4 byte boundary. uint32_t response[4]; + /// response timeout, in milliseconds + int timeout_ms; } sdspi_hw_cmd_t; #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 -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, void *data, uint32_t data_size, int flags); diff --git a/components/driver/sdspi_transaction.c b/components/driver/sdspi_transaction.c index 95f98240f9..64271749ad 100644 --- a/components/driver/sdspi_transaction.c +++ b/components/driver/sdspi_transaction.c @@ -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); } -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->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); memcpy(hw_cmd->arguments, &arg_s, sizeof(arg_s)); 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) @@ -55,7 +56,7 @@ esp_err_t sdspi_host_do_transaction(int slot, sdmmc_command_t *cmdinfo) _lock_acquire(&s_lock); // Convert the command to wire format 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 int flags = 0; From c778951547e70141bea814b1e2aa78986a9de21c Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Mon, 11 Dec 2017 11:06:29 +0800 Subject: [PATCH 30/56] fatfs: fix double free in bailout path of esp_vfs_fat_sdmmc_mount Fixes https://github.com/espressif/esp-idf/issues/1370 --- components/fatfs/src/vfs_fat_sdmmc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/components/fatfs/src/vfs_fat_sdmmc.c b/components/fatfs/src/vfs_fat_sdmmc.c index 68751cb50f..5e79d4a9f8 100644 --- a/components/fatfs/src/vfs_fat_sdmmc.c +++ b/components/fatfs/src/vfs_fat_sdmmc.c @@ -128,6 +128,7 @@ esp_err_t esp_vfs_fat_sdmmc_mount(const char* base_path, goto fail; } free(workbuf); + workbuf = NULL; ESP_LOGW(TAG, "mounting again"); res = f_mount(fs, drv, 0); if (res != FR_OK) { From fbff8eb95b244124e09b9b24526132d0cbb8e4ae Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Fri, 8 Dec 2017 13:14:36 +0800 Subject: [PATCH 31/56] esp32: fix incorrect clock enable bit name for UART0 Closes https://github.com/espressif/esp-idf/issues/1301 --- components/esp32/clk.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/esp32/clk.c b/components/esp32/clk.c index 15b8af36c6..4c6209762f 100644 --- a/components/esp32/clk.c +++ b/components/esp32/clk.c @@ -187,7 +187,7 @@ void esp_perip_clk_init(void) common_perip_clk = DPORT_WDG_CLK_EN | DPORT_I2S0_CLK_EN | #if CONFIG_CONSOLE_UART_NUM != 0 - DPORT_UART0_CLK_EN | + DPORT_UART_CLK_EN | #endif #if CONFIG_CONSOLE_UART_NUM != 1 DPORT_UART1_CLK_EN | From 42e411dafc14ea046765680104e1ff7c14755bcb Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Fri, 8 Dec 2017 13:15:40 +0800 Subject: [PATCH 32/56] bootloader: don't log anything before uart_console_configure is called --- .../bootloader/subproject/main/bootloader_start.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/components/bootloader/subproject/main/bootloader_start.c b/components/bootloader/subproject/main/bootloader_start.c index 99fb398fbe..83e6811895 100644 --- a/components/bootloader/subproject/main/bootloader_start.c +++ b/components/bootloader/subproject/main/bootloader_start.c @@ -796,24 +796,20 @@ static void IRAM_ATTR flash_gpio_configure() if (pkg_ver == EFUSE_RD_CHIP_VER_PKG_ESP32D2WDQ5) { // 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); 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) { // 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); 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) { // 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); SET_PERI_REG_BITS(PERIPHS_IO_MUX_SD_CLK_U, FUN_DRV, drv, FUN_DRV_S); } else { - ESP_LOGI(TAG, "Detected ESP32"); const uint32_t spiconfig = ets_efuse_get_spiconfig(); if (spiconfig == EFUSE_SPICONFIG_SPI_DEFAULTS) { gpio_matrix_out(FLASH_CS_IO, SPICS0_OUT_IDX, 0, 0); From 0448ee968533f68166f7893c437032a1e93b6830 Mon Sep 17 00:00:00 2001 From: krzychb Date: Fri, 8 Dec 2017 20:46:44 +0100 Subject: [PATCH 33/56] The WROOM and WROVER family of modules is now extended with ESP-WROOM-32D and ESP32-WROOM-32U --- docs/hw-reference/modules-and-boards.rst | 85 +++++++++++++++++++----- 1 file changed, 69 insertions(+), 16 deletions(-) diff --git a/docs/hw-reference/modules-and-boards.rst b/docs/hw-reference/modules-and-boards.rst index 2bd85e89a7..afbbe705e0 100644 --- a/docs/hw-reference/modules-and-boards.rst +++ b/docs/hw-reference/modules-and-boards.rst @@ -1,7 +1,8 @@ .. _esp-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. @@ -12,12 +13,33 @@ 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`. +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, boot strapping resistors and break out headers. The key characteristics of modules are summarized in the following table. Some additinal 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 + + .. _esp-modules-and-boards-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 :align: center @@ -26,17 +48,48 @@ The smallest module intended for installation in final products. Can be also use ESP-WROOM-32 module (front and back) +Documentation +^^^^^^^^^^^^^ + * `ESP-WROOM-32 Schematic `__ (PDF) * `ESP-WROOM-32 Datasheet `__ (PDF) * `ESP32 Module Reference Design `_ (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 `. 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 `__ (PDF) + + .. _esp-modules-and-boards-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 :align: center @@ -46,7 +99,7 @@ A step upgrade of ESP-WROOM-32 described above with an additional 4 MB SPI PSRAM ESP32-WROVER module (front and back) Documentation -""""""""""""" +^^^^^^^^^^^^^ * `ESP32-WROVER Datasheet `__ (PDF) * `ESP-PSRAM32 Datasheet `__ (PDF) @@ -55,7 +108,7 @@ Documentation .. _esp-modules-and-boards-esp32-pico-pit-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. @@ -70,14 +123,14 @@ Comparing to ESP32-PICO-KIT V3, this version has revised printout and reduced nu Documentation -""""""""""""" +------------- * :doc:`../get-started/get-started-pico-kit` * `ESP32-PICO-KIT V4 Schematic `_ (PDF) * `ESP32-PICO-D4 Datasheet `_ (PDF) Previous Versions -""""""""""""""""" +----------------- * :ref:`esp-modules-and-boards-esp32-pico-pit-v3` @@ -85,9 +138,9 @@ Previous Versions .. _esp-modules-and-boards-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 press buttons to reset the board and put it in upload mode. .. figure:: https://dl.espressif.com/dl/schematics/pictures/esp32-core-board-v2.png :align: center @@ -97,7 +150,7 @@ Small and convenient development board with ESP-WROOM-32 module installed, break ESP32 Core Board V2 / ESP32 DevKitC board Documentation -""""""""""""" +------------- * :doc:`../get-started/get-started-devkitc` * `ESP32 DevKitC Schematic `__ (PDF) @@ -108,13 +161,13 @@ Documentation .. _esp-modules-and-boards-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. 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 :align: center @@ -123,10 +176,10 @@ This is the first release of ESP-WROVER-KIT shipped with ESP32-WROVER module ins 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 -""""""""""""" +------------- * :doc:`../get-started/get-started-wrover-kit` * `ESP-WROVER-KIT V3 Schematic `__ (PDF) @@ -134,14 +187,14 @@ Documentation * `FTDI Virtual COM Port Drivers`_ Previous Versions -""""""""""""""""" +----------------- * :ref:`esp-modules-and-boards-esp-wrover-kit-v1` * :ref:`esp-modules-and-boards-esp-wrover-kit-v2` Related Documents ------------------ +================= * :doc:`modules-and-boards-previous` From 84bfc96f086105744a1c26e0f2b6dbc6a877e78a Mon Sep 17 00:00:00 2001 From: krzychb Date: Mon, 11 Dec 2017 08:01:37 +0100 Subject: [PATCH 34/56] Fixed issues identified during review --- docs/hw-reference/modules-and-boards.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/hw-reference/modules-and-boards.rst b/docs/hw-reference/modules-and-boards.rst index afbbe705e0..50992ba19d 100644 --- a/docs/hw-reference/modules-and-boards.rst +++ b/docs/hw-reference/modules-and-boards.rst @@ -16,7 +16,7 @@ For details please refer to documentation below, provided together with descript 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, boot strapping resistors and break out headers. The key characteristics of modules are summarized in the following table. Some additinal details are covered in the following chapters. +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] @@ -140,7 +140,7 @@ Previous Versions ESP32 Core Board V2 / ESP32 DevKitC =================================== -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 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 :align: center From ff008d2be3c82d2c0f4015ff0a5f0dec945c5cd7 Mon Sep 17 00:00:00 2001 From: krzychb Date: Tue, 12 Dec 2017 08:42:22 +0100 Subject: [PATCH 35/56] Linked ESP32 datasheet and regulatory certificates --- docs/hw-reference/index.rst | 15 ++++++++------- docs/hw-reference/modules-and-boards.rst | 1 + 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/docs/hw-reference/index.rst b/docs/hw-reference/index.rst index 4fcb91e24a..b6f9912031 100644 --- a/docs/hw-reference/index.rst +++ b/docs/hw-reference/index.rst @@ -5,10 +5,11 @@ ESP32 Hardware Reference .. toctree:: :maxdepth: 2 - Technical Reference Manual (PDF) - Datasheet (PDF) - Hardware Design Guidelines (PDF) - Silicon Errata (PDF) - Modules and Boards - Previous Versions of Modules and Boards - Espressif Products Ordering Information (PDF) + Technical Reference Manual (PDF) + Datasheet (PDF) + Hardware Design Guidelines (PDF) + Silicon Errata (PDF) + Modules and Boards + Previous Versions of Modules and Boards + Espressif Products Ordering Information (PDF) + Regulatory Certificates diff --git a/docs/hw-reference/modules-and-boards.rst b/docs/hw-reference/modules-and-boards.rst index 50992ba19d..b300a56688 100644 --- a/docs/hw-reference/modules-and-boards.rst +++ b/docs/hw-reference/modules-and-boards.rst @@ -32,6 +32,7 @@ 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 `__ (PDF) .. _esp-modules-and-boards-esp-wroom-32: From 5afafb00506803a25135102aeca31080c068b42d Mon Sep 17 00:00:00 2001 From: Kewal Date: Wed, 13 Dec 2017 22:52:48 +0800 Subject: [PATCH 36/56] fix typo for heap cap free size --- components/heap/include/esp_heap_alloc_caps.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/components/heap/include/esp_heap_alloc_caps.h b/components/heap/include/esp_heap_alloc_caps.h index f92e92e352..7e6e25d6a8 100644 --- a/components/heap/include/esp_heap_alloc_caps.h +++ b/components/heap/include/esp_heap_alloc_caps.h @@ -24,11 +24,11 @@ extern "C" { /* Please use heap_caps_malloc() instead of this function */ 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 */ -size_t xPortGetMinimumEverFreeHeapSizeCaps( uint32_t caps ) asm("heap_caps_get_minimum_free_heap_size") __attribute__((deprecated)); +/* Please use heap_caps_get_minimum_free_size() instead of this function */ +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 */ -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 } From c015dd6c41897690774e82acd86173b9b8d78b1c Mon Sep 17 00:00:00 2001 From: michael Date: Thu, 16 Nov 2017 14:39:57 +0800 Subject: [PATCH 37/56] feat(monitor): add new feature allowing disabling log display. --- docs/get-started/idf-monitor.rst | 8 ++++++++ tools/idf_monitor.py | 20 ++++++++++++++++++-- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/docs/get-started/idf-monitor.rst b/docs/get-started/idf-monitor.rst index 4dac96c59e..488b96abb1 100644 --- a/docs/get-started/idf-monitor.rst +++ b/docs/get-started/idf-monitor.rst @@ -89,6 +89,14 @@ Quick Reset The keyboard shortcut ``Ctrl-T Ctrl-R`` will reset the target board via the RTS line (if it is connected.) +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 ============== diff --git a/tools/idf_monitor.py b/tools/idf_monitor.py index 6bc63f7e3e..b74eceeeb8 100755 --- a/tools/idf_monitor.py +++ b/tools/idf_monitor.py @@ -55,6 +55,7 @@ CTRL_F = '\x06' CTRL_H = '\x08' CTRL_R = '\x12' CTRL_T = '\x14' +CTRL_Y = '\x19' CTRL_RBRACKET = '\x1d' # Ctrl+] # ANSI terminal codes @@ -256,6 +257,7 @@ class Monitor(object): self._pressed_menu_key = False self._read_line = b"" self._gdb_buffer = b"" + self._output_enabled = True def main_loop(self): self.console_reader.start() @@ -299,7 +301,8 @@ class Monitor(object): # this may need to be made more efficient, as it pushes out a byte # at a time to the console for b in data: - self.console.write_bytes(b) + if self._output_enabled: + self.console.write_bytes(b) if b == b'\n': # end of line self.handle_serial_input_line(self._read_line.strip()) self._read_line = b"" @@ -320,10 +323,13 @@ class Monitor(object): self.serial.setRTS(True) time.sleep(0.2) self.serial.setRTS(False) + self.output_enable(True) elif c == CTRL_F: # Recompile & upload self.run_make("flash") elif c == CTRL_A: # Recompile & upload app only self.run_make("app-flash") + elif c == CTRL_Y: # Toggle output display + self.output_toggle() else: red_print('--- unknown menu character {} --'.format(key_description(c))) @@ -340,13 +346,14 @@ class Monitor(object): --- {reset:7} Reset target board via RTS line --- {make:7} Run 'make flash' to build & flash --- {appmake:7} Run 'make app-flash to build & flash app +--- {output:7} Toggle output display """.format(version=__version__, exit=key_description(self.exit_key), menu=key_description(self.menu_key), reset=key_description(CTRL_R), make=key_description(CTRL_F), appmake=key_description(CTRL_A), - + output=key_description(CTRL_Y), ) def __enter__(self): @@ -393,6 +400,8 @@ class Monitor(object): p.wait() if p.returncode != 0: self.prompt_next_action("Build failed") + else: + self.output_enable(True) def lookup_pc_address(self, pc_addr): translation = subprocess.check_output( @@ -430,6 +439,13 @@ class Monitor(object): pass # happens on Windows, maybe other OSes 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(): parser = argparse.ArgumentParser("idf_monitor - a serial output monitor for esp-idf") From a0bdee0c9ce3101fd2b56f9259daab6155cfdb58 Mon Sep 17 00:00:00 2001 From: michael Date: Thu, 30 Nov 2017 12:46:08 +0800 Subject: [PATCH 38/56] feat(monitor): add pause feature. --- docs/get-started/idf-monitor.rst | 8 ++++++++ tools/idf_monitor.py | 13 +++++++++++++ 2 files changed, 21 insertions(+) diff --git a/docs/get-started/idf-monitor.rst b/docs/get-started/idf-monitor.rst index 488b96abb1..e1ec23cb3d 100644 --- a/docs/get-started/idf-monitor.rst +++ b/docs/get-started/idf-monitor.rst @@ -89,6 +89,14 @@ Quick Reset 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 ===================== diff --git a/tools/idf_monitor.py b/tools/idf_monitor.py index b74eceeeb8..ccaddffa13 100755 --- a/tools/idf_monitor.py +++ b/tools/idf_monitor.py @@ -56,6 +56,7 @@ CTRL_H = '\x08' CTRL_R = '\x12' CTRL_T = '\x14' CTRL_Y = '\x19' +CTRL_P = '\x10' CTRL_RBRACKET = '\x1d' # Ctrl+] # ANSI terminal codes @@ -330,6 +331,16 @@ class Monitor(object): 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: red_print('--- unknown menu character {} --'.format(key_description(c))) @@ -347,6 +358,7 @@ class Monitor(object): --- {make:7} Run 'make flash' to build & flash --- {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__, exit=key_description(self.exit_key), menu=key_description(self.menu_key), @@ -354,6 +366,7 @@ class Monitor(object): make=key_description(CTRL_F), appmake=key_description(CTRL_A), output=key_description(CTRL_Y), + pause=key_description(CTRL_P), ) def __enter__(self): From c4bb528c61a4e0c6ab8769831a98d33ad0c82539 Mon Sep 17 00:00:00 2001 From: Wangjialin Date: Tue, 28 Nov 2017 15:05:36 +0800 Subject: [PATCH 39/56] bugfix(i2c): use queue instead of event group for internal commands Reported from github: https://github.com/espressif/esp-idf/issues/1312 https://github.com/espressif/esp-idf/issues/1193 Issues: 1. We used to use event group in the driver, which would cause: a. longer operation time since the event group are based on FreeRTOS timer. b. Operation fails if the timer queue is not long enough. 2. There might be some issue with event group, we will still try to provide a small test code in other branch. modification: 1. use queue instead of event-bit for internal commands 2. use queue overwrite for cmd_done event --- components/driver/i2c.c | 54 +++++++++++++++++++++++------------------ 1 file changed, 30 insertions(+), 24 deletions(-) diff --git a/components/driver/i2c.c b/components/driver/i2c.c index c83570496e..6c9d4fed1d 100644 --- a/components/driver/i2c.c +++ b/components/driver/i2c.c @@ -23,7 +23,6 @@ #include "freertos/xtensa_api.h" #include "freertos/task.h" #include "freertos/ringbuf.h" -#include "freertos/event_groups.h" #include "soc/dport_reg.h" #include "soc/i2c_struct.h" #include "soc/i2c_reg.h" @@ -70,8 +69,9 @@ static DRAM_ATTR i2c_dev_t* const I2C[I2C_NUM_MAX] = { &I2C0, &I2C1 }; #define I2C_FIFO_EMPTY_THRESH_VAL (5) #define I2C_IO_INIT_LEVEL (1) #define I2C_CMD_ALIVE_INTERVAL_TICK (1000 / portTICK_PERIOD_MS) -#define I2C_CMD_EVT_ALIVE (BIT0) -#define I2C_CMD_EVT_DONE (BIT1) +#define I2C_CMD_EVT_ALIVE (0) +#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_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 */ @@ -107,6 +107,10 @@ typedef enum { I2C_STATUS_TIMEOUT, /*!< I2C bus status error, and operation timeout */ } i2c_status_t; +typedef struct { + int type; +} i2c_cmd_evt_t; + typedef struct { int i2c_num; /*!< I2C port number */ int mode; /*!< I2C mode, master or slave */ @@ -117,7 +121,7 @@ typedef struct { uint8_t data_buf[I2C_FIFO_LEN]; /*!< a buffer to store i2c fifo data */ 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 */ size_t tx_fifo_remain; /*!< tx fifo remain length, for master mode */ size_t rx_fifo_remain; /*!< rx fifo remain length, for master mode */ @@ -199,8 +203,8 @@ esp_err_t i2c_driver_install(i2c_port_t i2c_num, i2c_mode_t mode, size_t slv_rx_ } else { //semaphore to sync sending process, because we only have 32 bytes for hardware fifo. p_i2c->cmd_mux = xSemaphoreCreateMutex(); - p_i2c->cmd_evt = xEventGroupCreate(); - if (p_i2c->cmd_mux == NULL || p_i2c->cmd_evt == NULL) { + 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_queue == NULL) { ESP_LOGE(I2C_TAG, I2C_SEM_ERR_STR); goto err; } @@ -243,9 +247,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_buf_length = 0; } - if (p_i2c_obj[i2c_num]->cmd_evt) { - vEventGroupDelete(p_i2c_obj[i2c_num]->cmd_evt); - p_i2c_obj[i2c_num]->cmd_evt = NULL; + if (p_i2c_obj[i2c_num]->cmd_evt_queue) { + vQueueDelete(p_i2c_obj[i2c_num]->cmd_evt_queue); + p_i2c_obj[i2c_num]->cmd_evt_queue = NULL; } if (p_i2c_obj[i2c_num]->cmd_mux) { vSemaphoreDelete(p_i2c_obj[i2c_num]->cmd_mux); @@ -296,9 +300,9 @@ esp_err_t i2c_driver_delete(i2c_port_t i2c_num) xSemaphoreTake(p_i2c->cmd_mux, portMAX_DELAY); vSemaphoreDelete(p_i2c->cmd_mux); } - if (p_i2c_obj[i2c_num]->cmd_evt) { - vEventGroupDelete(p_i2c_obj[i2c_num]->cmd_evt); - p_i2c_obj[i2c_num]->cmd_evt = NULL; + if (p_i2c_obj[i2c_num]->cmd_evt_queue) { + vQueueDelete(p_i2c_obj[i2c_num]->cmd_evt_queue); + p_i2c_obj[i2c_num]->cmd_evt_queue = NULL; } if (p_i2c->slv_rx_mux) { vSemaphoreDelete(p_i2c->slv_rx_mux); @@ -437,7 +441,9 @@ static void IRAM_ATTR i2c_isr_handler_default(void* arg) } } 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) { portYIELD_FROM_ISR(); } @@ -990,7 +996,7 @@ static void IRAM_ATTR i2c_master_cmd_begin_static(i2c_port_t i2c_num) { i2c_obj_t* p_i2c = p_i2c_obj[i2c_num]; portBASE_TYPE HPTaskAwoken = pdFALSE; - + i2c_cmd_evt_t evt; //This should never happen if (p_i2c->mode == I2C_MODE_SLAVE) { return; @@ -1005,7 +1011,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_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) { portYIELD_FROM_ISR(); } @@ -1024,7 +1031,8 @@ static void IRAM_ATTR i2c_master_cmd_begin_static(i2c_port_t i2c_num) } if (p_i2c->cmd_link.head == 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) { portYIELD_FROM_ISR(); } @@ -1108,7 +1116,7 @@ esp_err_t i2c_master_cmd_begin(i2c_port_t i2c_num, i2c_cmd_handle_t cmd_handle, if (res == pdFALSE) { 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 || I2C[i2c_num]->status_reg.bus_busy == 1) { i2c_hw_fsm_reset(i2c_num); @@ -1137,16 +1145,15 @@ esp_err_t i2c_master_cmd_begin(i2c_port_t i2c_num, i2c_cmd_handle_t cmd_handle, ticks_to_wait = ticks_end - xTaskGetTickCount(); } // Wait event bits - EventBits_t uxBits; + i2c_cmd_evt_t evt; while (1) { TickType_t wait_time = (ticks_to_wait < (I2C_CMD_ALIVE_INTERVAL_TICK) ? ticks_to_wait : (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 // 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. - uxBits = xEventGroupWaitBits(p_i2c->cmd_evt, I2C_CMD_EVT_ALIVE | I2C_CMD_EVT_DONE, false, false, wait_time); - if (uxBits) { - if (uxBits & I2C_CMD_EVT_DONE) { - xEventGroupClearBits(p_i2c->cmd_evt, I2C_CMD_EVT_DONE); + portBASE_TYPE evt_res = xQueueReceive(p_i2c->cmd_evt_queue, &evt, wait_time); + if (evt_res == pdTRUE) { + if (evt.type == I2C_CMD_EVT_DONE) { if (p_i2c->status == I2C_STATUS_TIMEOUT) { // 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. @@ -1159,8 +1166,7 @@ esp_err_t i2c_master_cmd_begin(i2c_port_t i2c_num, i2c_cmd_handle_t cmd_handle, } break; } - if (uxBits & I2C_CMD_EVT_ALIVE) { - xEventGroupClearBits(p_i2c->cmd_evt, I2C_CMD_EVT_ALIVE); + if (evt.type == I2C_CMD_EVT_ALIVE) { } } else { ret = ESP_ERR_TIMEOUT; From b5f8cf0f036f2ece9b567c5406afb8f3da6ab7df Mon Sep 17 00:00:00 2001 From: Fabiano Kovalski Date: Wed, 6 Dec 2017 00:54:59 -0500 Subject: [PATCH 40/56] driver(i2c): corrected timeout range for i2c_set_timeout. Merges https://github.com/espressif/esp-idf/pull/1353 --- components/driver/i2c.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/driver/i2c.c b/components/driver/i2c.c index 6c9d4fed1d..2028b8b357 100644 --- a/components/driver/i2c.c +++ b/components/driver/i2c.c @@ -729,7 +729,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) { 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[i2c_num]->timeout.tout = timeout; From 492b926d501a811e2d20d4f3d04e4cf5f7de8606 Mon Sep 17 00:00:00 2001 From: Chuck Todd Date: Fri, 27 Oct 2017 16:59:10 -0600 Subject: [PATCH 41/56] i2c: rx <-> tx typo's, NULLing free'd variable, consistent CRITICAL sects A couple of typos referencing tx_ring_buf when rx_ring_buf, slv_tx_mux instead of slv_rx_mux. Also, I2C_ENTER_CRITICAL()/I2C_EXIT_CRITICAL() usage was not consistent. Only some of the _set_ functions had them. Most of the _get_ function had them? It is my understanding that they should be wrapped around writes, not reads? (I think we still need the lock for reading pairs of consistent values) Also, the ticks_to_wait timeout handling in i2c_master_cmd_begin() would not handle integer rollover correctly. Merges https://github.com/espressif/esp-idf/pull/1180 --- components/driver/i2c.c | 44 ++++++++++++++++++++--------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/components/driver/i2c.c b/components/driver/i2c.c index 2028b8b357..57ae7547d2 100644 --- a/components/driver/i2c.c +++ b/components/driver/i2c.c @@ -52,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_ADDR_ERROR_STR "i2c null address error" #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_SEM_ERR_STR "i2c semaphore error" #define I2C_BUF_ERR_STR "i2c ringbuffer error" @@ -64,7 +64,7 @@ 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_SCL_IO_ERR_STR "scl gpio number 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_FIFO_FULL_THRESH_VAL (28) #define I2C_FIFO_EMPTY_THRESH_VAL (5) #define I2C_IO_INIT_LEVEL (1) @@ -179,7 +179,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; } else { - p_i2c->tx_ring_buf = NULL; + p_i2c->rx_ring_buf = NULL; p_i2c->rx_buf_length = 0; } if (slv_tx_buf_len > 0) { @@ -195,7 +195,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_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); goto err; } @@ -262,6 +262,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]); + p_i2c_obj[i2c_num] = NULL; return ESP_FAIL; } @@ -657,8 +658,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((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_rstart_setup.time = setup_time; + I2C_EXIT_CRITICAL(&i2c_spinlock[i2c_num]); return ESP_OK; } @@ -682,8 +685,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((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_setup.time = setup_time; + I2C_EXIT_CRITICAL(&i2c_spinlock[i2c_num]); return ESP_OK; } @@ -707,8 +712,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((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_sample.time = sample_time; + I2C_EXIT_CRITICAL(&i2c_spinlock[i2c_num]); return ESP_OK; } @@ -729,7 +736,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) { I2C_CHECK(i2c_num < I2C_NUM_MAX, I2C_NUM_ERROR_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_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[i2c_num]->timeout.tout = timeout; @@ -1111,7 +1118,7 @@ esp_err_t i2c_master_cmd_begin(i2c_port_t i2c_num, i2c_cmd_handle_t cmd_handle, esp_err_t ret = ESP_FAIL; 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); if (res == pdFALSE) { return ESP_ERR_TIMEOUT; @@ -1137,17 +1144,19 @@ 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. 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 i2c_cmd_evt_t evt; 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 // 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. @@ -1175,12 +1184,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); 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; xSemaphoreGive(p_i2c->cmd_mux); @@ -1258,6 +1261,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); return cnt; } - - - From c4b861ad652975b9d00a3de8d4dadc5b47b0eb91 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Fri, 15 Dec 2017 10:39:07 +1100 Subject: [PATCH 42/56] log: Remove non-static TAG variables --- components/bootloader_support/src/bootloader_random.c | 2 -- components/esp32/event_default_handlers.c | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/components/bootloader_support/src/bootloader_random.c b/components/bootloader_support/src/bootloader_random.c index 9f3d602e02..36628e6c4f 100644 --- a/components/bootloader_support/src/bootloader_random.c +++ b/components/bootloader_support/src/bootloader_random.c @@ -25,8 +25,6 @@ #include "esp_system.h" #endif -const char *TAG = "boot_rng"; - void bootloader_fill_random(void *buffer, size_t length) { uint8_t *buffer_bytes = (uint8_t *)buffer; diff --git a/components/esp32/event_default_handlers.c b/components/esp32/event_default_handlers.c index 76d49f83eb..6b127e6091 100644 --- a/components/esp32/event_default_handlers.c +++ b/components/esp32/event_default_handlers.c @@ -35,7 +35,7 @@ #include "tcpip_adapter.h" #include "esp_log.h" -const char* TAG = "event"; +static const char* TAG = "event"; #define WIFI_API_CALL_CHECK(info, api_call, ret) \ do{\ From 50637f638fc6d705084e3b2f39c1646b3b16df34 Mon Sep 17 00:00:00 2001 From: Piyush Shah Date: Mon, 20 Nov 2017 19:23:25 +0530 Subject: [PATCH 43/56] freertos/ringbuf: Add an API xRingbufferGetCurFreeSize() to fetch current free size available The earlier available API (xRingbufferGetMaxItemSize())just gives a static max entry value possible for given ring buffer. There was a feature request for an API which could provide a real time available buffer size. See below: https://github.com/espressif/esp-idf/issues/806 Signed-off-by: Piyush Shah --- .../freertos/include/freertos/ringbuf.h | 15 +++++ components/freertos/ringbuf.c | 66 ++++++++++++++++++- 2 files changed, 80 insertions(+), 1 deletion(-) diff --git a/components/freertos/include/freertos/ringbuf.h b/components/freertos/include/freertos/ringbuf.h index 88e570aa59..f0cf82fca5 100644 --- a/components/freertos/include/freertos/ringbuf.h +++ b/components/freertos/include/freertos/ringbuf.h @@ -94,6 +94,21 @@ void vRingbufferDelete(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 Insert an item into the ring buffer diff --git a/components/freertos/ringbuf.c b/components/freertos/ringbuf.c index d7ee790a5e..f79bf3ed96 100644 --- a/components/freertos/ringbuf.c +++ b/components/freertos/ringbuf.c @@ -53,6 +53,7 @@ struct ringbuf_t { 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); void (*returnItemToRingbufImpl)(ringbuf_t *rb, void *item); + size_t (*getFreeSizeImpl)(ringbuf_t *rb); }; @@ -82,7 +83,6 @@ static int ringbufferFreeMem(ringbuf_t *rb) return free_size-1; } - //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 //success, pdFALSE if it can't make the item fit and the calling routine needs to retry @@ -415,6 +415,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) { @@ -437,6 +498,7 @@ RingbufHandle_t xRingbufferCreate(size_t buf_length, ringbuf_type_t type) rb->returnItemToRingbufImpl=returnItemToRingbufDefault; //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->getFreeSizeImpl=getCurFreeSizeAllowSplit; } else if (type==RINGBUF_TYPE_BYTEBUF) { rb->flags|=flag_bytebuf; rb->copyItemToRingbufImpl=copyItemToRingbufByteBuf; @@ -444,6 +506,7 @@ RingbufHandle_t xRingbufferCreate(size_t buf_length, ringbuf_type_t type) rb->returnItemToRingbufImpl=returnItemToRingbufBytebuf; //Calculate max item size. We have no headers and can split anywhere -> size is total size minus one. rb->maxItemSize=rb->size-1; + rb->getFreeSizeImpl=getCurFreeSizeByteBuf; } else if (type==RINGBUF_TYPE_NOSPLIT) { rb->copyItemToRingbufImpl=copyItemToRingbufNoSplit; rb->getItemFromRingbufImpl=getItemFromRingbufDefault; @@ -453,6 +516,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 //with the real item. (item size being header+data) rb->maxItemSize=(rb->size/2)-sizeof(buf_entry_hdr_t)-4; + rb->getFreeSizeImpl=getCurFreeSizeNoSplit; } else { configASSERT(0); } From ef6fe211b86a4ebd8c56483a3d1456277e5053bf Mon Sep 17 00:00:00 2001 From: Piyush Shah Date: Mon, 20 Nov 2017 20:22:20 +0530 Subject: [PATCH 44/56] freertos/ringbuf: Add an API xRingbufferIsNextItemWrapped() Useful to check if the next item to receive is wrapped or not. This is valid only if the ring buffer is initialised with type RINGBUF_TYPE_ALLOWSPLIT. This is as per the feature request here: https://github.com/espressif/esp-idf/issues/806 Signed-off-by: Piyush Shah --- components/freertos/include/freertos/ringbuf.h | 17 +++++++++++++++++ components/freertos/ringbuf.c | 14 ++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/components/freertos/include/freertos/ringbuf.h b/components/freertos/include/freertos/ringbuf.h index f0cf82fca5..259ce0f16b 100644 --- a/components/freertos/include/freertos/ringbuf.h +++ b/components/freertos/include/freertos/ringbuf.h @@ -110,6 +110,23 @@ size_t xRingbufferGetMaxItemSize(RingbufHandle_t ringbuf); */ 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 * diff --git a/components/freertos/ringbuf.c b/components/freertos/ringbuf.c index f79bf3ed96..7ae1723fb9 100644 --- a/components/freertos/ringbuf.c +++ b/components/freertos/ringbuf.c @@ -32,6 +32,7 @@ typedef enum { typedef enum { 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_wrap = 4, //Valid for RINGBUF_TYPE_ALLOWSPLIT, indicating that rest of the data is wrapped around } itemflag_t; @@ -203,6 +204,9 @@ static BaseType_t copyItemToRingbufAllowSplit(ringbuf_t *rb, uint8_t *buffer, si if (buffer_size == 0) { rb->write_ptr=rb->data; return pdTRUE; + } else { + /* Indicate the wrapping */ + hdr->flags|=iflag_wrap; } } else { //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_free)==0); //Mark the buffer as free. + hdr->flags&=~iflag_wrap; hdr->flags|=iflag_free; //Do a cleanup pass. @@ -553,6 +558,15 @@ size_t xRingbufferGetMaxItemSize(RingbufHandle_t ringbuf) 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) { ringbuf_t *rb=(ringbuf_t *)ringbuf; From 545c7e5cdde99a2b244d73e025142d393e7f0d0c Mon Sep 17 00:00:00 2001 From: Piyush Shah Date: Thu, 23 Nov 2017 15:39:17 +0530 Subject: [PATCH 45/56] freertos/ringbuf: Added an API xRingbufferCreateNoSplit() This is a wrapper API for creating a Ring Buffer, which ensures that the ringbuffer can hold the given number of items, each item being of the same given length. Signed-off-by: Piyush Shah --- components/freertos/include/freertos/ringbuf.h | 13 +++++++++++++ components/freertos/ringbuf.c | 6 ++++++ 2 files changed, 19 insertions(+) diff --git a/components/freertos/include/freertos/ringbuf.h b/components/freertos/include/freertos/ringbuf.h index 259ce0f16b..0f23a44e6b 100644 --- a/components/freertos/include/freertos/ringbuf.h +++ b/components/freertos/include/freertos/ringbuf.h @@ -77,6 +77,19 @@ typedef enum { */ 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 * diff --git a/components/freertos/ringbuf.c b/components/freertos/ringbuf.c index 7ae1723fb9..57ca1c6cda 100644 --- a/components/freertos/ringbuf.c +++ b/components/freertos/ringbuf.c @@ -541,6 +541,12 @@ err: 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) { ringbuf_t *rb=(ringbuf_t *)ringbuf; if (rb) { From 87f7d1875dff3470989e0423bd168f6d6cbd8e82 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Fri, 15 Dec 2017 10:32:53 +1100 Subject: [PATCH 46/56] esp32: Fix backwards compatibility for pre-v2.1 bootloaders Older bootloaders don't set RTC_XTAL_FREQ_REG or call rtc_clk_init(), app needs to pick this up. Reported at https://esp32.com/viewtopic.php?f=2&t=3939&p=17836 --- .../subproject/main/bootloader_start.c | 44 +------------ .../include/bootloader_clock.h | 21 +++++++ .../bootloader_support/src/bootloader_clock.c | 61 +++++++++++++++++++ components/esp32/Kconfig | 16 +++++ components/esp32/clk.c | 18 +++++- components/soc/esp32/include/soc/rtc.h | 15 ++++- 6 files changed, 130 insertions(+), 45 deletions(-) create mode 100644 components/bootloader_support/include/bootloader_clock.h create mode 100644 components/bootloader_support/src/bootloader_clock.c diff --git a/components/bootloader/subproject/main/bootloader_start.c b/components/bootloader/subproject/main/bootloader_start.c index 83e6811895..56839e2f86 100644 --- a/components/bootloader/subproject/main/bootloader_start.c +++ b/components/bootloader/subproject/main/bootloader_start.c @@ -48,6 +48,7 @@ #include "bootloader_flash.h" #include "bootloader_random.h" #include "bootloader_config.h" +#include "bootloader_clock.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 vddsdio_configure(); static void flash_gpio_configure(); -static void clock_configure(void); static void uart_console_configure(void); static void wdt_reset_check(void); @@ -447,7 +447,7 @@ void bootloader_main() { vddsdio_configure(); flash_gpio_configure(); - clock_configure(); + bootloader_clock_configure(); uart_console_configure(); wdt_reset_check(); ESP_LOGI(TAG, "ESP-IDF %s 2nd stage bootloader", IDF_VER); @@ -835,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) { diff --git a/components/bootloader_support/include/bootloader_clock.h b/components/bootloader_support/include/bootloader_clock.h new file mode 100644 index 0000000000..9eaba46a8c --- /dev/null +++ b/components/bootloader_support/include/bootloader_clock.h @@ -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); diff --git a/components/bootloader_support/src/bootloader_clock.c b/components/bootloader_support/src/bootloader_clock.c new file mode 100644 index 0000000000..2937210da9 --- /dev/null +++ b/components/bootloader_support/src/bootloader_clock.c @@ -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 +} diff --git a/components/esp32/Kconfig b/components/esp32/Kconfig index 426045ad2f..888bba17eb 100644 --- a/components/esp32/Kconfig +++ b/components/esp32/Kconfig @@ -776,6 +776,22 @@ config ESP_TIMER_PROFILING used for timer storage, and should only be used for debugging/testing 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 menu Wi-Fi diff --git a/components/esp32/clk.c b/components/esp32/clk.c index 4c6209762f..369542eeed 100644 --- a/components/esp32/clk.c +++ b/components/esp32/clk.c @@ -27,10 +27,10 @@ #include "soc/soc.h" #include "soc/rtc.h" #include "soc/rtc_cntl_reg.h" -#include "soc/dport_reg.h" #include "soc/i2s_reg.h" #include "driver/periph_ctrl.h" #include "xtensa/core-macros.h" +#include "bootloader_clock.h" /* 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 @@ -54,6 +54,22 @@ void esp_clk_init(void) { rtc_config_t cfg = RTC_CONFIG_DEFAULT(); 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); #ifdef CONFIG_ESP32_RTC_CLOCK_SOURCE_EXTERNAL_CRYSTAL diff --git a/components/soc/esp32/include/soc/rtc.h b/components/soc/esp32/include/soc/rtc.h index 1b64d0073d..fa34caaa8f 100644 --- a/components/soc/esp32/include/soc/rtc.h +++ b/components/soc/esp32/include/soc/rtc.h @@ -149,13 +149,24 @@ void rtc_clk_init(rtc_clk_config_t cfg); /** * @brief Get main XTAL frequency * - * This is the value passed to rtc_clk_init function, or if the value was - * RTC_XTAL_FREQ_AUTO, the detected XTAL frequency. + * This is the value stored in RTC register RTC_XTAL_FREQ_REG by the bootloader. As passed to + * 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 */ 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 * @param en true to enable, false to disable From 41baf592875ee9790733587b288dcfaf3f377b28 Mon Sep 17 00:00:00 2001 From: Jeroen Domburg Date: Tue, 19 Dec 2017 19:12:58 +0800 Subject: [PATCH 47/56] Fix the clock subsystem so it doesn't undo the weird condition spiram_psram.c jams the enable/reset bits of SPI3 in when 80MHz mode is selected anymore --- components/esp32/clk.c | 12 +++++++++--- components/esp32/spiram_psram.c | 13 ++++++------- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/components/esp32/clk.c b/components/esp32/clk.c index 369542eeed..f9621a2049 100644 --- a/components/esp32/clk.c +++ b/components/esp32/clk.c @@ -219,10 +219,7 @@ void esp_perip_clk_init(void) DPORT_LEDC_CLK_EN | DPORT_UHCI1_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 | -#endif DPORT_PWM0_CLK_EN | DPORT_I2C_EXT1_CLK_EN | DPORT_CAN_CLK_EN | @@ -244,6 +241,15 @@ void esp_perip_clk_init(void) 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, * the current is not reduced when disable I2S clock. */ diff --git a/components/esp32/spiram_psram.c b/components/esp32/spiram_psram.c index 5d87a0a2b6..39bb0a3881 100644 --- a/components/esp32/spiram_psram.c +++ b/components/esp32/spiram_psram.c @@ -34,6 +34,7 @@ #include "soc/efuse_reg.h" #include "driver/gpio.h" #include "driver/spi_common.h" +#include "driver/periph_ctrl.h" #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 from doing this using the drivers by claiming the port for ourselves*/ if (mode == PSRAM_CACHE_F80M_S80M) { + periph_module_enable(PERIPH_VSPI_MODULE); bool r=spicommon_periph_claim(VSPI_HOST); if (!r) { 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."); s_psram_mode = mode; - DPORT_SET_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_SPI_CLK_EN); - 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); + periph_module_enable(PERIPH_SPI_MODULE); WRITE_PERI_REG(SPI_EXT3_REG(0), 0x1); 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_CLK_IO, VSPICLK_OUT_IDX, 0, 0); //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_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); @@ -526,7 +525,7 @@ esp_err_t IRAM_ATTR psram_enable(psram_cache_mode_t mode, psram_vaddr_mode_t vad while (1) { spi_status = READ_PERI_REG(SPI_EXT2_REG(PSRAM_SPI_3)); 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; } } From c884931b0b59b6f0765075a08f560cc85d73a990 Mon Sep 17 00:00:00 2001 From: Tuan PM Date: Fri, 15 Dec 2017 23:37:10 +0700 Subject: [PATCH 48/56] Add .editorconfig file remove newlinet in binary file editorconfig style guide correct format for rst hyperlinks --- .editorconfig | 34 +++++++++++++++++++++++++++++++++ docs/contribute/style-guide.rst | 9 +++++++++ 2 files changed, 43 insertions(+) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000000..15977c64e9 --- /dev/null +++ b/.editorconfig @@ -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 diff --git a/docs/contribute/style-guide.rst b/docs/contribute/style-guide.rst index 9bf00f1f7d..bf0a2a0189 100644 --- a/docs/contribute/style-guide.rst +++ b/docs/contribute/style-guide.rst @@ -169,6 +169,15 @@ To re-format a file, run:: 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 `_ Website. + + Documenting code ---------------- From 33ca8874d4b401bd167e86d8abb1e6667496f8cf Mon Sep 17 00:00:00 2001 From: Jeroen Domburg Date: Wed, 13 Dec 2017 15:48:49 +0800 Subject: [PATCH 49/56] Add support for Makefile.componentbuild files --- docs/api-guides/build-system.rst | 10 ++++++++++ make/component_wrapper.mk | 27 ++++++++++++++++++++++++++- make/project.mk | 1 + 3 files changed, 37 insertions(+), 1 deletion(-) diff --git a/docs/api-guides/build-system.rst b/docs/api-guides/build-system.rst index 2ef47a4689..31debb431c 100644 --- a/docs/api-guides/build-system.rst +++ b/docs/api-guides/build-system.rst @@ -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. +- 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``. - ``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. @@ -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`. + +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 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/make/component_wrapper.mk b/make/component_wrapper.mk index cc0afaa05d..e43e82a515 100644 --- a/make/component_wrapper.mk +++ b/make/component_wrapper.mk @@ -73,11 +73,36 @@ COMPONENT_SUBMODULES ?= ################################################################################ # 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) + ################################################################################ # 3) Set variables that depend on values that may changed by component.mk ################################################################################ diff --git a/make/project.mk b/make/project.mk index a15b7d301e..d2eefad7bd 100644 --- a/make/project.mk +++ b/make/project.mk @@ -154,6 +154,7 @@ export COMPONENTS # NOTE: These paths must be generated WITHOUT a trailing / so we # 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))))) +export COMPONENT_PATHS TEST_COMPONENTS ?= TESTS_ALL ?= From 48fda0f27bc3649eef4b258ba7210707aed6df08 Mon Sep 17 00:00:00 2001 From: kooho <2229179028@qq.com> Date: Tue, 21 Nov 2017 21:10:17 +0800 Subject: [PATCH 50/56] add NACK for the last byte to read --- components/driver/i2c.c | 35 +++++++++++++++++++++----- components/driver/include/driver/i2c.h | 11 ++++++-- 2 files changed, 38 insertions(+), 8 deletions(-) diff --git a/components/driver/i2c.c b/components/driver/i2c.c index 57ae7547d2..e0778be701 100644 --- a/components/driver/i2c.c +++ b/components/driver/i2c.c @@ -65,6 +65,8 @@ static DRAM_ATTR i2c_dev_t* const I2C[I2C_NUM_MAX] = { &I2C0, &I2C1 }; #define I2C_SCL_IO_ERR_STR "scl gpio number error" #define I2C_CMD_LINK_INIT_ERR_STR "i2c command link error" #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_EMPTY_THRESH_VAL (5) #define I2C_IO_INIT_LEVEL (1) @@ -958,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); } -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 data_offset = 0; esp_err_t ret; @@ -985,20 +984,44 @@ esp_err_t i2c_master_read(i2c_cmd_handle_t cmd_handle, uint8_t* data, size_t dat 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(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; cmd.ack_en = 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.op_code = I2C_CMD_READ; cmd.data = data; 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) { i2c_obj_t* p_i2c = p_i2c_obj[i2c_num]; diff --git a/components/driver/include/driver/i2c.h b/components/driver/include/driver/i2c.h index 765eab01cf..b7aceb7b13 100644 --- a/components/driver/include/driver/i2c.h +++ b/components/driver/include/driver/i2c.h @@ -69,6 +69,13 @@ typedef enum { I2C_ADDR_BIT_MAX, } 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 */ @@ -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_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 @@ -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_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 From 60a642b31cf870bd4655a912f5f011b4186cce67 Mon Sep 17 00:00:00 2001 From: krzychb Date: Mon, 18 Dec 2017 05:31:39 +0100 Subject: [PATCH 51/56] A workaround for MSYS2 / Windows system to be able to install 'blockdiag' --- docs/contribute/documenting-code.rst | 32 +++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/docs/contribute/documenting-code.rst b/docs/contribute/documenting-code.rst index 458efb4953..228ff74b58 100644 --- a/docs/contribute/documenting-code.rst +++ b/docs/contribute/documenting-code.rst @@ -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``. +.. _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 graphviz’s 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 `_ 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: @@ -185,7 +186,7 @@ Try them out by modifying the source code and see the diagram instantly renderin .. 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 @@ -207,7 +208,7 @@ OK, but I am new to Sphinx! * Set up an account on `Read the Docs `_ 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 `_ editor together with `OmniMarkupPreviewer `_ plugin. +4. To preview documentation before building, use `Sublime Text `_ editor together with `OmniMarkupPreviewer `_ plugin. Setup for building documentation locally @@ -245,6 +246,28 @@ Installation of Doxygen is OS dependent: 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 ` 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 ` 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 `_ 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/ + +.. _interactive shell: http://interactive.blockdiag.com/?compression=deflate&src=eJxlUMFOwzAMvecrrO3aITYQQirlAIIzEseJQ5q4TUSIq8TVGIh_J2m7jbKc7Ge_5_dSO1Lv2soWvoVYgieNoMh7VGzJR9FJtugZ7lYQ0UcKEbYNOY36rRQHZHUPT68vV5tceGLbWCUzPfeaFFMoBZzecVc56vWwJFnWMmJ59CCZg617xpOFbTSyw0pmvT_HJ7hxtFNGBr6wvuu5SCkchcrZ1vAeXZomznh5YgTqfcpR02cBO6vZVDeXBRjMjKEcFRbLh8f18-Z2UUBDnqP9wmp9ncRmSSfND2ldGo2h_zse407g0Mxc1q7HzJ3-4jzYYTJjtQH3iSV-fgFzx50J + From 9b57d4aa0de0a981591bce9a145a160ede4c0a88 Mon Sep 17 00:00:00 2001 From: krzychb Date: Mon, 20 Nov 2017 04:51:30 +0100 Subject: [PATCH 52/56] RMT API docs update, new example --- components/driver/include/driver/rmt.h | 187 ++++++------------ docs/api-reference/peripherals/rmt.rst | 159 ++++++++++++++- examples/peripherals/rmt_tx/Makefile | 9 + examples/peripherals/rmt_tx/README.md | 25 +++ examples/peripherals/rmt_tx/main/component.mk | 4 + .../peripherals/rmt_tx/main/rmt_tx_main.c | 119 +++++++++++ 6 files changed, 374 insertions(+), 129 deletions(-) create mode 100644 examples/peripherals/rmt_tx/Makefile create mode 100644 examples/peripherals/rmt_tx/README.md create mode 100644 examples/peripherals/rmt_tx/main/component.mk create mode 100644 examples/peripherals/rmt_tx/main/rmt_tx_main.c diff --git a/components/driver/include/driver/rmt.h b/components/driver/include/driver/rmt.h index 56c5ed4952..79ac0e5d67 100644 --- a/components/driver/include/driver/rmt.h +++ b/components/driver/include/driver/rmt.h @@ -33,14 +33,14 @@ extern "C" { #define RMT_MEM_ITEM_NUM (RMT_MEM_BLOCK_BYTE_NUM/4) typedef enum { - RMT_CHANNEL_0=0, /*!< RMT Channel0 */ - RMT_CHANNEL_1, /*!< RMT Channel1 */ - RMT_CHANNEL_2, /*!< RMT Channel2 */ - RMT_CHANNEL_3, /*!< RMT Channel3 */ - RMT_CHANNEL_4, /*!< RMT Channel4 */ - RMT_CHANNEL_5, /*!< RMT Channel5 */ - RMT_CHANNEL_6, /*!< RMT Channel6 */ - RMT_CHANNEL_7, /*!< RMT Channel7 */ + RMT_CHANNEL_0 = 0, /*!< RMT Channel 0 */ + RMT_CHANNEL_1, /*!< RMT Channel 1 */ + RMT_CHANNEL_2, /*!< RMT Channel 2 */ + RMT_CHANNEL_3, /*!< RMT Channel 3 */ + RMT_CHANNEL_4, /*!< RMT Channel 4 */ + RMT_CHANNEL_5, /*!< RMT Channel 5 */ + RMT_CHANNEL_6, /*!< RMT Channel 6 */ + RMT_CHANNEL_7, /*!< RMT Channel 7 */ RMT_CHANNEL_MAX } rmt_channel_t; @@ -51,7 +51,7 @@ typedef enum { }rmt_mem_owner_t; 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_MAX, } rmt_source_clk_t; @@ -63,20 +63,20 @@ typedef enum { } rmt_data_mode_t; typedef enum { - RMT_MODE_TX=0, /*!< RMT TX mode */ - RMT_MODE_RX, /*!< RMT RX mode */ + RMT_MODE_TX = 0, /*!< RMT TX mode */ + RMT_MODE_RX, /*!< RMT RX mode */ RMT_MODE_MAX } rmt_mode_t; typedef enum { - RMT_IDLE_LEVEL_LOW=0, /*!< RMT TX idle level: low Level */ - RMT_IDLE_LEVEL_HIGH, /*!< RMT TX idle level: high Level */ + RMT_IDLE_LEVEL_LOW = 0, /*!< RMT TX idle level: low Level */ + RMT_IDLE_LEVEL_HIGH, /*!< RMT TX idle level: high Level */ RMT_IDLE_LEVEL_MAX, } rmt_idle_level_t; typedef enum { - RMT_CARRIER_LEVEL_LOW=0, /*!< RMT carrier wave is modulated for low Level output */ - RMT_CARRIER_LEVEL_HIGH, /*!< RMT carrier wave is modulated for high Level output */ + RMT_CARRIER_LEVEL_LOW = 0, /*!< RMT carrier wave is modulated for low Level output */ + RMT_CARRIER_LEVEL_HIGH, /*!< RMT carrier wave is modulated for high Level output */ RMT_CARRIER_LEVEL_MAX } rmt_carrier_level_t; @@ -84,21 +84,21 @@ typedef enum { * @brief Data struct of RMT TX configure parameters */ 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 */ 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 */ 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 */ }rmt_tx_config_t; /** * @brief Data struct of RMT RX configure parameters */ typedef struct { - bool filter_en; /*!< RMT receiver filer enable*/ - uint8_t filter_ticks_thresh; /*!< RMT filter tick number */ + bool filter_en; /*!< RMT receiver filter enable */ + uint8_t filter_ticks_thresh; /*!< RMT filter tick number */ uint16_t idle_threshold; /*!< RMT RX idle threshold */ }rmt_rx_config_t; @@ -186,14 +186,16 @@ 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 * by the processor cores over the APB bus, as well as read by the transmitters * and written by the receivers. + * * 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), * 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 + * * @note * 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. - * Channel0 can use at most 8 blocks of memory, accordingly channel7 can only use one memory block. + * Channel 0 can use at most 8 blocks of memory, accordingly channel 7 can only use one memory block. * * @param channel RMT channel (0-7) * @@ -232,11 +234,9 @@ 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 carrier_level Configure the way carrier wave is modulated for channel0-7. - * - * 1'b1:transmit on low output level - * - * 1'b0:transmit on high output level + * @param carrier_level Configure the way carrier wave is modulated for channel 0-7. + * - 1'b1:transmit on low output level + * - 1'b0:transmit on high output level * * @return * - ESP_ERR_INVALID_ARG Parameter error @@ -278,7 +278,7 @@ esp_err_t rmt_get_mem_pd(rmt_channel_t channel, bool* pd_en); * @param channel RMT channel (0-7) * * @param tx_idx_rst Set true to reset memory index for TX. - * Otherwise, transmitter will continue sending from the last index in memory. + * Otherwise, transmitter will continue sending from the last index in memory. * * @return * - 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 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 - * 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 * - ESP_ERR_INVALID_ARG Parameter error @@ -391,7 +390,7 @@ esp_err_t rmt_get_tx_loop_mode(rmt_channel_t channel, bool* loop_en); /** * @brief Set RMT RX filter. * - * In receive mode, channel0-7 will ignore input pulse when the pulse width is smaller than threshold. + * In receive mode, channel 0-7 will ignore input pulse when the pulse width is smaller than threshold. * Counted in source clock, not divided counter clock. * * @param channel RMT channel (0-7) @@ -409,9 +408,9 @@ esp_err_t rmt_set_rx_filter(rmt_channel_t channel, bool rx_filter_en, uint8_t th /** * @brief Set RMT source clock * - * RMT module has two source clock: + * RMT module has two clock sources: * 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). * * @param channel RMT channel (0-7) * @@ -426,9 +425,9 @@ esp_err_t rmt_set_source_clk(rmt_channel_t channel, rmt_source_clk_t base_clk); /** * @brief Get RMT source clock * - * RMT module has two source clock: + * RMT module has two clock sources: * 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). * * @param channel RMT channel (0-7) * @@ -447,7 +446,7 @@ esp_err_t rmt_get_source_clk(rmt_channel_t channel, rmt_source_clk_t* src_clk); * * @param idle_out_en To enable idle level output. * - * @param level To set the output signal's level for channel0-7 in idle state. + * @param level To set the output signal's level for channel 0-7 in idle state. * * @return * - ESP_ERR_INVALID_ARG Parameter error @@ -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 * - * 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) * @@ -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); /** - * @brief Set RMT pins + * @brief Set RMT pin * * @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 * - * @param rmt_param RMT parameter structor + * @param rmt_param RMT parameter struct * * @return * - ESP_ERR_INVALID_ARG Parameter error @@ -567,17 +566,18 @@ 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); /** - * @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. - * @note If you already called rmt_driver_install to use system RMT driver, - * please do not register ISR handler again. + * 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, + * please do not register ISR handler again. * * @param fn Interrupt handler function. - * @param arg Parameter for handler function - * @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. - * @param handle If non-zero, a handle to later clean up the ISR gets stored here. + * @param arg Parameter for the handler function + * @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. + * @param handle If non-zero, a handle to later clean up the ISR gets stored here. * * @return * - ESP_OK Success @@ -653,18 +653,18 @@ esp_err_t rmt_driver_uninstall(rmt_channel_t channel); * * @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 - * This function will not copy data, instead, it will point to the original items, - * and send the waveform items. - * If wait_tx_done is set to true, this function will block and will not return until - * all items have been sent out. - * If wait_tx_done is set to false, this function will return immediately, and the driver - * interrupt will continue sending the items. We must make sure the item data will not be - * damaged when the driver is still sending items in driver interrupt. + * @note + * This function will not copy data, instead, it will point to the original items, + * and send the waveform items. + * If wait_tx_done is set to true, this function will block and will not return until + * all items have been sent out. + * If wait_tx_done is set to false, this function will return immediately, and the driver + * interrupt will continue sending the items. We must make sure the item data will not be + * damaged when the driver is still sending items in driver interrupt. * * @return * - ESP_ERR_INVALID_ARG Parameter error @@ -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 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 * - 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_FAIL Driver not installed */ 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 buf_handle Pointer to buffer handler to accept RX ringbuffer handler. + * @param buf_handle Pointer to buffer handle to accept RX ringbuffer handle. * * @return * - 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); -/***************************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 ------------------ * @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. * 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. * //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 } #endif diff --git a/docs/api-reference/peripherals/rmt.rst b/docs/api-reference/peripherals/rmt.rst index 5097a5f0e5..7d16efd8de 100644 --- a/docs/api-reference/peripherals/rmt.rst +++ b/docs/api-reference/peripherals/rmt.rst @@ -1,15 +1,160 @@ 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. +There couple of typical steps to setup and operate the RMT and they are discussed in the following sections: -Application Example -------------------- +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 refereed 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** that to be configured 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 valve after the clock divider is called a 'tick'. The length 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, the second provides the signal level (high or low). For example how to define and populate items see an example application :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 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: + +* RMT's receiver has finished receiving a signal - :cpp:func:`rmt_set_rx_intr_en` +* Ownership to the RMT memory block has been violated - :cpp:func:`rmt_set_err_intr_en` +* RMT's 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` + +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 ------------- diff --git a/examples/peripherals/rmt_tx/Makefile b/examples/peripherals/rmt_tx/Makefile new file mode 100644 index 0000000000..f4b09f84a6 --- /dev/null +++ b/examples/peripherals/rmt_tx/Makefile @@ -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 + diff --git a/examples/peripherals/rmt_tx/README.md b/examples/peripherals/rmt_tx/README.md new file mode 100644 index 0000000000..5e63859845 --- /dev/null +++ b/examples/peripherals/rmt_tx/README.md @@ -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. diff --git a/examples/peripherals/rmt_tx/main/component.mk b/examples/peripherals/rmt_tx/main/component.mk new file mode 100644 index 0000000000..b4fa72791c --- /dev/null +++ b/examples/peripherals/rmt_tx/main/component.mk @@ -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.) diff --git a/examples/peripherals/rmt_tx/main/rmt_tx_main.c b/examples/peripherals/rmt_tx/main/rmt_tx_main.c new file mode 100644 index 0000000000..b19dbc2b49 --- /dev/null +++ b/examples/peripherals/rmt_tx/main/rmt_tx_main.c @@ -0,0 +1,119 @@ +/* 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 +#define RMT_MESSAGE_LENGTH 12 + +rmt_item32_t items[RMT_MESSAGE_LENGTH]; + +/* + * Prepare a table with a message in the Morse code + * + * The message is "ESP" : . ... .--. + * + * The table structure: + * {duration, level, duration, level} + * + */ +uint32_t message[RMT_MESSAGE_LENGTH][4] = { + // 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 +}; + + +/* + * Populate the RMT items array + * with a previously prepared message + */ +static void populate_rmt_items(void) +{ + for (int i = 0; i < RMT_MESSAGE_LENGTH; i++) { + items[i].duration0 = message[i][0]; + items[i].level0 = message[i][1]; + items[i].duration1 = message[i][2]; + items[i].level1 = message[i][3]; + } +} + + +/* + * 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)); +} + + +/* + * Transmit all the items in a loop + */ +static void rmt_tx_task(void *ignore) +{ + while (1) { + ESP_ERROR_CHECK(rmt_write_items(RMT_TX_CHANNEL, items, RMT_MESSAGE_LENGTH, 1)); + ESP_LOGI(RMT_TX_TAG, "Transmission complete"); + vTaskDelay(2000 / portTICK_PERIOD_MS); + } +} + + +void app_main(void *ignore) +{ + ESP_LOGI(RMT_TX_TAG, "Configuring transmitter"); + rmt_tx_int(); + populate_rmt_items(); + + ESP_LOGI(RMT_TX_TAG, "Spinning out transmit task"); + xTaskCreatePinnedToCore(&rmt_tx_task, "rmt_tx_task", 4 * 1024, NULL, 5, NULL, 0); +} From b6ee7f699c12f6cb5e5ea29f24967fd77bfd636d Mon Sep 17 00:00:00 2001 From: krzychb Date: Wed, 22 Nov 2017 07:07:49 +0100 Subject: [PATCH 53/56] Implement review comments --- docs/api-reference/peripherals/rmt.rst | 25 ++++++- .../peripherals/rmt_tx/main/rmt_tx_main.c | 74 +++++++------------ 2 files changed, 49 insertions(+), 50 deletions(-) diff --git a/docs/api-reference/peripherals/rmt.rst b/docs/api-reference/peripherals/rmt.rst index 7d16efd8de..2f7787c207 100644 --- a/docs/api-reference/peripherals/rmt.rst +++ b/docs/api-reference/peripherals/rmt.rst @@ -72,7 +72,30 @@ Now, depending on how the channel is configured, we are ready to either `Transmi 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, the second provides the signal level (high or low). For example how to define and populate items see an example application :example:`peripherals/rmt_tx`. +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. diff --git a/examples/peripherals/rmt_tx/main/rmt_tx_main.c b/examples/peripherals/rmt_tx/main/rmt_tx_main.c index b19dbc2b49..d71663a58e 100644 --- a/examples/peripherals/rmt_tx/main/rmt_tx_main.c +++ b/examples/peripherals/rmt_tx/main/rmt_tx_main.c @@ -15,55 +15,40 @@ static const char *RMT_TX_TAG = "RMT Tx"; #define RMT_TX_CHANNEL RMT_CHANNEL_0 #define RMT_TX_GPIO 18 -#define RMT_MESSAGE_LENGTH 12 - -rmt_item32_t items[RMT_MESSAGE_LENGTH]; /* - * Prepare a table with a message in the Morse code + * Prepare a raw table with a message in the Morse code * * The message is "ESP" : . ... .--. * - * The table structure: + * The table structure represents the RMT item structure: * {duration, level, duration, level} * */ -uint32_t message[RMT_MESSAGE_LENGTH][4] = { +rmt_item32_t items[] = { // E : dot - {32767, 1, 32767, 0}, // dot + {{{ 32767, 1, 32767, 0 }}}, // dot // - {32767, 0, 32767, 0}, // SPACE + {{{ 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, 1, 32767, 0 }}}, // dot + {{{ 32767, 1, 32767, 0 }}}, // dot + {{{ 32767, 1, 32767, 0 }}}, // dot // - {32767, 0, 32767, 0}, // SPACE + {{{ 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 + {{{ 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 }}} }; -/* - * Populate the RMT items array - * with a previously prepared message - */ -static void populate_rmt_items(void) -{ - for (int i = 0; i < RMT_MESSAGE_LENGTH; i++) { - items[i].duration0 = message[i][0]; - items[i].level0 = message[i][1]; - items[i].duration1 = message[i][2]; - items[i].level1 = message[i][3]; - } -} - - /* * Initialize the RMT Tx channel */ @@ -95,25 +80,16 @@ static void rmt_tx_int() } -/* - * Transmit all the items in a loop - */ -static void rmt_tx_task(void *ignore) -{ - while (1) { - ESP_ERROR_CHECK(rmt_write_items(RMT_TX_CHANNEL, items, RMT_MESSAGE_LENGTH, 1)); - ESP_LOGI(RMT_TX_TAG, "Transmission complete"); - vTaskDelay(2000 / portTICK_PERIOD_MS); - } -} - - void app_main(void *ignore) { ESP_LOGI(RMT_TX_TAG, "Configuring transmitter"); rmt_tx_int(); - populate_rmt_items(); + int number_of_items = sizeof(items) / sizeof(items[0]); - ESP_LOGI(RMT_TX_TAG, "Spinning out transmit task"); - xTaskCreatePinnedToCore(&rmt_tx_task, "rmt_tx_task", 4 * 1024, NULL, 5, NULL, 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); } From 44764222a511530d433a7d6a42a69415589d8641 Mon Sep 17 00:00:00 2001 From: krzychb Date: Thu, 23 Nov 2017 23:53:06 +0100 Subject: [PATCH 54/56] RMT transmitter and receiver overview pictures --- docs/_static/rmt-carrier.png | Bin 0 -> 4491 bytes docs/_static/rmt-waveform-modulated.png | Bin 0 -> 4250 bytes docs/_static/rmt-waveform.png | Bin 0 -> 3270 bytes docs/api-reference/peripherals/rmt.rst | 83 +++++++++++++++++++++++- 4 files changed, 82 insertions(+), 1 deletion(-) create mode 100644 docs/_static/rmt-carrier.png create mode 100644 docs/_static/rmt-waveform-modulated.png create mode 100644 docs/_static/rmt-waveform.png diff --git a/docs/_static/rmt-carrier.png b/docs/_static/rmt-carrier.png new file mode 100644 index 0000000000000000000000000000000000000000..41d1c8d8d0b168261e0f9514c444a87d7c1aa0a1 GIT binary patch literal 4491 zcmeAS@N?(olHy`uVBq!ia0y~yU~FJuU{v5>V_;xBb!WA6ML&y%A^`Q*JP43?WFIc)p3uj^26n1;Dk z*z0R=#kOxfDw&`2b(yhiwx+AjSCemS3vWqlM7<9^r1`|d&*4Vj=0+8PZxtR+9+fj9 z&lG*!r~TgQ>HMkpzZ)N(u;zR1{O7fw;`e_&AEjL>d}~MHG_%! ziSBs(^i7VyN%w2}jvo2V{Q2lwf$iJ2ZQJR8>(;HPz4yAK_SgOWc<0U?>AKs|QBi(R zo<8;cotvMRXE$^HeEZTbFD}Mc=H_Q*S%pSKSbRKde*ez&7vCn_{~aXwxwiV-{nj=9 z3ZZzM2XS<{&Vju>;(YD5@BWX=uiJ53N?O|bPWHmO+tN}}R?Z7Ae66^BH+$*FZQ>## zKEHC;uV4SZX#LcCb^T}F*J`f6{p|L))^hPvX4~&6pT4$zrdZRO{p_i`*VIlfU8ul# ztS+qK#r%Vve|O4kOHZ@rG;m|=31mnXIUvDG8Pi_7;r`NdH|Fhpt8yUXT0EQn?THM@ z+t)9aaFjCA{`G4G-~L4=$HnfvUzujjmC(Z^v6$hQMuQ<&f{w`F+RQKSH@oG{o-sqC zW zyXF61l>YhhrDWc3ixVH;$*8HTb1&-O|8?Va$B756tgWSg{rYun2lt5`lY8%O-@bkO zvc-!RU(B8Vw!(bj(T{)s{=K?V#@@mr;$?Y_zrTO(g~xZr@4wDI>pZ~_#gRzv9Wdsb zGR8Ywb(E(E_r!2--nDDj|8=|H{W|&jz=?hDcFn0fzCL#Mx1Qbip2f(#U-w^fr>?$! zzSWD5_Wyo7zFT+v`jsnR+~UKpy#Dci|NnpI|Mab2yY_A8zI7|Ef85S3uyaC&N%_XW z_51Z7l+M2X@!gVVuYR0x4ZK{r=l7$fMSHZ5&*-CxU({0I&UII1kLM3BKl znPI}Q`I}lztQep?v97l$vBOcsVq9583NF(~vhI7l%z zm@qL69tkmX21`pzpb7Dosjy-GEyg4K@kAFhgi|}e+7f~|Ml|na?^|7x5wtwPf*=lM33n|3Z;`n2WxKKFjPzoFUL+0jovU%GV3`{MCs$^ZKL`rOL* zO0BQm|L@o9-|u(7-31`~gVuZbx9cxkEk(v@f8LqbfdPNDI&U$4%;U;qDayt`fe&Z4LCdU1PpC{6u2T{pTcH793{#>}6x_xT>M?OJL4 zY2*F6jpYvx9z5u^zxM977Ysk{{xmlIf8j!aYYD@jcQq@|*c%uGi01!$VVz+0tbgBU zRjd8?=h{#Fx%hpJSSv z**DSf_3PKW_Z;iBYY%z8e&x!STj%WGwQJY?{JQ;x;+Ot}hK4fNeg0jlcKT-}-ylk# zM92JK&S$%o8De8&zrOo_?AS5B^|`#fymL2hfBxjj8J3x6vd=`^y?eKG+UNO}#c6Br z?~|34?VUMq|Ni~^tM;9%|Mlf%>CAcc2?-0b^q%imW)Kw>b*^JRlBcbuCAE9yiR}Bo ze*My0-=DnGaMIlUJ9bojS@x*pTzq7t<%{j_UtL{&{@n7P-d=d^ucyJ)>^<)NyGq!O|Obmf03=9IU%nWD@(X9M@et&=e z=d3p?KR!BoebuT}Y-zXi6A~7@y0taCySZoP?Ag_e_wC!KZEs(%b+1lYSy}b);locI zkCmJ{eYzbKI*;BOo0?9&fA?;zc*);xG2LIurKP3+bAGGt`nPM>u6IpMO(!dU_xAOP z&9|@rC+>KxMCUrFqPDcN+oy7326y3KXJ=>8@bz)AQYmMA*4AHL6}n$vS9h;S{`{Yt z{@uEDYu=$lhdL{M_xARR&b2OoH{J1ANzKLRyx6O zuEOhbCtqLRa`-ScN^7pn>etzcM+4nLf>F@m7Z#NhB+k86lzarPW?C*Ko?{{`r ze}DJ4_xd(v_dc619}e^X&tH^mH8bM>^7(bYruWHkWvuZ!L9U2-OUUgb0U?{9B^&t9J!U-R*3v$T2MADfGYbA8tQe{irlTCV!d z#{b`LZA(7hH#dLZ&u915t)2z@+kQRx`~Cj^Tdr@@-~Z>6zFftF#{b?WKI+1+|38`R z|1azHwYBVfw(b3NO8ftw;^*gNJNtevbEqzrVk~J~}n^X~G1<)2B}9{W$yo&F1s}+`lj{xW8j#@-B9U{f5cU zy`RJwZ>-z@cTURn_fiY)&-i|?`u+Bk^Rm^h$-keuv|6D%k0I-I{jZnH@Av01I9}gh z`}^D7y5xJs=WXw+R2lWD`=>hJ&5etpipz3nZ3)g9~idp_^EzU|?| zho4f8m3;dAd3n>Fc_(J1pPv_d_riq>FSowETYkS*Z)V}+wf_&ba;JimHmJeybo;;B z-{1aP%`E)>=H}bA?5FIPGqi`i50aqFs8s~Gpc&Az_Q z*4Ws1@?*I(>tc6rvp6wh=gytxuU@^{#m&W~_V-(%W8L4c*Z(WdeSLpR=H-Vj*Y{^! zTx1^>9{&AzZvUG5wZ{!0MX%$v{ng*!z5QQt`RC{7^@1rr$BR(Q;{Bk;<)^2o^?&Eq zzgXDbcV_z6>({P*6A=^h`|);j_Vso5_kO#Tov*U#U+C(vzx%dq`7-H#+4g@cSFZd6 zDw==p`v)qpE52S0|NrvN&f@(%M$3$E|B|oya8Uo*;a|-AS)Lq{(Z6uAKvr9ZZ~O9M zAN9D&9${u{_ix%%WNZS;1z9hs}Yb}}$L3wr@7V%PXHF&KQW;N)O%xyZoaAi~Rl e#<=pId2z1W3=5A5hZz_c7(8A5T-G@yGywpKz1so+ literal 0 HcmV?d00001 diff --git a/docs/_static/rmt-waveform-modulated.png b/docs/_static/rmt-waveform-modulated.png new file mode 100644 index 0000000000000000000000000000000000000000..448b73758c35b285581ff6a1b57001225fdb814d GIT binary patch literal 4250 zcmeAS@N?(olHy`uVBq!ia0y~yU~FJuU{v5>V_;xBb!W&1vEE&Pk4mTh-|{ zx%gPRO|rdQ-TnTSG|TsUp6{u9W`15LO(tr0+1r!B%l+2M%F3So;W_K%i4zjMOJ5(f zez(K|^|Ak3OxazISi*G5<{i_&*CAl{o zV_LJ3AzSBw6jtV~#hV){-j}-Nf0vR@`5hs&w>G6(<5$;<;QgN;-?f|e@y@@8&zT*| z4MJO+_VwC^?&MPWQ}!c7;H3Mlx+5Z%@4lRG_{ylFeIY$XJ8aDlD`Vr&bqym7M2{QKK-Z@;U!y?34T``?Qn@2t(u zeLK(nvEGz(a&>Og*2x>&a$Wi-H|5=>Z_d};Svd`+l`#IPJFfn1SLdy~+CL|i7>{W* z7;+`_Fi9+CpoppSl4;4O)whmqzWeLPWLKu1rGKwKSZXbl@TUEJV2=W?u=cnAb)Vv= zs71axKf7HaS>%8OtAQJ1Pas3G$bpC;)*a>V<20?W>AijXw)gDWv$gMvpEt2cBs!l5ng?a(mwW zeSc?I+t>d3V*T#Vg$n_G7BX@@Js*GFI`)45|9{K*)wx>VxvndJe)r`hRd1_1-*c

jSwd>@&`>&sW-_CSrS82A}8{RvQ z_Ws+~aJ@7xKIIi(-KM?se)X5vlPmVuU%qi8A~-hou1HGGmpRvdZ=bg6 z>wLS~s;6C|+WYv7%Cy$rH{^AAB7R<}-rKUM_JaI^-`1z6PMvzTZvPguFvhjt^L0aC z_y1e!xqoAD;a@)gMulDm2Pwt|6DEdC7KTe43`+!IwhtZ&8)k##V?EK^^Y7Q`eEaqC z^78wg`|2_>G}LAI_^Zwa#>fBv)+cLyuQTS}-sux*mVK}xp=i8f`%pc!9wqb1|I(pKi8g>>xk9+<4_37`o z#C~h9sf%ARW&Zs8|(2{tPxQ_P35yvlRBPo4Cj#uHS3v(xq=V zCMy?Dn?8NJ-b&%SrLue=Z}_i-lo>69X)42Xk5; zoH-2BRa@j)AAEUvSv!1f)YS*3I~FcXyn1S?w!gdH^($9Q%uG!~t9wDsjE;SCCQdYz zdeU&~)~!=_GE-Aii{EU!cJ11yuCA_WH(ocNduy7$rmwG0wJsS{g{W8kzqV`FF0VT) zuWy`ZU7i=YYmNPjzvuTbKY8{nu76)lM8u8P=4NHladC0~nJeRxPoF*=z4u)I*8htZ zEt1>6(ckXplh%;;_ixRyEY5R@_e=Tv^XJc6{wLMFzSF<{*I;-jXlG_yCnYVN-Skf5 z#hlJJ_use6pI&PJVh*T1q4PaI_vWV5=S_02w`N~|#wKYldhPx8`p1hJ;Ta#RM2S z7BMn3Xh<=jGhVV8*x1aud+l17nUT>X(Ud=DW*YC;*3^6{l^s)FUjFgyZ1Z&1nZ}^T zQeW%*(@bhRfaw;k8Fncu5; zeE#>hx7D^6Eg!FqzY7vRy*2y#JgM`!>Wlsz?~}d#;ZE`SzlXDGyb++>!2P)oX5Yve$Q2q63qO!Z0sHo`Iir&)m8RogSN>qv}3knS0nCgA| zzP2b;ajqo4>#PQSP_rzTNpZ_+-BSreo+FDvsd$#p{ z{dOefoMisJ&KbrtD=%mMeYIl0niNI^kBVST_d_*Uxb86g`t|FRe*C^Y1}}cJa*M~E zJAeLtw|QDcg~jf*YuDzaq^M}0n6o+U?5@a|m^-%DcD#G{?$K=X{BvFA%a$)+u5rR{ z?;QL3x|bI&1e{AbGxN+IVZ-!2kmOVUYo=xKvvO5c)!5|T*KgnI?kRkHY>q-PTNcCZ z?fLh2W?x^oOCfpj)vH(E-r8CGylj2$yT<){_UtjeXnB`^!RF1y8}EO+bm`JJP^sH_ zuKMAjR&_{Hm9;8)@$|*%*=D&`LMi8(+4=VsJvh*KHtzPin4OyvY>KUJ-n@C(ah-pP z-*uC}*ZyC0m;YO4^Z6YpYu}h3dn6=!|E_P}z6I_7RsZMX@%^3qWWGHA^{`$3-@6YF z59?Rt{{D75|9Nk^pZm^#-D0}CN?*n@B38M)zeSp_f#vaUcbKHdg|-={dIpkU9Q_- zGj34zo>tP+*O#^<-G`Z(d2{m_$&|mKGURVRf6T6u%)kkjvZueQf8z0&yZZm7rQYY; z4?lF=Z&>~PU2XC4KH2vwLH~b#eqJ9O5KwS6|M#xa*M7Uo8KR=2Pp_;uaExO#F*OZc z{ztUo@gbXQ(N>*BveiF7Jxvd`TrT`1cah7|%fGt2x~hU~)8pde_Wz!K%2GY+Z)V_;xBb!WH*%WBu_)gzmT{r>saAUL0Jh?#!a6$Tn*8h z?D0Ij{!-=sxKI(!68rPtXP^K4uKW_OXZG%nv2OECxjmC#*4_VTy|dn3IO4_W9KGZ5zouVEN@u>BvtSl) z$mYf@vw*W~t8O}&O0P&m2%4K+uyMa>bM$)Mx}rz7H*cNrJ%9ar(~taW|LlIgy&3W1 zcYJ+qf}VN=Pt;rQPp&buGc z+w*MA?*9G$?(Xl+*6(&auK)h#=I77Xo;3T^RNvoM%bvdP%m459s^9iYU;`@z?x zLIH1%eGkl;+}3LP^MBnheXm%vpO4=vu0XMFqb2WyTU)bF&w9VwD8Bn;$m*-RjLgig z*{LkKAym&Elb93|kt@)S# zU3)FOKlkzW`*pvY^YnJ)?%P-T#Qc8E=j(T^{)fo#muB8k^77KDljRZ;5)yM?J?9Sj zf2J|c{{NBR)f`$VvG(TIdBN5Hw(za0U{RXIAmGc;@yz|EkS{ZcrN-c*&3m9-zV641 zX}ZyU;p-OO7Q1{s=6JvS{ZBWN`)e!^{p@qP{rTzN z_#NLLJ6>=2ZvVqzg+-hU7g-uIm>f(P6(Dw~F}Mgb46yL6DU1<2i=Hk$Jzf8Q!KeA) zFc$t_Q&LirP*Ct;_sYJF_5c6P-}mK`_y0?E{~sJ|{@vEz&hJxtd*jB9|7Pm%HUIkc zYj61a#7${^cdf#=f7@UF{@(n(pH6B2zf<@B!b0cW;2d{y`)nhI>3XqIH&1%9v9Y-w zKYskT)y}v3YJc;Y?YG+bHt5RB4r;&L$8|L3;3o^i&Zp#T1s>i+X&Ztbc3 z-1#j0Yt*t$7v%qc`zK%f*~=~yW>gs{>QT{sBD=(XWmO0+p3c2 zxceCZB9G;^XlsG_!V)-lgvOxzLC+ULe04U-|v>+&wsb;^*a4k z*EilcDt6#@{{FxH{-p76Z1MJcRj=oly}9u*?W>j8 zCFAoppXaaN`|Vc$>gyY492Gk-xBT8ud*S=`H9tPAD}8-!?%F8+;_`2AZkFyYduw&4 z?*Go`^L~Fn^;IVgRFmeb@7pi3?*GZp^$%Oc|7|U>v{l^3pmuvh;^CJI7cQK9^>xBG zPyw*|`i0fg4t>r4XS^IUT+o7Pl#85bXdW6N$5{&=9%{Y%{QUg*%P-$vUlUne_x06P z@qO>jl9QAFDl02XuaDciYtQ85dsVO3#@m0n;5`4%o3i;epH4c**Z=*x_I~;7&FSaY zy?!$1%eu?U{qrScWo1DLbV|Wr{jERm7oFCvulez?{r~T@?bhY*?%e7Y*I)PQ$qZT1 z1Ks-jetc?VW^c3J`PSv|;lu24bw8hqFaNdsS!4a>WxmVPs?I!P{_yd*{QtWp_517o z{xSv?rpm^)@0QQ6`}F|SsF``M|LEfV`|EQa{RSn^n61;|il3dC`E_sIU-dKp8`de|4{~tvzn{<3aOrE894C)(bWr_W%EU{%09G&wu{oTCj71 z^-rz^xi>eZUZ1sF;0PVZkC=dYi!?mw@z*&I0; znLq2Qbc?Uf`|o4N{7!Xl4Qs)h8ynYt{wM0NxYA(%&0s6zt0vz-O^Nf*4W;MW_#V0R z^+7X#UDRH-S8HN-e~bAlrFAyte;f-NxR5}L@2zF;?)>~U|LvMM4u-o|s~-27&s!JA m!f^MwrUFXY0@ZfqKjU_bn=uNWEJh3r3=E#GelF{r5}E+K-*OEA literal 0 HcmV?d00001 diff --git a/docs/api-reference/peripherals/rmt.rst b/docs/api-reference/peripherals/rmt.rst index 2f7787c207..77c3e06956 100644 --- a/docs/api-reference/peripherals/rmt.rst +++ b/docs/api-reference/peripherals/rmt.rst @@ -3,6 +3,87 @@ RMT 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 signal, that consists of a series of pulses, is generated by RMT's transmitter basing 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. + +.. 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; + } + } + +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`_ @@ -155,9 +236,9 @@ Registering of an interrupt handler for the RMT controller is done be calling :c The RMT controller triggers interrupts on four specific events describes below. To enable interrupts on these events, the following functions are provided: * RMT's receiver has finished receiving a signal - :cpp:func:`rmt_set_rx_intr_en` -* Ownership to the RMT memory block has been violated - :cpp:func:`rmt_set_err_intr_en` * RMT's 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`. From e589cad07e655d9b92830b0bd1936b4cec4aeff4 Mon Sep 17 00:00:00 2001 From: krzychb Date: Fri, 1 Dec 2017 07:29:54 +0100 Subject: [PATCH 55/56] Review comments implemented --- docs/api-reference/peripherals/rmt.rst | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/api-reference/peripherals/rmt.rst b/docs/api-reference/peripherals/rmt.rst index 77c3e06956..359344a764 100644 --- a/docs/api-reference/peripherals/rmt.rst +++ b/docs/api-reference/peripherals/rmt.rst @@ -3,7 +3,7 @@ RMT 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 signal, that consists of a series of pulses, is generated by RMT's transmitter basing 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. +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. .. blockdiag:: :scale: 100 @@ -45,7 +45,7 @@ The signal, that consists of a series of pulses, is generated by RMT's transmitt } } -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. +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 @@ -91,7 +91,7 @@ There couple of typical steps to setup and operate the RMT and they are discusse 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 refereed to using indexes defined in structure :cpp:type:`rmt_channel_t`. +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 @@ -103,7 +103,7 @@ There are several parameters that define how particular channel operates. Most o Common Parameters ^^^^^^^^^^^^^^^^^ -* The **channel** that to be configured to be configured, select one from the :cpp:type:`rmt_channel_t` enumerator. +* 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**. @@ -111,7 +111,7 @@ Common Parameters .. note:: - The period of a square valve after the clock divider is called a 'tick'. The length the pulses generated by the RMT transmitter or discriminated by the receiver is configured in number of 'ticks'. + 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`_: @@ -231,12 +231,12 @@ Registering of an interrupt handler for the RMT controller is done be calling :c .. note:: - When calling :cpp:func:`rmt_driver_install` to use the system RMT driver, a default ISR is being installed. In such case you cannot register a generic ISR handler with :cpp:func:`rmt_isr_register`. + 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: -* RMT's receiver has finished receiving a signal - :cpp:func:`rmt_set_rx_intr_en` -* RMT's transmitter has finished transmitting the signal - :cpp:func:`rmt_set_tx_intr_en` +* 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` From ecc608011755ba11e7b05b827c167ba623b0775e Mon Sep 17 00:00:00 2001 From: Darian Leung Date: Mon, 18 Dec 2017 21:58:15 +0800 Subject: [PATCH 56/56] freertos: prvCheckTasksWaitingTermination bugfix Bugfix to prevent a self deleting no affinity task's memory from being freed by the idle task of the other core before the self deleting no affinity task is able to context switch out. prvCheckTasksWaitingTermination now checks if the task is still on pxCurrentTCB before freeing task memory. --- components/freertos/tasks.c | 13 +++++++++---- .../freertos/test/test_freertos_task_delete.c | 17 +++++++++++++++++ 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/components/freertos/tasks.c b/components/freertos/tasks.c index ff5c46bb40..e95a8b04bc 100644 --- a/components/freertos/tasks.c +++ b/components/freertos/tasks.c @@ -3643,10 +3643,15 @@ static void prvCheckTasksWaitingTermination( void ) /* 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 */ ListItem_t *target = listGET_HEAD_ENTRY(&xTasksWaitingTermination); - for( ; target != listGET_END_MARKER(&xTasksWaitingTermination); target = listGET_NEXT(target) ){ - int coreid = (( TCB_t * )listGET_LIST_ITEM_OWNER(target))->xCoreID; - if(coreid == core || coreid == tskNO_AFFINITY){ //Find first item not pinned to other core - pxTCB = ( TCB_t * )listGET_LIST_ITEM_OWNER(target); + for( ; target != listGET_END_MARKER(&xTasksWaitingTermination); target = listGET_NEXT(target) ){ //Walk the list + TCB_t *tgt_tcb = ( TCB_t * )listGET_LIST_ITEM_OWNER(target); + int affinity = tgt_tcb->xCoreID; + //Self deleting tasks are added to Termination List before they switch context. Ensure they aren't still currently running + if( pxCurrentTCB[core] == tgt_tcb || (portNUM_PROCESSORS > 1 && pxCurrentTCB[!core] == tgt_tcb) ){ + 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; } } diff --git a/components/freertos/test/test_freertos_task_delete.c b/components/freertos/test/test_freertos_task_delete.c index 72f5cc85ad..7c3394cbe6 100644 --- a/components/freertos/test/test_freertos_task_delete.c +++ b/components/freertos/test/test_freertos_task_delete.c @@ -17,6 +17,7 @@ #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "esp_heap_caps.h" +#include "rom/ets_sys.h" #include "unity.h" @@ -24,6 +25,8 @@ #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) { @@ -35,6 +38,13 @@ 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); +} + TEST_CASE("FreeRTOS Delete Tasks", "[freertos]") { /* -------------- Test vTaskDelete() on currently running tasks ----------------*/ @@ -63,4 +73,11 @@ TEST_CASE("FreeRTOS Delete Tasks", "[freertos]") } 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 + } + }