// -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*- // Copyright (C) 2013 Henner Zeller // // This program 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 version 2. // // This program 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 // Controlling 16x32 or 32x32 RGB matrixes via GPIO. It allows daisy chaining // of a string of these, and also connecting a parallel string on newer // Raspberry Pis with more GPIO pins available. #ifndef RPI_RGBMATRIX_H #define RPI_RGBMATRIX_H #include #include #include #include #include "canvas.h" #include "thread.h" #include "pixel-mapper.h" namespace rgb_matrix { class RGBMatrix; class FrameCanvas; // Canvas for Double- and Multibuffering struct RuntimeOptions; // The RGB matrix provides the framebuffer and the facilities to constantly // update the LED matrix. // // This implement the Canvas interface that represents the display with // (led_cols * chained_displays)x(rows * parallel_displays) pixels. // // If can do multi-buffering using the CreateFrameCanvas() and SwapOnVSync() // methods. This is useful for animations and to prevent tearing. // // If you arrange the panels in a different way in the physical space, write // a CanvasTransformer that does coordinate remapping and which should be added // to the transformers, like with UArrangementTransformer in demo-main.cc. class RGBMatrix : public Canvas { public: // Options to initialize the RGBMatrix. Also see the main README.md for // detailed descriptions of the command line flags. struct Options { Options(); // Creates a default option set. // Validate the options and possibly output a message to string. If // "err" is NULL, outputs validation problems to stderr. // Returns 'true' if all options look good. bool Validate(std::string *err) const; // Name of the hardware mapping. Something like "regular" or "adafruit-hat" const char *hardware_mapping; // The "rows" are the number // of rows supported by the display, so 32 or 16. Default: 32. // Flag: --led-rows int rows; // The "cols" are the number of columns per panel. Typically something // like 32, but also 64 is possible. Sometimes even 40. // cols * chain_length is the total length of the display, so you can // represent a 64 wide display as cols=32, chain=2 or cols=64, chain=1; // same thing, but more convenient to think of. // Flag: --led-cols int cols; // The chain_length is the number of displays daisy-chained together // (output of one connected to input of next). Default: 1 // Flag: --led-chain int chain_length; // The number of parallel chains connected to the Pi; in old Pis with 26 // GPIO pins, that is 1, in newer Pis with 40 interfaces pins, that can // also be 2 or 3. The effective number of pixels in vertical direction is // then thus rows * parallel. Default: 1 // Flag: --led-parallel int parallel; // Set PWM bits used for output. Default is 11, but if you only deal with // limited comic-colors, 1 might be sufficient. Lower require less CPU and // increases refresh-rate. // Flag: --led-pwm-bits int pwm_bits; // Change the base time-unit for the on-time in the lowest // significant bit in nanoseconds. // Higher numbers provide better quality (more accurate color, less // ghosting), but have a negative impact on the frame rate. // Flag: --led-pwm-lsb-nanoseconds int pwm_lsb_nanoseconds; // The lower bits can be time-dithered for higher refresh rate. // Flag: --led-pwm-dither-bits int pwm_dither_bits; // The initial brightness of the panel in percent. Valid range is 1..100 // Default: 100 // Flag: --led-brightness int brightness; // Scan mode: 0=progressive, 1=interlaced. // Flag: --led-scan-mode int scan_mode; // Default row address type is 0, corresponding to direct setting of the // row, while row address type 1 is used for panels that only have A/B, // typically some 64x64 panels int row_address_type; // Flag --led-row-addr-type // Type of multiplexing. 0 = direct, 1 = stripe, 2 = checker,... // Flag: --led-multiplexing int multiplexing; // Disable the PWM hardware subsystem to create pulses. // Typically, you don't want to disable hardware pulsing, this is mostly // for debugging and figuring out if there is interference with the // sound system. // This won't do anything if output enable is not connected to GPIO 18 in // non-standard wirings. bool disable_hardware_pulsing; // Flag: --led-hardware-pulse // Show refresh rate on the terminal for debugging and tweaking purposes. bool show_refresh_rate; // Flag: --led-show-refresh // Some panels have inversed colors. bool inverse_colors; // Flag: --led-inverse // In case the internal sequence of mapping is not "RGB", this contains the // real mapping. Some panels mix up these colors. String of length three // which has to contain all characters R, G and B. const char *led_rgb_sequence; // Flag: --led-rgb-sequence // A string describing a sequence of pixel mappers that should be applied // to this matrix. A semicolon-separated list of pixel-mappers with optional // parameter. const char *pixel_mapper_config; // Flag: --led-pixel-mapper // Panel type. Typically an empty string or NULL, but some panels need // a particular initialization sequence, so this is used for that. // This can be e.g. "FM6126A" for that particular panel type. const char *panel_type; // Flag: --led-panel-type // Limit refresh rate of LED panel. This will help on a loaded system // to keep a constant refresh rate. <= 0 for no limit. int limit_refresh_rate_hz; // Flag: --led-limit-refresh }; // Factory to create a matrix. Additional functionality includes dropping // privileges and becoming a daemon. // Returns NULL, if there was a problem (a message then is written to stderr). static RGBMatrix *CreateFromOptions(const Options &options, const RuntimeOptions &runtime_options); // A factory that parses your main() commandline flags to read options // meant to configure the the matrix and returns a freshly allocated matrix. // // Optionally, you can pass in option structs with a couple of defaults // which are used unless overwritten on the command line. // A matrix is created and returned; also the options structs are // updated to reflect the values that were used and set on the command line. // // If you allow the user to start a daemon with --led-daemon, make sure to // call this function before you have started any threads, so early on in // main() (see RuntimeOptions documentation). // // Note, the permissions are dropped by default from 'root' to 'daemon', so // if you are required to stay root after this, disable this option in // the default RuntimeOptions (set drop_privileges = -1). // Returns NULL, if there was a problem (a message then is written to stderr). static RGBMatrix *CreateFromFlags(int *argc, char ***argv, RGBMatrix::Options *default_options = NULL, RuntimeOptions *default_runtime_opts = NULL, bool remove_consumed_flags = true); // Stop matrix, delete all resources. virtual ~RGBMatrix(); // -- Canvas interface. These write to the active FrameCanvas // (see documentation in canvas.h) // // Since this is updating the canvas that is currently displayed, this // might result in tearing. // Prefer using a FrameCanvas and do double-buffering, see section below. virtual int width() const; virtual int height() const; virtual void SetPixel(int x, int y, uint8_t red, uint8_t green, uint8_t blue); virtual void Clear(); virtual void Fill(uint8_t red, uint8_t green, uint8_t blue); // -- Double- and Multibuffering. // Create a new buffer to be used for multi-buffering. The returned new // Buffer implements a Canvas with the same size of thie RGBMatrix. // You can use it to draw off-screen on it, then swap it with the active // buffer using SwapOnVSync(). That would be classic double-buffering. // // You can also create as many FrameCanvas as you like and for instance use // them to pre-fill scenes of an animation for fast playback later. // // The ownership of the created Canvases remains with the RGBMatrix, so you // don't have to worry about deleting them (but you also don't want to create // more than needed as this will fill up your memory as they are only deleted // when the RGBMatrix is deleted). FrameCanvas *CreateFrameCanvas(); // This method waits to the next VSync and swaps the active buffer with the // supplied buffer. The formerly active buffer is returned. // // If you pass in NULL, the active buffer is returned, but it won't be // replaced with NULL. You can use the NULL-behavior to just wait on // VSync or to retrieve the initial buffer when preparing a multi-buffer // animation. // // The optional "framerate_fraction" parameter allows to choose which // multiple of the global frame-count to use. So it slows down your animation // to an exact integer fraction of the refresh rate. // Default is 1, so immediately next available frame. // (Say you have 140Hz refresh rate, then a value of 5 would give you an // 28Hz animation, nicely locked to the refresh-rate). // If you combine this with Options::limit_refresh_rate_hz you can create // time-correct animations. FrameCanvas *SwapOnVSync(FrameCanvas *other, unsigned framerate_fraction = 1); // -- Setting shape and behavior of matrix. // Apply a pixel mapper. This is used to re-map pixels according to some // scheme implemented by the PixelMapper. Does _not_ take ownership of the // mapper. Mapper can be NULL, in which case nothing happens. // Returns a boolean indicating if this was successful. bool ApplyPixelMapper(const PixelMapper *mapper); // Note, there used to be ApplyStaticTransformer(), which has been deprecated // since 2018 and changed to a compile-time option, then finally removed // in 2020. Use PixelMapper instead, which is simpler and more intuitive. // Set PWM bits used for output. Default is 11, but if you only deal with // limited comic-colors, 1 might be sufficient. Lower require less CPU and // increases refresh-rate. // // Returns boolean to signify if value was within range. // // This sets the PWM bits for the current active FrameCanvas and future // ones that are created with CreateFrameCanvas(). bool SetPWMBits(uint8_t value); uint8_t pwmbits(); // return the pwm-bits of the currently active buffer. // Map brightness of output linearly to input with CIE1931 profile. void set_luminance_correct(bool on); bool luminance_correct() const; // Set brightness in percent for all created FrameCanvas. 1%..100%. // This will only affect newly set pixels. void SetBrightness(uint8_t brightness); uint8_t brightness(); //-- GPIO interaction. // This library uses the GPIO pins to drive the matrix; this is a safe way // to request the 'remaining' bits to be used for user purposes. // Request user readable GPIO bits. // This function allows you to request pins you'd like to read with // AwaitInputChange(). // Only bits that are not already in use for reading or wrtiting // by the matrix are allowed. // Input is a bitmap of all the GPIO bits you're interested in; returns all // the bits that are actually available. uint64_t RequestInputs(uint64_t all_interested_bits); // This function will return whenever the GPIO input pins // change (pins that are not already in use for output, that is) or the // timeout is reached. You need to have reserved the inputs with // matrix->RequestInputs(...) first (e.g. // matrix->RequestInputs((1<<25)|(1<<24)); // // A positive timeout waits the given amount of milliseconds for a change // (e.g. a button-press) to occur; if there is no change, it will just // return the last value. // If you just want to know how the pins are right now, call with zero // timeout. // A negative number waits forever and will only return if there is a change. // // This function only samples between display refreshes so polling some // input does not generate flicker and provide a convenient change interface. // // Returns the bitmap of all GPIO input pins. uint64_t AwaitInputChange(int timeout_ms); // Request user writable GPIO bits. // This allows to request a bitmap of GPIO-bits to be used by the user for // writing. // Only bits that are not already in use for reading or wrtiting // by the matrix are allowed. // Returns the subset bits that are _actually_ available, uint64_t RequestOutputs(uint64_t output_bits); // Set the user-settable bits according to output bits. void OutputGPIO(uint64_t output_bits); // Legacy way to set gpio pins. We're not doing this anymore but need to // be source-compatible with old calls of the form // matrix->gpio()->RequestInputs(...) // // Don't use, use AwaitInputChange() directly. RGBMatrix *gpio() __attribute__((deprecated)) { return this; } //-- Rarely needed // Start the refresh thread. // This is only needed if you chose RuntimeOptions::daemon = -1 (see below), // otherwise the refresh thread is already started. bool StartRefresh(); private: class Impl; RGBMatrix(Impl *impl) : impl_(impl) {} Impl *const impl_; }; namespace internal { class Framebuffer; } class FrameCanvas : public Canvas { public: // Set PWM bits used for this Frame. // Simple comic-colors, 1 might be sufficient (111 RGB, i.e. 8 colors). // Lower require less CPU. // Returns boolean to signify if value was within range. bool SetPWMBits(uint8_t value); uint8_t pwmbits(); // Map brightness of output linearly to input with CIE1931 profile. void set_luminance_correct(bool on); bool luminance_correct() const; void SetBrightness(uint8_t brightness); uint8_t brightness(); //-- Serialize()/Deserialize() are fast ways to store and re-create a canvas. // Provides a pointer to a buffer of the internal representation to // be copied out for later Deserialize(). // // Returns a "data" pointer and the data "len" in the given out-paramters; // the content can be copied from there by the caller. // // Note, the content is not simply RGB, it is the opaque and platform // specific representation which allows to make deserialization very fast. // It is also bigger than just RGB; if you want to store it somewhere, // using compression is a good idea. void Serialize(const char **data, size_t *len) const; // Load data previously stored with Serialize(). Needs to be restored into // a FrameCanvas with exactly the same settings (rows, chain, transformer,...) // as serialized. // Returns 'false' if size is unexpected. // This method should only be called if FrameCanvas is off-screen. bool Deserialize(const char *data, size_t len); // Copy content from other FrameCanvas owned by the same RGBMatrix. void CopyFrom(const FrameCanvas &other); // -- Canvas interface. virtual int width() const; virtual int height() const; virtual void SetPixel(int x, int y, uint8_t red, uint8_t green, uint8_t blue); virtual void Clear(); virtual void Fill(uint8_t red, uint8_t green, uint8_t blue); private: friend class RGBMatrix; FrameCanvas(internal::Framebuffer *frame) : frame_(frame){} virtual ~FrameCanvas(); // Any FrameCanvas is owned by RGBMatrix. internal::Framebuffer *framebuffer() { return frame_; } internal::Framebuffer *const frame_; }; // Runtime options to simplify doing common things for many programs such as // dropping privileges and becoming a daemon. struct RuntimeOptions { RuntimeOptions(); int gpio_slowdown; // 0 = no slowdown. Flag: --led-slowdown-gpio // ---------- // If the following options are set to disabled with -1, they are not // even offered via the command line flags. // ---------- // Thre are three possible values here // -1 : don't leave choise of becoming daemon to the command line parsing. // If set to -1, the --led-daemon option is not offered. // 0 : do not becoma a daemon, run in forgreound (default value) // 1 : become a daemon, run in background. // // If daemon is disabled (= -1), the user has to call // RGBMatrix::StartRefresh() manually once the matrix is created, to leave // the decision to become a daemon // after the call (which requires that no threads have been started yet). // In the other cases (off or on), the choice is already made, so the thread // is conveniently already started for you. int daemon; // -1 disabled. 0=off, 1=on. Flag: --led-daemon // Drop privileges from 'root' to 'daemon' once the hardware is initialized. // This is usually a good idea unless you need to stay on elevated privs. int drop_privileges; // -1 disabled. 0=off, 1=on. flag: --led-drop-privs // By default, the gpio is initialized for you, but if you run on a platform // not the Raspberry Pi, this will fail. If you don't need to access GPIO // e.g. you want to just create a stream output (see content-streamer.h), // set this to false. bool do_gpio_init; }; // Convenience utility functions to read standard rgb-matrix flags and create // a RGBMatrix. Commandline flags are something like --led-rows, --led-chain, // --led-parallel. See output of PrintMatrixFlags() for all available options // and detailed description in // https://github.com/hzeller/rpi-rgb-led-matrix#changing-parameters-via-command-line-flags // // Example use: /* using rgb_matrix::RGBMatrix; int main(int argc, char **argv) { RGBMatrix::Options led_options; rgb_matrix::RuntimeOptions runtime; // Set defaults led_options.chain_length = 3; led_options.show_refresh_rate = true; runtime.drop_privileges = 1; if (!rgb_matrix::ParseOptionsFromFlags(&argc, &argv, &led_options, &runtime)) { rgb_matrix::PrintMatrixFlags(stderr); return 1; } // Do your own command line handling with the remaining flags. while (getopt()) {...} // Looks like we're ready to start RGBMatrix *matrix = RGBMatrix::CreateFromOptions(led_options, runtime); if (matrix == NULL) { return 1; } // .. now use matrix delete matrix; // Make sure to delete it in the end to switch off LEDs. return 0; } */ // This parses the flags from argv and updates the structs with the parsed-out // values. Structs can be NULL if you are not interested in it. // // The recongized flags are removed from argv if "remove_consumed_flags" is // true; this simplifies your command line processing for the remaining options. // // Returns 'true' on success, 'false' if there was flag parsing problem. bool ParseOptionsFromFlags(int *argc, char ***argv, RGBMatrix::Options *default_options, RuntimeOptions *rt_options, bool remove_consumed_flags = true); // Show all the available options in a style that can be used in a --help // output on the command line. void PrintMatrixFlags(FILE *out, const RGBMatrix::Options &defaults = RGBMatrix::Options(), const RuntimeOptions &rt_opt = RuntimeOptions()); // Legacy version of RGBMatrix::CreateFromOptions() inline RGBMatrix *CreateMatrixFromOptions( const RGBMatrix::Options &options, const RuntimeOptions &runtime_options) { return RGBMatrix::CreateFromOptions(options, runtime_options); } // Legacy version of RGBMatrix::CreateFromFlags() inline RGBMatrix *CreateMatrixFromFlags( int *argc, char ***argv, RGBMatrix::Options *default_options = NULL, RuntimeOptions *default_runtime_opts = NULL, bool remove_consumed_flags = true) { return RGBMatrix::CreateFromFlags(argc, argv, default_options, default_runtime_opts, remove_consumed_flags); } } // end namespace rgb_matrix #endif // RPI_RGBMATRIX_H