machine/esp32: add PWM support using the LEDC peripheral for classic esp32 board - #5669
zombieleet wants to merge 1 commit into
Conversation
IMG_4246.mov |
8850e1f to
763c83b
Compare
|
Hi @deadprogram PTAL |
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.
763c83b to
b975321
Compare
|
Thanks @zombieleet. Notes below, edited from an automated review. I built the three targets. The
The following items are not new problems in this PR. What do you think, would it be a good idea to address them now, or to leave for a separate PR?
|
Why
The classic ESP32 is the only ESP32 chip in TinyGo without PWM. This adds it.
It uses the high speed half of the LEDC peripheral. This gives 4 timers
(
PWM0toPWM3) and 8 channels. You can use any pin, because the signal goesthrough the GPIO matrix.
This closes the ESP32 part of #5186. Thanks to @jespino for the first version
and for the hardware research. His register values were a good check while I
wrote this.
What changed
The C3 and the S3 have LEDC PWM. Most of
machine_esp32xx_pwm.gois the samefor all of these chips. This change uses that code again. It does not add a
second driver for the same peripheral.
Three functions in that file work only on the newer chips. They move to a file
for each chip.
enableClockSYSTEM. This chip usesDPORT. It also has noLEDC.CONF_CLK_ENbit.setTimerConfHSTIMER0_CONF, notTIMER0_CONF.chanOpHSCH0_CONF0, notCH0_CONF0.The files are now:
machine_esp32xx_ls_pwm.goholds code that moved out of the shared file. Thecode did not change. The C3 and the S3 keep their behaviour.
This also adds
src/examples/pwm/esp32-coreboard-v2.goand one line in thesmoketest-esptarget.Why these decisions
Why this is not a change to #5186.
That PR is older than the
esp32xxshared layer from #5215. It adds a type ofits own in one file of 601 lines. The tree would then have two LEDC drivers.
One uses the generated SVD setters. The other uses register offsets and masks
written by hand. The shared type keeps one driver. It also checks the register
names against the description from Espressif.
Why the high speed block.
This chip has a high speed block and a low speed block. High speed channels
apply a duty change themselves at the end of the period. There is no glitch and
no
PARA_UPbit to set. The low speed block can come later if somebody needsthe 8 more channels.
Why a new file and not a change to the shared file.
TICK_SELhas the opposite meaning on this chip.A shared
setTimerConfwould select the 1MHz clock on the ESP32. Allfrequencies would then be 80 times too slow. A file for each chip prevents
this.
Why
chanOpis a long switch.Each channel has its own registers and its own generated setter. There is no
array to index. The C3 and S3 files have the same shape.
Why GPIO18 and GPIO19 in the example.
These pins have no function at boot. They also do not go to an LED on the
board.
MCPWM is not in this change. Motor control PWM is a different peripheral. It
needs dead time and fault inputs, so it needs a different API.
Tests
gofmtesp32-coreboard-v2,esp32-generic,esp32c3-generic,esp32c3-supermini,esp32s3-genericandxiao-esp32s3examples/pwmbinaries foresp32c3-superminiandxiao-esp32s3arethe same byte for byte before and after this change
A servo on GPIO15 turns from 0 to 180 degrees and back. The movement is smooth.
A servo moves only with pulses between 0.5ms and 2.5ms that repeat near 50Hz. A
wrong divider puts the arm in the wrong place. A wrong duty stops the movement.
One known limit
The shared
Configurecuts the clock divider to a whole number.At 50Hz with 14 bit resolution this is
97.66, and the code uses97. Theperiod is then 19.87ms and not 20ms. Pulses are about 0.67% too short.
This is the behaviour that the C3 and the S3 have now. This change does not add
it. A divider that rounds to the nearest number would give about 0.35%. That
changes the output of all three chips, so it belongs in its own PR.