diff --git a/msg/CMakeLists.txt b/msg/CMakeLists.txt index 7a776d71f3..9925f0d009 100644 --- a/msg/CMakeLists.txt +++ b/msg/CMakeLists.txt @@ -50,6 +50,7 @@ set(msg_files airspeed.msg airspeed_validated.msg airspeed_wind.msg + apa102_control.msg autotune_attitude_control_status.msg battery_status.msg button_event.msg diff --git a/msg/apa102_control.msg b/msg/apa102_control.msg new file mode 100644 index 0000000000..5702b61695 --- /dev/null +++ b/msg/apa102_control.msg @@ -0,0 +1,10 @@ +# Control up to 1001 LEDs on APA102 + +uint64 timestamp # time since system start (microseconds) + +uint16 number_leds #total number of leds [0,1001] +uint8 offset_group #offset group (multiple of 13) of leds to control [0,77] +uint32[13] led_values #follows spi 32 bit LED schema for apa102 + +# [ PAD 1's ][ BRIGHT 0:31 ][ BLU 0:255 ][ GRN 0:255 ][ RED 0:255 ] +# [ 31 ... 29 ][ 28 ... 24 ][ 23 ... 16 ][ 15 ... 8 ][ 7 ... 0 ] diff --git a/src/drivers/lights/CMakeLists.txt b/src/drivers/lights/CMakeLists.txt index 5cdfcdbb85..8748768a20 100644 --- a/src/drivers/lights/CMakeLists.txt +++ b/src/drivers/lights/CMakeLists.txt @@ -34,4 +34,5 @@ #add_subdirectory(neopixel) # requires board support (BOARD_HAS_N_S_RGB_LED) add_subdirectory(rgbled) add_subdirectory(rgbled_ncp5623c) +add_subdirectory(apa102) #add_subdirectory(rgbled_pwm) # requires board support (BOARD_HAS_LED_PWM/BOARD_HAS_UI_LED_PWM) diff --git a/src/drivers/lights/Kconfig b/src/drivers/lights/Kconfig index 821cd01308..3c63d3661e 100644 --- a/src/drivers/lights/Kconfig +++ b/src/drivers/lights/Kconfig @@ -2,6 +2,7 @@ menu "Lights" menuconfig COMMON_LIGHT bool "Common Light's" default n + select DRIVERS_LIGHT_APA102 select DRIVERS_LIGHTS_RGBLED select DRIVERS_LIGHTS_RGBLED_NCP5623C ---help--- diff --git a/src/drivers/lights/apa102/CMakeLists.txt b/src/drivers/lights/apa102/CMakeLists.txt new file mode 100644 index 0000000000..0de5b9c581 --- /dev/null +++ b/src/drivers/lights/apa102/CMakeLists.txt @@ -0,0 +1,41 @@ +############################################################################ +# +# Copyright (c) 2021 PX4 Development Team. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# 3. Neither the name PX4 nor the names of its contributors may be +# used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +############################################################################ + +px4_add_module( + MODULE drivers__apa102 + MAIN apa102 + SRCS + apa102.cpp + DEPENDS + led + ) diff --git a/src/drivers/lights/apa102/Kconfig b/src/drivers/lights/apa102/Kconfig new file mode 100644 index 0000000000..3238117c78 --- /dev/null +++ b/src/drivers/lights/apa102/Kconfig @@ -0,0 +1,5 @@ +menuconfig DRIVERS_LIGHTS_APA102 + bool "apa102" + default n + ---help--- + Enable support for apa102 \ No newline at end of file diff --git a/src/drivers/lights/apa102/apa102.cpp b/src/drivers/lights/apa102/apa102.cpp new file mode 100644 index 0000000000..af6f6c1c6a --- /dev/null +++ b/src/drivers/lights/apa102/apa102.cpp @@ -0,0 +1,275 @@ +/**************************************************************************** + * + * Copyright (c) 2021 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/** + * @file apa102.cpp + * + * Driver for the the apa102 class of RGB LED drivers. + * this driver is based on the PX4 led driver + * + */ + +#include "apa102.hpp" + +// Constructor +APA102::APA102(unsigned int number_of_packages, int bus, uint32_t device, int bus_frequency, spi_mode_e spi_mode) : + SPI(DRV_DEVTYPE_UNUSED, MODULE_NAME, bus, device, spi_mode, bus_frequency), + ScheduledWorkItem(MODULE_NAME, px4::wq_configurations::lp_default), + _number_of_packages(number_of_packages) +{ +} + +// Deconstructor +APA102::~APA102() +{ + //TODO: deinit SPI bus + //apa102_deinit(); +} + +// Init +// This runs after calling the constructor +int APA102::init() +{ + // Init SPI bus + int ret = SPI::init(); + + if (ret != OK) { + printf("SPI::init() failed\n"); + DEVICE_DEBUG("SPI init failed"); + return -EIO; + } + + // Fill buffer with zeros + for (uint8_t i = 0; i < (_number_of_packages * 4) + 8; i++) { + buf[i] = 0x00; + } + + // APA102LEDData is just the data format for the BRG LED + _leds = new apa102::APA102LEDData [_number_of_packages]; + + if (_leds == nullptr) { + return PX4_ERROR; + } + + ScheduleNow(); + return OK; +} + +// +int APA102::task_spawn(int argc, char *argv[]) +{ + int myoptind = 1; + int ch; + const char *myoptarg = nullptr; + unsigned int number_of_packages = BOARD_MAX_LEDS; + + while ((ch = px4_getopt(argc, argv, "n:", &myoptind, &myoptarg)) != EOF) { + switch (ch) { + case 'n': + number_of_packages = atoi(myoptarg) + 1; + + if (number_of_packages > BOARD_MAX_LEDS) { + number_of_packages = BOARD_MAX_LEDS; + PX4_INFO("Number of packages can not exceed BOARD_MAX_LEDS"); + } + + break; + + default: + print_usage("unrecognized option"); + return 1; + } + } + + printf("Number of packages: %d\n", number_of_packages); + APA102 *instance = new APA102(number_of_packages, 1, 0, 4000000, SPIDEV_MODE0); + + if (instance) { + _object.store(instance); + _task_id = task_id_is_work_queue; + + if (instance->init() == PX4_OK) { + return PX4_OK; + } + + } else { + PX4_ERR("alloc failed"); + } + + delete instance; + _object.store(nullptr); + _task_id = -1; + return PX4_ERROR; +} + +int APA102::print_status() +{ + + PX4_INFO("Controlling %i LEDs", _number_of_packages); + + return 0; +} + +int APA102::print_usage(const char *reason) +{ + if (reason) { + PX4_WARN("%s\n", reason); + } + + PRINT_MODULE_DESCRIPTION( + R"DESCR_STR( +### Description +This module is responsible for driving interfasing to the Neopixel Serial LED + +### Examples +It is typically started with: +$ apa102 -n 8 +To drive all available leds. +)DESCR_STR"); + +PRINT_MODULE_USAGE_NAME("newpixel", "driver"); +PRINT_MODULE_USAGE_DEFAULT_COMMANDS(); +return 0; +} + +int APA102::custom_command(int argc, char *argv[]) +{ + return print_usage("unrecognized option"); +} + +/** + * Main loop function + * This will run periodically by the scheduler + */ +void APA102::Run() +{ + if (should_exit()) { + ScheduleClear(); + exit_and_cleanup(); + return; + } + + // Structure for uORB data + LedControlData led_control_data; + + // Get LED data from uORB (led_control) + if (_led_controller.update(led_control_data) == 1) { + + // Loop through each LED + for (unsigned int led = 1; led < math::min(_number_of_packages, arraySize(led_control_data.leds)); led++) { + + // Set brightness + uint8_t brightness = led_control_data.leds[led].brightness; + + /* Brightness is not 0-255, it is 5 bit, so 0-31. 256/32=8 */ + brightness = brightness / 8; + //printf("BRIGHTNESS: %d\n", brightness); + //printf("arraySize(%d)\n", arraySize(led_control_data.leds)); + + // Use data from uORB to set specific APA102LEDData fields + switch (led_control_data.leds[led].color) { + case led_control_s::COLOR_RED: + _leds[led].R() = 255; _leds[led].G() = 0; _leds[led].B() = 0; + break; + + case led_control_s::COLOR_DIM_RED: + _leds[led].R() = 16; _leds[led].G() = 0; _leds[led].B() = 0; + break; + + case led_control_s::COLOR_GREEN: + _leds[led].R() = 0; _leds[led].G() = 255; _leds[led].B() = 0; + break; + + case led_control_s::COLOR_BLUE: + _leds[led].R() = 0; _leds[led].G() = 0; _leds[led].B() = 255; + break; + + case led_control_s::COLOR_AMBER: //make it the same as yellow + case led_control_s::COLOR_YELLOW: + _leds[led].R() = 255; _leds[led].G() = 255; _leds[led].B() = 0; + break; + + case led_control_s::COLOR_PURPLE: + _leds[led].R() = 255; _leds[led].G() = 0; _leds[led].B() = 255; + break; + + case led_control_s::COLOR_CYAN: + _leds[led].R() = 0; _leds[led].G() = 255; _leds[led].B() = 255; + break; + + case led_control_s::COLOR_WHITE: + _leds[led].R() = 255; _leds[led].G() = 255; _leds[led].B() = 255; + break; + + default: // led_control_s::COLOR_OFF + _leds[led].R() = 0; _leds[led].G() = 0; _leds[led].B() = 0; + break; + } // end switch + + /* APA102 Frame + * 0x000000000 [start frame] (this is already done in init) + * 0xE0 + [brightness] + * 0xXXXXXX [bgr] + * 0xFFFFFFFF [end frame] + */ + + for(uint8_t i = 1; i < _number_of_packages; i++) + { + buf[4+(4*(i-1))] = 0b11100000 + brightness; + buf[5+(4*(i-1))] = _leds[i].B(); + buf[6+(4*(i-1))] = _leds[i].G(); + buf[7+(4*(i-1))] = _leds[i].R(); + } // end FOR loop + + // Fill with end frame + buf[8+(4*_number_of_packages)] = 0xFF; + buf[9+(4*_number_of_packages)] = 0xFF; + buf[10+(4*_number_of_packages)] = 0xFF; + buf[11+(4*_number_of_packages)] = 0xFF; + + } // end FOR loop + + transfer(buf, rbuf, (_number_of_packages * 4) + 8); + + } // end IF statement + + ScheduleDelayed(_led_controller.maximum_update_interval()); +} // end FUNCTION + +// Main function +// This runs when calling the command on the command line +extern "C" __EXPORT int apa102_main(int argc, char *argv[]) +{ + // This calls task_spawn + return APA102::main(argc, argv); +} diff --git a/src/drivers/lights/apa102/apa102.hpp b/src/drivers/lights/apa102/apa102.hpp new file mode 100644 index 0000000000..ee5db5910a --- /dev/null +++ b/src/drivers/lights/apa102/apa102.hpp @@ -0,0 +1,54 @@ +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +class APA102 : public device::SPI, public px4::ScheduledWorkItem, public ModuleBase +{ +public: + APA102(unsigned int number_of_packages, int bus, uint32_t device, int bus_frequency, spi_mode_e spi_mode); + virtual ~APA102(); + + /** @see ModuleBase */ + static int task_spawn(int argc, char *argv[]); + + /** @see ModuleBase */ + static int custom_command(int argc, char *argv[]); + + /** @see ModuleBase */ + static int print_usage(const char *reason = nullptr); + + void Run() override; + + /** @see ModuleBase::print_status() */ + int print_status() override; + + + int init(); + int status(); + +private: + // We don't want to use BOARD_HAS_N_S_RGB_LED, SPI leds are external + unsigned int _number_of_packages; + + uint8_t buf[(BOARD_MAX_LEDS * 4) + 8]; + uint8_t rbuf[(BOARD_MAX_LEDS * 4) + 8]; + + // LedController is a driver that takes care of LEDs using the led_control ORB msg. + // Do we really want to use this? + LedController _led_controller; + + // ?? + APA102(const APA102 &) = delete; + APA102 operator=(const APA102 &) = delete; + + // What is this? + // _leds is the actual buffer I believe + apa102::APA102LEDData *_leds; +}; \ No newline at end of file