From 467d44970e7079203fe91a82421273fb56f529ee Mon Sep 17 00:00:00 2001 From: morris Date: Wed, 9 Sep 2020 15:28:14 +0800 Subject: [PATCH 1/2] rmt: support setting loop count at runtime --- components/driver/include/driver/rmt.h | 13 +++++++++++++ components/driver/rmt.c | 11 +++++++++++ 2 files changed, 24 insertions(+) diff --git a/components/driver/include/driver/rmt.h b/components/driver/include/driver/rmt.h index 210bddc073..930aac01ce 100644 --- a/components/driver/include/driver/rmt.h +++ b/components/driver/include/driver/rmt.h @@ -859,6 +859,19 @@ esp_err_t rmt_add_channel_to_group(rmt_channel_t channel); esp_err_t rmt_remove_channel_from_group(rmt_channel_t channel); #endif +#if SOC_RMT_SUPPORT_TX_LOOP_COUNT +/** + * @brief Set loop count for RMT TX channel + * + * @param channel RMT channel + * @param count loop count + * @return + * - ESP_ERR_INVALID_ARG Parameter error + * - ESP_OK Success + */ +esp_err_t rmt_set_tx_loop_count(rmt_channel_t channel, uint32_t count); +#endif + /** * @brief Reset RMT TX/RX memory index. * diff --git a/components/driver/rmt.c b/components/driver/rmt.c index 1b266a3249..74b5e3116b 100644 --- a/components/driver/rmt.c +++ b/components/driver/rmt.c @@ -1365,3 +1365,14 @@ esp_err_t rmt_memory_rw_rst(rmt_channel_t channel) return ESP_OK; } #endif + +#if SOC_RMT_SUPPORT_TX_LOOP_COUNT +esp_err_t rmt_set_tx_loop_count(rmt_channel_t channel, uint32_t count) +{ + RMT_CHECK(RMT_IS_TX_CHANNEL(channel), RMT_CHANNEL_ERROR_STR, ESP_ERR_INVALID_ARG); + RMT_ENTER_CRITICAL(); + rmt_ll_tx_set_loop_count(rmt_contex.hal.regs, channel, count); + RMT_EXIT_CRITICAL(); + return ESP_OK; +} +#endif From 1969313b6f8bdc9cbf17519860bdbcb3a45dfbe4 Mon Sep 17 00:00:00 2001 From: morris Date: Tue, 8 Sep 2020 10:28:38 +0800 Subject: [PATCH 2/2] rmt: add musical buzzer example --- docs/en/api-reference/peripherals/rmt.rst | 5 +- .../rmt/morse_code/main/morse_code_main.c | 2 +- .../rmt/musical_buzzer/CMakeLists.txt | 6 + .../peripherals/rmt/musical_buzzer/README.md | 58 ++++++++ .../components/musical_buzzer/CMakeLists.txt | 7 + .../musical_buzzer/include/musical_buzzer.h | 108 ++++++++++++++ .../musical_buzzer/src/musical_buzzer_rmt.c | 135 ++++++++++++++++++ .../rmt/musical_buzzer/main/CMakeLists.txt | 2 + .../main/musical_buzzer_example_main.c | 63 ++++++++ 9 files changed, 383 insertions(+), 3 deletions(-) create mode 100644 examples/peripherals/rmt/musical_buzzer/CMakeLists.txt create mode 100644 examples/peripherals/rmt/musical_buzzer/README.md create mode 100644 examples/peripherals/rmt/musical_buzzer/components/musical_buzzer/CMakeLists.txt create mode 100644 examples/peripherals/rmt/musical_buzzer/components/musical_buzzer/include/musical_buzzer.h create mode 100644 examples/peripherals/rmt/musical_buzzer/components/musical_buzzer/src/musical_buzzer_rmt.c create mode 100644 examples/peripherals/rmt/musical_buzzer/main/CMakeLists.txt create mode 100644 examples/peripherals/rmt/musical_buzzer/main/musical_buzzer_example_main.c diff --git a/docs/en/api-reference/peripherals/rmt.rst b/docs/en/api-reference/peripherals/rmt.rst index 4540bcf9c8..cb85379495 100644 --- a/docs/en/api-reference/peripherals/rmt.rst +++ b/docs/en/api-reference/peripherals/rmt.rst @@ -287,9 +287,10 @@ If the RMT driver has been installed with :cpp:func:`rmt_driver_install` for som Application Examples -------------------- -* A simple RMT TX example: :example:`peripherals/rmt/morse_code`. -* Another RMT TX example, specific to drive a common RGB LED strip: :example:`peripherals/rmt/led_strip`. +* Using RMT to send morse code: :example:`peripherals/rmt/morse_code`. +* Using RMT to drive RGB LED strip: :example:`peripherals/rmt/led_strip`. * NEC remote control TX and RX example: :example:`peripherals/rmt/ir_protocols`. +* Musical buzzer example: :example:`peripherals/rmt/musical_buzzer`. API Reference diff --git a/examples/peripherals/rmt/morse_code/main/morse_code_main.c b/examples/peripherals/rmt/morse_code/main/morse_code_main.c index 3f1bec9720..d3066c8e54 100644 --- a/examples/peripherals/rmt/morse_code/main/morse_code_main.c +++ b/examples/peripherals/rmt/morse_code/main/morse_code_main.c @@ -67,7 +67,7 @@ static void rmt_tx_init(void) ESP_ERROR_CHECK(rmt_driver_install(config.channel, 0, 0)); } -void app_main(void *ignore) +void app_main(void) { ESP_LOGI(TAG, "Configuring transmitter"); rmt_tx_init(); diff --git a/examples/peripherals/rmt/musical_buzzer/CMakeLists.txt b/examples/peripherals/rmt/musical_buzzer/CMakeLists.txt new file mode 100644 index 0000000000..6a278361a9 --- /dev/null +++ b/examples/peripherals/rmt/musical_buzzer/CMakeLists.txt @@ -0,0 +1,6 @@ +# The following lines of boilerplate have to be in your project's CMakeLists +# in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(musical_buzzer) diff --git a/examples/peripherals/rmt/musical_buzzer/README.md b/examples/peripherals/rmt/musical_buzzer/README.md new file mode 100644 index 0000000000..32e3c551e8 --- /dev/null +++ b/examples/peripherals/rmt/musical_buzzer/README.md @@ -0,0 +1,58 @@ +| Supported Targets | ESP32-S2 | ESP32-C3 | +| ----------------- | -------- | -------- | + +# RMT Transmit Loop Example -- Musical Buzzer + +(See the README.md file in the upper level 'examples' directory for more information about examples.) + +RMT peripheral can send customized RMT items in a loop, which means we can use it to generate a configurable length of periodic signal. + +This example will show how to drive a passive buzzer to play a simple music, based on the RMT loop feature. + +## How to Use Example + +### Hardware Required + +* A development board with ESP32-S2 SoC +* A USB cable for Power supply and programming +* A passive buzzer + +Connection : + +``` +VCC +--------------+ + | /+ + +++ | + | | | Passive Buzzer + +++ | + | \+ + | + + | + +<----+ +GPIO +--------+ + +-----+ + + | + | +GND +--------------+ +``` + +### Build and Flash + +Run `idf.py -p PORT flash monitor` to build, flash and monitor the project. + +(To exit the serial monitor, type ``Ctrl-]``.) + +See the [Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html) for full steps to configure and use ESP-IDF to build projects. + + +## Example Output + +``` +I (325) example: Playing Beethoven's Ode to joy +``` + +After you seeing this log, you should hear the music from your buzzer. You can also play other music by updating the `notation` array in the `musical_buzzer_example_main.c`. + +## Troubleshooting + +For any technical queries, please open an [issue] (https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you soon. diff --git a/examples/peripherals/rmt/musical_buzzer/components/musical_buzzer/CMakeLists.txt b/examples/peripherals/rmt/musical_buzzer/components/musical_buzzer/CMakeLists.txt new file mode 100644 index 0000000000..3bf7b77c25 --- /dev/null +++ b/examples/peripherals/rmt/musical_buzzer/components/musical_buzzer/CMakeLists.txt @@ -0,0 +1,7 @@ +set(component_srcs "src/musical_buzzer_rmt.c") + +idf_component_register(SRCS "${component_srcs}" + INCLUDE_DIRS "include" + PRIV_INCLUDE_DIRS "" + PRIV_REQUIRES "driver" + REQUIRES "") diff --git a/examples/peripherals/rmt/musical_buzzer/components/musical_buzzer/include/musical_buzzer.h b/examples/peripherals/rmt/musical_buzzer/components/musical_buzzer/include/musical_buzzer.h new file mode 100644 index 0000000000..c0a3892caa --- /dev/null +++ b/examples/peripherals/rmt/musical_buzzer/components/musical_buzzer/include/musical_buzzer.h @@ -0,0 +1,108 @@ +// Copyright 2020 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 + +#include "esp_err.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Type of musical buzzer interface + * + */ +typedef struct musical_buzzer_t musical_buzzer_t; + +/** + * @brief Type of musical buzzer underlying device + * + */ +typedef void *musical_buzzer_dev_t; + +/** + * @brief Type of musical buzzer notation + * + */ +typedef struct { + uint32_t note_freq_hz; /*!< Note frequency, in Hz */ + uint32_t note_duration_ms; /*!< Note duration, in ms */ +} musical_buzzer_notation_t; + +/** + * @brief Declaration of musical buzzer interface + * + */ +struct musical_buzzer_t { + /** + * @brief Start to play the given notation + * + * @param buzzer musical buzzer handle + * @param notation music notation + * @param notation_len notation length + * @return + * - ESP_OK: Start playing notation successfully + * - ESP_ERR_INVALID_ARG: wrong parameter + */ + esp_err_t (*play)(musical_buzzer_t *buzzer, const musical_buzzer_notation_t *notation, uint32_t notation_len); + + /** + * @brief Stop playing + * + * @param buzzer musical buzzer handle + * @return + * - ESP_OK: Stop playing successfully + */ + esp_err_t (*stop)(musical_buzzer_t *buzzer); + + /** + * @brief Free memory used by musical buzzer + * + * @param buzzer musical buzzer handle + * @return + * - ESP_OK: Recycle memory successfully + */ + esp_err_t (*del)(musical_buzzer_t *buzzer); +}; + +typedef struct { + musical_buzzer_dev_t dev; /*!< Musical buzzer device (e.g. RMT channel, PWM channel, etc) */ + int flags; /*!< Extra flags */ +} musical_buzzer_config_t; + +/** + * @brief Default musical buzzer configuration + * + */ +#define MUSICAL_BUZZER_DEFAULT_CONFIG(dev_hdl) \ + { \ + .dev = dev_hdl, \ + .flags = 0, \ + } + +/** + * @brief Create musical buzzer instance based on RMT driver + * + * @param config musical buzzer configuration + * @param[out] ret_handle returned handle of musical buzzer instance + * @return + * - ESP_OK: create musical buzzer instance successfully + * - ESP_ERR_INVALID_ARG: wrong parameter + * - ESP_ERR_NO_MEM: no memory to allocate instance + */ +esp_err_t musical_buzzer_create_rmt(const musical_buzzer_config_t *config, musical_buzzer_t **ret_handle); + +#ifdef __cplusplus +} +#endif diff --git a/examples/peripherals/rmt/musical_buzzer/components/musical_buzzer/src/musical_buzzer_rmt.c b/examples/peripherals/rmt/musical_buzzer/components/musical_buzzer/src/musical_buzzer_rmt.c new file mode 100644 index 0000000000..e85e350ba6 --- /dev/null +++ b/examples/peripherals/rmt/musical_buzzer/components/musical_buzzer/src/musical_buzzer_rmt.c @@ -0,0 +1,135 @@ +// Copyright 2020 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 +#include +#include +#include "esp_log.h" +#include "esp_attr.h" +#include "esp_compiler.h" +#include "driver/rmt.h" +#include "musical_buzzer.h" + +static const char *TAG = "buzzer_rmt"; + +#define BUZZER_CHECK(a, msg, tag, ret, ...) \ + do { \ + if (unlikely(!(a))) { \ + ESP_LOGE(TAG, "%s(%d): " msg, __FUNCTION__, __LINE__, ##__VA_ARGS__); \ + ret_code = ret; \ + goto tag; \ + } \ + } while (0) + +typedef struct { + musical_buzzer_t parent; + rmt_channel_t channel; + uint32_t counter_clk_hz; + const musical_buzzer_notation_t *notation; + uint32_t notation_length; + uint32_t next_notation_index; +} rmt_buzzer_t; + +static IRAM_ATTR rmt_item32_t update_notation_freq_duration(rmt_buzzer_t *rmt_buzzer) +{ + rmt_item32_t notation_code = {.level0 = 1, .duration0 = 1, .level1 = 0, .duration1 = 1}; + const musical_buzzer_notation_t *notation = &rmt_buzzer->notation[rmt_buzzer->next_notation_index]; + + // convert frequency to RMT item format + notation_code.duration0 = rmt_buzzer->counter_clk_hz / notation->note_freq_hz / 2; + notation_code.duration1 = notation_code.duration0; + // convert duration to RMT loop count + rmt_set_tx_loop_count(rmt_buzzer->channel, notation->note_duration_ms * notation->note_freq_hz / 1000); + + rmt_buzzer->next_notation_index++; + return notation_code; +} + +static esp_err_t buzzer_play(musical_buzzer_t *buzzer, const musical_buzzer_notation_t *notation, uint32_t notation_length) +{ + esp_err_t ret_code = ESP_OK; + rmt_buzzer_t *rmt_buzzer = __containerof(buzzer, rmt_buzzer_t, parent); + + BUZZER_CHECK(notation, "notation can't be null", err, ESP_ERR_INVALID_ARG); + + // update notation with the new one + rmt_buzzer->notation = notation; + rmt_buzzer->next_notation_index = 0; + rmt_buzzer->notation_length = notation_length; + + rmt_item32_t notation_code = update_notation_freq_duration(rmt_buzzer); + // start TX + rmt_write_items(rmt_buzzer->channel, ¬ation_code, 1, false); +err: + return ret_code; +} + +static esp_err_t buzzer_stop(musical_buzzer_t *buzzer) +{ + rmt_buzzer_t *rmt_buzzer = __containerof(buzzer, rmt_buzzer_t, parent); + rmt_tx_stop(rmt_buzzer->channel); + return ESP_OK; +} + +static esp_err_t buzzer_del(musical_buzzer_t *buzzer) +{ + rmt_buzzer_t *rmt_buzzer = __containerof(buzzer, rmt_buzzer_t, parent); + free(rmt_buzzer); + return ESP_OK; +} + +static void rmt_tx_loop_end(rmt_channel_t channel, void *args) +{ + rmt_buzzer_t *rmt_buzzer = (rmt_buzzer_t *)args; + + // stop it firstly, RMT TX engine won't stop automatically in loop mode + rmt_tx_stop(rmt_buzzer->channel); + + // update rmt loop freq and duration if the notation doesn't reach the end + if (rmt_buzzer->next_notation_index < rmt_buzzer->notation_length) { + rmt_item32_t notation_code = update_notation_freq_duration(rmt_buzzer); + // issue a new TX transaction + rmt_write_items(rmt_buzzer->channel, ¬ation_code, 1, false); + } +} + +esp_err_t musical_buzzer_create_rmt(const musical_buzzer_config_t *config, musical_buzzer_t **ret_handle) +{ + esp_err_t ret_code = ESP_OK; + rmt_buzzer_t *rmt_buzzer = NULL; + BUZZER_CHECK(config, "configuration can't be null", err, ESP_ERR_INVALID_ARG); + BUZZER_CHECK(ret_handle, "can't assign handle to null", err, ESP_ERR_INVALID_ARG); + + rmt_buzzer = calloc(1, sizeof(rmt_buzzer_t)); + BUZZER_CHECK(rmt_buzzer, "allocate context memory failed", err, ESP_ERR_NO_MEM); + + rmt_buzzer->channel = (rmt_channel_t)config->dev; + + rmt_get_counter_clock(rmt_buzzer->channel, &rmt_buzzer->counter_clk_hz); + + // register tx end callback function, which got invoked when tx loop comes to the end + rmt_register_tx_end_callback(rmt_tx_loop_end, rmt_buzzer); + + rmt_buzzer->parent.del = buzzer_del; + rmt_buzzer->parent.play = buzzer_play; + rmt_buzzer->parent.stop = buzzer_stop; + + *ret_handle = &(rmt_buzzer->parent); + return ESP_OK; + +err: + if (rmt_buzzer) { + free(rmt_buzzer); + } + return ret_code; +} diff --git a/examples/peripherals/rmt/musical_buzzer/main/CMakeLists.txt b/examples/peripherals/rmt/musical_buzzer/main/CMakeLists.txt new file mode 100644 index 0000000000..26d4896b85 --- /dev/null +++ b/examples/peripherals/rmt/musical_buzzer/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "musical_buzzer_example_main.c" + INCLUDE_DIRS ".") diff --git a/examples/peripherals/rmt/musical_buzzer/main/musical_buzzer_example_main.c b/examples/peripherals/rmt/musical_buzzer/main/musical_buzzer_example_main.c new file mode 100644 index 0000000000..b77d5079e0 --- /dev/null +++ b/examples/peripherals/rmt/musical_buzzer/main/musical_buzzer_example_main.c @@ -0,0 +1,63 @@ +/* RMT example -- Musical Buzzer + + 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 "esp_log.h" +#include "driver/rmt.h" +#include "musical_buzzer.h" + +static const char *TAG = "example"; + +#define RMT_TX_CHANNEL RMT_CHANNEL_0 +#define RMT_TX_GPIO_NUM (4) + +/** + * @brief Musical Notation: Beethoven's Ode to joy + * + */ +static const musical_buzzer_notation_t notation[] = { + {740, 400}, {740, 600}, {784, 400}, {880, 400}, + {880, 400}, {784, 400}, {740, 400}, {659, 400}, + {587, 400}, {587, 400}, {659, 400}, {740, 400}, + {740, 400}, {740, 200}, {659, 200}, {659, 800}, + + {740, 400}, {740, 600}, {784, 400}, {880, 400}, + {880, 400}, {784, 400}, {740, 400}, {659, 400}, + {587, 400}, {587, 400}, {659, 400}, {740, 400}, + {659, 400}, {659, 200}, {587, 200}, {587, 800}, + + {659, 400}, {659, 400}, {740, 400}, {587, 400}, + {659, 400}, {740, 200}, {784, 200}, {740, 400}, {587, 400}, + {659, 400}, {740, 200}, {784, 200}, {740, 400}, {659, 400}, + {587, 400}, {659, 400}, {440, 400}, {440, 400}, + + {740, 400}, {740, 600}, {784, 400}, {880, 400}, + {880, 400}, {784, 400}, {740, 400}, {659, 400}, + {587, 400}, {587, 400}, {659, 400}, {740, 400}, + {659, 400}, {659, 200}, {587, 200}, {587, 800}, +}; + +void app_main(void) +{ + // Apply default RMT configuration + rmt_config_t dev_config = RMT_DEFAULT_CONFIG_TX(RMT_TX_GPIO_NUM, RMT_TX_CHANNEL); + dev_config.tx_config.loop_en = true; // Enable loop mode + + // Install RMT driver + ESP_ERROR_CHECK(rmt_config(&dev_config)); + ESP_ERROR_CHECK(rmt_driver_install(RMT_TX_CHANNEL, 0, 0)); + + // This example take the RMT channel number as the device handle + musical_buzzer_config_t buzzer_config = MUSICAL_BUZZER_DEFAULT_CONFIG((musical_buzzer_dev_t)RMT_TX_CHANNEL); + musical_buzzer_t *buzzer = NULL; + // Install buzzer driver + ESP_ERROR_CHECK(musical_buzzer_create_rmt(&buzzer_config, &buzzer)); + + ESP_LOGI(TAG, "Playing Beethoven's Ode to joy"); + + ESP_ERROR_CHECK(buzzer->play(buzzer, notation, sizeof(notation) / sizeof(notation[0]))); +}