mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-05 03:27:48 +00:00
esp3s3 & esp32c3 PWM implements (#5215)
* esp3s3 pwm * esp32s3-pwm: added spi alias for esp32s3 for tests * esp32s3-pwm: fix linters * esp32s3-pwm: fix rename * esp32s3-pwm: replace if to switch * esp32s3-pwm: chip specific registry * esp32s3-pwm: fix linters
This commit is contained in:
+19
-1
@@ -923,17 +923,34 @@ ifneq ($(XTENSA), 0)
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target mch2022 examples/machinetest
|
||||
@$(MD5SUM) test.bin
|
||||
# xiao-esp32s3
|
||||
$(TINYGO) build -size short -o test.bin -target=xiao-esp32s3 examples/blinky1
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target=xiao-esp32s3 examples/blinkm
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target=xiao-esp32s3 examples/mcp3008
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target=esp32s3-wroom1 examples/mcp3008
|
||||
$(TINYGO) build -size short -o test.bin -target=xiao-esp32s3 examples/pwm
|
||||
@$(MD5SUM) test.bin
|
||||
|
||||
# esp32s3-wroom1
|
||||
$(TINYGO) build -size short -o test.bin -target=esp32s3-wroom1 examples/blinkm
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target=esp32s3-wroom1 examples/mcp3008
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target=esp32s3-wroom1 examples/pwm
|
||||
@$(MD5SUM) test.bin
|
||||
endif
|
||||
# esp32c3-supermini
|
||||
$(TINYGO) build -size short -o test.bin -target=esp32c3-supermini examples/blinky1
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target=esp32c3-supermini examples/blinkm
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target=esp32c3-supermini examples/mcp3008
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target=esp32c3-supermini examples/pwm
|
||||
@$(MD5SUM) test.bin
|
||||
|
||||
$(TINYGO) build -size short -o test.bin -target=esp-c3-32s-kit examples/blinky1
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target=qtpy-esp32c3 examples/machinetest
|
||||
@@ -946,6 +963,7 @@ endif
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target=esp32c3-12f examples/blinky1
|
||||
@$(MD5SUM) test.bin
|
||||
|
||||
$(TINYGO) build -size short -o test.bin -target=makerfabs-esp32c3spi35 examples/machinetest
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.hex -target=hifive1b examples/blinky1
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
//go:build esp32c3_supermini
|
||||
|
||||
package main
|
||||
|
||||
import "machine"
|
||||
|
||||
var (
|
||||
pwm = machine.PWM0
|
||||
pinA = machine.GPIO5
|
||||
pinB = machine.GPIO6
|
||||
)
|
||||
@@ -0,0 +1,11 @@
|
||||
//go:build esp32s3_wroom1
|
||||
|
||||
package main
|
||||
|
||||
import "machine"
|
||||
|
||||
var (
|
||||
pwm = machine.PWM0
|
||||
pinA = machine.GPIO17
|
||||
pinB = machine.GPIO18
|
||||
)
|
||||
@@ -0,0 +1,11 @@
|
||||
//go:build xiao_esp32s3
|
||||
|
||||
package main
|
||||
|
||||
import "machine"
|
||||
|
||||
var (
|
||||
pwm = machine.PWM0
|
||||
pinA = machine.D0
|
||||
pinB = machine.D1
|
||||
)
|
||||
@@ -0,0 +1,178 @@
|
||||
//go:build esp32c3
|
||||
|
||||
package machine
|
||||
|
||||
import "device/esp"
|
||||
|
||||
// LEDC PWM for ESP32-C3: 4 timers (PWM0–PWM3), 6 channels per timer; each timer has its own frequency.
|
||||
// Range: frequency from a few Hz up to ~40 MHz (at 1-bit resolution); duty resolution 1–15 bits
|
||||
// (higher frequency gives lower resolution). Clock source: APB 80 MHz. Low-speed mode only.
|
||||
// See ESP-IDF LEDC driver, TRM LED PWM Controller.
|
||||
|
||||
// GPIO matrix output signal indices for LEDC (soc/gpio_sig_map.h)
|
||||
const (
|
||||
LEDC_LS_SIG_OUT0_IDX = 45
|
||||
)
|
||||
|
||||
const ledcChannelsC3 = 6
|
||||
|
||||
var (
|
||||
PWM0 = &LEDCPWM{SigOutBase: LEDC_LS_SIG_OUT0_IDX, NumChannels: ledcChannelsC3, timerNum: 0}
|
||||
PWM1 = &LEDCPWM{SigOutBase: LEDC_LS_SIG_OUT0_IDX, NumChannels: ledcChannelsC3, timerNum: 1}
|
||||
PWM2 = &LEDCPWM{SigOutBase: LEDC_LS_SIG_OUT0_IDX, NumChannels: ledcChannelsC3, timerNum: 2}
|
||||
PWM3 = &LEDCPWM{SigOutBase: LEDC_LS_SIG_OUT0_IDX, NumChannels: ledcChannelsC3, timerNum: 3}
|
||||
)
|
||||
|
||||
// chanOp implements LEDC low-speed channel ops for ESP32-C3 (channels 0–5 only).
|
||||
func (pwm *LEDCPWM) chanOp(ch uint8, op ledcChanOp, duty uint32, inverting bool) {
|
||||
invVal := uint32(0)
|
||||
if inverting {
|
||||
invVal = 1
|
||||
}
|
||||
switch ch {
|
||||
case 0:
|
||||
switch op {
|
||||
case ledcChanOpInit:
|
||||
esp.LEDC.SetCH0_CONF0_TIMER_SEL(uint32(pwm.timerNum))
|
||||
esp.LEDC.SetCH0_CONF0_SIG_OUT_EN(1)
|
||||
esp.LEDC.SetCH0_CONF0_IDLE_LV(0)
|
||||
esp.LEDC.SetCH0_HPOINT_HPOINT(0)
|
||||
esp.LEDC.SetCH0_DUTY_DUTY(0)
|
||||
esp.LEDC.SetCH0_CONF1_DUTY_CYCLE(1)
|
||||
esp.LEDC.SetCH0_CONF1_DUTY_NUM(1)
|
||||
esp.LEDC.SetCH0_CONF1_DUTY_INC(1)
|
||||
esp.LEDC.SetCH0_CONF1_DUTY_START(1)
|
||||
esp.LEDC.SetCH0_CONF0_PARA_UP(1)
|
||||
case ledcChanOpSetDuty:
|
||||
esp.LEDC.SetCH0_DUTY_DUTY(duty)
|
||||
esp.LEDC.SetCH0_CONF1_DUTY_CYCLE(1)
|
||||
esp.LEDC.SetCH0_CONF1_DUTY_NUM(1)
|
||||
esp.LEDC.SetCH0_CONF1_DUTY_INC(1)
|
||||
esp.LEDC.SetCH0_CONF1_DUTY_START(1)
|
||||
esp.LEDC.SetCH0_CONF0_SIG_OUT_EN(1)
|
||||
esp.LEDC.SetCH0_CONF0_PARA_UP(1)
|
||||
case ledcChanOpSetInvert:
|
||||
esp.LEDC.SetCH0_CONF0_IDLE_LV(invVal)
|
||||
}
|
||||
case 1:
|
||||
switch op {
|
||||
case ledcChanOpInit:
|
||||
esp.LEDC.SetCH1_CONF0_TIMER_SEL(uint32(pwm.timerNum))
|
||||
esp.LEDC.SetCH1_CONF0_SIG_OUT_EN(1)
|
||||
esp.LEDC.SetCH1_CONF0_IDLE_LV(0)
|
||||
esp.LEDC.SetCH1_HPOINT_HPOINT(0)
|
||||
esp.LEDC.SetCH1_DUTY_DUTY(0)
|
||||
esp.LEDC.SetCH1_CONF1_DUTY_CYCLE(1)
|
||||
esp.LEDC.SetCH1_CONF1_DUTY_NUM(1)
|
||||
esp.LEDC.SetCH1_CONF1_DUTY_INC(1)
|
||||
esp.LEDC.SetCH1_CONF1_DUTY_START(1)
|
||||
esp.LEDC.SetCH1_CONF0_PARA_UP(1)
|
||||
case ledcChanOpSetDuty:
|
||||
esp.LEDC.SetCH1_DUTY_DUTY(duty)
|
||||
esp.LEDC.SetCH1_CONF1_DUTY_CYCLE(1)
|
||||
esp.LEDC.SetCH1_CONF1_DUTY_NUM(1)
|
||||
esp.LEDC.SetCH1_CONF1_DUTY_INC(1)
|
||||
esp.LEDC.SetCH1_CONF1_DUTY_START(1)
|
||||
esp.LEDC.SetCH1_CONF0_SIG_OUT_EN(1)
|
||||
esp.LEDC.SetCH1_CONF0_PARA_UP(1)
|
||||
case ledcChanOpSetInvert:
|
||||
esp.LEDC.SetCH1_CONF0_IDLE_LV(invVal)
|
||||
}
|
||||
case 2:
|
||||
switch op {
|
||||
case ledcChanOpInit:
|
||||
esp.LEDC.SetCH2_CONF0_TIMER_SEL(uint32(pwm.timerNum))
|
||||
esp.LEDC.SetCH2_CONF0_SIG_OUT_EN(1)
|
||||
esp.LEDC.SetCH2_CONF0_IDLE_LV(0)
|
||||
esp.LEDC.SetCH2_HPOINT_HPOINT(0)
|
||||
esp.LEDC.SetCH2_DUTY_DUTY(0)
|
||||
esp.LEDC.SetCH2_CONF1_DUTY_CYCLE(1)
|
||||
esp.LEDC.SetCH2_CONF1_DUTY_NUM(1)
|
||||
esp.LEDC.SetCH2_CONF1_DUTY_INC(1)
|
||||
esp.LEDC.SetCH2_CONF1_DUTY_START(1)
|
||||
esp.LEDC.SetCH2_CONF0_PARA_UP(1)
|
||||
case ledcChanOpSetDuty:
|
||||
esp.LEDC.SetCH2_DUTY_DUTY(duty)
|
||||
esp.LEDC.SetCH2_CONF1_DUTY_CYCLE(1)
|
||||
esp.LEDC.SetCH2_CONF1_DUTY_NUM(1)
|
||||
esp.LEDC.SetCH2_CONF1_DUTY_INC(1)
|
||||
esp.LEDC.SetCH2_CONF1_DUTY_START(1)
|
||||
esp.LEDC.SetCH2_CONF0_SIG_OUT_EN(1)
|
||||
esp.LEDC.SetCH2_CONF0_PARA_UP(1)
|
||||
case ledcChanOpSetInvert:
|
||||
esp.LEDC.SetCH2_CONF0_IDLE_LV(invVal)
|
||||
}
|
||||
case 3:
|
||||
switch op {
|
||||
case ledcChanOpInit:
|
||||
esp.LEDC.SetCH3_CONF0_TIMER_SEL(uint32(pwm.timerNum))
|
||||
esp.LEDC.SetCH3_CONF0_SIG_OUT_EN(1)
|
||||
esp.LEDC.SetCH3_CONF0_IDLE_LV(0)
|
||||
esp.LEDC.SetCH3_HPOINT_HPOINT(0)
|
||||
esp.LEDC.SetCH3_DUTY_DUTY(0)
|
||||
esp.LEDC.SetCH3_CONF1_DUTY_CYCLE(1)
|
||||
esp.LEDC.SetCH3_CONF1_DUTY_NUM(1)
|
||||
esp.LEDC.SetCH3_CONF1_DUTY_INC(1)
|
||||
esp.LEDC.SetCH3_CONF1_DUTY_START(1)
|
||||
esp.LEDC.SetCH3_CONF0_PARA_UP(1)
|
||||
case ledcChanOpSetDuty:
|
||||
esp.LEDC.SetCH3_DUTY_DUTY(duty)
|
||||
esp.LEDC.SetCH3_CONF1_DUTY_CYCLE(1)
|
||||
esp.LEDC.SetCH3_CONF1_DUTY_NUM(1)
|
||||
esp.LEDC.SetCH3_CONF1_DUTY_INC(1)
|
||||
esp.LEDC.SetCH3_CONF1_DUTY_START(1)
|
||||
esp.LEDC.SetCH3_CONF0_SIG_OUT_EN(1)
|
||||
esp.LEDC.SetCH3_CONF0_PARA_UP(1)
|
||||
case ledcChanOpSetInvert:
|
||||
esp.LEDC.SetCH3_CONF0_IDLE_LV(invVal)
|
||||
}
|
||||
case 4:
|
||||
switch op {
|
||||
case ledcChanOpInit:
|
||||
esp.LEDC.SetCH4_CONF0_TIMER_SEL(uint32(pwm.timerNum))
|
||||
esp.LEDC.SetCH4_CONF0_SIG_OUT_EN(1)
|
||||
esp.LEDC.SetCH4_CONF0_IDLE_LV(0)
|
||||
esp.LEDC.SetCH4_HPOINT_HPOINT(0)
|
||||
esp.LEDC.SetCH4_DUTY_DUTY(0)
|
||||
esp.LEDC.SetCH4_CONF1_DUTY_CYCLE(1)
|
||||
esp.LEDC.SetCH4_CONF1_DUTY_NUM(1)
|
||||
esp.LEDC.SetCH4_CONF1_DUTY_INC(1)
|
||||
esp.LEDC.SetCH4_CONF1_DUTY_START(1)
|
||||
esp.LEDC.SetCH4_CONF0_PARA_UP(1)
|
||||
case ledcChanOpSetDuty:
|
||||
esp.LEDC.SetCH4_DUTY_DUTY(duty)
|
||||
esp.LEDC.SetCH4_CONF1_DUTY_CYCLE(1)
|
||||
esp.LEDC.SetCH4_CONF1_DUTY_NUM(1)
|
||||
esp.LEDC.SetCH4_CONF1_DUTY_INC(1)
|
||||
esp.LEDC.SetCH4_CONF1_DUTY_START(1)
|
||||
esp.LEDC.SetCH4_CONF0_SIG_OUT_EN(1)
|
||||
esp.LEDC.SetCH4_CONF0_PARA_UP(1)
|
||||
case ledcChanOpSetInvert:
|
||||
esp.LEDC.SetCH4_CONF0_IDLE_LV(invVal)
|
||||
}
|
||||
case 5:
|
||||
switch op {
|
||||
case ledcChanOpInit:
|
||||
esp.LEDC.SetCH5_CONF0_TIMER_SEL(uint32(pwm.timerNum))
|
||||
esp.LEDC.SetCH5_CONF0_SIG_OUT_EN(1)
|
||||
esp.LEDC.SetCH5_CONF0_IDLE_LV(0)
|
||||
esp.LEDC.SetCH5_HPOINT_HPOINT(0)
|
||||
esp.LEDC.SetCH5_DUTY_DUTY(0)
|
||||
esp.LEDC.SetCH5_CONF1_DUTY_CYCLE(1)
|
||||
esp.LEDC.SetCH5_CONF1_DUTY_NUM(1)
|
||||
esp.LEDC.SetCH5_CONF1_DUTY_INC(1)
|
||||
esp.LEDC.SetCH5_CONF1_DUTY_START(1)
|
||||
esp.LEDC.SetCH5_CONF0_PARA_UP(1)
|
||||
case ledcChanOpSetDuty:
|
||||
esp.LEDC.SetCH5_DUTY_DUTY(duty)
|
||||
esp.LEDC.SetCH5_CONF1_DUTY_CYCLE(1)
|
||||
esp.LEDC.SetCH5_CONF1_DUTY_NUM(1)
|
||||
esp.LEDC.SetCH5_CONF1_DUTY_INC(1)
|
||||
esp.LEDC.SetCH5_CONF1_DUTY_START(1)
|
||||
esp.LEDC.SetCH5_CONF0_SIG_OUT_EN(1)
|
||||
esp.LEDC.SetCH5_CONF0_PARA_UP(1)
|
||||
case ledcChanOpSetInvert:
|
||||
esp.LEDC.SetCH5_CONF0_IDLE_LV(invVal)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -53,6 +53,7 @@ type SPI struct {
|
||||
var (
|
||||
// SPI0 and SPI1 are reserved for use by the caching system etc.
|
||||
SPI2 = &SPI{esp.SPI2}
|
||||
SPI0 = SPI2
|
||||
)
|
||||
|
||||
// SPIConfig is used to store config info for SPI.
|
||||
|
||||
@@ -0,0 +1,226 @@
|
||||
//go:build esp32s3
|
||||
|
||||
package machine
|
||||
|
||||
import "device/esp"
|
||||
|
||||
// LEDC PWM for ESP32-S3: 4 timers (PWM0–PWM3), 8 channels per timer; each timer has its own frequency.
|
||||
// Range: frequency from a few Hz up to ~40 MHz (at 1-bit resolution); duty resolution 1–15 bits
|
||||
// (higher frequency gives lower resolution). Clock source: APB 80 MHz. Low-speed mode only.
|
||||
// Duty must not equal 2^resolution (overflow risk). See ESP-IDF LEDC driver, TRM LED PWM Controller.
|
||||
|
||||
// GPIO matrix output signal indices for LEDC (soc/gpio_sig_map.h)
|
||||
const (
|
||||
LEDC_LS_SIG_OUT0_IDX = 73
|
||||
)
|
||||
|
||||
const ledcChannelsS3 = 8
|
||||
|
||||
var (
|
||||
PWM0 = &LEDCPWM{SigOutBase: LEDC_LS_SIG_OUT0_IDX, NumChannels: ledcChannelsS3, timerNum: 0}
|
||||
PWM1 = &LEDCPWM{SigOutBase: LEDC_LS_SIG_OUT0_IDX, NumChannels: ledcChannelsS3, timerNum: 1}
|
||||
PWM2 = &LEDCPWM{SigOutBase: LEDC_LS_SIG_OUT0_IDX, NumChannels: ledcChannelsS3, timerNum: 2}
|
||||
PWM3 = &LEDCPWM{SigOutBase: LEDC_LS_SIG_OUT0_IDX, NumChannels: ledcChannelsS3, timerNum: 3}
|
||||
)
|
||||
|
||||
// chanOp implements LEDC low-speed channel ops for ESP32-S3 (channels 0–7).
|
||||
func (pwm *LEDCPWM) chanOp(ch uint8, op ledcChanOp, duty uint32, inverting bool) {
|
||||
invVal := uint32(0)
|
||||
if inverting {
|
||||
invVal = 1
|
||||
}
|
||||
switch ch {
|
||||
case 0:
|
||||
switch op {
|
||||
case ledcChanOpInit:
|
||||
esp.LEDC.SetCH0_CONF0_TIMER_SEL(uint32(pwm.timerNum))
|
||||
esp.LEDC.SetCH0_CONF0_SIG_OUT_EN(1)
|
||||
esp.LEDC.SetCH0_CONF0_IDLE_LV(0)
|
||||
esp.LEDC.SetCH0_HPOINT_HPOINT(0)
|
||||
esp.LEDC.SetCH0_DUTY_DUTY(0)
|
||||
esp.LEDC.SetCH0_CONF1_DUTY_CYCLE(1)
|
||||
esp.LEDC.SetCH0_CONF1_DUTY_NUM(1)
|
||||
esp.LEDC.SetCH0_CONF1_DUTY_INC(1)
|
||||
esp.LEDC.SetCH0_CONF1_DUTY_START(1)
|
||||
esp.LEDC.SetCH0_CONF0_PARA_UP(1)
|
||||
case ledcChanOpSetDuty:
|
||||
esp.LEDC.SetCH0_DUTY_DUTY(duty)
|
||||
esp.LEDC.SetCH0_CONF1_DUTY_CYCLE(1)
|
||||
esp.LEDC.SetCH0_CONF1_DUTY_NUM(1)
|
||||
esp.LEDC.SetCH0_CONF1_DUTY_INC(1)
|
||||
esp.LEDC.SetCH0_CONF1_DUTY_START(1)
|
||||
esp.LEDC.SetCH0_CONF0_SIG_OUT_EN(1)
|
||||
esp.LEDC.SetCH0_CONF0_PARA_UP(1)
|
||||
case ledcChanOpSetInvert:
|
||||
esp.LEDC.SetCH0_CONF0_IDLE_LV(invVal)
|
||||
}
|
||||
case 1:
|
||||
switch op {
|
||||
case ledcChanOpInit:
|
||||
esp.LEDC.SetCH1_CONF0_TIMER_SEL(uint32(pwm.timerNum))
|
||||
esp.LEDC.SetCH1_CONF0_SIG_OUT_EN(1)
|
||||
esp.LEDC.SetCH1_CONF0_IDLE_LV(0)
|
||||
esp.LEDC.SetCH1_HPOINT_HPOINT(0)
|
||||
esp.LEDC.SetCH1_DUTY_DUTY(0)
|
||||
esp.LEDC.SetCH1_CONF1_DUTY_CYCLE(1)
|
||||
esp.LEDC.SetCH1_CONF1_DUTY_NUM(1)
|
||||
esp.LEDC.SetCH1_CONF1_DUTY_INC(1)
|
||||
esp.LEDC.SetCH1_CONF1_DUTY_START(1)
|
||||
esp.LEDC.SetCH1_CONF0_PARA_UP(1)
|
||||
case ledcChanOpSetDuty:
|
||||
esp.LEDC.SetCH1_DUTY_DUTY(duty)
|
||||
esp.LEDC.SetCH1_CONF1_DUTY_CYCLE(1)
|
||||
esp.LEDC.SetCH1_CONF1_DUTY_NUM(1)
|
||||
esp.LEDC.SetCH1_CONF1_DUTY_INC(1)
|
||||
esp.LEDC.SetCH1_CONF1_DUTY_START(1)
|
||||
esp.LEDC.SetCH1_CONF0_SIG_OUT_EN(1)
|
||||
esp.LEDC.SetCH1_CONF0_PARA_UP(1)
|
||||
case ledcChanOpSetInvert:
|
||||
esp.LEDC.SetCH1_CONF0_IDLE_LV(invVal)
|
||||
}
|
||||
case 2:
|
||||
switch op {
|
||||
case ledcChanOpInit:
|
||||
esp.LEDC.SetCH2_CONF0_TIMER_SEL(uint32(pwm.timerNum))
|
||||
esp.LEDC.SetCH2_CONF0_SIG_OUT_EN(1)
|
||||
esp.LEDC.SetCH2_CONF0_IDLE_LV(0)
|
||||
esp.LEDC.SetCH2_HPOINT_HPOINT(0)
|
||||
esp.LEDC.SetCH2_DUTY_DUTY(0)
|
||||
esp.LEDC.SetCH2_CONF1_DUTY_CYCLE(1)
|
||||
esp.LEDC.SetCH2_CONF1_DUTY_NUM(1)
|
||||
esp.LEDC.SetCH2_CONF1_DUTY_INC(1)
|
||||
esp.LEDC.SetCH2_CONF1_DUTY_START(1)
|
||||
esp.LEDC.SetCH2_CONF0_PARA_UP(1)
|
||||
case ledcChanOpSetDuty:
|
||||
esp.LEDC.SetCH2_DUTY_DUTY(duty)
|
||||
esp.LEDC.SetCH2_CONF1_DUTY_CYCLE(1)
|
||||
esp.LEDC.SetCH2_CONF1_DUTY_NUM(1)
|
||||
esp.LEDC.SetCH2_CONF1_DUTY_INC(1)
|
||||
esp.LEDC.SetCH2_CONF1_DUTY_START(1)
|
||||
esp.LEDC.SetCH2_CONF0_SIG_OUT_EN(1)
|
||||
esp.LEDC.SetCH2_CONF0_PARA_UP(1)
|
||||
case ledcChanOpSetInvert:
|
||||
esp.LEDC.SetCH2_CONF0_IDLE_LV(invVal)
|
||||
}
|
||||
case 3:
|
||||
switch op {
|
||||
case ledcChanOpInit:
|
||||
esp.LEDC.SetCH3_CONF0_TIMER_SEL(uint32(pwm.timerNum))
|
||||
esp.LEDC.SetCH3_CONF0_SIG_OUT_EN(1)
|
||||
esp.LEDC.SetCH3_CONF0_IDLE_LV(0)
|
||||
esp.LEDC.SetCH3_HPOINT_HPOINT(0)
|
||||
esp.LEDC.SetCH3_DUTY_DUTY(0)
|
||||
esp.LEDC.SetCH3_CONF1_DUTY_CYCLE(1)
|
||||
esp.LEDC.SetCH3_CONF1_DUTY_NUM(1)
|
||||
esp.LEDC.SetCH3_CONF1_DUTY_INC(1)
|
||||
esp.LEDC.SetCH3_CONF1_DUTY_START(1)
|
||||
esp.LEDC.SetCH3_CONF0_PARA_UP(1)
|
||||
case ledcChanOpSetDuty:
|
||||
esp.LEDC.SetCH3_DUTY_DUTY(duty)
|
||||
esp.LEDC.SetCH3_CONF1_DUTY_CYCLE(1)
|
||||
esp.LEDC.SetCH3_CONF1_DUTY_NUM(1)
|
||||
esp.LEDC.SetCH3_CONF1_DUTY_INC(1)
|
||||
esp.LEDC.SetCH3_CONF1_DUTY_START(1)
|
||||
esp.LEDC.SetCH3_CONF0_SIG_OUT_EN(1)
|
||||
esp.LEDC.SetCH3_CONF0_PARA_UP(1)
|
||||
case ledcChanOpSetInvert:
|
||||
esp.LEDC.SetCH3_CONF0_IDLE_LV(invVal)
|
||||
}
|
||||
case 4:
|
||||
switch op {
|
||||
case ledcChanOpInit:
|
||||
esp.LEDC.SetCH4_CONF0_TIMER_SEL(uint32(pwm.timerNum))
|
||||
esp.LEDC.SetCH4_CONF0_SIG_OUT_EN(1)
|
||||
esp.LEDC.SetCH4_CONF0_IDLE_LV(0)
|
||||
esp.LEDC.SetCH4_HPOINT_HPOINT(0)
|
||||
esp.LEDC.SetCH4_DUTY_DUTY(0)
|
||||
esp.LEDC.SetCH4_CONF1_DUTY_CYCLE(1)
|
||||
esp.LEDC.SetCH4_CONF1_DUTY_NUM(1)
|
||||
esp.LEDC.SetCH4_CONF1_DUTY_INC(1)
|
||||
esp.LEDC.SetCH4_CONF1_DUTY_START(1)
|
||||
esp.LEDC.SetCH4_CONF0_PARA_UP(1)
|
||||
case ledcChanOpSetDuty:
|
||||
esp.LEDC.SetCH4_DUTY_DUTY(duty)
|
||||
esp.LEDC.SetCH4_CONF1_DUTY_CYCLE(1)
|
||||
esp.LEDC.SetCH4_CONF1_DUTY_NUM(1)
|
||||
esp.LEDC.SetCH4_CONF1_DUTY_INC(1)
|
||||
esp.LEDC.SetCH4_CONF1_DUTY_START(1)
|
||||
esp.LEDC.SetCH4_CONF0_SIG_OUT_EN(1)
|
||||
esp.LEDC.SetCH4_CONF0_PARA_UP(1)
|
||||
case ledcChanOpSetInvert:
|
||||
esp.LEDC.SetCH4_CONF0_IDLE_LV(invVal)
|
||||
}
|
||||
case 5:
|
||||
switch op {
|
||||
case ledcChanOpInit:
|
||||
esp.LEDC.SetCH5_CONF0_TIMER_SEL(uint32(pwm.timerNum))
|
||||
esp.LEDC.SetCH5_CONF0_SIG_OUT_EN(1)
|
||||
esp.LEDC.SetCH5_CONF0_IDLE_LV(0)
|
||||
esp.LEDC.SetCH5_HPOINT_HPOINT(0)
|
||||
esp.LEDC.SetCH5_DUTY_DUTY(0)
|
||||
esp.LEDC.SetCH5_CONF1_DUTY_CYCLE(1)
|
||||
esp.LEDC.SetCH5_CONF1_DUTY_NUM(1)
|
||||
esp.LEDC.SetCH5_CONF1_DUTY_INC(1)
|
||||
esp.LEDC.SetCH5_CONF1_DUTY_START(1)
|
||||
esp.LEDC.SetCH5_CONF0_PARA_UP(1)
|
||||
case ledcChanOpSetDuty:
|
||||
esp.LEDC.SetCH5_DUTY_DUTY(duty)
|
||||
esp.LEDC.SetCH5_CONF1_DUTY_CYCLE(1)
|
||||
esp.LEDC.SetCH5_CONF1_DUTY_NUM(1)
|
||||
esp.LEDC.SetCH5_CONF1_DUTY_INC(1)
|
||||
esp.LEDC.SetCH5_CONF1_DUTY_START(1)
|
||||
esp.LEDC.SetCH5_CONF0_SIG_OUT_EN(1)
|
||||
esp.LEDC.SetCH5_CONF0_PARA_UP(1)
|
||||
case ledcChanOpSetInvert:
|
||||
esp.LEDC.SetCH5_CONF0_IDLE_LV(invVal)
|
||||
}
|
||||
case 6:
|
||||
switch op {
|
||||
case ledcChanOpInit:
|
||||
esp.LEDC.SetCH6_CONF0_TIMER_SEL(uint32(pwm.timerNum))
|
||||
esp.LEDC.SetCH6_CONF0_SIG_OUT_EN(1)
|
||||
esp.LEDC.SetCH6_CONF0_IDLE_LV(0)
|
||||
esp.LEDC.SetCH6_HPOINT_HPOINT(0)
|
||||
esp.LEDC.SetCH6_DUTY_DUTY(0)
|
||||
esp.LEDC.SetCH6_CONF1_DUTY_CYCLE(1)
|
||||
esp.LEDC.SetCH6_CONF1_DUTY_NUM(1)
|
||||
esp.LEDC.SetCH6_CONF1_DUTY_INC(1)
|
||||
esp.LEDC.SetCH6_CONF1_DUTY_START(1)
|
||||
esp.LEDC.SetCH6_CONF0_PARA_UP(1)
|
||||
case ledcChanOpSetDuty:
|
||||
esp.LEDC.SetCH6_DUTY_DUTY(duty)
|
||||
esp.LEDC.SetCH6_CONF1_DUTY_CYCLE(1)
|
||||
esp.LEDC.SetCH6_CONF1_DUTY_NUM(1)
|
||||
esp.LEDC.SetCH6_CONF1_DUTY_INC(1)
|
||||
esp.LEDC.SetCH6_CONF1_DUTY_START(1)
|
||||
esp.LEDC.SetCH6_CONF0_SIG_OUT_EN(1)
|
||||
esp.LEDC.SetCH6_CONF0_PARA_UP(1)
|
||||
case ledcChanOpSetInvert:
|
||||
esp.LEDC.SetCH6_CONF0_IDLE_LV(invVal)
|
||||
}
|
||||
case 7:
|
||||
switch op {
|
||||
case ledcChanOpInit:
|
||||
esp.LEDC.SetCH7_CONF0_TIMER_SEL(uint32(pwm.timerNum))
|
||||
esp.LEDC.SetCH7_CONF0_SIG_OUT_EN(1)
|
||||
esp.LEDC.SetCH7_CONF0_IDLE_LV(0)
|
||||
esp.LEDC.SetCH7_HPOINT_HPOINT(0)
|
||||
esp.LEDC.SetCH7_DUTY_DUTY(0)
|
||||
esp.LEDC.SetCH7_CONF1_DUTY_CYCLE(1)
|
||||
esp.LEDC.SetCH7_CONF1_DUTY_NUM(1)
|
||||
esp.LEDC.SetCH7_CONF1_DUTY_INC(1)
|
||||
esp.LEDC.SetCH7_CONF1_DUTY_START(1)
|
||||
esp.LEDC.SetCH7_CONF0_PARA_UP(1)
|
||||
case ledcChanOpSetDuty:
|
||||
esp.LEDC.SetCH7_DUTY_DUTY(duty)
|
||||
esp.LEDC.SetCH7_CONF1_DUTY_CYCLE(1)
|
||||
esp.LEDC.SetCH7_CONF1_DUTY_NUM(1)
|
||||
esp.LEDC.SetCH7_CONF1_DUTY_INC(1)
|
||||
esp.LEDC.SetCH7_CONF1_DUTY_START(1)
|
||||
esp.LEDC.SetCH7_CONF0_SIG_OUT_EN(1)
|
||||
esp.LEDC.SetCH7_CONF0_PARA_UP(1)
|
||||
case ledcChanOpSetInvert:
|
||||
esp.LEDC.SetCH7_CONF0_IDLE_LV(invVal)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
//go:build esp32c3 || esp32s3
|
||||
|
||||
// PWM on ESP32-C3/S3 uses the LEDC (LED Control) peripheral, low-speed mode only.
|
||||
// One timer drives multiple channels; each channel has its own duty, shared frequency.
|
||||
// Pin routing is via GPIO matrix (SigOutBase + channel index).
|
||||
//
|
||||
// Channel config (chanOp) follows the hardware contract from:
|
||||
// - ESP-IDF: https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/ledc.html
|
||||
// (timer config → channel config → duty + update_duty).
|
||||
// - SVD (e.g. lib/cmsis-svd/data/Espressif/esp32s3.svd): CONF0.PARA_UP "updates
|
||||
// HPOINT, DUTY_START, SIG_OUT_EN, TIMER_SEL, DUTY_NUM, DUTY_CYCLE, DUTY_SCALE,
|
||||
// DUTY_INC for channel and is auto-cleared by hardware"; CONF1.DUTY_START "other
|
||||
// CONF1 fields take effect when this bit is set to 1".
|
||||
|
||||
package machine
|
||||
|
||||
import (
|
||||
"device/esp"
|
||||
"errors"
|
||||
)
|
||||
|
||||
const ledcApbClock = 80_000000
|
||||
|
||||
const ledcDutyFracBits = 4 // DUTY register has 4 fractional bits; write value<<4
|
||||
|
||||
const ledcDividerFracBits = 8 // Clock divider register = actual_divider * 256
|
||||
|
||||
var errPWMNoChannel = errors.New("pwm: no free channel")
|
||||
|
||||
type LEDCPWM struct {
|
||||
SigOutBase uint32 // GPIO matrix signal index for channel 0 (e.g. 73 on S3, 45 on C3)
|
||||
NumChannels uint8
|
||||
timerNum uint8 // 0–3: which LEDC timer (frequency) this PWM uses
|
||||
dutyRes uint8
|
||||
configured bool
|
||||
channelPin [8]Pin
|
||||
}
|
||||
|
||||
type ledcChanOp uint8
|
||||
|
||||
const (
|
||||
ledcChanOpInit ledcChanOp = iota // initial per-channel setup (timer, enable, HPOINT/DUTY/CONF1, PARA_UP)
|
||||
ledcChanOpSetDuty // update duty and latch it (DUTY + CONF1 + PARA_UP)
|
||||
ledcChanOpSetInvert // change idle level (IDLE_LV)
|
||||
)
|
||||
|
||||
func (pwm *LEDCPWM) Configure(config PWMConfig) error {
|
||||
// Enable LEDC clock and release reset (SYSTEM perip_clk_en0 / perip_rst_en0).
|
||||
esp.SYSTEM.SetPERIP_RST_EN0_LEDC_RST(1)
|
||||
esp.SYSTEM.SetPERIP_CLK_EN0_LEDC_CLK_EN(1)
|
||||
esp.SYSTEM.SetPERIP_RST_EN0_LEDC_RST(0)
|
||||
|
||||
// LEDC global: APB clock source, enable internal clock.
|
||||
esp.LEDC.SetCONF_APB_CLK_SEL(1)
|
||||
esp.LEDC.SetCONF_CLK_EN(1)
|
||||
|
||||
period := config.Period
|
||||
if period == 0 {
|
||||
period = 1_000_000
|
||||
}
|
||||
freq := uint64(1e9) / period
|
||||
dutyRes := uint8(10)
|
||||
switch {
|
||||
case freq < 100:
|
||||
dutyRes = 14
|
||||
case freq < 1000:
|
||||
dutyRes = 12
|
||||
case freq > 100_000:
|
||||
dutyRes = 8
|
||||
}
|
||||
|
||||
// Timer divider: period_ns = (2^dutyRes * divActual/256) / 80MHz * 1e9 => divReg = divActual<<8.
|
||||
divActual := ledcApbClock / (uint32(freq) * (1 << dutyRes))
|
||||
if divActual == 0 {
|
||||
divActual = 1
|
||||
}
|
||||
divReg := divActual << ledcDividerFracBits
|
||||
if divReg > 0x3ffff {
|
||||
return ErrPWMPeriodTooLong
|
||||
}
|
||||
|
||||
// Selected timer: resolution, divider, no pause, reset then latch config with PARA_UP.
|
||||
pwm.setTimerConf(dutyRes, divReg)
|
||||
|
||||
pwm.dutyRes = dutyRes
|
||||
pwm.configured = true
|
||||
for i := range pwm.channelPin {
|
||||
pwm.channelPin[i] = NoPin
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pwm *LEDCPWM) setTimerConf(dutyRes uint8, divReg uint32) {
|
||||
t := pwm.timerNum
|
||||
switch t {
|
||||
case 0:
|
||||
esp.LEDC.SetTIMER0_CONF_DUTY_RES(uint32(dutyRes))
|
||||
esp.LEDC.SetTIMER0_CONF_CLK_DIV(divReg)
|
||||
esp.LEDC.SetTIMER0_CONF_TICK_SEL(0)
|
||||
esp.LEDC.SetTIMER0_CONF_PAUSE(0)
|
||||
esp.LEDC.SetTIMER0_CONF_RST(1)
|
||||
esp.LEDC.SetTIMER0_CONF_RST(0)
|
||||
esp.LEDC.SetTIMER0_CONF_PARA_UP(1)
|
||||
case 1:
|
||||
esp.LEDC.SetTIMER1_CONF_DUTY_RES(uint32(dutyRes))
|
||||
esp.LEDC.SetTIMER1_CONF_CLK_DIV(divReg)
|
||||
esp.LEDC.SetTIMER1_CONF_TICK_SEL(0)
|
||||
esp.LEDC.SetTIMER1_CONF_PAUSE(0)
|
||||
esp.LEDC.SetTIMER1_CONF_RST(1)
|
||||
esp.LEDC.SetTIMER1_CONF_RST(0)
|
||||
esp.LEDC.SetTIMER1_CONF_PARA_UP(1)
|
||||
case 2:
|
||||
esp.LEDC.SetTIMER2_CONF_DUTY_RES(uint32(dutyRes))
|
||||
esp.LEDC.SetTIMER2_CONF_CLK_DIV(divReg)
|
||||
esp.LEDC.SetTIMER2_CONF_TICK_SEL(0)
|
||||
esp.LEDC.SetTIMER2_CONF_PAUSE(0)
|
||||
esp.LEDC.SetTIMER2_CONF_RST(1)
|
||||
esp.LEDC.SetTIMER2_CONF_RST(0)
|
||||
esp.LEDC.SetTIMER2_CONF_PARA_UP(1)
|
||||
case 3:
|
||||
esp.LEDC.SetTIMER3_CONF_DUTY_RES(uint32(dutyRes))
|
||||
esp.LEDC.SetTIMER3_CONF_CLK_DIV(divReg)
|
||||
esp.LEDC.SetTIMER3_CONF_TICK_SEL(0)
|
||||
esp.LEDC.SetTIMER3_CONF_PAUSE(0)
|
||||
esp.LEDC.SetTIMER3_CONF_RST(1)
|
||||
esp.LEDC.SetTIMER3_CONF_RST(0)
|
||||
esp.LEDC.SetTIMER3_CONF_PARA_UP(1)
|
||||
}
|
||||
}
|
||||
|
||||
func (pwm *LEDCPWM) Channel(pin Pin) (uint8, error) {
|
||||
if !pwm.configured {
|
||||
return 0, errors.New("pwm: not configured")
|
||||
}
|
||||
if pin == NoPin {
|
||||
return 0, ErrInvalidOutputPin
|
||||
}
|
||||
var ch uint8
|
||||
for ch = 0; ch < pwm.NumChannels; ch++ {
|
||||
if pwm.channelPin[ch] == NoPin {
|
||||
break
|
||||
}
|
||||
}
|
||||
if ch >= pwm.NumChannels {
|
||||
return 0, errPWMNoChannel
|
||||
}
|
||||
|
||||
pwm.channelPin[ch] = pin
|
||||
signal := pwm.SigOutBase + uint32(ch)
|
||||
pin.configure(PinConfig{Mode: PinOutput}, signal) // GPIO matrix: pin <- LEDC_LS_SIG_OUTn
|
||||
pwm.chanOp(ch, ledcChanOpInit, 0, false)
|
||||
return ch, nil
|
||||
}
|
||||
|
||||
func (pwm *LEDCPWM) Set(channel uint8, value uint32) {
|
||||
if channel >= pwm.NumChannels {
|
||||
return
|
||||
}
|
||||
top := uint32(1<<pwm.dutyRes) - 1
|
||||
if value > top {
|
||||
value = top
|
||||
}
|
||||
dutyVal := value << ledcDutyFracBits
|
||||
pwm.chanOp(channel, ledcChanOpSetDuty, dutyVal, false)
|
||||
}
|
||||
|
||||
func (pwm *LEDCPWM) Top() uint32 {
|
||||
if !pwm.configured {
|
||||
return 0
|
||||
}
|
||||
return uint32(1<<pwm.dutyRes) - 1
|
||||
}
|
||||
|
||||
func (pwm *LEDCPWM) SetInverting(channel uint8, inverting bool) {
|
||||
if channel >= pwm.NumChannels {
|
||||
return
|
||||
}
|
||||
pwm.chanOp(channel, ledcChanOpSetInvert, 0, inverting)
|
||||
}
|
||||
Reference in New Issue
Block a user