arduino-esp32/cores/esp32/esp32-hal-rmt.c
Me No Dev 6f7a1ca76a
ESP-IDF v5.1 (#7733)
* Initial changes to compile under ESP-IDF v5.1

* Initial import for ESP-IDF v5.1 libs

* Update toolchain

* Update esp32-hal-psram.c

* Add missing LDs

* Update platform.txt

* Stop some CI jobs, because they will always fail

* Fix examples

* Update app_httpd.cpp

* Update ResetReason.ino

* Warnings fixes

* Added the example guideline and template (#7665)

* Added the example guideline and template

* PR review changes with some typos and grammar fixes

* Changes according to the PR review

* Added ESP32-S3 link to the datasheet (#7738)

* Update HiFreq_ADC.ino

* Replace periph_ctrl.h use because of deprecation

* Replace esp_spi_flash.h use because of deprecation

* Add includes to male mDNS::enableWorkstation compile

* Fix ssl_client mbedtls_pk_parse_key callback

* Update temperature sensor driver

* Allow sketch_utils to compile with arduino-cli

* Run CI with arduino-cli

* Fix arduino-cli CI build on Windows

* Refactor platform.txt to not use components installed through the board manager when running from git

* Initial Peripheral Manager Implementation

* Update SigmaDelta driver to use the new ESP-IDF driver API

* Small improvements to peripheral manager and SigmaDelta

* Remove deleted function from SigmaDelta header

* Update DAC driver to use the new ESP-IDF driver API

* Adds softAp(String) to make it compatible with ESP8266 (#7801)

* Fix commentary (#7800)

Minor fix based on observation done in https://github.com/espressif/arduino-esp32/issues/7795#issuecomment-1416868611

* add adafruit new board feather esp32s2 reserve tft (#7794)

* bugfix: add <stdint.h> for uint8_t to avoid compilation failure (GCC 11.2.0) (#7744)

* Adding 3rd party boards for VALTRACK-V4-VTS-ESP32-C3 & VALTRACK-V4-MFW-ESP32-C3 (#7735)

* Added VALTRACK-V4-VTS-ESP32-C3 board definition

Created pins_arduino.h & made changes to boards.txt with necessary changes

* Modified the URL

* Renamed json

* renamed all auRL

* Adding VALTRACK-V4 series board definitions

Added VALTRACK-V4-VTS-ESP32C3 & VALTRACK-V4-MFW-ESP32-C3 board variants

* Adding VALTRACK-V4 series board definitions

Added VALTRACK-V4-VTS-ESP32C3 & VALTRACK-V4-MFW-ESP32-C3 board variants

* Reverted package_esp32_index.template.json

restored package_esp32_index.template.json from edits

* Reverted package_esp32_index.template.json

Added new line to package_esp32_index.template.json

* Update Platformio CI (#7725)

* WiFiClient example fix (#7711)

* Modified WiFiClient example to use thingspeak instead of non-functionig sparkfun

* Moved instructions to README

* Fixed spelling

* Added link to S3 datasheet

---------

Co-authored-by: Jan Procházka <90197375+P-R-O-C-H-Y@users.noreply.github.com>

* Mirror update from Heltec repository (#7709)

Heltec updated the I2C pins in b10f4bf85d

* Fixes BLE data printing (#7699)

* Fixes BLE data printing

BLE data has no '\0' terminator, therefore it can't be printed as a regular C string.
This fix just prints the BLE data based on its length.

* Simplify printing to a single call

* split menu options + lora_32_V3 fix (#7697)

* Change header gaurd name (#7696)

* Fix Name (#7691)

Wrong name in definitions.

* Fix error in WiFiClient.cpp where the connect function fails for timeouts below 1 second (#7686)

* Update WiFiClient.cpp

This change will allow specifying connect timeouts below 1 second. Without this change, if connect timeouts under 1 second are given, the connect defaults to 0ms and fails. 
This will also allow timeouts in fractions of seconds, e.g. 1500ms. Without this change, connect timeouts are truncated to full second increments.

* Make parameter timeout_ms clear

* Change connection timeout_ms name for clarity

---------

Co-authored-by: Rodrigo Garcia <rodrigo.garcia@espressif.com>

* fixed the function header (#7674)

* fixed the function header

* fixed function name and paramaters

---------

Co-authored-by: Jan Procházka <90197375+P-R-O-C-H-Y@users.noreply.github.com>

* Ticker fix solving #6155 (#7664)

* Wrapped Ticker functions with #pragma disabling -Wcast-function-type

* Revert "Wrapped Ticker functions with #pragma disabling -Wcast-function-type"

This reverts commit 160be7e67a.

* Fixed Ticker example

* Modified Ticker example

* Fixed LED_BUILTIN err for ESP32

---------

Co-authored-by: Jan Procházka <90197375+P-R-O-C-H-Y@users.noreply.github.com>

* setPins fix ESP32 "specified pins are not supported by this chip." (#7646)

[ESP32: SDMMCFS::begin hardcodes the usage of slot 1, only check if the pins match slot 1 pins.]

setPins() was testing pins D1, D2 and D3 all against D1 ... fine in 1 pin mode when all are -1 not so much if you're trying to get 4 pin mode working.
I now see this function doesn't really do anything on the ESP32...accept now correctly checks that you are trying to use the slot 1 pins.

Co-authored-by: Jan Procházka <90197375+P-R-O-C-H-Y@users.noreply.github.com>

* Allow passing IP as connect method parameter in WiFiClientSecure and skip unnecessary host-ip conversions (#7643)

* Add LED_BUILTIN* definitions and initialization for LEDs to stop them floating. (#7636)

Co-authored-by: Rodrigo Garcia <rodrigo.garcia@espressif.com>

* Expand path to tinuf2 image when checking existence in platformio-build.py (#7631)

* Expand path to tinuf2 image when checking existence

* More isFiles fixed

* Remove (useless) trailing semicolon from Print.cpp (#7622)

* ADD: New variant Edgebox-ESP-100 (#7771)

* ADD: New variant Edgebox-ESP-100

* FIX: Edgebox-ESP-100 Board.txt usb mode option change back to default value as ESP32S3

* Add Crabik Slot ESP32-S3 board (#7790)

* Added Crabik Slot ESP32-S3

* Adding CPU frequency settings and removing excess from partition scheme settings

* new variant LilyGO T-Display-S3 (#7763)

* new variant LilyGO T-Display-S3

https://github.com/Xinyuan-LilyGO/T-Display-S3

* Add boards.txt definition

---------

Co-authored-by: Me No Dev <me-no-dev@users.noreply.github.com>

* Update get.py to support Apple ARM64

* Update package_esp32_index.template.json

* WString Return bool (#7774)

* Add Roboheart Hercules development board to the esp32-core (#7672)

* added Roboheart Hercules pin definitions and board.txt entries

* added package_roboheat.json for prototyping

* Roboheart Hercules pins

* Updated the pins

* Delete package_roboheart.json

* Requested changes

---------

Co-authored-by: renebohne <rene.bohne@gmail.com>

* Reword "ESP-IDF as Component" (#7812)

I think "Arduino as an ESP-IDF component" or just "As ESP-IDF component" instead of  "ESP-IDF as Component" is more correct way to name the link.

1. "ESP-IDF as Component" would imply that ESP-IDF is some sort of library for Arduino, which is (IMO) misleading, because it's true the other way around.
2. It's written as "Arduino as an ESP-IDF component" on the webpage it points to as well.

- Also I removed the capitalization from "Component" as I have not found a reason why is it capitalized.

* add new board Adafruit Feather ESP32-S3 Reverse TFT (#7811)

* Multi threading examples (tasks, queues, semaphores, mutexes) (#7660)

* Moved and renamed example ESP32/FreeRTOS to MultiThreading/BasicMultiThreading

* Added dummy files

* Modified original example

* Fixed BasicMultiThreading.ino

* Added Example demonstrating use of queues

* Extended info in BasicMultiThreading

* Renamed Queues to singular Queue

* Added Mutex example

* Added Semaphore example

* Moved info from example to README

* Moved doc from Mutex to README

* Added Queue README

* Removed unecesary text

* Fixed grammar

* Increased stack size for Sempahore example

* Added headers into .ino files

* Added word Example at the end of title in README

* removed unused line

* Added forgotten README

* Modified BasicMultiThreading example

* Added missing S3 entry in README

* moved location

* Update ESP-IDF libs

* Update CMakeLists.txt

* Update esptool to v4.4

* Add function timerAttachInterruptFlag (#7809)

* Update esptool to v4.5

* ADC refactoring (#7827)

* Adc refactored + periman implementation

Peripheral manager still needs to be checked if the implementation is right.

* switched to working solution for milivolts read

* Periman detachbus fix

* coding style

* fix CI warnings

* fix FreeRTOS example

* Update ETH.cpp

* Update FunctionalInterruptStruct.ino

* Update package_esp32_index.template.json

* Update package_esp32_index.template.json

* Fixes for the latest IDF v5.1

* update esp-idf libs and toolchain

* Turn OFF auto crystal frequency for ESP32 (needed by TWAI)

* Update examples

* Switch build to mostly use flags from files

Includes can not be done this way

* Reorganize flag files

* Optimize chip build flags further

* Revert defines from file. MBEDTLS_CONFIG_FILE does not properly expand

* Add support for includes and defines from file

* Replace old sdk path references in platform.txt

* use gcc-ar (#8013)

* Makes F_CPU generic for all SoC (#8007)

Based on CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ that is correctly defined in the sdkconfig file for each SoC.

* TIMER refactoring (#7904)

* refactor using GPtimer

* Updated timer HW test

* fix examples

* Add v2.0.7 in issue template (#7871)

* refactor using GPtimer

* Updated timer HW test

* fix examples

* Revert "Add v2.0.7 in issue template (#7871)"

This reverts commit fcc3b17d62.

* Update upload-artifact to v3 in HW CI

* Revert "Update upload-artifact to v3 in HW CI"

This reverts commit 1ba2280717.

* replace resolution with frequency

* remove count_down option

* countup removed from examples + header

* Refactored timer object

* code cleanup + examples and tests fixes

* TimerAlarm fix

---------

Co-authored-by: Vojtěch Bartoška <76958047+VojtechBartoska@users.noreply.github.com>

* [Docs] ADC and Timer API Update (+some docs fixes) (#7906)

* updated docs

* remove hall sensor docs

Removed Hall sensor documentation as its no longer supported in IDF-5

* Fixed ESPNow examples location in docs

* Last timer refactored API + gpio small fix

* AlarmWrite fix

* Fixes APLL/PLL with RTC Frequency (#8025)

log_d() was displaying APLL for any SoC, but S3 and C3 has not such option, causing compilation errors.

* Update IDF libs and fix OPI PSRAM on S3

* Add setMode function HardwareSerial.c to set the esp32 uart mode for use with RS485 auto RTS (#7935)

* Added setMode function to set the esp32 uart mode

Used to set the esp32 uart mode for use with RS485 Half Duplex and the auto RTS pin mode. This will set/clear the RTS pin output to control the RE/DE pin on most RS485 chips.

* Add Success (bool) return in some functions

* Add Success (bool) return code to some functions

* Add Success (bool) return to some functions

* Add Success (bool) return to some functions

* Fix uartSetRxTimeout return type

---------

Co-authored-by: Rodrigo Garcia <rodrigo.garcia@espressif.com>

* Add support for esp-elf-gdb

* WFG Crashfix (#8044)

* Update component libs

* IDF release/v5.1 (#8061)

* IDF release/v5.1 bb9200acec

* Update Esp.cpp

* IDF release/v5.1 420ebd208a

* Update esp32-hal-psram.c

* Switch SDK to be an external package

* fix path (#8096)

* Makes UART work at any APB Frequency (#8097)

Fixes HardwareSerial to work with IDF 5.1 on any CPU/APB Frequency (240MHz to 10MHZ), including user created low power modes.

* Add required callbacks for TinyUSB DFU

* Update version to 3.0.0

* Add ESP.getCoreVersion() and update ESP.getChipModel()

* Update timer hal for the latest 5.1

* Use separate RX and TX buffer sizes in HTTP client

optimizes download by allowing up to 4K packets to be received

* Rename clock tree enum name in latest 5.1

* ESP32-H4 support was removed in ESP-IDF v5.1

* IDF release/v5.1 2004bf4e11 (#8165)

* Deinit previous bus first (#8180)

* TIMER - add timer_started flag, fix timerEnd() + timer HW test (#8135)

* Add timer_started flag and stop before disable

* Fix timer HW test

* TOUCH - Peripheral manager implementation (#8129)

* Touch periman implemented

* Deinit previous bus first

* LEDC Refactoring - Peripheral manager implemented (#8126)

* LEDC periman implementation

* Fix examples

* Rework tone

* Update ledc docs

* fix missing bracket

* Update analog funtions esp32-hal.h

* Update CameraWebServer example

* Fix HiFreq_ADC example

* minor fixes - typos

* Avoid calling tone/notone when tone already runs on dif. pin

* Remove unused channels_resolution

* GPIO - Peripheral manager implementation (#8179)

* periman-implementation

* fix RGB_BUILTIN and remove space

* Enforces more consistency into Peripheral Manager (#8188)

* Avoid log_i() message the first time a bus is assigned

* Prevent operation with ESP32_BUS_TYPE_INIT

* keeps coding style

* do not print messages on INIT bus type

* [Arduino Core 3.0.0] RMT IDF5.1 Refactoring (#7994)

* RMT IDF5.1 refactoring

* Fixes initial value setting

* removed rmtRead() with user callback

* simplify/remove Read data structure

* Deep API simplification

* fixes the examples

* fix rmt.h

* adds support to APB different frequencies

* fixes CI and not defined RGB_BUILTIN

* new RMT API and examples

* fixing commentaties

* Update esp32-hal-rgb-led.c

* changes Filter API

* Fixes example with Filter API

* Update PlatformIO scripts for the upcoming 3.0 core (#8183)

* Update PlatformIO scripts for the upcoming 3.0 core

* Dynamically select proper framework-arduinoespressif32-libs package

With this change the dev-platform will be dynamically configured to
pull the latest .zip package with precompiled libraries from extracted from
package_esp32_index.template.json

* free memory on detach (#8264)

* SPI - Peripheral manager implementation  (#8255)

* spi periman implementation

* fix header file

* remove unused struct

* fix missing braces

* Update esp32-hal-rmt.c (#8216)

Optimizing Peripheral Manager Test

* I2C - Peripheral manager implementation (#8220)

* i2c-master periman initial commit

* i2c-master make detachbus static + comment remove

* i2c-slave periman implementation

* SetPinBus to INIT on i2cDeinits

* Fix slave pins deinit

* remove dbg logs

* set ret to ESP_FAIL instead of returning

* Fix warnings in hal-spi caused by pariman transition

* Update esptool.py to version 4.6

* Add platform support for ESP_SR

* Add USB Type and valid pin check to periman

* replace bus with spi->num+1 (#8279)

* Remove default pins from SPI HAL

* Add commented out handlers for esptool.js in TinyUSB CDC

For future use

* Add build defines for host os and fqbn (for debug purposes)

* Provide proper memory caps total size

* Update Esp.cpp

* SDMMC - Peripheral manager implementation (#8289)

* sdmmc periman implemented

* save pins when SOC_SDMMC_USE_IOMUX

* IDF release/v5.1 4bc762621d (#8292)

* Adds missing pinMode (#8312)

* Adds missing pinMode

The example code lacks a pinMode() to initialize the GPIO 0 (button). In Arduino Core 3.0.0, it prints an error message when trying to read a not initialized GPIO.

* Update KeyboardLogout.ino

Adds <buttonPin> to keep code standard

* Update KeyboardReprogram.ino

Adds <buttonPin> to keep code standard

* LEDC Fade implementation (#8338)

* fade API + pointer fixes

* Add fade api

* Add fade example

* update ledc docs

* remove unused variables

* fix path to example

* Adds USB to Peripheral Manager - Arduino Core 3.0.0 (#8335)

* ETHERNET - Peripheral manager implementation (#8297)

* Peripheral manager implemented

* remove unused variable

* Add all RMII pins

* fix typo

* Adds HardwareSerial to Peripheral Manager Arduino 3.0.0 (#8328)

* Do not limit ETHERNET in periman to only ESP32. SPI is also an option

* Initial support for ESP32-C6 (#8337)

* Add checks for SOC defines (#8351)

* Add checks for SOC defines

* Add SoC checks to BLE library

* fix i2c compilation error

* fix wrong placement of include

* add check to SPI library

* add check to USB library

* add checks to Wire library

* Feature/esp32h2 support (#8373)

* Initial support for ESP32H2

* Additional changes for ESP32H2

* Update libs for ESP32H2

* Fix flashing on ESP32-H2

* Fix GPIO Configs for ESP32-C6 and ESP32-H2

* Update Timer test sketch

* Fix upload flash parameters

* Use ets_write_char_uart instead of ets_printf in log_printfv

* Print full chip report when log level is sufficient (#8282)

* ESP32-C3 does not have ets_write_char_uart

* Fix BLE gap event name

* HW Testing - Pytest update (#8389)

* update tests requirements

* remove already handled components

* Update version of pytest

* Add missing ESP32-H2 to hil.yml

* Updated FreeRTOS names (#8418)

* HW Testing -  ESP32-C6 + ESP32-H2 fixes (#8404)

* add C6/H2 to tests cfg.json

* remove ,

* workflow runs-on runner by matrix

* Add need for arduino tag to select runner

* Add cryptography to requirements.txt

* Removed duplicate TX1 define for H2 (#8402)

* Fix broken examples

* Fixes RMT filter & idle timing and setup (#8359)

* Fixes Filter and Idle parameter to uint32

* Fixes Filter and Idle setup

* Fixes it to 5.1Libs branch

* fix RMT CLK source and Filter API

* fixes missing ;

* fixes missing ;

* fixes RMT example

* IDF release/v5.1 a7b62bbcaf (#8438)

* Add workflow to build executables from python scripts (#8290)

* Add workflow to build executables from python scripts

* Push binary to tools

* Enable executable signing on Windows

* Update get.py

* Push binary to tools

* Try with multiple files

* Try more actions

* Try powershell

* Restore tools so they do not get rebuilt

* Finalize scripts

* Push binary to tools

* App rollback should be after PSRAM is initialized

* Correcting RX1 to GPIO4 and TX1 to GPIO5 to be consistent with documentation. Previous pin use works but is inconsistent with C6 docs.

* Fixes Memory Leak (#8486)

* fixes preprocessor test (#8485)

* fixes preprocessor test

When using `#define USE_SOFT_AP` 
Change
`&& not USE_SOFT_AP` ==> `&& !defined(USE_SOFT_AP)`

* Adds any BLE capable device in WiFiProv.ino

Removing ESP32 restriction for BLE Provisioning.

* fix flash mode read out for C6

* Add option for custom partitions without restrictions

* SD_MMC update (#8298)

* Updated SD_MMC lib and examples

* Removed getter implementation and commented usage in examples

* squashed updates

* IDF release/v5.1 f0437b945f (#8599)

* Update package_esp32_index.template.json

* Fix printf format build error in BTAdvertisedDeviceSet.cpp

---------

Co-authored-by: Pedro Minatel <pedro.minatel@espressif.com>
Co-authored-by: Rodrigo Garcia <rodrigo.garcia@espressif.com>
Co-authored-by: Ha Thach <thach@tinyusb.org>
Co-authored-by: Martin Turski <quiret@vfemail.net>
Co-authored-by: raviypujar <raviypujar@gmail.com>
Co-authored-by: Jason2866 <24528715+Jason2866@users.noreply.github.com>
Co-authored-by: Tomáš Pilný <34927466+PilnyTomas@users.noreply.github.com>
Co-authored-by: Jan Procházka <90197375+P-R-O-C-H-Y@users.noreply.github.com>
Co-authored-by: Daniel Berlin <dberlin@dberlin.org>
Co-authored-by: Nima Askari (نیما عسکری) <nimaltd@yahoo.com>
Co-authored-by: rtpmsys <106180646+rtpmsys@users.noreply.github.com>
Co-authored-by: bytiful <55647551+bytiful@users.noreply.github.com>
Co-authored-by: tmfarrington <tmfarrington@users.noreply.github.com>
Co-authored-by: Krzysiek S <chris.streh@gmail.com>
Co-authored-by: surt <carl.olsson@gmail.com>
Co-authored-by: Max Scheffler <max.scheffler@pm.me>
Co-authored-by: Clemens Kirchgatterer <clemens@1541.org>
Co-authored-by: Peter Pan's Techland <twinkle-pirate@hotmail.com>
Co-authored-by: Roman <programmeofficemilkyway@gmail.com>
Co-authored-by: Eistee <Eistee82@users.noreply.github.com>
Co-authored-by: David McCurley <44048235+mrengineer7777@users.noreply.github.com>
Co-authored-by: Gaya3N25 <30388176+Gaya3N25@users.noreply.github.com>
Co-authored-by: renebohne <rene.bohne@gmail.com>
Co-authored-by: Olivér Remény <25034625+remenyo@users.noreply.github.com>
Co-authored-by: davidk88 <david.kotar@gmail.com>
Co-authored-by: Vojtěch Bartoška <76958047+VojtechBartoska@users.noreply.github.com>
Co-authored-by: James Armstrong <jamesarmstrong3@me.com>
Co-authored-by: Valerii Koval <valeros@users.noreply.github.com>
Co-authored-by: Peter G. Jensen <root@petergjoel.dk>
2023-10-05 14:54:25 +03:00

598 lines
21 KiB
C

// Copyright 2023 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 "soc/soc_caps.h"
#if SOC_RMT_SUPPORTED
#include "esp32-hal.h"
#include "driver/gpio.h"
#include "driver/rmt_tx.h"
#include "driver/rmt_rx.h"
#include "hal/rmt_ll.h"
#include "esp32-hal-rmt.h"
#include "esp32-hal-periman.h"
// Arduino Task Handle indicates if the Arduino Task has been started already
extern TaskHandle_t loopTaskHandle;
// RMT Events
#define RMT_FLAG_RX_DONE (1)
#define RMT_FLAG_TX_DONE (2)
/**
Internal macros
*/
#if CONFIG_DISABLE_HAL_LOCKS
# define RMT_MUTEX_LOCK(busptr)
# define RMT_MUTEX_UNLOCK(busptr)
#else
# define RMT_MUTEX_LOCK(busptr) do {} while (xSemaphoreTake(busptr->g_rmt_objlocks, portMAX_DELAY) != pdPASS)
# define RMT_MUTEX_UNLOCK(busptr) xSemaphoreGive(busptr->g_rmt_objlocks)
#endif /* CONFIG_DISABLE_HAL_LOCKS */
/**
Typedefs for internal stuctures, enums
*/
struct rmt_obj_s {
// general RMT information
rmt_channel_handle_t rmt_channel_h; // IDF RMT channel handler
rmt_encoder_handle_t rmt_copy_encoder_h; // RMT simple copy encoder handle
uint32_t signal_range_min_ns; // RX Filter data - Low Pass pulse width
uint32_t signal_range_max_ns; // RX idle time that defines end of reading
EventGroupHandle_t rmt_events; // read/write done event RMT callback handle
bool rmt_ch_is_looping; // Is this RMT TX Channel in LOOPING MODE?
size_t *num_symbols_read; // Pointer to the number of RMT symbol read by IDF RMT RX Done
uint32_t frequency_Hz; // RMT Frequency
#if !CONFIG_DISABLE_HAL_LOCKS
SemaphoreHandle_t g_rmt_objlocks; // Channel Semaphore Lock
#endif /* CONFIG_DISABLE_HAL_LOCKS */
};
typedef struct rmt_obj_s *rmt_bus_handle_t;
/**
Internal variables used in RMT API
*/
static SemaphoreHandle_t g_rmt_block_lock = NULL;
/**
Internal method (private) declarations
*/
// This is called from an IDF ISR code, therefore this code is part of an ISR
static bool _rmt_rx_done_callback(rmt_channel_handle_t channel, const rmt_rx_done_event_data_t *data, void *args)
{
BaseType_t high_task_wakeup = pdFALSE;
rmt_bus_handle_t bus = (rmt_bus_handle_t) args;
// sets the returning number of RMT symbols (32 bits) effectively read
*bus->num_symbols_read = data->num_symbols;
// set RX event group and signal the received RMT symbols of that channel
xEventGroupSetBitsFromISR(bus->rmt_events, RMT_FLAG_RX_DONE, &high_task_wakeup);
// A "need to yield" is returned in order to execute portYIELD_FROM_ISR() in the main IDF RX ISR
return high_task_wakeup == pdTRUE;
}
// This is called from an IDF ISR code, therefore this code is part of an ISR
static bool _rmt_tx_done_callback(rmt_channel_handle_t channel, const rmt_tx_done_event_data_t *data, void *args)
{
BaseType_t high_task_wakeup = pdFALSE;
rmt_bus_handle_t bus = (rmt_bus_handle_t) args;
// set RX event group and signal the received RMT symbols of that channel
xEventGroupSetBitsFromISR(bus->rmt_events, RMT_FLAG_TX_DONE, &high_task_wakeup);
// A "need to yield" is returned in order to execute portYIELD_FROM_ISR() in the main IDF RX ISR
return high_task_wakeup == pdTRUE;
}
// This function must be called only after checking the pin and its bus with _rmtGetBus()
static bool _rmtCheckDirection(uint8_t gpio_num, rmt_ch_dir_t rmt_dir, const char* labelFunc)
{
// gets bus RMT direction from the Peripheral Manager information
rmt_ch_dir_t bus_rmt_dir = perimanGetPinBusType(gpio_num) == ESP32_BUS_TYPE_RMT_TX ? RMT_TX_MODE : RMT_RX_MODE;
if (bus_rmt_dir == rmt_dir) { // matches expected RX/TX channel
return true;
}
// print error message
if (rmt_dir == RMT_RX_MODE) {
log_w("==>%s():Channel set as TX instead of RX.", labelFunc);
} else {
log_w("==>%s():Channel set as RX instead of TX.", labelFunc);
}
return false; // mismatched
}
static rmt_bus_handle_t _rmtGetBus(int pin, const char* labelFunc)
{
// Is pin RX or TX? Let's find it out
peripheral_bus_type_t rmt_bus_type = perimanGetPinBusType(pin);
if (rmt_bus_type != ESP32_BUS_TYPE_RMT_TX && rmt_bus_type != ESP32_BUS_TYPE_RMT_RX) {
log_e("==>%s():GPIO %u is not attached to an RMT channel.", labelFunc, pin);
return NULL;
}
return (rmt_bus_handle_t)perimanGetPinBus(pin, rmt_bus_type);
}
// Peripheral Manager detach callback
static bool _rmtDetachBus(void *busptr)
{
// sanity check - it should never happen
assert(busptr && "_rmtDetachBus bus NULL pointer.");
bool retCode = true;
rmt_bus_handle_t bus = (rmt_bus_handle_t) busptr;
log_v("Detaching RMT GPIO Bus");
// lock it
while (xSemaphoreTake(g_rmt_block_lock, portMAX_DELAY) != pdPASS) {}
// free Event Group
if (bus->rmt_events != NULL) {
vEventGroupDelete(bus->rmt_events);
bus->rmt_events = NULL;
}
// deallocate the channel encoder
if (bus->rmt_copy_encoder_h != NULL) {
if (ESP_OK != rmt_del_encoder(bus->rmt_copy_encoder_h)) {
log_w("RMT Encoder Deletion has failed.");
retCode = false;
}
}
// disable and deallocate RMT channel
if (bus->rmt_channel_h != NULL) {
// force stopping rmt TX/RX processing and unlock Power Management (APB Freq)
rmt_disable(bus->rmt_channel_h);
if (ESP_OK != rmt_del_channel(bus->rmt_channel_h)) {
log_w("RMT Channel Deletion has failed.");
retCode = false;
}
}
#if !CONFIG_DISABLE_HAL_LOCKS
// deallocate channel semaphore
if (bus->g_rmt_objlocks != NULL) {
vSemaphoreDelete(bus->g_rmt_objlocks);
}
#endif
// free the allocated bus data structure
free(bus);
// release the mutex
xSemaphoreGive(g_rmt_block_lock);
return retCode;
}
/**
Public method definitions
*/
bool rmtSetCarrier(int pin, bool carrier_en, bool carrier_level, uint32_t frequency_Hz, float duty_percent)
{
rmt_bus_handle_t bus = _rmtGetBus(pin, __FUNCTION__);
if (bus == NULL) {
return false;
}
if (duty_percent > 1) {
log_w("GPIO %d - RMT Carrier must be a float percentage from 0 to 1. Setting to 50%.", pin);
duty_percent = 0.5;
}
rmt_carrier_config_t carrier_cfg = {0};
carrier_cfg.duty_cycle = duty_percent; // duty cycle
carrier_cfg.frequency_hz = carrier_en ? frequency_Hz : 0; // carrier frequency in Hz
carrier_cfg.flags.polarity_active_low = carrier_level; // carrier modulation polarity level
bool retCode = true;
RMT_MUTEX_LOCK(bus);
// modulate carrier to TX channel
if (ESP_OK != rmt_apply_carrier(bus->rmt_channel_h, &carrier_cfg)) {
log_w("GPIO %d - Error applying RMT carrier.", pin);
retCode = false;
}
RMT_MUTEX_UNLOCK(bus);
return retCode;
}
bool rmtSetRxMinThreshold(int pin, uint8_t filter_pulse_ticks)
{
rmt_bus_handle_t bus = _rmtGetBus(pin, __FUNCTION__);
if (bus == NULL) {
return false;
}
if (!_rmtCheckDirection(pin, RMT_RX_MODE, __FUNCTION__)) {
return false;
}
uint32_t filter_pulse_ns = (1000000000 / bus->frequency_Hz) * filter_pulse_ticks;
// RMT_LL_MAX_FILTER_VALUE is 255 for ESP32, S2, S3, C3, C6 and H2;
// filter_pulse_ticks is 8 bits, thus it will not exceed 255
#if 0 // for the future, in case some other SoC has different limit
if (filter_pulse_ticks > RMT_LL_MAX_FILTER_VALUE) {
log_e("filter_pulse_ticks is too big. Max = %d", RMT_LL_MAX_FILTER_VALUE);
return false;
}
#endif
RMT_MUTEX_LOCK(bus);
bus->signal_range_min_ns = filter_pulse_ns; // set zero to disable it
RMT_MUTEX_UNLOCK(bus);
return true;
}
bool rmtSetRxMaxThreshold(int pin, uint16_t idle_thres_ticks)
{
rmt_bus_handle_t bus = _rmtGetBus(pin, __FUNCTION__);
if (bus == NULL) {
return false;
}
if (!_rmtCheckDirection(pin, RMT_RX_MODE, __FUNCTION__)) {
return false;
}
uint32_t idle_thres_ns = (1000000000 / bus->frequency_Hz) * idle_thres_ticks;
// RMT_LL_MAX_IDLE_VALUE is 65535 for ESP32,S2 and 32767 for S3, C3, C6 and H2
#if RMT_LL_MAX_IDLE_VALUE < 65535 // idle_thres_ticks is 16 bits anyway - save some bytes
if (idle_thres_ticks > RMT_LL_MAX_IDLE_VALUE) {
log_e("idle_thres_ticks is too big. Max = %ld", RMT_LL_MAX_IDLE_VALUE);
return false;
}
#endif
RMT_MUTEX_LOCK(bus);
bus->signal_range_max_ns = idle_thres_ns;
RMT_MUTEX_UNLOCK(bus);
return true;
}
bool rmtDeinit(int pin)
{
log_v("Deiniting RMT GPIO %d", pin);
if (_rmtGetBus(pin, __FUNCTION__) != NULL) {
// release all allocated data
return perimanSetPinBus(pin, ESP32_BUS_TYPE_INIT, NULL);
}
log_e("GPIO %d - No RMT channel associated.", pin);
return false;
}
static bool _rmtWrite(int pin, rmt_data_t* data, size_t num_rmt_symbols, bool blocking, bool loop, uint32_t timeout_ms)
{
rmt_bus_handle_t bus = _rmtGetBus(pin, __FUNCTION__);
if (bus == NULL) {
return false;
}
if (!_rmtCheckDirection(pin, RMT_TX_MODE, __FUNCTION__)) {
return false;
}
bool loopCancel = false; // user wants to cancel the writing loop mode
if (data == NULL || num_rmt_symbols == 0) {
if (!loop) {
log_w("GPIO %d - RMT Write Data NULL pointer or size is zero.", pin);
return false;
} else {
loopCancel = true;
}
}
log_v("GPIO: %d - Request: %d RMT Symbols - %s - Timeout: %d", pin, num_rmt_symbols, blocking ? "Blocking" : "Non-Blocking", timeout_ms);
log_v("GPIO: %d - Currently in Loop Mode: [%s] | Asked to Loop: %s, LoopCancel: %s", pin, bus->rmt_ch_is_looping ? "YES" : "NO", loop ? "YES" : "NO", loopCancel ? "YES" : "NO");
if ((xEventGroupGetBits(bus->rmt_events) & RMT_FLAG_TX_DONE) == 0) {
log_v("GPIO %d - RMT Write still pending to be completed.", pin);
return false;
}
rmt_transmit_config_t transmit_cfg = {0}; // loop mode disabled
bool retCode = true;
RMT_MUTEX_LOCK(bus);
// wants to start in writing or looping over a previous looping --> resets the channel
if (bus->rmt_ch_is_looping == true) {
// must force stopping a previous loop transmission first
rmt_disable(bus->rmt_channel_h);
// enable it again for looping or writing
rmt_enable(bus->rmt_channel_h);
bus->rmt_ch_is_looping = false; // not looping anymore
}
if (loopCancel) {
// just resets and releases the channel, maybe, already done above, then exits
bus->rmt_ch_is_looping = false;
} else { // new writing | looping request
// looping | Writing over a previous looping state is valid
if (loop) {
transmit_cfg.loop_count = -1; // enable infinite loop mode
// keeps RMT_FLAG_TX_DONE set - it never changes
} else {
// looping mode never sets this flag (IDF 5.1) in the callback
xEventGroupClearBits(bus->rmt_events, RMT_FLAG_TX_DONE);
}
// transmits just once or looping data
if (ESP_OK != rmt_transmit(bus->rmt_channel_h, bus->rmt_copy_encoder_h,
(const void *) data, num_rmt_symbols * sizeof(rmt_data_t), &transmit_cfg)) {
retCode = false;
log_w("GPIO %d - RMT Transmission failed.", pin);
} else { // transmit OK
if (loop) {
bus->rmt_ch_is_looping = true; // for ever... until a channel canceling or new writing
} else {
if (blocking) {
// wait for transmission confirmation | timeout
retCode = (xEventGroupWaitBits(bus->rmt_events, RMT_FLAG_TX_DONE, pdFALSE /* do not clear on exit */,
pdFALSE /* wait for all bits */, timeout_ms) & RMT_FLAG_TX_DONE) != 0;
}
}
}
}
RMT_MUTEX_UNLOCK(bus);
return retCode;
}
static bool _rmtRead(int pin, rmt_data_t* data, size_t *num_rmt_symbols, bool waitForData, uint32_t timeout_ms)
{
rmt_bus_handle_t bus = _rmtGetBus(pin, __FUNCTION__);
if (bus == NULL) {
return false;
}
if (!_rmtCheckDirection(pin, RMT_RX_MODE, __FUNCTION__)) {
return false;
}
if (data == NULL || num_rmt_symbols == NULL) {
log_w("GPIO %d - RMT Read Data and/or Size NULL pointer.", pin);
return false;
}
log_v("GPIO: %d - Request: %d RMT Symbols - %s - Timeout: %d", pin, *num_rmt_symbols, waitForData ? "Blocking" : "Non-Blocking", timeout_ms);
bool retCode = true;
RMT_MUTEX_LOCK(bus);
// request reading RMT Channel Data
rmt_receive_config_t receive_config;
receive_config.signal_range_min_ns = bus->signal_range_min_ns;
receive_config.signal_range_max_ns = bus->signal_range_max_ns;
xEventGroupClearBits(bus->rmt_events, RMT_FLAG_RX_DONE);
bus->num_symbols_read = num_rmt_symbols;
rmt_receive(bus->rmt_channel_h, data, *num_rmt_symbols * sizeof(rmt_data_t), &receive_config);
// wait for data if requested
if (waitForData) {
retCode = (xEventGroupWaitBits(bus->rmt_events, RMT_FLAG_RX_DONE, pdFALSE /* do not clear on exit */,
pdFALSE /* wait for all bits */, timeout_ms) & RMT_FLAG_RX_DONE) != 0;
}
RMT_MUTEX_UNLOCK(bus);
return retCode;
}
bool rmtWrite(int pin, rmt_data_t *data, size_t num_rmt_symbols, uint32_t timeout_ms) {
return _rmtWrite(pin, data, num_rmt_symbols, true /*blocks*/, false /*looping*/, timeout_ms);
}
bool rmtWriteAsync(int pin, rmt_data_t *data, size_t num_rmt_symbols) {
return _rmtWrite(pin, data, num_rmt_symbols, false /*blocks*/, false /*looping*/, 0 /*N/A*/);
}
bool rmtWriteLooping(int pin, rmt_data_t* data, size_t num_rmt_symbols) {
return _rmtWrite(pin, data, num_rmt_symbols, false /*blocks*/, true /*looping*/, 0 /*N/A*/);
}
bool rmtTransmitCompleted(int pin) {
rmt_bus_handle_t bus = _rmtGetBus(pin, __FUNCTION__);
if (bus == NULL) {
return false;
}
if (!_rmtCheckDirection(pin, RMT_TX_MODE, __FUNCTION__)) {
return false;
}
bool retCode = true;
RMT_MUTEX_LOCK(bus);
retCode = (xEventGroupGetBits(bus->rmt_events) & RMT_FLAG_TX_DONE) != 0;
RMT_MUTEX_UNLOCK(bus);
return retCode;
}
bool rmtRead(int pin, rmt_data_t* data, size_t *num_rmt_symbols, uint32_t timeout_ms) {
return _rmtRead(pin, data, num_rmt_symbols, true /* blocking */, timeout_ms);
}
bool rmtReadAsync(int pin, rmt_data_t* data, size_t *num_rmt_symbols) {
return _rmtRead(pin, data, num_rmt_symbols, false /* non-blocking */, 0 /* N/A */);
}
bool rmtReceiveCompleted(int pin) {
rmt_bus_handle_t bus = _rmtGetBus(pin, __FUNCTION__);
if (bus == NULL) {
return false;
}
if (!_rmtCheckDirection(pin, RMT_RX_MODE, __FUNCTION__)) {
return false;
}
bool retCode = true;
RMT_MUTEX_LOCK(bus);
retCode = (xEventGroupGetBits(bus->rmt_events) & RMT_FLAG_RX_DONE) != 0;
RMT_MUTEX_UNLOCK(bus);
return retCode;
}
bool rmtInit(int pin, rmt_ch_dir_t channel_direction, rmt_reserve_memsize_t mem_size, uint32_t frequency_Hz)
{
log_v("GPIO %d - %s - MemSize[%d] - Freq=%dHz", pin, channel_direction == RMT_RX_MODE ? "RX MODE" : "TX MODE", mem_size * RMT_SYMBOLS_PER_CHANNEL_BLOCK, frequency_Hz);
// create common block mutex for protecting allocs from multiple threads allocating RMT channels
if (!g_rmt_block_lock) {
g_rmt_block_lock = xSemaphoreCreateMutex();
if (g_rmt_block_lock == NULL) {
log_e("GPIO %d - Failed creating RMT Mutex.", pin);
return false;
}
}
// set Peripheral Manager deInit Callback
perimanSetBusDeinit(ESP32_BUS_TYPE_RMT_TX, _rmtDetachBus);
perimanSetBusDeinit(ESP32_BUS_TYPE_RMT_RX, _rmtDetachBus);
// check is pin is valid and in the right direction
if ((channel_direction == RMT_TX_MODE && !GPIO_IS_VALID_OUTPUT_GPIO(pin)) || (!GPIO_IS_VALID_GPIO(pin))) {
log_e("GPIO %d is not valid or can't be used for output in TX mode.", pin);
return false;
}
// validate the RMT ticks by the requested frequency
// Based on 80Mhz using a divider of 8 bits (calculated as 1..256)
if (frequency_Hz > 80000000 || frequency_Hz < 312500) {
log_e("GPIO %d - Bad RMT frequency resolution. Must be between 312.5KHz to 80MHz.", pin);
return false;
}
// Try to dettach any (Tx|Rx|Whatever) previous bus or just keep it as not attached
if (!perimanSetPinBus(pin, ESP32_BUS_TYPE_INIT, NULL)) {
log_w("GPIO %d - Can't detach previous peripheral.", pin);
return false;
}
// lock it
while (xSemaphoreTake(g_rmt_block_lock, portMAX_DELAY) != pdPASS) {}
// allocate the rmt bus object and sets all fields to NULL
rmt_bus_handle_t bus = (rmt_bus_handle_t)heap_caps_calloc(1, sizeof(struct rmt_obj_s), MALLOC_CAP_DEFAULT);
if (bus == NULL) {
log_e("GPIO %d - Bus Memory allocation fault.", pin);
goto Err;
}
// store the RMT Freq to check Filter and Idle valid values in the RMT API
bus->frequency_Hz = frequency_Hz;
// pulses with width smaller than min_ns will be ignored (as a glitch)
bus->signal_range_min_ns = 0; // disabled
// RMT stops reading if the input stays idle for longer than max_ns
bus->signal_range_max_ns = (1000000000 / frequency_Hz) * RMT_LL_MAX_IDLE_VALUE; // maximum possible
// creates the event group to control read_done and write_done
bus->rmt_events = xEventGroupCreate();
if (bus->rmt_events == NULL) {
log_e("GPIO %d - RMT Group Event allocation fault.", pin);
goto Err;
}
// Starting with Receive|Transmit DONE bits set, for allowing a new request from user
xEventGroupSetBits(bus->rmt_events, RMT_FLAG_RX_DONE | RMT_FLAG_TX_DONE);
// channel particular configuration
if (channel_direction == RMT_TX_MODE) {
// TX Channel
rmt_tx_channel_config_t tx_cfg;
tx_cfg.gpio_num = pin;
// CLK_APB for ESP32|S2|S3|C3 -- CLK_PLL_F80M for C6 -- CLK_XTAL for H2
tx_cfg.clk_src = RMT_CLK_SRC_DEFAULT;
tx_cfg.resolution_hz = frequency_Hz;
tx_cfg.mem_block_symbols = SOC_RMT_MEM_WORDS_PER_CHANNEL * mem_size;
tx_cfg.trans_queue_depth = 10; // maximum allowed
tx_cfg.flags.invert_out = 0;
tx_cfg.flags.with_dma = 0;
tx_cfg.flags.io_loop_back = 0;
tx_cfg.flags.io_od_mode = 0;
if (rmt_new_tx_channel(&tx_cfg, &bus->rmt_channel_h) != ESP_OK) {
log_e("GPIO %d - RMT TX Initialization error.", pin);
goto Err;
}
// set TX Callback
rmt_tx_event_callbacks_t cbs = { .on_trans_done = _rmt_tx_done_callback };
if (ESP_OK != rmt_tx_register_event_callbacks(bus->rmt_channel_h, &cbs, bus)) {
log_e("GPIO %d RMT - Error registering TX Callback.", pin);
goto Err;
}
} else {
// RX Channel
rmt_rx_channel_config_t rx_cfg;
rx_cfg.gpio_num = pin;
// CLK_APB for ESP32|S2|S3|C3 -- CLK_PLL_F80M for C6 -- CLK_XTAL for H2
rx_cfg.clk_src = RMT_CLK_SRC_DEFAULT;
rx_cfg.resolution_hz = frequency_Hz;
rx_cfg.mem_block_symbols = SOC_RMT_MEM_WORDS_PER_CHANNEL * mem_size;
rx_cfg.flags.invert_in = 0;
rx_cfg.flags.with_dma = 0;
rx_cfg.flags.io_loop_back = 0;
// try to allocate the RMT Channel
if (ESP_OK != rmt_new_rx_channel(&rx_cfg, &bus->rmt_channel_h)) {
log_e("GPIO %d RMT - RX Initialization error.", pin);
goto Err;
}
// set RX Callback
rmt_rx_event_callbacks_t cbs = { .on_recv_done = _rmt_rx_done_callback };
if (ESP_OK != rmt_rx_register_event_callbacks(bus->rmt_channel_h, &cbs, bus)) {
log_e("GPIO %d RMT - Error registering RX Callback.", pin);
goto Err;
}
}
// allocate memory for the RMT Copy encoder
rmt_copy_encoder_config_t copy_encoder_config = {};
if (rmt_new_copy_encoder(&copy_encoder_config, &bus->rmt_copy_encoder_h) != ESP_OK) {
log_e("GPIO %d - RMT Encoder Memory Allocation error.", pin);
goto Err;
}
// create each channel Mutex for multi thread operations
#if !CONFIG_DISABLE_HAL_LOCKS
bus->g_rmt_objlocks = xSemaphoreCreateMutex();
if (bus->g_rmt_objlocks == NULL) {
log_e("GPIO %d - Failed creating RMT Channel Mutex.", pin);
goto Err;
}
#endif
rmt_enable(bus->rmt_channel_h); // starts/enables the channel
// Finally, allocate Peripheral Manager RMT bus and associate it to its GPIO
peripheral_bus_type_t pinBusType =
channel_direction == RMT_TX_MODE ? ESP32_BUS_TYPE_RMT_TX : ESP32_BUS_TYPE_RMT_RX;
if (!perimanSetPinBus(pin, pinBusType, (void *) bus)) {
log_e("Can't allocate the GPIO %d in the Peripheral Manager.", pin);
goto Err;
}
// this delay is necessary when CPU frequency changes, but internal RMT setup is "old/wrong"
// The use case is related to the RMT_CPUFreq_Test example. The very first RMT Write
// goes in the wrong pace (frequency). The delay allows other IDF tasks to run to fix it.
if (loopTaskHandle != NULL) {
// it can only run when Arduino task has been already started.
delay(1);
} // prevent panic when rmtInit() is executed within an C++ object constructor
// release the mutex
xSemaphoreGive(g_rmt_block_lock);
return true;
Err:
// release LOCK and the RMT object
xSemaphoreGive(g_rmt_block_lock);
_rmtDetachBus((void *)bus);
return false;
}
#endif /* SOC_RMT_SUPPORTED */