Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 0 additions & 96 deletions docs/LED pin PWM.md

This file was deleted.

18 changes: 9 additions & 9 deletions docs/OSD Joystick.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# OSD joystick

LED pin can be used to emulate 5key OSD joystick for OSD camera pin, while still driving ws2812 LEDs (shared functionality).
A PINIO channel can be used to emulate a 5-key OSD joystick for OSD camera control.

See [LED pin PWM](LED%20pin%20PWM.md) for more details.
See [PINIO PWM](PINIO%20PWM.md) for more details.

Note that for cameras which support RuncamDevice protocol, there is alternative functionality using serial communication: [Runcam device](Runcam%20device.md)

Expand All @@ -22,17 +22,17 @@ To simulate 5key joystick, it is sufficient to generate correct voltage on camer

# Enabling OSD Joystick emulation

```set led_pin_pwm_mode=shared_high```

```set osd_joystick_enabled=on```

Also enable "Multi-color RGB LED Strip support" in Configuration tab.
```set osd_joystick_pinio_channel=<N>```

Where `<N>` is the PINIO channel (0-3) connected to the camera OSD pin.

# Connection diagram

We use LED pin PWM functionality with RC filter to generate voltage:
We use PINIO PWM with an RC filter to generate voltage:

![alt text](/docs/assets/images/ledpinpwmfilter.png "led pin pwm filter")
![alt text](/docs/assets/images/ledpinpwmfilter.png "PINIO PWM filter")

# Example PCB layout (SMD components)

Expand All @@ -48,7 +48,7 @@ If default voltages does not work with your camera model, then you have to measu
2. Measure voltages on OSD pin while each key is pressed.
3. Connect camera to FC throught RC filter as shown on schematix above.
4. Enable OSD Joystick emulation (see "Enabling OSD Joystick emulation" above)
4. Use cli command ```led_pin_pwm <value>```, value = 0...100 to find out PWM values for each voltage.
4. Use CLI command `piniopwm <channel> <value>`, value = 0...100 to find out PWM values for each voltage.
5. Specify PWM values in configuration and save:

```set osd_joystick_down=0```
Expand Down Expand Up @@ -87,7 +87,7 @@ There are 3 RC Boxes which can be used in armed and unarmed state:
- Camera 2 - Up
- Camera 3 - Down

Other keys can be emulated using Programming framework ( see [LED pin PWM](LED%20pin%20PWM.md) for more details ).
Other keys can be emulated using the Programming framework (see [PINIO PWM](PINIO%20PWM.md) for more details).

# Behavior on boot

Expand Down
71 changes: 71 additions & 0 deletions docs/PINIO PWM.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# PINIO PWM

INAV provides two mechanisms for generating output signals on GPIO pins:

1. **PINIO channels (0-3)** — Any PWM-capable timer output defined as `PINIOx_PIN` in the target. Supports full 0-100% duty cycle PWM at 24 kHz.
2. **LED strip idle level (channel 4)** — The WS2812 LED strip pin can be switched between idle-LOW and idle-HIGH between LED update bursts. Binary on/off only.

## PINIO PWM channels

PINIO channels are configured per-target in `target.h` using `PINIO1_PIN` through `PINIO4_PIN`. When a PINIO pin has a timer, it is automatically configured as a 24 kHz PWM output.

PWM duty cycle can be controlled via:
- **CLI:** `piniopwm [channel] <duty>` (duty = 0-100)
- **Programming framework:** Operation 52, Operand A = channel (0-3), Operand B = duty (0-100)

Setting duty to 0 stops PWM generation (pin goes LOW, or HIGH if `PINIO_FLAGS_INVERTED` is set in target.h).

Feature can be used to drive external devices such as a VTX power switch. Setting the PWM duty cycle to 100% or 0% effectively provides a digital on/off output. It is also used to simulate [OSD joystick](OSD%20Joystick.md) to control cameras.

PWM frequency is fixed to 24kHz with duty ratio between 0 and 100%:

![alt text](/docs/assets/images/led_pin_pwm.png "PINIO PWM signal")

## LED strip idle level (channel 4)

When the LED strip feature is enabled, the WS2812 pin sends data bursts (~1 ms) every 10-20 ms. Between bursts, the pin idles at a configurable level.

The LED strip idle level is accessible as channel `4` (the next channel after PINIO hardware channels 0-3):

