2
2
3
3
#include < algorithm>
4
4
#include < cmath>
5
+ #include < mutex>
5
6
#include < optional>
6
7
#include < vector>
7
8
@@ -65,79 +66,20 @@ class Led : public BaseComponent {
65
66
* @brief Initialize the LEDC subsystem according to the configuration.
66
67
* @param config The configuration structure for the LEDC subsystem.
67
68
*/
68
- explicit Led (const Config &config) noexcept
69
- : BaseComponent(" Led" , config.log_level)
70
- , duty_resolution_(config.duty_resolution)
71
- , max_raw_duty_((uint32_t )(std::pow(2 , (int )duty_resolution_) - 1))
72
- , channels_(config.channels) {
73
-
74
- logger_.info (" Initializing timer" );
75
- ledc_timer_config_t ledc_timer;
76
- memset (&ledc_timer, 0 , sizeof (ledc_timer));
77
- ledc_timer.duty_resolution = duty_resolution_;
78
- ledc_timer.freq_hz = config.frequency_hz ;
79
- ledc_timer.speed_mode = config.speed_mode ;
80
- ledc_timer.timer_num = config.timer ;
81
- ledc_timer.clk_cfg = config.clock_config ;
82
- ledc_timer_config (&ledc_timer);
83
-
84
- logger_.info (" Initializing channels" );
85
- for (const auto &conf : channels_) {
86
- uint32_t actual_duty = std::clamp (conf.duty , 0 .0f , 100 .0f ) * max_raw_duty_ / 100 .0f ;
87
- ledc_channel_config_t channel_conf;
88
- memset (&channel_conf, 0 , sizeof (channel_conf));
89
- channel_conf.channel = conf.channel ;
90
- channel_conf.duty = actual_duty;
91
- channel_conf.gpio_num = conf.gpio ;
92
- channel_conf.speed_mode = conf.speed_mode ;
93
- channel_conf.hpoint = 0 ;
94
- channel_conf.timer_sel = conf.timer ;
95
- channel_conf.flags .output_invert = conf.output_invert ;
96
- ledc_channel_config (&channel_conf);
97
- }
98
-
99
- logger_.info (" Initializing the fade service" );
100
- static bool fade_service_installed = false ;
101
- if (!fade_service_installed) {
102
- auto install_fn = []() -> esp_err_t { return ledc_fade_func_install (0 ); };
103
- auto err = espp::task::run_on_core (install_fn, config.isr_core_id );
104
- if (err != ESP_OK) {
105
- logger_.error (" install ledc fade service failed {}" , esp_err_to_name (err));
106
- }
107
- fade_service_installed = err == ESP_OK;
108
- }
109
-
110
- ledc_cbs_t callbacks = {.fade_cb = &Led::cb_ledc_fade_end_event};
111
-
112
- // we associate each channel with its own semaphore so that they can be
113
- // faded / controlled independently.
114
- logger_.info (" Creating semaphores" );
115
- fade_semaphores_.resize (channels_.size ());
116
- for (auto &sem : fade_semaphores_) {
117
- sem = xSemaphoreCreateBinary ();
118
- // go ahead and give to the semaphores so the functions will work
119
- xSemaphoreGive (sem);
120
- }
121
- for (int i = 0 ; i < channels_.size (); i++) {
122
- ledc_cb_register (channels_[i].speed_mode , channels_[i].channel , &callbacks,
123
- (void *)fade_semaphores_[i]);
124
- }
125
- }
69
+ explicit Led (const Config &config) noexcept ;
126
70
127
71
/* *
128
72
* @brief Stop the LEDC subsystem and free memory.
129
73
*/
130
- ~Led () {
131
- // clean up the semaphores
132
- for (auto &sem : fade_semaphores_) {
133
- // take the semaphore (so that we don't delete it until no one is
134
- // blocked on it)
135
- xSemaphoreTake (sem, portMAX_DELAY);
136
- // and delete it
137
- vSemaphoreDelete (sem);
138
- }
139
- ledc_fade_func_uninstall ();
140
- }
74
+ ~Led ();
75
+
76
+ /* *
77
+ * @brief Uninstall the fade service. This should be called if you want to
78
+ * stop the fade service and free up the ISR.
79
+ * @note This function should only be called if you are sure that no other
80
+ * Led objects are using the fade service.
81
+ */
82
+ static void uninstall_isr ();
141
83
142
84
/* *
143
85
* @brief Can the LED settings can be changed for the channel? If this
@@ -146,30 +88,15 @@ class Led : public BaseComponent {
146
88
* @param channel The channel to check
147
89
* @return True if the channel settings can be changed, false otherwise
148
90
*/
149
- bool can_change (ledc_channel_t channel) {
150
- int index = get_channel_index (channel);
151
- if (index == -1 ) {
152
- return false ;
153
- }
154
- auto &sem = fade_semaphores_[index ];
155
- return uxSemaphoreGetCount (sem) == 1 ;
156
- }
91
+ bool can_change (ledc_channel_t channel);
157
92
158
93
/* *
159
94
* @brief Get the current duty cycle this channel has.
160
95
* @param channel The channel in question
161
96
* @return The duty percentage [0.0f, 100.0f] if the channel is managed,
162
97
* std::nullopt otherwise
163
98
*/
164
- std::optional<float > get_duty (ledc_channel_t channel) const {
165
- int index = get_channel_index (channel);
166
- if (index == -1 ) {
167
- return {};
168
- }
169
- const auto &conf = channels_[index ];
170
- auto raw_duty = ledc_get_duty (conf.speed_mode , conf.channel );
171
- return (float )raw_duty / (float )max_raw_duty_ * 100 .0f ;
172
- }
99
+ std::optional<float > get_duty (ledc_channel_t channel) const ;
173
100
174
101
/* *
175
102
* @brief Set the duty cycle for this channel.
@@ -178,21 +105,7 @@ class Led : public BaseComponent {
178
105
* @param channel The channel to set the duty cycle for.
179
106
* @param duty_percent The new duty percentage, [0.0, 100.0].
180
107
*/
181
- void set_duty (ledc_channel_t channel, float duty_percent) {
182
- int index = get_channel_index (channel);
183
- if (index == -1 ) {
184
- return ;
185
- }
186
- auto conf = channels_[index ];
187
- auto &sem = fade_semaphores_[index ];
188
- // ensure that it's not fading if it is
189
- xSemaphoreTake (sem, portMAX_DELAY);
190
- uint32_t actual_duty = std::clamp (duty_percent, 0 .0f , 100 .0f ) * max_raw_duty_ / 100 .0f ;
191
- ledc_set_duty (conf.speed_mode , conf.channel , actual_duty);
192
- ledc_update_duty (conf.speed_mode , conf.channel );
193
- // make sure others can set this channel now as well
194
- xSemaphoreGive (sem);
195
- }
108
+ void set_duty (ledc_channel_t channel, float duty_percent);
196
109
197
110
/* *
198
111
* @brief Set the duty cycle for this channel, fading from the current duty
@@ -203,23 +116,12 @@ class Led : public BaseComponent {
203
116
* @param duty_percent The new duty percentage to fade to, [0.0, 100.0].
204
117
* @param fade_time_ms The number of milliseconds for which to fade.
205
118
*/
206
- void set_fade_with_time (ledc_channel_t channel, float duty_percent, uint32_t fade_time_ms) {
207
- int index = get_channel_index (channel);
208
- if (index == -1 ) {
209
- return ;
210
- }
211
- auto conf = channels_[index ];
212
- auto &sem = fade_semaphores_[index ];
213
- // ensure that it's not fading if it is
214
- xSemaphoreTake (sem, portMAX_DELAY);
215
- uint32_t actual_duty = std::clamp (duty_percent, 0 .0f , 100 .0f ) * max_raw_duty_ / 100 .0f ;
216
- ledc_set_fade_with_time (conf.speed_mode , conf.channel , actual_duty, fade_time_ms);
217
- ledc_fade_start (conf.speed_mode , conf.channel , LEDC_FADE_NO_WAIT);
218
- // NOTE: we don't give the semaphore back here because that is the job of
219
- // the ISR
220
- }
119
+ void set_fade_with_time (ledc_channel_t channel, float duty_percent, uint32_t fade_time_ms);
221
120
222
121
protected:
122
+ static std::mutex fade_service_mutex;
123
+ static bool fade_service_installed;
124
+
223
125
/* *
224
126
* @brief Get the index of channel in channels_, -1 if not found.
225
127
* @note We implement this instead of using std::find because we cannot use
@@ -228,30 +130,14 @@ class Led : public BaseComponent {
228
130
* @param channel Channel to find.
229
131
* @return -1 if not found, index of channel if found.
230
132
*/
231
- int get_channel_index (ledc_channel_t channel) const {
232
- for (int i = 0 ; i < channels_.size (); i++) {
233
- if (channels_[i].channel == channel) {
234
- return i;
235
- }
236
- }
237
- return -1 ;
238
- }
133
+ int get_channel_index (ledc_channel_t channel) const ;
239
134
240
135
/* *
241
136
* This callback function will be called when fade operation has ended
242
137
* Use callback only if you are aware it is being called inside an ISR
243
138
* Otherwise, you can use a semaphore to unblock tasks
244
139
*/
245
- static bool IRAM_ATTR cb_ledc_fade_end_event (const ledc_cb_param_t *param, void *user_arg) {
246
- portBASE_TYPE taskAwoken = pdFALSE;
247
-
248
- if (param->event == LEDC_FADE_END_EVT) {
249
- SemaphoreHandle_t sem = (SemaphoreHandle_t)user_arg;
250
- xSemaphoreGiveFromISR (sem, &taskAwoken);
251
- }
252
-
253
- return (taskAwoken == pdTRUE);
254
- }
140
+ static bool cb_ledc_fade_end_event (const ledc_cb_param_t *param, void *user_arg);
255
141
256
142
ledc_timer_bit_t duty_resolution_;
257
143
uint32_t max_raw_duty_;
0 commit comments