Skip to content

Commit 763c83b

Browse files
committed
machine/esp32: add PWM support using the LEDC peripheral
The classic ESP32 had no PWM. This adds it. It uses the high speed half of the LEDC peripheral. This gives 4 timers (PWM0 to PWM3) and 8 channels. You can use any pin, because the signal goes through the GPIO matrix. The ESP32-C3 and the ESP32-S3 have LEDC PWM. Most of that code is the same for all of these chips. This change uses that code again. It does not add a second driver for the same peripheral. Three functions in the shared file work only on the newer chips. They move to a file for each chip. enableClock The C3 and S3 turn on the clock in SYSTEM. This chip uses DPORT. It also has no LEDC.CONF_CLK_EN bit. setTimerConf The timer registers here are HSTIMER0_CONF. On the other chips they are TIMER0_CONF. chanOp The channel registers here are HSCH0_CONF0. On the other chips they are CH0_CONF0. The files are now: machine_esp32xx_pwm.go The shared part. It builds for esp32 also. It calls enableClock and setTimerConf. machine_esp32xx_ls_pwm.go New. It holds the two low speed functions. The code moved without a change. It builds for esp32c3 and esp32s3 only. machine_esp32_pwm.go New. The versions for the classic ESP32. Two hardware details are important. TICK_SEL has the opposite meaning on this chip. Here 1 selects APB_CLK at 80MHz and 0 selects REF_TICK at 1MHz. The low speed timers on the C3 and the S3 write 0. A 0 here makes all frequencies 80 times too slow. There is no PARA_UP bit. On the C3 and the S3 you set PARA_UP to apply a change to a channel. High speed channels apply the change themselves at the end of the period. Tests: The examples/pwm binaries for esp32c3-supermini and xiao-esp32s3 are the same byte for byte before and after this change. The move of the code did not change it. Tested on an ESP32-WROVER board. A servo on GPIO15 turns from 0 to 180 degrees and back. The movement is smooth. This shows that the timing is correct. A servo moves only with pulses between 0.5ms and 2.5ms that repeat near 50Hz. Motor control PWM (the MCPWM peripheral) is not in this change. It needs a different API.
1 parent d88abc2 commit 763c83b

5 files changed

Lines changed: 407 additions & 61 deletions

File tree

make/smoketest.mk

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -473,6 +473,8 @@ ifneq ($(XTENSA), 0)
473473
@$(MD5SUM) $(SMOKE_OUT).bin
474474
$(TINYGO) build -size short -o $(SMOKE_OUT).bin -target=esp32-coreboard-v2 examples/adc
475475
@$(MD5SUM) $(SMOKE_OUT).bin
476+
$(TINYGO) build -size short -o $(SMOKE_OUT).bin -target=esp32-coreboard-v2 examples/pwm
477+
@$(MD5SUM) $(SMOKE_OUT).bin
476478
$(TINYGO) build -size short -o $(SMOKE_OUT).bin -target=esp32c3-generic examples/machinetest
477479
@$(MD5SUM) $(SMOKE_OUT).bin
478480
$(TINYGO) build -size short -o $(SMOKE_OUT).bin -target=esp32s3-generic examples/machinetest
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
//go:build esp32_coreboard_v2
2+
3+
package main
4+
5+
import "machine"
6+
7+
var (
8+
pwm = machine.PWM0
9+
pinA = machine.GPIO18
10+
pinB = machine.GPIO19
11+
)

src/machine/machine_esp32_pwm.go

