# How the 160x80 LCD Backlight Is Managed in the RPi Pico WAV Player

> Discover how the 160x80 LCD backlight is managed on the RPi Pico WAV player using PWM for dynamic brightness control based on idle time and user settings.

- Repository: [Elehobica/rpi_pico_wav_player](https://github.com/elehobica/rpi_pico_wav_player)
- Tags: internals
- Published: 2026-03-01

---

**The backlight is controlled via PWM on the BLK pin, dynamically switching between high and low brightness levels based on UI idle time and user-configurable thresholds.**

The `elehobica/rpi_pico_wav_player` project drives a Waveshare 160x80 LCD display (RP2040-LCD-0.96) using a sophisticated PWM-based backlight management system. This implementation automatically dims the display after periods of inactivity while providing full user control over brightness levels and timeout durations through the configuration menu.

## PWM-Based Backlight Control Architecture

### GPIO Pin Assignment in LcdCanvas.cpp

During initialization, the driver selects the correct backlight pin based on the board variant. In [`src/LcdCanvas.cpp`](https://github.com/elehobica/rpi_pico_wav_player/blob/main/src/LcdCanvas.cpp), the code assigns `pin_blk` to the appropriate GPIO for Waveshare displays:

```cpp
// LcdCanvas.cpp – board-specific pin mapping
case WAVESHARE_RP2040_LCD_096: // fall-through
case WAVESHARE_RP2350_LCD_096:
    pin_blk = PIN_LCD_BLK_WAVESHARE;   // backlight pin for Waveshare LCD
    break;
default:
    pin_blk = PIN_LCD_BLK_DEFAULT;     // fallback for other boards

```

(See [`src/LcdCanvas.cpp`](https://github.com/elehobica/rpi_pico_wav_player/blob/main/src/LcdCanvas.cpp) lines 81-91)

### Hardware PWM Interface

The actual PWM output is handled by the `OLED_BLK_Set_PWM(uint16_t val)` function provided by the LCD driver. This hardware abstraction layer allows the power management logic to set duty cycles from 0 (off) to 255 (full brightness) without directly manipulating GPIO registers.

## Dynamic Brightness Management

### Idle Detection and Auto-Dimming Logic

The core brightness algorithm resides in `pm_backlight_update()` within [`src/power_manage.cpp`](https://github.com/elehobica/rpi_pico_wav_player/blob/main/src/power_manage.cpp). This function compares the current UI idle count against the user-configured timeout to determine backlight intensity:

```cpp
void pm_backlight_update()
{
    const int LoopCycleMs = UIMode::UpdateCycleMs;      // 50 ms per UI loop
    const int OneSec = 1000 / LoopCycleMs;
    uint32_t bl_val;
    ConfigMenu& cfg = ConfigMenu::instance();

    if (ui_get_idle_count() < cfg.get(ConfigMenuId::DISPLAY_TIME_TO_BACKLIGHT_LOW) * OneSec)
        bl_val = cfg.get(ConfigMenuId::DISPLAY_BACKLIGHT_HIGH_LEVEL);
    else
        bl_val = cfg.get(ConfigMenuId::DISPLAY_BACKLIGHT_LOW_LEVEL);

    OLED_BLK_Set_PWM(bl_val);   // set PWM on BLK pin
}

```

(See [`src/power_manage.cpp`](https://github.com/elehobica/rpi_pico_wav_player/blob/main/src/power_manage.cpp) lines 108-119)

The logic calculates idle time using a 50-millisecond UI loop cycle. When user activity is detected within the timeout window, the system applies the **high brightness** PWM value; once idle time exceeds the threshold, it transitions automatically to the **low brightness** level.

### User-Configurable Parameters

Brightness levels and timing are exposed through the `ConfigMenu` class. The system stores three critical parameters identified by `ConfigMenuId` enumerations:

- **DISPLAY_BACKLIGHT_HIGH_LEVEL**: PWM duty cycle for active use (default typically 200/255)
- **DISPLAY_BACKLIGHT_LOW_LEVEL**: PWM duty cycle for idle state (default typically 30/255)
- **DISPLAY_TIME_TO_BACKLIGHT_LOW**: Timeout in seconds before dimming occurs

## Power State Transitions

### Initialization to High Brightness

During power-up sequence in [`src/power_manage.cpp`](https://github.com/elehobica/rpi_pico_wav_player/blob/main/src/power_manage.cpp), the backlight initializes immediately to the configured high level to ensure visibility during boot:

```cpp
ConfigMenu& cfg = ConfigMenu::instance();
OLED_BLK_Set_PWM(cfg.get(ConfigMenuId::DISPLAY_BACKLIGHT_HIGH_LEVEL));

```

(See [`src/power_manage.cpp`](https://github.com/elehobica/rpi_pico_wav_player/blob/main/src/power_manage.cpp) lines 196-200)

### Dormant Mode Shutdown

When the device enters low-power dormant sleep mode, the system forces the backlight completely off to minimize current draw:

```cpp
OLED_BLK_Set_PWM(0);

```

(See [`src/power_manage.cpp`](https://github.com/elehobica/rpi_pico_wav_player/blob/main/src/power_manage.cpp) lines 299-300)

This ensures the 160x80 LCD consumes zero backlight power during extended sleep periods.

## Implementation Examples

### Updating Backlight in the Main UI Loop

Integrate automatic brightness control by calling the update function within the primary execution loop:

```cpp
// Typical UI loop implementation
while (true) {
    // Handle user input and display updates
    process_ui_events();
    
    // Adjust PWM based on idle time
    pm_backlight_update();
    
    sleep_ms(50);  // UIMode::UpdateCycleMs
}

```

### Manual PWM Override

For testing or custom brightness profiles, bypass the idle detection and write PWM values directly:

```cpp
#include "power_manage.h"

// Force maximum brightness (100% duty cycle)
OLED_BLK_Set_PWM(255);

// Force minimum visible brightness (~10% duty cycle)
OLED_BLK_Set_PWM(25);

```

### Modifying Configuration via ConfigMenu

Programmatically adjust user preferences for backlight behavior:

```cpp
ConfigMenu& cfg = ConfigMenu::instance();

// Set high brightness to ~78% duty cycle (200/255)
cfg.set(ConfigMenuId::DISPLAY_BACKLIGHT_HIGH_LEVEL, 200);

// Set dimmed brightness to ~12% duty cycle (30/255)
cfg.set(ConfigMenuId::DISPLAY_BACKLIGHT_LOW_LEVEL, 30);

// Set 5-second timeout before auto-dimming
cfg.set(ConfigMenuId::DISPLAY_TIME_TO_BACKLIGHT_LOW, 5);

```

## Summary

- The **160x80 LCD backlight** is driven by PWM on the **BLK pin** assigned in [`LcdCanvas.cpp`](https://github.com/elehobica/rpi_pico_wav_player/blob/main/LcdCanvas.cpp) based on board variant detection.
- **Dynamic brightness** is managed by `pm_backlight_update()` in [`power_manage.cpp`](https://github.com/elehobica/rpi_pico_wav_player/blob/main/power_manage.cpp), which monitors `ui_get_idle_count()` against configurable timeout thresholds.
- **Three configuration parameters** control the behavior: high level PWM, low level PWM, and seconds-to-dim timeout via `ConfigMenu`.
- The system initializes to **high brightness** on boot and forces **PWM to zero** when entering dormant sleep mode.
- Hardware abstraction through `OLED_BLK_Set_PWM()` allows duty cycle control from 0-255 without direct GPIO manipulation.

## Frequently Asked Questions

### Which GPIO pin controls the 160x80 LCD backlight?

The backlight uses the **BLK pin** defined as `PIN_LCD_BLK_WAVESHARE` for Waveshare RP2040/RP2350 LCD boards, or `PIN_LCD_BLK_DEFAULT` for other configurations. This assignment occurs during LCD initialization in [`src/LcdCanvas.cpp`](https://github.com/elehobica/rpi_pico_wav_player/blob/main/src/LcdCanvas.cpp) at lines 81-91 according to the specific board variant detected at compile time.

### How does the backlight automatically dim after inactivity?

The `pm_backlight_update()` function checks `ui_get_idle_count()` against the user-configured `DISPLAY_TIME_TO_BACKLIGHT_LOW` value. If the idle counter exceeds the timeout threshold multiplied by the 50-millisecond UI loop cycle, the system switches from `DISPLAY_BACKLIGHT_HIGH_LEVEL` to `DISPLAY_BACKLIGHT_LOW_LEVEL` PWM values. This logic runs continuously in the main UI loop.

### Can I disable the auto-dimming feature entirely?

Yes. Set `DISPLAY_TIME_TO_BACKLIGHT_LOW` to a very large value (e.g., 3600 seconds) via the `ConfigMenu`, or modify `pm_backlight_update()` to always use the high brightness level. Alternatively, you can call `OLED_BLK_Set_PWM()` directly with a constant value to override the automatic management.

### What happens to the backlight when the device enters sleep mode?

When the Raspberry Pi Pico enters dormant sleep mode through the power management system, the code explicitly calls `OLED_BLK_Set_PWM(0)` in [`src/power_manage.cpp`](https://github.com/elehobica/rpi_pico_wav_player/blob/main/src/power_manage.cpp) (lines 299-300). This forces the backlight completely off to maximize power savings during sleep, regardless of previous brightness settings.