Peripheral for SCOMP: Modular LED Brightness Controller with Built-In Gamma Correction

May 3, 2025

Introduction

This document summarizes the design and implementation of our custom SCOMP peripheral: a modular LED brightness controller with built-in gamma correction. Designed for clarity, flexibility, and ease of use, the peripheral allows users to set and read brightness values across an LED array using a memory-mapped Input/Output (I/O) map. Key features include a hardware-based gamma correction look-up table (LUT) and support for both individual and multi-LED control using a bit mask interface.

Our team prioritized modular design, separating functionality across control logic, brightness tracking, and gamma correction units. This approach streamlined development and made testing more efficient. The final peripheral successfully met all project specifications and closely aligned with our initial proposal goals, including the creation of a visually smooth shooting star animation and diffusion tool to demonstrate real-time control and accuracy.

Device Functionality

The peripheral supports both writing and reading LED brightness values through memory-mapped I/O registers (see Figure 1). Users interact with the device by writing values to specific addresses on the SCOMP bus, enabling precise and flexible control over an LED array.

Address Name B15–B10 B9 B8 B7 B6 B5 B4 B3 B2 B1 B0
0x06 LED Mask LED9 LED8 LED7 LED6 LED5 LED4 LED3 LED2 LED1 LED0
0x07 Set Brightness 0 0 0 Brightness
0x08 OUT Read Brightness LED9 LED8 LED7 LED6 LED5 LED4 LED3 LED2 LED1 LED0
0x08 IN Read Brightness 0 0 0 Brightness
Figure 1. Memory-mapped I/O register map for the LED brightness controller peripheral.

Set Brightness

To adjust brightness, the user first writes to I/O 0x06 with a 10-bit mask in the AC. Any bit set to ‘1’ selects the corresponding LED for the upcoming operation. Next, the user writes to the I/O 0x07 with a brightness value from 0 (off) to 255 (full brightness) in the AC. After the second write, all selected LEDs will be set to the brightness value. Gamma correction is automatically applied to the brightness value (see Figure 2). This makes changes in LED brightness appear more natural to the human eye, as brightness perception is nonlinear.

visual appearance of brightness values with and without gamma correction
Figure 2. Visual appearance of brightness values with and without gamma correction.

Read Brightness

To read brightness, the user first writes to I/O address 0x08 with a 10-bit mask in AC with only a single bit set to ‘1’ (e.g. 0b0010000000), corresponding to a single LED. A subsequent read from the same address returns the stored brightness value (0-255) of that specific LED in AC.

Design Decisions & Implementation

We maintained consistency in the user interface by having both set/read brightness operate on a bit mask of LEDs. This allows users to control single LEDs or multiple LEDs at once. Since the bit mask consumes 10 out of 11 bits of our operand, we don’t have enough bits to also include our 4-bit brightness value. Thus, we made the trade-off to separate setting the bit mask and brightness values into separate I/O addresses.

state diagram of set led brightness
Figure 3. State diagram of peripheral internals as a user reads LED Brightness.

Another key design choice was to include gamma correction using a hardware-based LUT. Implementing multi-LED control and gamma correction in hardware — rather than software — allowed for faster, more intuitive updates for the user.

state diagram of read led brightness
Figure 4. State diagram of peripheral internals as a user reads LED Brightness.

Internally, we kept the design modular by separating the LUT, control logic, and brightness tracking into different parts (see Figures 3 & 4). The LUT independently applies gamma correction to brightness values, the control logic manages I/O writes and LED routing, and the brightness module stores current values and supports state-based reading. This approach made testing easier and helped us build a more flexible, reliable system.

Conclusions

Our LED controller peripheral achieved both functional correctness and user-centric design. By offloading gamma correction and control logic to hardware, we improved ease of use and responsiveness. Future users can benefit from the clear I/O structure, state-based reading, and modular architecture for better understanding, maintainability, and expansion.

If revisiting this project, we would explore additional animation modes, increase LED count scalability, and consider integrating a software API to simplify multi-LED control even further.