- **CLI:** `piniopwm 4 <value>` — value > 0 sets idle HIGH, 0 sets idle LOW
- **Programming framework:** Operation 52, Operand A = 4, Operand B = value (>0 = HIGH, 0 = LOW)

This can be used to drive a MOSFET or similar device connected to the LED pin, toggled by the programming framework based on flight mode, RC channel, GPS state, etc.

*Note: there will be a ~2 second LOW pulse on the LED pin during boot.*

### LED strip idle level timing

Normally LED pin is held low between WS2812 updates:

![alt text](/docs/assets/images/ws2811_packets.png "ws2811 packets")
![alt text](/docs/assets/images/ws2811_data.png "ws2811 data")

When idle is set HIGH, the pin is held high between updates. Total ws2812 pulse duration is ~1ms with ~9ms pauses. Connected devices should be tolerant of these brief transients.

# Generating PWM/output signals with programming framework

See operation 52 "PINIO PWM" in [Programming Framework](Programming%20Framework.md)

# Generating PWM/output signals from CLI

`piniopwm [channel] <duty>` — channel = 0-4, duty = 0-100

- One argument: sets duty on channel 0 (backward compatible)
- Two arguments: first is channel, second is duty
- No arguments: stops PWM on channel 0

# Example of driving LED

It is possible to drive single color LED with brightness control. Current consumption should not be greater then 1-2ma, thus LED can be used for indication only.

![alt text](/docs/assets/images/ledpinpwmled.png "PINIO PWM LED")

# Example of driving powerful white LED

To drive power LED with brightness control, a MOSFET should be used:

![alt text](/docs/assets/images/ledpinpwmpowerled.png "PINIO PWM power LED")

# Programming tab example for using a PINIO channel to switch a VTX or camera on and off
![screenshot of programming tab using PINIO](/docs/assets/images/led-as-pinio.png)
2 changes: 1 addition & 1 deletion docs/Programming Framework.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ for complete documentation on using JavaScript to program your flight controller
| 49 | Timer | A simple on - off timer. `true` for the duration of `Operand A` [ms]. Then `false` for the duration of `Operand B` [ms]. |
| 50 | Delta | This returns `true` when the value of `Operand A` has changed by the value of `Operand B` or greater within 100ms. ( \|ΔA\| >= B ) |
| 51 | Approx Equals (A ~ B) | `true` if `Operand B` is within 1% of `Operand A`. |
| 52 | LED Pin PWM | Value `Operand A` from [`0` : `100`] PWM / PINIO generation on LED Pin. See [LED pin PWM](LED%20pin%20PWM.md). Any other value stops PWM generation (stop to allow ws2812 LEDs updates in shared modes). |
| 52 | PINIO PWM | `Operand A` = channel (0-3 for PINIO hardware, 4 for LED strip idle level). `Operand B` = duty cycle (0-100). Channels 0-3 support full PWM; channel 4 is binary (>0 = HIGH). See [PINIO PWM](PINIO%20PWM.md). |
| 53 | Disable GPS Sensor Fix | Disables the GNSS sensor fix. For testing GNSS failure. |
| 54 | Mag calibration | Trigger a magnetometer calibration. |
| 55 | Set Gimbal Sensitivity | Scales `Operand A` from [`-16` : `15`]
Expand Down
87 changes: 14 additions & 73 deletions src/main/drivers/light_ws2811strip.c
Original file line number Diff line number Diff line change
Expand Up @@ -43,26 +43,17 @@
#include "drivers/timer.h"
#include "drivers/light_ws2811strip.h"

#include "config/parameter_group_ids.h"
#include "fc/settings.h"
#include "fc/runtime_config.h"

#define WS2811_PERIOD (WS2811_TIMER_HZ / WS2811_CARRIER_HZ)
#define WS2811_BIT_COMPARE_1 ((WS2811_PERIOD * 2) / 3)
#define WS2811_BIT_COMPARE_0 (WS2811_PERIOD / 3)

PG_REGISTER_WITH_RESET_TEMPLATE(ledPinConfig_t, ledPinConfig, PG_LEDPIN_CONFIG, 0);

PG_RESET_TEMPLATE(ledPinConfig_t, ledPinConfig,
.led_pin_pwm_mode = SETTING_LED_PIN_PWM_MODE_DEFAULT
);

static DMA_RAM timerDMASafeType_t ledStripDMABuffer[WS2811_DMA_BUFFER_SIZE];

