Browse Source

rmt: add musical buzzer example

morris 5 năm trước cách đây
mục cha
commit
1969313b6f

+ 3 - 2
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

+ 1 - 1
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();

+ 6 - 0
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)

+ 58 - 0
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.

+ 7 - 0
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 "")

+ 108 - 0
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

+ 135 - 0
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 <stdlib.h>
+#include <string.h>
+#include <sys/cdefs.h>
+#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, &notation_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, &notation_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;
+}

+ 2 - 0
examples/peripherals/rmt/musical_buzzer/main/CMakeLists.txt

@@ -0,0 +1,2 @@
+idf_component_register(SRCS "musical_buzzer_example_main.c"
+                       INCLUDE_DIRS ".")

+ 63 - 0
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])));
+}