-
Notifications
You must be signed in to change notification settings - Fork 1k
machine/esp32: add PWM support using LEDC peripheral #5186
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
jespino
wants to merge
9
commits into
tinygo-org:dev
Choose a base branch
from
jespino:esp32-pwm-support
base: dev
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add PWM support for ESP32 using the LEDC (LED Control) peripheral. Features: - 4 high-speed timers (PWM0-PWM3) with glitch-free duty updates - Up to 8 channels shared across timers - Configurable duty resolution (1-20 bits) - Flexible frequency control via period setting - Any GPIO can be used via GPIO matrix routing The implementation follows the standard TinyGo PWM interface with Configure, Channel, Set, SetPeriod, Top, and SetInverting methods. Co-authored-by: Ona <no-reply@ona.com>
fdc7b55 to
ee33b25
Compare
Fix several issues with the LEDC PWM implementation: 1. Timer reset: The reset bit must be set then cleared (not just set) 2. CONF1 register: For non-fading operation, duty_cycle and duty_num must be set to 1, and duty_inc must be enabled 3. Duty update: Properly configure CONF1 when updating duty values These fixes align with the ESP-IDF LEDC driver implementation. Co-authored-by: Ona <no-reply@ona.com>
The LEDC clock divider register uses 8 fractional bits, meaning the register value is actual_divider * 256. The previous implementation was setting the divider directly without accounting for this, resulting in a divider that was 256x smaller than intended. Also added: - Set LEDC.CONF.APB_CLK_SEL = 1 to select APB clock source - Updated Period() calculation to account for fractional bits Co-authored-by: Ona <no-reply@ona.com>
- Always write full CONF1 register value when updating duty - Re-enable sig_out_en on each duty update (matching ESP-IDF behavior) This should improve the smoothness of duty cycle transitions. Co-authored-by: Ona <no-reply@ona.com>
The duty_resolution register stores the bit width directly, not bit width - 1. Setting resolution-1 caused the timer counter to wrap at half the expected value, resulting in duty cycle glitches when the duty value exceeded the actual counter range. For example, with 20-bit resolution intended: - Before: register = 19, counter wraps at 524287 - After: register = 20, counter wraps at 1048575 This fixes the 'reset in the middle' behavior during fades. Co-authored-by: Ona <no-reply@ona.com>
Improvements: - Fix SetPeriod using resolution-1 instead of resolution - Refactor channel tracking to properly associate channels with timers - Add pwmChannelInfo struct to track pin, timer, and usage state - Prevent same pin from being used on different timers - Add ReleaseChannel() method to free channels - Add Get() method to read current duty value - Fix SetInverting to use GPIO matrix inversion (bit 9) - SetPeriod now only scales channels bound to this timer - Remove unused constants, add named constants for defaults - Add bounds checking in Period() for zero values Co-authored-by: Ona <no-reply@ona.com>
Additional improvements: - Add errPWMPeriodTooShort error for periods that are too short - Add pwmChannelCount constant and use it consistently - Add gpioMatrixInvertBit constant for clarity - Use dividerFracBits constant in calculations instead of magic 256 - Add isValidChannel() helper for centralized validation - Add IsConnected() method to check if channel is in use - Add SetCounter() method for timer synchronization (API compatibility) - Handle unconfigured timer in Set() by using default resolution - Improve Enable() documentation - Remove unused chanIdleLvMask constant Co-authored-by: Ona <no-reply@ona.com>
Additional improvements: - Add pwmTimerCount constant and use it for pwmStates array - Use pwmChannelCount constant for pwmChannels array declaration - Add IsEnabled() method to check if timer is running - Add Frequency() method to get current PWM frequency in Hz - Add Resolution() method to get current duty resolution in bits - Add GetPin() method to get pin assigned to a channel - Rename SetCounter() to ResetCounter() (ESP32 only supports reset to 0) - Fix Period() to use dividerFracBits constant instead of magic 256 - Remove misleading 'Idle level low' comment in Channel() Co-authored-by: Ona <no-reply@ona.com>
Final improvements: - Add apbClockMHz constant (80) instead of hardcoded values - Add timerRegisterStride and channelRegisterStride constants - Add SetFrequency() method for convenience - Add ChannelCount() method to query available channels - Optimize Channel() to use single-pass loop instead of three loops - Add error documentation to Channel() function comment - Simplify register access helper comments Co-authored-by: Ona <no-reply@ona.com>
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Summary
Add PWM support for ESP32 using the LEDC (LED Control) peripheral. We are not including Motor control PWM because it would probably require a different API and the current existing LEDC should work well for leds and basic motors and servos.
Features
PWM0-PWM3) with glitch-free duty updatesImplementation Details
The implementation uses the ESP32's LEDC peripheral which is designed for LED dimming but works well for general PWM applications. Key characteristics:
API
Follows the standard TinyGo PWM interface:
Files Changed
src/machine/machine_esp32_pwm.go- Main PWM implementationsrc/examples/pwm/esp32.go- Example configuration for ESP32 boardsTesting
gofmt