static IO_t ws2811IO = IO_NONE;
static TCH_t * ws2811TCH = NULL;
static bool ws2811Initialised = false;
static bool pwmMode = false;

static hsvColor_t ledColorBuffer[WS2811_LED_STRIP_LENGTH];

Expand Down Expand Up @@ -112,14 +103,6 @@ bool ledConfigureDMA(void) {
return timerPWMConfigChannelDMA(ws2811TCH, ledStripDMABuffer, sizeof(ledStripDMABuffer[0]), WS2811_DMA_BUFFER_SIZE);
}

void ledConfigurePWM(void) {
timerConfigBase(ws2811TCH, 100, WS2811_TIMER_HZ );
timerPWMConfigChannel(ws2811TCH, 0);
timerPWMStart(ws2811TCH);
timerEnable(ws2811TCH);
pwmMode = true;
}

void ws2811LedStripInit(void)
{
const timerHardware_t * timHw = timerGetByTag(IO_TAG(WS2811_PIN), TIM_USE_ANY);
Expand All @@ -141,28 +124,17 @@ void ws2811LedStripInit(void)
IOInit(ws2811IO, OWNER_LED_STRIP, RESOURCE_OUTPUT, 0);
IOConfigGPIOAF(ws2811IO, IOCFG_AF_PP_FAST, timHw->alternateFunction);

if (ledPinConfig()->led_pin_pwm_mode == LED_PIN_PWM_MODE_LOW) {
ledConfigurePWM();
*timerCCR(ws2811TCH) = 0;
} else if (ledPinConfig()->led_pin_pwm_mode == LED_PIN_PWM_MODE_HIGH) {
ledConfigurePWM();
*timerCCR(ws2811TCH) = 100;
} else {
if (!ledConfigureDMA()) {
// If DMA failed - abort
ws2811Initialised = false;
return;
}

// Zero out DMA buffer
memset(&ledStripDMABuffer, 0, sizeof(ledStripDMABuffer));
if ( ledPinConfig()->led_pin_pwm_mode == LED_PIN_PWM_MODE_SHARED_HIGH ) {
ledStripDMABuffer[WS2811_DMA_BUFFER_SIZE-1] = 255;
}
ws2811Initialised = true;

ws2811UpdateStrip();
if (!ledConfigureDMA()) {
// If DMA failed - abort
ws2811Initialised = false;
return;
}

// Zero out DMA buffer — LED pin idles LOW between WS2812 bursts
memset(&ledStripDMABuffer, 0, sizeof(ledStripDMABuffer));
ws2811Initialised = true;

ws2811UpdateStrip();
}

bool isWS2811LedStripReady(void)
Expand Down Expand Up @@ -191,7 +163,7 @@ void ws2811UpdateStrip(void)
static rgbColor24bpp_t *rgb24;

// don't wait - risk of infinite block, just get an update next time round
if (pwmMode || timerPWMDMAInProgress(ws2811TCH)) {
if (timerPWMDMAInProgress(ws2811TCH)) {
return;
}

Expand All @@ -216,40 +188,9 @@ void ws2811UpdateStrip(void)
timerPWMStartDMA(ws2811TCH);
}

//value
void ledPinStartPWM(uint16_t value) {
if (ws2811TCH == NULL) {
return;
}

if ( !pwmMode ) {
timerPWMStopDMA(ws2811TCH);
//FIXME: implement method to release DMA
ws2811TCH->dma->owner = OWNER_FREE;

ledConfigurePWM();
}
*timerCCR(ws2811TCH) = value;
}

void ledPinStopPWM(void) {
if (ws2811TCH == NULL || !pwmMode ) {
return;
}

if ( ledPinConfig()->led_pin_pwm_mode == LED_PIN_PWM_MODE_HIGH ) {
*timerCCR(ws2811TCH) = 100;
return;
} else if ( ledPinConfig()->led_pin_pwm_mode == LED_PIN_PWM_MODE_LOW ) {
*timerCCR(ws2811TCH) = 0;
return;
}
pwmMode = false;

if (!ledConfigureDMA()) {
ws2811Initialised = false;
}
void ws2811SetIdleHigh(bool high)
{
ledStripDMABuffer[WS2811_DMA_BUFFER_SIZE - 1] = high ? 255 : 0;
}


#endif
Loading
Loading