1 // Copyright 2020 Espressif Systems (Shanghai) PTE LTD
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 #include <stdlib.h>
15 #include <string.h>
16 #include <sys/cdefs.h>
17 #include "esp_log.h"
18 #include "esp_attr.h"
19 #include "esp_compiler.h"
20 #include "driver/rmt.h"
21 #include "musical_buzzer.h"
22
23 static const char *TAG = "buzzer_rmt";
24
25 #define BUZZER_CHECK(a, msg, tag, ret, ...) \
26 do { \
27 if (unlikely(!(a))) { \
28 ESP_LOGE(TAG, "%s(%d): " msg, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
29 ret_code = ret; \
30 goto tag; \
31 } \
32 } while (0)
33
34 typedef struct {
35 musical_buzzer_t parent;
36 rmt_channel_t channel;
37 uint32_t counter_clk_hz;
38 const musical_buzzer_notation_t *notation;
39 uint32_t notation_length;
40 uint32_t next_notation_index;
41 } rmt_buzzer_t;
42
update_notation_freq_duration(rmt_buzzer_t * rmt_buzzer)43 static IRAM_ATTR rmt_item32_t update_notation_freq_duration(rmt_buzzer_t *rmt_buzzer)
44 {
45 rmt_item32_t notation_code = {.level0 = 1, .duration0 = 1, .level1 = 0, .duration1 = 1};
46 const musical_buzzer_notation_t *notation = &rmt_buzzer->notation[rmt_buzzer->next_notation_index];
47
48 // convert frequency to RMT item format
49 notation_code.duration0 = rmt_buzzer->counter_clk_hz / notation->note_freq_hz / 2;
50 notation_code.duration1 = notation_code.duration0;
51 // convert duration to RMT loop count
52 rmt_set_tx_loop_count(rmt_buzzer->channel, notation->note_duration_ms * notation->note_freq_hz / 1000);
53
54 rmt_buzzer->next_notation_index++;
55 return notation_code;
56 }
57
buzzer_play(musical_buzzer_t * buzzer,const musical_buzzer_notation_t * notation,uint32_t notation_length)58 static esp_err_t buzzer_play(musical_buzzer_t *buzzer, const musical_buzzer_notation_t *notation, uint32_t notation_length)
59 {
60 esp_err_t ret_code = ESP_OK;
61 rmt_buzzer_t *rmt_buzzer = __containerof(buzzer, rmt_buzzer_t, parent);
62
63 BUZZER_CHECK(notation, "notation can't be null", err, ESP_ERR_INVALID_ARG);
64
65 // update notation with the new one
66 rmt_buzzer->notation = notation;
67 rmt_buzzer->next_notation_index = 0;
68 rmt_buzzer->notation_length = notation_length;
69
70 rmt_item32_t notation_code = update_notation_freq_duration(rmt_buzzer);
71 // start TX
72 rmt_write_items(rmt_buzzer->channel, ¬ation_code, 1, false);
73 err:
74 return ret_code;
75 }
76
buzzer_stop(musical_buzzer_t * buzzer)77 static esp_err_t buzzer_stop(musical_buzzer_t *buzzer)
78 {
79 rmt_buzzer_t *rmt_buzzer = __containerof(buzzer, rmt_buzzer_t, parent);
80 rmt_tx_stop(rmt_buzzer->channel);
81 return ESP_OK;
82 }
83
buzzer_del(musical_buzzer_t * buzzer)84 static esp_err_t buzzer_del(musical_buzzer_t *buzzer)
85 {
86 rmt_buzzer_t *rmt_buzzer = __containerof(buzzer, rmt_buzzer_t, parent);
87 free(rmt_buzzer);
88 return ESP_OK;
89 }
90
rmt_tx_loop_end(rmt_channel_t channel,void * args)91 static void rmt_tx_loop_end(rmt_channel_t channel, void *args)
92 {
93 rmt_buzzer_t *rmt_buzzer = (rmt_buzzer_t *)args;
94
95 // stop it firstly, RMT TX engine won't stop automatically in loop mode
96 rmt_tx_stop(rmt_buzzer->channel);
97
98 // update rmt loop freq and duration if the notation doesn't reach the end
99 if (rmt_buzzer->next_notation_index < rmt_buzzer->notation_length) {
100 rmt_item32_t notation_code = update_notation_freq_duration(rmt_buzzer);
101 // issue a new TX transaction
102 rmt_write_items(rmt_buzzer->channel, ¬ation_code, 1, false);
103 }
104 }
105
musical_buzzer_create_rmt(const musical_buzzer_config_t * config,musical_buzzer_t ** ret_handle)106 esp_err_t musical_buzzer_create_rmt(const musical_buzzer_config_t *config, musical_buzzer_t **ret_handle)
107 {
108 esp_err_t ret_code = ESP_OK;
109 rmt_buzzer_t *rmt_buzzer = NULL;
110 BUZZER_CHECK(config, "configuration can't be null", err, ESP_ERR_INVALID_ARG);
111 BUZZER_CHECK(ret_handle, "can't assign handle to null", err, ESP_ERR_INVALID_ARG);
112
113 rmt_buzzer = calloc(1, sizeof(rmt_buzzer_t));
114 BUZZER_CHECK(rmt_buzzer, "allocate context memory failed", err, ESP_ERR_NO_MEM);
115
116 rmt_buzzer->channel = (rmt_channel_t)config->dev;
117
118 rmt_get_counter_clock(rmt_buzzer->channel, &rmt_buzzer->counter_clk_hz);
119
120 // register tx end callback function, which got invoked when tx loop comes to the end
121 rmt_register_tx_end_callback(rmt_tx_loop_end, rmt_buzzer);
122
123 rmt_buzzer->parent.del = buzzer_del;
124 rmt_buzzer->parent.play = buzzer_play;
125 rmt_buzzer->parent.stop = buzzer_stop;
126
127 *ret_handle = &(rmt_buzzer->parent);
128 return ESP_OK;
129
130 err:
131 if (rmt_buzzer) {
132 free(rmt_buzzer);
133 }
134 return ret_code;
135 }
136