Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit d6934a5

Browse filesBrowse files
authored
Implement LEDC based on ESP-IDF API (espressif#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
Copy full SHA for d6934a5

File tree

Expand file treeCollapse file tree

1 file changed

+87
-225
lines changed
Filter options
Expand file treeCollapse file tree

1 file changed

+87
-225
lines changed

‎cores/esp32/esp32-hal-ledc.c

Copy file name to clipboardExpand all lines: cores/esp32/esp32-hal-ledc.c
+87-225Lines changed: 87 additions & 225 deletions
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 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.