Lines changed: 300 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,300 @@
1+
//go:build esp32
2+
3+
// PWM on the classic ESP32 uses the LEDC peripheral.
4+
//
5+
// LEDC here has two halves: a high-speed one and a low-speed one. This file uses
6+
// the high-speed half. It gives 4 timers (PWM0-PWM3) and 8 channels.
7+
//
8+
// The code that is the same on every ESP32 chip is in machine_esp32xx_pwm.go.
9+
// This file adds only the parts that are different here.
10+
//
11+
// Three things are different from the ESP32-C3 and the ESP32-S3:
12+
//
13+
// - The clock is switched on in DPORT. The newer chips renamed that block to
14+
// SYSTEM. This chip also has no LEDC.CONF_CLK_EN bit.
15+
// - Register names start with HS, for example HSTIMER0_CONF and HSCH0_CONF0.
16+
// - There is no PARA_UP bit. On the C3 and S3 you set PARA_UP to say "apply my
17+
// changes now". Here the channel applies them by itself, at the end of the
18+
// period it is in. That is what makes it "high speed".
19+
// See the ESP32 TRM, chapter "LED PWM Controller".
20+
21+
package machine
22+
23+
import "device/esp"
24+
25+
// The GPIO matrix can send an internal signal to almost any pin. Each signal has
26+
// a number. High-speed LEDC channel 0 is number 71, and the rest follow on from
27+
// there, so channels 0 to 7 are 71 to 78.
28+
// (From Espressif's soc/gpio_sig_map.h: LEDC_HS_SIG_OUT0_IDX.)
29+
const LEDC_HS_SIG_OUT0_IDX = 71
30+
31+
const ledcChannelsESP32 = 8
32+
33+
var (
34+
PWM0 = &LEDCPWM{SigOutBase: LEDC_HS_SIG_OUT0_IDX, NumChannels: ledcChannelsESP32, timerNum: 0}
35+
PWM1 = &LEDCPWM{SigOutBase: LEDC_HS_SIG_OUT0_IDX, NumChannels: ledcChannelsESP32, timerNum: 1}
36+
PWM2 = &LEDCPWM{SigOutBase: LEDC_HS_SIG_OUT0_IDX, NumChannels: ledcChannelsESP32, timerNum: 2}
37+
PWM3 = &LEDCPWM{SigOutBase: LEDC_HS_SIG_OUT0_IDX, NumChannels: ledcChannelsESP32, timerNum: 3}
38+
)
39+
40+
// enableClock turns the LEDC hardware on and picks its clock.
41+
func (pwm *LEDCPWM) enableClock() {
42+
// Every peripheral starts switched off, to save power. Turn the LEDC clock
43+
// on, then take it out of reset. These registers are in DPORT on this chip.
44+
// The C3 and S3 call the same block SYSTEM.
45+
esp.DPORT.SetPERIP_RST_EN_LEDC_RST(1)
46+
esp.DPORT.SetPERIP_CLK_EN_LEDC_CLK_EN(1)
47+
esp.DPORT.SetPERIP_RST_EN_LEDC_RST(0)
48+
49+
// Use APB_CLK, which runs at 80MHz. This chip has no CONF_CLK_EN bit, so
50+
// there is nothing more to switch on.
51+
esp.LEDC.SetCONF_APB_CLK_SEL(1)
52+
}
53+
54+
// setTimerConf writes the resolution and the divider that Configure worked out
55+
// into one timer.
56+
//
57+
// Watch TICK_SEL. It is 1 here, not 0. On this chip:
58+
//
59+
// 1 = APB_CLK, 80MHz <- what we want
60+
// 0 = REF_TICK, 1MHz
61+
//
62+
// The low-speed timers on the C3 and S3 use the opposite meaning, so their code
63+
// writes 0. Writing 0 here would make every frequency 80 times too slow.
64+
//
65+
// These timers have no PARA_UP bit. The short reset pulse at the end is what
66+
// makes the new values take effect.
67+
func (pwm *LEDCPWM) setTimerConf(dutyRes uint8, divReg uint32) {
68+
switch pwm.timerNum {
69+
case 0:
70+
esp.LEDC.SetHSTIMER0_CONF_DUTY_RES(uint32(dutyRes))
71+
esp.LEDC.SetHSTIMER0_CONF_DIV_NUM(divReg)
72+
esp.LEDC.SetHSTIMER0_CONF_TICK_SEL(1)
73+
esp.LEDC.SetHSTIMER0_CONF_PAUSE(0)
74+
esp.LEDC.SetHSTIMER0_CONF_RST(1)
75+
esp.LEDC.SetHSTIMER0_CONF_RST(0)
76+
case 1:
77+
esp.LEDC.SetHSTIMER1_CONF_DUTY_RES(uint32(dutyRes))
78+
esp.LEDC.SetHSTIMER1_CONF_DIV_NUM(divReg)
79+
esp.LEDC.SetHSTIMER1_CONF_TICK_SEL(1)
80+
esp.LEDC.SetHSTIMER1_CONF_PAUSE(0)
81+
esp.LEDC.SetHSTIMER1_CONF_RST(1)
82+
esp.LEDC.SetHSTIMER1_CONF_RST(0)
83+
case 2:
84+
esp.LEDC.SetHSTIMER2_CONF_DUTY_RES(uint32(dutyRes))
85+
esp.LEDC.SetHSTIMER2_CONF_DIV_NUM(divReg)
86+
esp.LEDC.SetHSTIMER2_CONF_TICK_SEL(1)
87+
esp.LEDC.SetHSTIMER2_CONF_PAUSE(0)
88+
esp.LEDC.SetHSTIMER2_CONF_RST(1)
89+
esp.LEDC.SetHSTIMER2_CONF_RST(0)
90+
case 3:
91+
esp.LEDC.SetHSTIMER3_CONF_DUTY_RES(uint32(dutyRes))
92+
esp.LEDC.SetHSTIMER3_CONF_DIV_NUM(divReg)
93+
esp.LEDC.SetHSTIMER3_CONF_TICK_SEL(1)
94+
esp.LEDC.SetHSTIMER3_CONF_PAUSE(0)
95+
esp.LEDC.SetHSTIMER3_CONF_RST(1)
96+
esp.LEDC.SetHSTIMER3_CONF_RST(0)
97+
}
98+
}
99+
100+
// chanOp does the work for one channel, numbered 0 to 7. It either sets the
101+
// channel up, changes its duty, or flips its idle level.
102+
//
103+
// Each channel has its own registers, and each register has its own generated
104+
// setter function. There is no array to index, so this has to be a long switch.
105+
// The C3 and S3 files have the same shape.
106+
//
107+
// DUTY_SCALE is set to 0. LEDC can fade slowly from one duty to the next; 0
108+
// turns that off, so the duty changes in a single step.
109+
func (pwm *LEDCPWM) chanOp(ch uint8, op ledcChanOp, duty uint32, inverting bool) {
110+
invVal := uint32(0)
111+
if inverting {
112+
invVal = 1
113+
}
114+
switch ch {
115+
case 0:
116+
switch op {
117+
case ledcChanOpInit:
118+
esp.LEDC.SetHSCH0_CONF0_TIMER_SEL(uint32(pwm.timerNum))
119+
esp.LEDC.SetHSCH0_CONF0_SIG_OUT_EN(1)
120+
esp.LEDC.SetHSCH0_CONF0_IDLE_LV(0)
121+
esp.LEDC.SetHSCH0_HPOINT_HPOINT(0)
122+
esp.LEDC.SetHSCH0_DUTY_DUTY(0)
123+
esp.LEDC.SetHSCH0_CONF1_DUTY_SCALE(0)
124+
esp.LEDC.SetHSCH0_CONF1_DUTY_CYCLE(1)
125+
esp.LEDC.SetHSCH0_CONF1_DUTY_NUM(1)
126+
esp.LEDC.SetHSCH0_CONF1_DUTY_INC(1)
127+
esp.LEDC.SetHSCH0_CONF1_DUTY_START(1)
128+
case ledcChanOpSetDuty:
129+
esp.LEDC.SetHSCH0_DUTY_DUTY(duty)
130+
esp.LEDC.SetHSCH0_CONF1_DUTY_CYCLE(1)
131+
esp.LEDC.SetHSCH0_CONF1_DUTY_NUM(1)
132+
esp.LEDC.SetHSCH0_CONF1_DUTY_INC(1)
133+
esp.LEDC.SetHSCH0_CONF1_DUTY_START(1)
134+
esp.LEDC.SetHSCH0_CONF0_SIG_OUT_EN(1)
135+
case ledcChanOpSetInvert:
136+
esp.LEDC.SetHSCH0_CONF0_IDLE_LV(invVal)
137+
}
138+
case 1:
139+
switch op {
140+
case ledcChanOpInit:
141+
esp.LEDC.SetHSCH1_CONF0_TIMER_SEL(uint32(pwm.timerNum))
142+
esp.LEDC.SetHSCH1_CONF0_SIG_OUT_EN(1)
143+
esp.LEDC.SetHSCH1_CONF0_IDLE_LV(0)
144+
esp.LEDC.SetHSCH1_HPOINT_HPOINT(0)
145+
esp.LEDC.SetHSCH1_DUTY_DUTY(0)
146+
esp.LEDC.SetHSCH1_CONF1_DUTY_SCALE(0)
147+
esp.LEDC.SetHSCH1_CONF1_DUTY_CYCLE(1)
148+
esp.LEDC.SetHSCH1_CONF1_DUTY_NUM(1)
149+
esp.LEDC.SetHSCH1_CONF1_DUTY_INC(1)
150+
esp.LEDC.SetHSCH1_CONF1_DUTY_START(1)
151+
case ledcChanOpSetDuty:
152+
esp.LEDC.SetHSCH1_DUTY_DUTY(duty)
153+
esp.LEDC.SetHSCH1_CONF1_DUTY_CYCLE(1)
154+
esp.LEDC.SetHSCH1_CONF1_DUTY_NUM(1)
155+
esp.LEDC.SetHSCH1_CONF1_DUTY_INC(1)
156+
esp.LEDC.SetHSCH1_CONF1_DUTY_START(1)
157+
esp.LEDC.SetHSCH1_CONF0_SIG_OUT_EN(1)
158+
case ledcChanOpSetInvert:
159+
esp.LEDC.SetHSCH1_CONF0_IDLE_LV(invVal)
160+
}
161+
case 2:
162+
switch op {
163+
case ledcChanOpInit:
164+
esp.LEDC.SetHSCH2_CONF0_TIMER_SEL(uint32(pwm.timerNum))
165+
esp.LEDC.SetHSCH2_CONF0_SIG_OUT_EN(1)
166+
esp.LEDC.SetHSCH2_CONF0_IDLE_LV(0)
167+
esp.LEDC.SetHSCH2_HPOINT_HPOINT(0)
168+
esp.LEDC.SetHSCH2_DUTY_DUTY(0)
169+
esp.LEDC.SetHSCH2_CONF1_DUTY_SCALE(0)
170+
esp.LEDC.SetHSCH2_CONF1_DUTY_CYCLE(1)
171+
esp.LEDC.SetHSCH2_CONF1_DUTY_NUM(1)
172+
esp.LEDC.SetHSCH2_CONF1_DUTY_INC(1)
173+
esp.LEDC.SetHSCH2_CONF1_DUTY_START(1)
174+
case ledcChanOpSetDuty:
175+
esp.LEDC.SetHSCH2_DUTY_DUTY(duty)
176+
esp.LEDC.SetHSCH2_CONF1_DUTY_CYCLE(1)
177+
esp.LEDC.SetHSCH2_CONF1_DUTY_NUM(1)
178+
esp.LEDC.SetHSCH2_CONF1_DUTY_INC(1)
179+
esp.LEDC.SetHSCH2_CONF1_DUTY_START(1)
180+
esp.LEDC.SetHSCH2_CONF0_SIG_OUT_EN(1)
181+
case ledcChanOpSetInvert:
182+
esp.LEDC.SetHSCH2_CONF0_IDLE_LV(invVal)
183+
}
184+
case 3:
185+
switch op {
186+
case ledcChanOpInit:
187+
esp.LEDC.SetHSCH3_CONF0_TIMER_SEL(uint32(pwm.timerNum))
188+
esp.LEDC.SetHSCH3_CONF0_SIG_OUT_EN(1)
189+
esp.LEDC.SetHSCH3_CONF0_IDLE_LV(0)
190+
esp.LEDC.SetHSCH3_HPOINT_HPOINT(0)
191+
esp.LEDC.SetHSCH3_DUTY_DUTY(0)
192+
esp.LEDC.SetHSCH3_CONF1_DUTY_SCALE(0)
193+
esp.LEDC.SetHSCH3_CONF1_DUTY_CYCLE(1)
194+
esp.LEDC.SetHSCH3_CONF1_DUTY_NUM(1)
195+
esp.LEDC.SetHSCH3_CONF1_DUTY_INC(1)
196+
esp.LEDC.SetHSCH3_CONF1_DUTY_START(1)
197+
case ledcChanOpSetDuty:
198+
esp.LEDC.SetHSCH3_DUTY_DUTY(duty)
199+
esp.LEDC.SetHSCH3_CONF1_DUTY_CYCLE(1)
200+
esp.LEDC.SetHSCH3_CONF1_DUTY_NUM(1)
201+
esp.LEDC.SetHSCH3_CONF1_DUTY_INC(1)
202+
esp.LEDC.SetHSCH3_CONF1_DUTY_START(1)
203+
esp.LEDC.SetHSCH3_CONF0_SIG_OUT_EN(1)
204+
case ledcChanOpSetInvert:
205+
esp.LEDC.SetHSCH3_CONF0_IDLE_LV(invVal)
206+
}
207+
case 4:
208+
switch op {
209+
case ledcChanOpInit:
210+
esp.LEDC.SetHSCH4_CONF0_TIMER_SEL(uint32(pwm.timerNum))
211+
esp.LEDC.SetHSCH4_CONF0_SIG_OUT_EN(1)
212+
esp.LEDC.SetHSCH4_CONF0_IDLE_LV(0)
213+
esp.LEDC.SetHSCH4_HPOINT_HPOINT(0)
214+
esp.LEDC.SetHSCH4_DUTY_DUTY(0)
215+
esp.LEDC.SetHSCH4_CONF1_DUTY_SCALE(0)
216+
esp.LEDC.SetHSCH4_CONF1_DUTY_CYCLE(1)
217+
esp.LEDC.SetHSCH4_CONF1_DUTY_NUM(1)
218+
esp.LEDC.SetHSCH4_CONF1_DUTY_INC(1)
219+
esp.LEDC.SetHSCH4_CONF1_DUTY_START(1)
220+
case ledcChanOpSetDuty:
221+
esp.LEDC.SetHSCH4_DUTY_DUTY(duty)
222+
esp.LEDC.SetHSCH4_CONF1_DUTY_CYCLE(1)
223+
esp.LEDC.SetHSCH4_CONF1_DUTY_NUM(1)
224+
esp.LEDC.SetHSCH4_CONF1_DUTY_INC(1)
225+
esp.LEDC.SetHSCH4_CONF1_DUTY_START(1)
226+
esp.LEDC.SetHSCH4_CONF0_SIG_OUT_EN(1)
227+
case ledcChanOpSetInvert:
228+
esp.LEDC.SetHSCH4_CONF0_IDLE_LV(invVal)
229+
}
230+
case 5:
231+
switch op {
232+
case ledcChanOpInit:
233+
esp.LEDC.SetHSCH5_CONF0_TIMER_SEL(uint32(pwm.timerNum))
234+
esp.LEDC.SetHSCH5_CONF0_SIG_OUT_EN(1)
235+
esp.LEDC.SetHSCH5_CONF0_IDLE_LV(0)
236+
esp.LEDC.SetHSCH5_HPOINT_HPOINT(0)
237+
esp.LEDC.SetHSCH5_DUTY_DUTY(0)
238+
esp.LEDC.SetHSCH5_CONF1_DUTY_SCALE(0)
239+
esp.LEDC.SetHSCH5_CONF1_DUTY_CYCLE(1)
240+
esp.LEDC.SetHSCH5_CONF1_DUTY_NUM(1)
241+
esp.LEDC.SetHSCH5_CONF1_DUTY_INC(1)
242+
esp.LEDC.SetHSCH5_CONF1_DUTY_START(1)
243+
case ledcChanOpSetDuty:
244+
esp.LEDC.SetHSCH5_DUTY_DUTY(duty)
245+
esp.LEDC.SetHSCH5_CONF1_DUTY_CYCLE(1)
246+
esp.LEDC.SetHSCH5_CONF1_DUTY_NUM(1)
247+
esp.LEDC.SetHSCH5_CONF1_DUTY_INC(1)
248+
esp.LEDC.SetHSCH5_CONF1_DUTY_START(1)
249+
esp.LEDC.SetHSCH5_CONF0_SIG_OUT_EN(1)
250+
case ledcChanOpSetInvert:
251+
esp.LEDC.SetHSCH5_CONF0_IDLE_LV(invVal)
252+
}
253+
case 6:
254+
switch op {
255+
case ledcChanOpInit:
256+
esp.LEDC.SetHSCH6_CONF0_TIMER_SEL(uint32(pwm.timerNum))
257+
esp.LEDC.SetHSCH6_CONF0_SIG_OUT_EN(1)
258+
esp.LEDC.SetHSCH6_CONF0_IDLE_LV(0)
259+
esp.LEDC.SetHSCH6_HPOINT_HPOINT(0)
260+
esp.LEDC.SetHSCH6_DUTY_DUTY(0)
261+
esp.LEDC.SetHSCH6_CONF1_DUTY_SCALE(0)
262+
esp.LEDC.SetHSCH6_CONF1_DUTY_CYCLE(1)
263+
esp.LEDC.SetHSCH6_CONF1_DUTY_NUM(1)
264+
esp.LEDC.SetHSCH6_CONF1_DUTY_INC(1)
265+
esp.LEDC.SetHSCH6_CONF1_DUTY_START(1)
266+
case ledcChanOpSetDuty:
267+
esp.LEDC.SetHSCH6_DUTY_DUTY(duty)
268+
esp.LEDC.SetHSCH6_CONF1_DUTY_CYCLE(1)
269+
esp.LEDC.SetHSCH6_CONF1_DUTY_NUM(1)
270+
esp.LEDC.SetHSCH6_CONF1_DUTY_INC(1)
271+
esp.LEDC.SetHSCH6_CONF1_DUTY_START(1)
272+
esp.LEDC.SetHSCH6_CONF0_SIG_OUT_EN(1)
273+
case ledcChanOpSetInvert:
274+
esp.LEDC.SetHSCH6_CONF0_IDLE_LV(invVal)
275+
}
276+
case 7:
277+
switch op {
278+
case ledcChanOpInit:
279+
esp.LEDC.SetHSCH7_CONF0_TIMER_SEL(uint32(pwm.timerNum))
280+
esp.LEDC.SetHSCH7_CONF0_SIG_OUT_EN(1)
281+
esp.LEDC.SetHSCH7_CONF0_IDLE_LV(0)
282+
esp.LEDC.SetHSCH7_HPOINT_HPOINT(0)
283+
esp.LEDC.SetHSCH7_DUTY_DUTY(0)
284+
esp.LEDC.SetHSCH7_CONF1_DUTY_SCALE(0)
285+
esp.LEDC.SetHSCH7_CONF1_DUTY_CYCLE(1)
286+
esp.LEDC.SetHSCH7_CONF1_DUTY_NUM(1)
287+
esp.LEDC.SetHSCH7_CONF1_DUTY_INC(1)
288+
esp.LEDC.SetHSCH7_CONF1_DUTY_START(1)
289+
case ledcChanOpSetDuty:
290+
esp.LEDC.SetHSCH7_DUTY_DUTY(duty)
291+
esp.LEDC.SetHSCH7_CONF1_DUTY_CYCLE(1)
292+
esp.LEDC.SetHSCH7_CONF1_DUTY_NUM(1)
293+
esp.LEDC.SetHSCH7_CONF1_DUTY_INC(1)
294+
esp.LEDC.SetHSCH7_CONF1_DUTY_START(1)
295+
esp.LEDC.SetHSCH7_CONF0_SIG_OUT_EN(1)
296+
case ledcChanOpSetInvert:
297+
esp.LEDC.SetHSCH7_CONF0_IDLE_LV(invVal)
298+
}
299+
}
300+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
//go:build esp32c3 || esp32s3
2+
3+
// Low-speed LEDC code, shared by the ESP32-C3 and the ESP32-S3.
4+
//
5+
// Both chips have only the low-speed LEDC block. Their clock and timer registers
6+
// use the same names, so one copy of this code serves both.
7+
//
8+
// The classic ESP32 has a high-speed block instead, with different register
9+
// names. Its versions of these two functions are in machine_esp32_pwm.go.
10+
//
11+
// The SVD (lib/cmsis-svd/data/Espressif/esp32s3.svd) describes the two bits that
12+
// make a change take effect on these chips. CONF0.PARA_UP "updates HPOINT,
13+
// DUTY_START, SIG_OUT_EN, TIMER_SEL, DUTY_NUM, DUTY_CYCLE, DUTY_SCALE, DUTY_INC
14+
// for channel and is auto-cleared by hardware". CONF1.DUTY_START "other CONF1
15+
// fields take effect when this bit is set to 1".
16+
17+
package machine
18+
19+
import "device/esp"
20+
21+
// enableClock turns the LEDC hardware on and picks APB_CLK as its clock.
22+
func (pwm *LEDCPWM) enableClock() {
23+
// Enable LEDC clock and release reset (SYSTEM perip_clk_en0 / perip_rst_en0).
24+
esp.SYSTEM.SetPERIP_RST_EN0_LEDC_RST(1)
25+
esp.SYSTEM.SetPERIP_CLK_EN0_LEDC_CLK_EN(1)
26+
esp.SYSTEM.SetPERIP_RST_EN0_LEDC_RST(0)
27+
28+
// LEDC global: APB clock source, enable internal clock.
29+
esp.LEDC.SetCONF_APB_CLK_SEL(1)
30+
esp.LEDC.SetCONF_CLK_EN(1)
31+
}
32+
33+
func (pwm *LEDCPWM) setTimerConf(dutyRes uint8, divReg uint32) {
34+
t := pwm.timerNum
35+
switch t {
36+
case 0:
37+
esp.LEDC.SetTIMER0_CONF_DUTY_RES(uint32(dutyRes))
38+
esp.LEDC.SetTIMER0_CONF_CLK_DIV(divReg)
39+
esp.LEDC.SetTIMER0_CONF_TICK_SEL(0)
40+
esp.LEDC.SetTIMER0_CONF_PAUSE(0)
41+
esp.LEDC.SetTIMER0_CONF_RST(1)
42+
esp.LEDC.SetTIMER0_CONF_RST(0)
43+
esp.LEDC.SetTIMER0_CONF_PARA_UP(1)
44+
case 1:
45+
esp.LEDC.SetTIMER1_CONF_DUTY_RES(uint32(dutyRes))
46+
esp.LEDC.SetTIMER1_CONF_CLK_DIV(divReg)
47+
esp.LEDC.SetTIMER1_CONF_TICK_SEL(0)
48+
esp.LEDC.SetTIMER1_CONF_PAUSE(0)
49+
esp.LEDC.SetTIMER1_CONF_RST(1)
50+
esp.LEDC.SetTIMER1_CONF_RST(0)
51+
esp.LEDC.SetTIMER1_CONF_PARA_UP(1)
52+
case 2:
53+
esp.LEDC.SetTIMER2_CONF_DUTY_RES(uint32(dutyRes))
54+
esp.LEDC.SetTIMER2_CONF_CLK_DIV(divReg)
55+
esp.LEDC.SetTIMER2_CONF_TICK_SEL(0)
56+
esp.LEDC.SetTIMER2_CONF_PAUSE(0)
57+
esp.LEDC.SetTIMER2_CONF_RST(1)
58+
esp.LEDC.SetTIMER2_CONF_RST(0)
59+
esp.LEDC.SetTIMER2_CONF_PARA_UP(1)
60+
case 3:
61+
esp.LEDC.SetTIMER3_CONF_DUTY_RES(uint32(dutyRes))
62+
esp.LEDC.SetTIMER3_CONF_CLK_DIV(divReg)
63+
esp.LEDC.SetTIMER3_CONF_TICK_SEL(0)
64+
esp.LEDC.SetTIMER3_CONF_PAUSE(0)
65+
esp.LEDC.SetTIMER3_CONF_RST(1)
66+
esp.LEDC.SetTIMER3_CONF_RST(0)
67+
esp.LEDC.SetTIMER3_CONF_PARA_UP(1)
68+
}
69+
}

0 commit comments

Comments
 (0)