Files
ardupilot/libraries/AP_HAL_Linux/SerialLED_SPI.h
T
Andy Piper 34e3dc466d AP_HAL_Linux: add SPI-based serial LED driver for NeoPixel/WS2812
Add modular SPI-based serial LED driver that uses SPI MOSI to generate
WS2812 timing waveforms. Uses 8-bit encoding at 6.4MHz SPI clock where
each WS2812 bit becomes one SPI byte.

SerialLED_SPI is a standalone class that can be composed into any
RCOutput implementation. RCOutput_RPI provides the RCOutput wrapper
for Raspberry Pi boards.

Features:
- Supports MODE_NEOPIXEL (GRB) and MODE_NEOPIXELRGB (RGB) color ordering
- Up to 128 LEDs per channel
- Uses /dev/spidev0.0 (GPIO10/pin 19) on Raspberry Pi
- Thread-safe with mutex protection
- Embedded object instead of heap allocation for SerialLED_SPI
- Uses calloc/free for dynamic arrays (less overhead than new/delete)
2026-02-11 18:46:23 +11:00

122 lines
3.5 KiB
C++

/*
* Code by Andy Piper <github@andypiper.com>
*
* This file is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This file is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Standalone SPI-based NeoPixel/WS2812 serial LED driver for Linux.
* This class can be used by any RCOutput implementation to add serial LED support.
*/
#pragma once
#include "AP_HAL_Linux.h"
#ifndef HAL_LINUX_SERIALLED_ENABLED
#define HAL_LINUX_SERIALLED_ENABLED 0
#endif
#if HAL_LINUX_SERIALLED_ENABLED
#include "Semaphores.h"
#include <AP_HAL/RCOutput.h>
namespace Linux {
/*
* Standalone SPI-based serial LED driver for NeoPixel/WS2812 LEDs.
*
* Uses SPI MOSI to generate the precise timing required by WS2812 LEDs.
* This class is designed to be composed into RCOutput implementations
* that need serial LED support.
*/
class SerialLED_SPI {
public:
SerialLED_SPI() {}
~SerialLED_SPI();
// Initialize the SPI device with given parameters
bool init(const char *spi_device, uint8_t num_channels);
// Check if initialized
bool initialized() const { return _initialized; }
// Configure number of LEDs on a channel
bool set_num_leds(uint16_t chan, uint8_t num_leds, AP_HAL::RCOutput::output_mode mode);
// Set RGB data for an LED (led=-1 sets all LEDs)
bool set_rgb_data(uint16_t chan, int8_t led, uint8_t red, uint8_t green, uint8_t blue);
// Send LED data via SPI
bool send(uint16_t chan);
// Get number of channels supported
uint8_t num_channels() const { return _num_channels; }
private:
// Maximum supported values (must be defined before use in arrays)
static constexpr uint8_t MAX_CHANNELS = 4;
static constexpr uint8_t MAX_LEDS_PER_CHANNEL = 128;
// SPI buffer sizing:
// Each LED = 24 color bits, each bit = 8 SPI bits = 24 bytes per LED
static constexpr uint16_t RESET_BYTES = 64;
static constexpr uint16_t BYTES_PER_LED = 24;
static constexpr uint16_t SPI_BUFFER_SIZE = (MAX_LEDS_PER_CHANNEL * BYTES_PER_LED) + RESET_BYTES;
// SPI bit patterns for WS2812 encoding at 6.4MHz
static constexpr uint8_t WS_BIT_0 = 0xC0; // 11000000
static constexpr uint8_t WS_BIT_1 = 0xF8; // 11111000
// SPI configuration
static constexpr uint32_t SPI_SPEED_HZ = 6400000;
// LED color data structure
struct SerialLed {
uint8_t red;
uint8_t green;
uint8_t blue;
};
// Per-channel state with dynamically allocated LED data
struct ChannelState {
SerialLed *led_data;
uint8_t num_leds;
AP_HAL::RCOutput::output_mode mode;
bool pending;
};
// Open and configure the SPI device
bool open_spi();
// Encode LED data to SPI buffer
void encode_led_data(uint8_t channel);
// Send data via SPI
bool send_spi(uint8_t channel);
const char *_spi_device;
uint8_t _num_channels;
int _spi_fd = -1;
bool _initialized;
ChannelState _channels[MAX_CHANNELS];
uint8_t *_spi_buffer;
uint16_t _spi_buffer_size;
Semaphore _mutex;
};
} // namespace Linux
#endif // HAL_LINUX_SERIALLED_ENABLED