Skip to content

Commit d6934a5

Browse files
authored
Implement LEDC based on ESP-IDF API (#6045)
This PR is refactoring of LEDC HAL in order to use IDF instead of current Register manipulation approach. Fixing duty -> if all bits in resolution are set -> FULL ON
1 parent 6b90627 commit d6934a5

File tree

1 file changed

+87
-225
lines changed

1 file changed

+87
-225
lines changed

Diff for: cores/esp32/esp32-hal-ledc.c

+87-225
Original file line numberDiff line numberDiff line change
@@ -13,46 +13,25 @@
1313
// limitations under the License.
1414

1515
#include "esp32-hal.h"
16-
#include "freertos/FreeRTOS.h"
17-
#include "freertos/task.h"
18-
#include "freertos/semphr.h"
19-
#include "esp32-hal-matrix.h"
2016
#include "soc/soc_caps.h"
21-
#include "soc/ledc_reg.h"
22-
#include "soc/ledc_struct.h"
23-
#include "driver/periph_ctrl.h"
17+
#include "driver/ledc.h"
2418

25-
#include "esp_system.h"
26-
#ifdef ESP_IDF_VERSION_MAJOR // IDF 4+
27-
#if CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4
28-
#include "soc/dport_reg.h"
29-
#include "esp32/rom/ets_sys.h"
30-
#define LAST_CHAN (15)
31-
#elif CONFIG_IDF_TARGET_ESP32S2
32-
#include "soc/dport_reg.h"
33-
#include "esp32s2/rom/ets_sys.h"
34-
#define LAST_CHAN (7)
35-
#define LEDC_DIV_NUM_HSTIMER0_V LEDC_CLK_DIV_LSTIMER0_V
36-
#elif CONFIG_IDF_TARGET_ESP32C3
37-
#include "esp32c3/rom/ets_sys.h"
38-
#define LAST_CHAN (7)
39-
#define LEDC_DIV_NUM_HSTIMER0_V LEDC_CLK_DIV_LSTIMER0_V
40-
#else
41-
#error Target CONFIG_IDF_TARGET is not supported
42-
#endif
43-
#else // ESP32 Before IDF 4.0
44-
#include "rom/ets_sys.h"
19+
#ifdef SOC_LEDC_SUPPORT_HS_MODE
20+
#define LEDC_CHANNELS (SOC_LEDC_CHANNEL_NUM<<1)
21+
#else
22+
#define LEDC_CHANNELS (SOC_LEDC_CHANNEL_NUM)
4523
#endif
4624

47-
#if CONFIG_DISABLE_HAL_LOCKS
48-
#define LEDC_MUTEX_LOCK()
49-
#define LEDC_MUTEX_UNLOCK()
25+
//Use XTAL clock if possible to avoid timer frequency error when setting APB clock < 80 Mhz
26+
//Need to be fixed in ESP-IDF
27+
#ifdef SOC_LEDC_SUPPORT_XTAL_CLOCK
28+
#define LEDC_DEFAULT_CLK LEDC_USE_XTAL_CLK
5029
#else
51-
#define LEDC_MUTEX_LOCK() do {} while (xSemaphoreTake(_ledc_sys_lock, portMAX_DELAY) != pdPASS)
52-
#define LEDC_MUTEX_UNLOCK() xSemaphoreGive(_ledc_sys_lock)
53-
xSemaphoreHandle _ledc_sys_lock = NULL;
30+
#define LEDC_DEFAULT_CLK LEDC_AUTO_CLK
5431
#endif
5532

33+
#define LEDC_MAX_BIT_WIDTH SOC_LEDC_TIMER_BIT_WIDE_NUM
34+
5635
/*
5736
* LEDC Chan to Group/Channel/Timer Mapping
5837
** ledc: 0 => Group: 0, Channel: 0, Timer: 0
@@ -72,223 +51,89 @@ xSemaphoreHandle _ledc_sys_lock = NULL;
7251
** ledc: 14 => Group: 1, Channel: 6, Timer: 3
7352
** ledc: 15 => Group: 1, Channel: 7, Timer: 3
7453
*/
75-
#define LEDC_CHAN(g,c) LEDC.channel_group[(g)].channel[(c)]
76-
#define LEDC_TIMER(g,t) LEDC.timer_group[(g)].timer[(t)]
7754

78-
static void _on_apb_change(void * arg, apb_change_ev_t ev_type, uint32_t old_apb, uint32_t new_apb){
79-
if(ev_type == APB_AFTER_CHANGE && old_apb != new_apb){
80-
uint16_t iarg = *(uint16_t*)arg;
81-
uint8_t chan = 0;
82-
old_apb /= 1000000;
83-
new_apb /= 1000000;
84-
while(iarg){ // run though all active channels, adjusting timing configurations
85-
if(iarg & 1) {// this channel is active
86-
uint8_t group=(chan/8), timer=((chan/2)%4);
87-
if(LEDC_TIMER(group, timer).conf.tick_sel){
88-
LEDC_MUTEX_LOCK();
89-
uint32_t old_div = LEDC_TIMER(group, timer).conf.clock_divider;
90-
uint32_t div_num = (new_apb * old_div) / old_apb;
91-
if(div_num > LEDC_DIV_NUM_HSTIMER0_V){
92-
div_num = ((REF_CLK_FREQ /1000000) * old_div) / old_apb;
93-
if(div_num > LEDC_DIV_NUM_HSTIMER0_V) {
94-
div_num = LEDC_DIV_NUM_HSTIMER0_V;//lowest clock possible
95-
}
96-
LEDC_TIMER(group, timer).conf.tick_sel = 0;
97-
} else if(div_num < 256) {
98-
div_num = 256;//highest clock possible
99-
}
100-
LEDC_TIMER(group, timer).conf.clock_divider = div_num;
101-
LEDC_MUTEX_UNLOCK();
102-
}
103-
else {
104-
log_d("using REF_CLK chan=%d",chan);
105-
}
106-
}
107-
iarg = iarg >> 1;
108-
chan++;
109-
}
110-
}
111-
}
55+
uint8_t channels_resolution[LEDC_CHANNELS] = {0};
11256

113-
//uint32_t frequency = (80MHz or 1MHz)/((div_num / 256.0)*(1 << bit_num));
114-
static void _ledcSetupTimer(uint8_t chan, uint32_t div_num, uint8_t bit_num, bool apb_clk)
115-
{
116-
uint8_t group=(chan/8), timer=((chan/2)%4);
117-
static bool tHasStarted = false;
118-
static uint16_t _activeChannels = 0;
119-
#if CONFIG_IDF_TARGET_ESP32S2
120-
// ESP32-S2 TRM v1.0 on Page 789 -> BIT LEDC_TICK_SEL_TIMERx is 0 for LEDC_PWM_CLK and 1 for REF_TICK
121-
apb_clk = 0;
122-
#endif
123-
if(!tHasStarted) {
124-
tHasStarted = true;
125-
periph_module_enable(PERIPH_LEDC_MODULE);
126-
LEDC.conf.apb_clk_sel = 1;//LS use apb clock
127-
addApbChangeCallback((void*)&_activeChannels, _on_apb_change);
128-
129-
#if !CONFIG_DISABLE_HAL_LOCKS
130-
_ledc_sys_lock = xSemaphoreCreateMutex();
131-
#endif
132-
}
133-
LEDC_MUTEX_LOCK();
134-
LEDC_TIMER(group, timer).conf.clock_divider = div_num;//18 bit (10.8) This register is used to configure parameter for divider in timer the least significant eight bits represent the decimal part.
135-
LEDC_TIMER(group, timer).conf.duty_resolution = bit_num;//5 bit This register controls the range of the counter in timer. the counter range is [0 2**bit_num] the max bit width for counter is 20.
136-
LEDC_TIMER(group, timer).conf.tick_sel = apb_clk;//apb clock
137-
#if CONFIG_IDF_TARGET_ESP32
138-
if(group) {
139-
#endif
140-
LEDC_TIMER(group, timer).conf.low_speed_update = 1;//This bit is only useful for low speed timer channels, reserved for high speed timers
141-
#if CONFIG_IDF_TARGET_ESP32
142-
}
143-
#endif
144-
LEDC_TIMER(group, timer).conf.pause = 0;
145-
LEDC_TIMER(group, timer).conf.rst = 1;//This bit is used to reset timer the counter will be 0 after reset.
146-
LEDC_TIMER(group, timer).conf.rst = 0;
147-
LEDC_MUTEX_UNLOCK();
148-
_activeChannels |= (1 << chan); // mark as active for APB callback
149-
}
150-
151-
//max div_num 0x3FFFF (262143)
152-
//max bit_num 0x1F (31)
153-
static double _ledcSetupTimerFreq(uint8_t chan, double freq, uint8_t bit_num)
57+
double ledcSetup(uint8_t chan, double freq, uint8_t bit_num)
15458
{
155-
uint64_t clk_freq = getApbFrequency();
156-
clk_freq <<= 8;//div_num is 8 bit decimal
157-
uint32_t div_num = (clk_freq >> bit_num) / freq;
158-
bool apb_clk = true;
159-
if(div_num > LEDC_DIV_NUM_HSTIMER0_V) {
160-
clk_freq /= 80;
161-
div_num = (clk_freq >> bit_num) / freq;
162-
if(div_num > LEDC_DIV_NUM_HSTIMER0_V) {
163-
div_num = LEDC_DIV_NUM_HSTIMER0_V;//lowest clock possible
164-
}
165-
apb_clk = false;
166-
} else if(div_num < 256) {
167-
div_num = 256;//highest clock possible
59+
if(chan >= LEDC_CHANNELS){
60+
log_e("No more LEDC channels available! You can have maximum %u", LEDC_CHANNELS);
61+
return 0;
16862
}
169-
_ledcSetupTimer(chan, div_num, bit_num, apb_clk);
170-
//log_i("Fin: %f, Fclk: %uMhz, bits: %u, DIV: %u, Fout: %f",
171-
// freq, apb_clk?80:1, bit_num, div_num, (clk_freq >> bit_num) / (double)div_num);
172-
return (clk_freq >> bit_num) / (double)div_num;
173-
}
174-
175-
static double _ledcTimerRead(uint8_t chan)
176-
{
177-
uint32_t div_num;
178-
uint8_t bit_num;
179-
bool apb_clk;
18063
uint8_t group=(chan/8), timer=((chan/2)%4);
181-
LEDC_MUTEX_LOCK();
182-
div_num = LEDC_TIMER(group, timer).conf.clock_divider;//18 bit (10.8) This register is used to configure parameter for divider in timer the least significant eight bits represent the decimal part.
183-
bit_num = LEDC_TIMER(group, timer).conf.duty_resolution;//5 bit This register controls the range of the counter in timer. the counter range is [0 2**bit_num] the max bit width for counter is 20.
184-
apb_clk = LEDC_TIMER(group, timer).conf.tick_sel;//apb clock
185-
LEDC_MUTEX_UNLOCK();
186-
uint64_t clk_freq = 1000000;
187-
if(apb_clk) {
188-
clk_freq = getApbFrequency();
189-
}
190-
clk_freq <<= 8;//div_num is 8 bit decimal
191-
return (clk_freq >> bit_num) / (double)div_num;
192-
}
19364

194-
static void _ledcSetupChannel(uint8_t chan, uint8_t idle_level)
195-
{
196-
uint8_t group=(chan/8), channel=(chan%8), timer=((chan/2)%4);
197-
LEDC_MUTEX_LOCK();
198-
LEDC_CHAN(group, channel).conf0.timer_sel = timer;//2 bit Selects the timer to attach 0-3
199-
LEDC_CHAN(group, channel).conf0.idle_lv = idle_level;//1 bit This bit is used to control the output value when channel is off.
200-
LEDC_CHAN(group, channel).hpoint.hpoint = 0;//20 bit The output value changes to high when timer selected by channel has reached hpoint
201-
LEDC_CHAN(group, channel).conf1.duty_inc = 1;//1 bit This register is used to increase the duty of output signal or decrease the duty of output signal for high speed channel
202-
LEDC_CHAN(group, channel).conf1.duty_num = 1;//10 bit This register is used to control the number of increased or decreased times for channel
203-
LEDC_CHAN(group, channel).conf1.duty_cycle = 1;//10 bit This register is used to increase or decrease the duty every duty_cycle cycles for channel
204-
LEDC_CHAN(group, channel).conf1.duty_scale = 0;//10 bit This register controls the increase or decrease step scale for channel.
205-
LEDC_CHAN(group, channel).duty.duty = 0;
206-
LEDC_CHAN(group, channel).conf0.sig_out_en = 0;//This is the output enable control bit for channel
207-
LEDC_CHAN(group, channel).conf1.duty_start = 0;//When duty_num duty_cycle and duty_scale has been configured. these register won't take effect until set duty_start. this bit is automatically cleared by hardware.
208-
#if CONFIG_IDF_TARGET_ESP32
209-
if(group) {
210-
#endif
211-
LEDC_CHAN(group, channel).conf0.low_speed_update = 1;
212-
#if CONFIG_IDF_TARGET_ESP32
213-
} else {
214-
LEDC_CHAN(group, channel).conf0.clk_en = 0;
215-
}
216-
#endif
217-
LEDC_MUTEX_UNLOCK();
218-
}
65+
ledc_timer_config_t ledc_timer = {
66+
.speed_mode = group,
67+
.timer_num = timer,
68+
.duty_resolution = bit_num,
69+
.freq_hz = freq,
70+
.clk_cfg = LEDC_DEFAULT_CLK
71+
};
72+
ledc_timer_config(&ledc_timer);
73+
channels_resolution[chan] = bit_num;
21974

220-
double ledcSetup(uint8_t chan, double freq, uint8_t bit_num)
221-
{
222-
if(chan > LAST_CHAN) {
223-
return 0;
224-
}
225-
double res_freq = _ledcSetupTimerFreq(chan, freq, bit_num);
226-
_ledcSetupChannel(chan, LOW);
227-
return res_freq;
75+
return ledc_get_freq(group,timer);
22876
}
22977

23078
void ledcWrite(uint8_t chan, uint32_t duty)
23179
{
232-
if(chan > LAST_CHAN) {
80+
if(chan >= LEDC_CHANNELS){
23381
return;
23482
}
23583
uint8_t group=(chan/8), channel=(chan%8);
236-
LEDC_MUTEX_LOCK();
237-
LEDC_CHAN(group, channel).duty.duty = duty << 4;//25 bit (21.4)
238-
if(duty) {
239-
LEDC_CHAN(group, channel).conf0.sig_out_en = 1;//This is the output enable control bit for channel
240-
LEDC_CHAN(group, channel).conf1.duty_start = 1;//When duty_num duty_cycle and duty_scale has been configured. these register won't take effect until set duty_start. this bit is automatically cleared by hardware.
241-
#if CONFIG_IDF_TARGET_ESP32
242-
if(group) {
243-
#endif
244-
LEDC_CHAN(group, channel).conf0.low_speed_update = 1;
245-
#if CONFIG_IDF_TARGET_ESP32
246-
} else {
247-
LEDC_CHAN(group, channel).conf0.clk_en = 1;
248-
}
249-
#endif
250-
} else {
251-
LEDC_CHAN(group, channel).conf0.sig_out_en = 0;//This is the output enable control bit for channel
252-
LEDC_CHAN(group, channel).conf1.duty_start = 0;//When duty_num duty_cycle and duty_scale has been configured. these register won't take effect until set duty_start. this bit is automatically cleared by hardware.
253-
#if CONFIG_IDF_TARGET_ESP32
254-
if(group) {
255-
#endif
256-
LEDC_CHAN(group, channel).conf0.low_speed_update = 1;
257-
#if CONFIG_IDF_TARGET_ESP32
258-
} else {
259-
LEDC_CHAN(group, channel).conf0.clk_en = 0;
260-
}
261-
#endif
84+
85+
//Fixing if all bits in resolution is set = LEDC FULL ON
86+
uint32_t max_duty = (1 << channels_resolution[chan]) - 1;
87+
88+
if(duty == max_duty){
89+
duty = max_duty + 1;
26290
}
263-
LEDC_MUTEX_UNLOCK();
91+
92+
ledc_set_duty(group, channel, duty);
93+
ledc_update_duty(group, channel);
26494
}
26595

26696
uint32_t ledcRead(uint8_t chan)
26797
{
268-
if(chan > LAST_CHAN) {
98+
if(chan >= LEDC_CHANNELS){
26999
return 0;
270100
}
271-
return LEDC.channel_group[chan/8].channel[chan%8].duty.duty >> 4;
101+
uint8_t group=(chan/8), channel=(chan%8);
102+
return ledc_get_duty(group,channel);
272103
}
273104

274105
double ledcReadFreq(uint8_t chan)
275106
{
276107
if(!ledcRead(chan)){
277108
return 0;
278109
}
279-
return _ledcTimerRead(chan);
110+
uint8_t group=(chan/8), timer=((chan/2)%4);
111+
return ledc_get_freq(group,timer);
280112
}
281113

282114
double ledcWriteTone(uint8_t chan, double freq)
283115
{
284-
if(chan > LAST_CHAN) {
116+
if(chan >= LEDC_CHANNELS){
285117
return 0;
286118
}
287-
if(!freq) {
119+
if(!freq){
288120
ledcWrite(chan, 0);
289121
return 0;
290122
}
291-
double res_freq = _ledcSetupTimerFreq(chan, freq, 10);
123+
124+
uint8_t group=(chan/8), timer=((chan/2)%4);
125+
126+
ledc_timer_config_t ledc_timer = {
127+
.speed_mode = group,
128+
.timer_num = timer,
129+
.duty_resolution = 10,
130+
.freq_hz = freq,
131+
.clk_cfg = LEDC_DEFAULT_CLK
132+
};
133+
ledc_timer_config(&ledc_timer);
134+
channels_resolution[chan] = 10;
135+
136+
double res_freq = ledc_get_freq(group,timer);
292137
ledcWrite(chan, 0x1FF);
293138
return res_freq;
294139
}
@@ -308,15 +153,21 @@ double ledcWriteNote(uint8_t chan, note_t note, uint8_t octave){
308153

309154
void ledcAttachPin(uint8_t pin, uint8_t chan)
310155
{
311-
if(chan > LAST_CHAN) {
156+
if(chan >= LEDC_CHANNELS){
312157
return;
313158
}
314-
pinMode(pin, OUTPUT);
315-
#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32C3
316-
pinMatrixOutAttach(pin, LEDC_LS_SIG_OUT0_IDX + chan, false, false);
317-
#else
318-
pinMatrixOutAttach(pin, ((chan/8)?LEDC_LS_SIG_OUT0_IDX:LEDC_HS_SIG_OUT0_IDX) + (chan%8), false, false);
319-
#endif
159+
uint8_t group=(chan/8), channel=(chan%8), timer=((chan/2)%4);
160+
161+
ledc_channel_config_t ledc_channel = {
162+
.speed_mode = group,
163+
.channel = channel,
164+
.timer_sel = timer,
165+
.intr_type = LEDC_INTR_DISABLE,
166+
.gpio_num = pin,
167+
.duty = 0,
168+
.hpoint = 0
169+
};
170+
ledc_channel_config(&ledc_channel);
320171
}
321172

322173
void ledcDetachPin(uint8_t pin)
@@ -326,21 +177,32 @@ void ledcDetachPin(uint8_t pin)
326177

327178
double ledcChangeFrequency(uint8_t chan, double freq, uint8_t bit_num)
328179
{
329-
if (chan > 15) {
180+
if(chan >= LEDC_CHANNELS){
330181
return 0;
331182
}
332-
double res_freq = _ledcSetupTimerFreq(chan, freq, bit_num);
333-
return res_freq;
183+
uint8_t group=(chan/8), timer=((chan/2)%4);
184+
185+
ledc_timer_config_t ledc_timer = {
186+
.speed_mode = group,
187+
.timer_num = timer,
188+
.duty_resolution = bit_num,
189+
.freq_hz = freq,
190+
.clk_cfg = LEDC_DEFAULT_CLK
191+
};
192+
ledc_timer_config(&ledc_timer);
193+
channels_resolution[chan] = bit_num;
194+
195+
return ledc_get_freq(group,timer);
334196
}
335197

336198
static int8_t pin_to_channel[SOC_GPIO_PIN_COUNT] = { 0 };
337-
static int cnt_channel = SOC_LEDC_CHANNEL_NUM;
199+
static int cnt_channel = LEDC_CHANNELS;
338200
void analogWrite(uint8_t pin, int value) {
339201
// Use ledc hardware for internal pins
340202
if (pin < SOC_GPIO_PIN_COUNT) {
341203
if (pin_to_channel[pin] == 0) {
342204
if (!cnt_channel) {
343-
log_e("No more analogWrite channels available! You can have maximum %u", SOC_LEDC_CHANNEL_NUM);
205+
log_e("No more analogWrite channels available! You can have maximum %u", LEDC_CHANNELS);
344206
return;
345207
}
346208
pin_to_channel[pin] = cnt_channel--;

0 commit comments

Comments
 (0)