Merge branch 'feature/musical_buzzer' into 'master'

RMT example: musical buzzer

See merge request espressif/esp-idf!10346
This commit is contained in:
Michael (XIAO Xufeng)
2021-01-28 01:04:24 +08:00
11 changed files with 407 additions and 3 deletions

View File

@@ -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); esp_err_t rmt_remove_channel_from_group(rmt_channel_t channel);
#endif #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. * @brief Reset RMT TX/RX memory index.
* *

View File

@@ -1365,3 +1365,14 @@ esp_err_t rmt_memory_rw_rst(rmt_channel_t channel)
return ESP_OK; return ESP_OK;
} }
#endif #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

View File

@@ -293,9 +293,10 @@ If the RMT driver has been installed with :cpp:func:`rmt_driver_install` for som
Application Examples Application Examples
-------------------- --------------------
* A simple RMT TX example: :example:`peripherals/rmt/morse_code`. * Using RMT to send morse code: :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 drive RGB LED strip: :example:`peripherals/rmt/led_strip`.
* NEC remote control TX and RX example: :example:`peripherals/rmt/ir_protocols`. * NEC remote control TX and RX example: :example:`peripherals/rmt/ir_protocols`.
* Musical buzzer example: :example:`peripherals/rmt/musical_buzzer`.
API Reference API Reference

View File

@@ -67,7 +67,7 @@ static void rmt_tx_init(void)
ESP_ERROR_CHECK(rmt_driver_install(config.channel, 0, 0)); ESP_ERROR_CHECK(rmt_driver_install(config.channel, 0, 0));
} }
void app_main(void *ignore) void app_main(void)
{ {
ESP_LOGI(TAG, "Configuring transmitter"); ESP_LOGI(TAG, "Configuring transmitter");
rmt_tx_init(); rmt_tx_init();

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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])));
}