Skip to content

PINIO: add PWM duty control and unify output mode handling#11375

Draft
sensei-hacker wants to merge 2 commits intoiNavFlight:maintenance-9.xfrom
sensei-hacker:feature/unified-pinio-pwm-output
Draft

PINIO: add PWM duty control and unify output mode handling#11375
sensei-hacker wants to merge 2 commits intoiNavFlight:maintenance-9.xfrom
sensei-hacker:feature/unified-pinio-pwm-output

Conversation

@sensei-hacker
Copy link
Member

@sensei-hacker sensei-hacker commented Feb 28, 2026

image

Problem

Users sometimes need PINIO that isn't natively provided by the board, or want to do duty-cycle based PWM.
Turning a pin on and off is of course easy to do technically, but INAV didn't provide a way for users to configure that.

This was useful but very limited: only that one pin could do duty-cycle PWM.

As a workaround, a clever user who needed a a PINIO might discover INAV had a special-case PWM duty-cycle mode tied specifically to the LED strip pin — primarily used for camera control (OSD joystick).
A clever user could use the Programming tab to set 0% or 100% duty cycle — a roundabout approach to make a PINIO out of a little-known feature.

Meanwhile, the PINIO subsystem (USER1–USER4 outputs, controllable via the Modes tab) provided clean on/off GPIO control, but only for the one or or two pins specifically chosen by the manufacturer.

Then we had another special case for the LED pin - it could be used as a regular PWM output for a servo or whatever. Lots of special cases, handled by special-case code paths.

Solution

This PR is the configurator side of consolidating duty-cycle PWM control into the PINIO subsystem (firmware PR: #11375), making it available on any PWM-capable timer output — not just the LED pin, removing the special cases and allowing the user to assign these functions to any capable output pin.

Users can now:

  • Set any timer output to "PINIO / DUTY CYCLE" mode in the mixer Output tab
  • Control its duty cycle directly from the Programming tab using the PINIO PWM condition with a channel index and duty value
  • See the correct USER number (USER1, USER2, etc.) displayed in the Output Mapping tab automatically — the firmware reports the channel assignment, so the configurator doesn't need to figure it out

No roundabout workarounds needed.

Currently, by removing special-case code paths and consolidating, the more flexible system adds 6 lines of code net.

Testing

  • Transpiler tests: 28/28 passing
  • UI functional testing (output mapping USER label, PINIO PWM condition with two operands): pending physical FC connection

Notes

Breaking change: The LED Pin PWM programming condition is renamed to PINIO PWM. The second operand (duty cycle) is now enabled — operandA = channel index, operandB = duty. Users with existing LED Pin PWM conditions will see them relabeled and will need to set the operands correctly.

Companion configurator PR: iNavFlight/inav-configurator#2579

- pinio: auto-detect timer capability in pinioInit(); configure PWM when
  timer is free (OWNER_FREE), fall back to GPIO for occupied timers
- pinio: add pinioSetDuty(channel, duty) for 0-100% PWM control at 24 kHz
- Remove LED strip PWM code path (ledConfigurePWM/ledPinStartPWM/StopPWM);
  these functions are superseded by pinioSetDuty()
- osd_joystick: migrate from led pin PWM to pinioSetDuty(); add
  osd_joystick_pinio_channel setting; bump PG version to 1
- Guard osd_joystick and rcdevice_cam with USE_PINIO instead of USE_LED_STRIP
- programming: rename LOGIC_CONDITION_LED_PIN_PWM to LOGIC_CONDITION_PINIO_PWM
  (value 52 preserved for backward compat); operandA=channel, operandB=duty
- mixer: add OUTPUT_MODE_PINIO=4; clears MOTOR/SERVO/LED flags so pinioInit()
  can claim the timer output
- MSP OUTPUT_MAPPING_EXT2: set specialLabels=PIN_LABEL_PINIO_BASE+j for
  PINIO channel j, enabling configurator to display correct USER number
- Fix missing bounds check in pinioSet() (index < 0 || >= pinioHardwareCount)
- Validate cliLedPinPWM duty input range (0-100)
Remove the led_pin_pwm_mode setting (SHARED_LOW/SHARED_HIGH/LOW/HIGH)
and PG_LEDPIN_CONFIG parameter group. Dedicated PWM is now handled by
PINIO channels; LED strip idle level is controllable at runtime via
programming framework operation 52 (channel=PINIO_COUNT) or CLI
command `piniopwm [channel] <duty>`.

- Remove led_pin_pwm_mode_e enum, ledPinConfig_t, 3 dead declarations
- Add ws2811SetIdleHigh(bool) for binary LED idle level control
- Rename CLI command ledpinpwm -> piniopwm with channel parameter
- Route operation 52 channel==PINIO_COUNT to LED strip idle level
- Rename docs/LED pin PWM.md -> docs/PINIO PWM.md, rewrite
- Update OSD Joystick.md and Programming Framework.md references

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant