From b68d100ae0e15a91a34ad1c857e6ea606bba6c48 Mon Sep 17 00:00:00 2001 From: Henner Zeller Date: Fri, 16 Sep 2016 19:02:53 -0700 Subject: [PATCH] o Next step in avoiding the need to re-compile: make hardware GPIO mapping choosable at run-time (and hence: via command line flag). Now, Adafruit HAT users don't have to recompile. --- README.md | 44 ++- examples-api-use/README.md | 2 + examples-api-use/c-example.c | 5 + examples-api-use/demo-main.cc | 3 +- examples-api-use/minimal-example.cc | 1 + include/led-matrix-c.h | 6 + include/led-matrix.h | 3 + lib/Makefile | 32 +- lib/framebuffer-internal.h | 14 +- lib/framebuffer.cc | 280 ++++++++---------- lib/hardware-mapping.c | 185 ++++++++++++ lib/hardware-mapping.h | 53 ++++ lib/hardware/README | 3 - .../adafruit-hat/led-panel-pin-mapping.h | 45 --- lib/hardware/classic/led-panel-pin-mapping.h | 54 ---- lib/hardware/regular/led-panel-pin-mapping.h | 46 --- lib/led-matrix-c.cc | 2 + lib/led-matrix.cc | 35 ++- lib/options-initialize.cc | 32 ++ utils/README.md | 1 + utils/led-image-viewer.cc | 3 + 21 files changed, 504 insertions(+), 345 deletions(-) create mode 100644 lib/hardware-mapping.c create mode 100644 lib/hardware-mapping.h delete mode 100644 lib/hardware/README delete mode 100644 lib/hardware/adafruit-hat/led-panel-pin-mapping.h delete mode 100644 lib/hardware/classic/led-panel-pin-mapping.h delete mode 100644 lib/hardware/regular/led-panel-pin-mapping.h diff --git a/README.md b/README.md index 52fc296..a2a0b58 100644 --- a/README.md +++ b/README.md @@ -84,8 +84,8 @@ This documentation is split into parts that help you through the process * [Wire up the matrix to your Pi](./wiring.md). This document describes what goes where. You might also be interested in [breakout boards](./adapter) - for that. If you have an [Adafruit HAT], necessary steps are - [described below](#if-you-have-an-adafruit-hat) + for that. If you have an [Adafruit HAT], you can choose that with + a command line option [described below](#if-you-have-an-adafruit-hat) * Run a demo. You find that in the [examples-api-use/](./examples-api-use#running-some-demos) directory: ``` @@ -133,8 +133,22 @@ Some might need to be changed for your particular kind of panel. Here is a little run-down of what these command-line flags do and when you'd like to change them. +First things first: if you have a different wiring than described in +[wiring](./wiring.md), for instance if you have an Adafruit HAT, you can +choose these here: + +``` +--led-gpio-mapping=: Name of GPIO mapping used. Default "regular" +``` + +This can have values such as + - `--led-gpio-mapping=regular` The standard mapping of this library, described in the [wiring](./wiring.md) page. + - `--led-gpio-mapping=adafruit-hat` standard Adafruit HAT or + - `--led-gpio-mapping=adafruit-hat-pwm` Adafruit HAT with the anti-flicker hardware mod [described below](#improving-flicker). + +The next most important flags describe the type and number of displays connected + ``` -# These are the most important --led-rows= : Panel rows. 8, 16, 32 or 64. (Default: 32). --led-chain= : Number of daisy-chained panels. (Default: 1). --led-parallel= : For A/B+ models or RPi2,3b: parallel chains. range=1..3 (Default: 1). @@ -365,16 +379,19 @@ ready-made vs. single-chain tradeoff is worthwhile, then you might go for that The Adafruit HAT uses this library but a modified pinout to support other features on the HAT. So they forked this library and modified the pinout there. However, that fork is _ancient_, so I strongly suggest to use this original -library instead - which in the meantime also has a way to switch to their pinout. +library instead. You can choose the Adafruit pinout with a command line flag. -In this library here, you can choose the Adafruit HAT pinout by editing -`lib/Makefile` and change `HARDWARE_DESC?=regular` to `HARDWARE_DESC=adafruit-hat`. +Just pass the option `--led-gpio-mapping=adafruit-hat`. -Alternatively, you can prefix the compilation call with this variable like so: +If you want to have this the default whenever you start (or if you are using +the Python library that does not support to set this at runtime yet), add the +following setting in front of your compilation: ``` HARDWARE_DESC=adafruit-hat make ``` -Then re-compile and a display connected to the HAT should work. +(alternatively, you can modify the `lib/Makefile` and change it there directly) +Then re-compile and the new flag default is now `adafruit-hat`, so +no need to set it on the command line. ### Improving flicker @@ -384,14 +401,17 @@ following picture (click to enlarge): -Then, uncomment the following line in the Makefile and recompile. +Then, start your programs with `--led-gpio-mapping=adafruit-hat-pwm`. +If you want to make this the default setting your program starts with, you can +also manually choose this with ``` -#DEFINES+=-DADAFRUIT_RGBMATRIX_HAT_PWM +HARDWARE_DESC=adafruit-hat-pwm make ``` +to get this as default setting. -Reboot the Pi and you now should have less visible flicker. This essentially -gives you the hardware pulses feature. +Now you should have less visible flicker. This essentially +switches on the hardware pulses feature for the Adafruit HAT. ### 64x64 with E-line on Adafruit HAT There are LED panels that have 64x64 LEDs packed, but they need 5 address lines, diff --git a/examples-api-use/README.md b/examples-api-use/README.md index 8ee0512..bc8a3a4 100644 --- a/examples-api-use/README.md +++ b/examples-api-use/README.md @@ -17,6 +17,7 @@ Options: in the middle in an U-arrangement to get more vertical space. -R : Sets the rotation of matrix. Allowed: 0, 90, 180, 270. Default: 0. -t : Run for these number of seconds, then exit. + --led-gpio-mapping= : Name of GPIO mapping used. Default "regular" --led-rows= : Panel rows. 8, 16, 32 or 64. (Default: 32). --led-chain= : Number of daisy-chained panels. (Default: 1). --led-parallel= : For A/B+ models or RPi2,3b: parallel chains. range=1..3 (Default: 1). @@ -86,6 +87,7 @@ using rgb_matrix::RGBMatrix; int main(int argc, char **argv) { // Set some defaults RGBMatrix::Options my_defaults; + my_defaults.hardware_mapping = "regular"; // or e.g. "adafruit-hat" or "adafruit-hat-pwm" my_defaults.chain_length = 3; my_defaults.show_refresh_rate = true; rgb_matrix::RuntimeOptions runtime_defaults; diff --git a/examples-api-use/c-example.c b/examples-api-use/c-example.c index b99d441..74bd088 100644 --- a/examples-api-use/c-example.c +++ b/examples-api-use/c-example.c @@ -5,6 +5,7 @@ */ #include "led-matrix-c.h" +#include #include #include @@ -31,6 +32,10 @@ int main(int argc, char **argv) { offscreen_canvas = led_matrix_create_offscreen_canvas(matrix); led_canvas_get_size(offscreen_canvas, &width, &height); + + fprintf(stderr, "Size: %dx%d. Hardware gpio mapping: %s\n", + width, height, options.hardware_mapping); + for (i = 0; i < 1000; ++i) { for (y = 0; y < height; ++y) { for (x = 0; x < width; ++x) { diff --git a/examples-api-use/demo-main.cc b/examples-api-use/demo-main.cc index 0796cee..5892bd9 100644 --- a/examples-api-use/demo-main.cc +++ b/examples-api-use/demo-main.cc @@ -1160,7 +1160,8 @@ int main(int argc, char *argv[]) { matrix->ApplyStaticTransformer(RotateTransformer(rotation)); } - printf("Size: %dx%d\n", matrix->width(), matrix->height()); + printf("Size: %dx%d. Hardware gpio mapping: %s\n", + matrix->width(), matrix->height(), matrix_options.hardware_mapping); Canvas *canvas = matrix; diff --git a/examples-api-use/minimal-example.cc b/examples-api-use/minimal-example.cc index 2f0ace9..930f2ef 100644 --- a/examples-api-use/minimal-example.cc +++ b/examples-api-use/minimal-example.cc @@ -45,6 +45,7 @@ static void DrawOnCanvas(Canvas *canvas) { int main(int argc, char *argv[]) { RGBMatrix::Options defaults; + defaults.hardware_mapping = "regular"; // or e.g. "adafruit-hat" defaults.rows = 32; defaults.chain_length = 1; defaults.parallel = 1; diff --git a/include/led-matrix-c.h b/include/led-matrix-c.h index e999e6a..9f99adc 100644 --- a/include/led-matrix-c.h +++ b/include/led-matrix-c.h @@ -43,6 +43,12 @@ struct LedCanvas; * should zero out this struct before setting anything. */ struct RGBLedMatrixOptions { + /* + * Name of the hardware mapping used. If passed NULL here, the default + * is used. + */ + const char *hardware_mapping; + /* The "rows" are the number of rows supported by the display, so 32 or 16. * Default: 32. * Corresponding flag: --led-rows diff --git a/include/led-matrix.h b/include/led-matrix.h index 706e862..f4d3692 100644 --- a/include/led-matrix.h +++ b/include/led-matrix.h @@ -61,6 +61,9 @@ public: // 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 diff --git a/lib/Makefile b/lib/Makefile index 355f37f..e2d7ffa 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -4,13 +4,14 @@ # -lrgbmatrix ## OBJECTS=gpio.o led-matrix.o options-initialize.o framebuffer.o \ - thread.o bdf-font.o graphics.o transformer.o led-matrix-c.o + thread.o bdf-font.o graphics.o transformer.o led-matrix-c.o \ + hardware-mapping.o TARGET=librgbmatrix # There are several different pinouts for various breakout boards that uses # this library. If you are using the described pinout in the toplevel README.md # or the standard active-3 breakout board, then 'regular' is the one you'd like -# to use (there is also a 'classic' one for an early form). +# to use. # # Adafruit also made a breakout board, if you want to use that, choose # 'adafruit-hat' @@ -18,15 +19,17 @@ TARGET=librgbmatrix # These are the choices # regular # Following this project wiring and using these PCBs # adafruit-hat # If you have a RGB matrix HAT from Adafruit -# classic # Not really used anymore +# adafruit-hat-pwm # If you have an Adafruit HAT with PWM hardware mod. +# classic # (deprecated) Classic Pi1/2/. Not used anymore. +# classic-pi1 # (deprecated) Classic pinout on Rasperry Pi 1 HARDWARE_DESC?=regular ### # After you change any of the following DEFINES, make sure to 'make' again. # -# Note, all of these options that don't directly influence the hardware mapping -# can now can be set programmatically and via command line flags as well. -# So be prepared for these to be removed in this Makefile. +# Note, all of these options can now can be set programmatically and +# via command line flags as well. No real need to change them in the Makefile. +# (So be prepared for these to be removed at some point) ### # If you see that your display is inverse, you might have a matrix variant @@ -105,12 +108,9 @@ HARDWARE_DESC?=regular # connect GPIO 4 (old OE) with 18 (the new OE); there are # convenient solder holes labeled 4 and 18 on the Adafruit HAT, pretty # close together. -# Then uncomment the following define and recompile. -#DEFINES+=-DADAFRUIT_RGBMATRIX_HAT_PWM - -# If you use HARDWARE_DESC=classic and a Raspberry Pi 1, Revision A, -# this might be useful (untested). -#DEFINES+=-DPI_REV1_RGB_PINOUT +# Then you can set the flag --led-gpio-mapping=adafruit-hat-pwm +# .. or uncomment the following line. +#HARDWARE_DESC=adafruit-hat-pwm # Typically, a Hub75 panel is split in two half displays, so that a 1:16 # multiplexing actually multiplexes over two half displays and gives 32 lines. @@ -125,8 +125,10 @@ HARDWARE_DESC?=regular # make USER_DEFINES="-DSHOW_REFRESH_RATE" DEFINES+=$(USER_DEFINES) +DEFINES+=-DDEFAULT_HARDWARE='"$(HARDWARE_DESC)"' INCDIR=../include -CXXFLAGS=-Wall -O3 -g -fPIC $(DEFINES) -Ihardware/$(HARDWARE_DESC) +CFLAGS=-Wall -O3 -g -fPIC $(DEFINES) +CXXFLAGS=$(CFLAGS) all : $(TARGET).a $(TARGET).so.1 @@ -144,11 +146,13 @@ graphics.o: graphics.cc utf8-internal.h %.o : %.cc compiler-flags $(CXX) -I$(INCDIR) $(CXXFLAGS) -c -o $@ $< +%.o : %.c compiler-flags + $(CC) -I$(INCDIR) $(CXXFLAGS) -c -o $@ $< + clean: rm -f $(OBJECTS) $(TARGET).a $(TARGET).so.1 compiler-flags: FORCE - @if [ ! -d hardware/$(HARDWARE_DESC) ] ; then echo "HARDWARE_DESC='$(HARDWARE_DESC)' is an unsupported hardware description. Typo ?"; exit 1; fi @echo '$(CXX) $(CXXFLAGS)' | cmp -s - $@ || echo '$(CXX) $(CXXFLAGS)' > $@ .PHONY: FORCE diff --git a/lib/framebuffer-internal.h b/lib/framebuffer-internal.h index 47b674f..df98b07 100644 --- a/lib/framebuffer-internal.h +++ b/lib/framebuffer-internal.h @@ -17,6 +17,8 @@ #include +#include "hardware-mapping.h" + namespace rgb_matrix { class GPIO; class PinPulser; @@ -64,6 +66,7 @@ public: ~Framebuffer(); // Initialize GPIO bits for output. Only call once. + static void InitHardwareMapping(const char *named_hardware); static void InitGPIO(GPIO *io, int rows, int parallel, bool allow_hardware_pulsing, int pwm_lsb_nanoseconds); @@ -96,9 +99,7 @@ public: void Fill(uint8_t red, uint8_t green, uint8_t blue); private: - // Define the type to do the pin-mapping. These are include fils - // found in include directory hardware/$(name-of-mapping) -#include "led-panel-pin-mapping.h" // see HARDWARE_DESC in lib/Makefile + static const struct HardwareMapping *hardware_mapping_; void InitDefaultDesignator(int x, int y, PixelDesignator *designator); inline void MapColors(uint8_t r, uint8_t g, uint8_t b, @@ -125,11 +126,8 @@ private: // Each bitplane-column is pre-filled IoBits, of which the colors are set. // Of course, that means that we store unrelated bits in the frame-buffer, // but it allows easy access in the critical section. - IoBits *bitplane_buffer_; - inline IoBits *ValueAt(int double_row, int column, int bit); - inline IoBits &color_bits(uint32_t *val) { - return *reinterpret_cast(val); - } + gpio_bits_t *bitplane_buffer_; + inline gpio_bits_t *ValueAt(int double_row, int column, int bit); PixelMapper **shared_mapper_; // Storage in RGBMatrix. }; diff --git a/lib/framebuffer.cc b/lib/framebuffer.cc index 67adee8..394c6e5 100644 --- a/lib/framebuffer.cc +++ b/lib/framebuffer.cc @@ -19,11 +19,12 @@ #include "framebuffer-internal.h" -#include #include -#include -#include #include +#include +#include +#include +#include #include "gpio.h" @@ -33,22 +34,6 @@ enum { kBitPlanes = 11 // maximum usable bitplanes. }; -// Leave this in here for a while. -#if defined(ADAFRUIT_RGBMATRIX_HAT) && !FYI_ADAFRUIT_HAT_PIN_MAPPING_INCLUDED_ -# error "You are using an old way to select the Adafruit HAT." -# error "Set HARDWARE_DESC=adafruit-hat instead of ADAFRUIT_RGBMATRIX_HAT" -# error "Check out https://github.com/hzeller/rpi-rgb-led-matrix#switch-the-pinout" -#endif - -// Remind forgetful Makefile option editors -#if defined(ADAFRUIT_RGBMATRIX_HAT_PWM) && !FYI_ADAFRUIT_HAT_PIN_MAPPING_INCLUDED_ -# error "You have defined ADAFRUIT_RGBMATRIX_HAT_PWM which is for the Adafruit HAT. So you also need to set HARDWARE_DESC=adafruit-hat" -#endif - -#ifndef RGB_PARALLEL_CHAINS -# error "Your pin-mapping.h file should contain an RGB_PARALLEL_CHAINS macro" -#endif - // We need one global instance of a timing correct pulser. There are different // implementations depending on the context. static PinPulser *sOutputEnablePulser = NULL; @@ -74,6 +59,8 @@ PixelMapper::~PixelMapper() { delete [] buffer_; } +const struct HardwareMapping *Framebuffer::hardware_mapping_ = NULL; + Framebuffer::Framebuffer(int rows, int columns, int parallel, int scan_mode, bool swap_green_blue, bool inverse_color, @@ -87,16 +74,19 @@ Framebuffer::Framebuffer(int rows, int columns, int parallel, pwm_bits_(kBitPlanes), do_luminance_correct_(true), brightness_(100), double_rows_(rows / SUB_PANELS_), row_mask_(double_rows_ - 1), shared_mapper_(mapper) { + assert(hardware_mapping_ != NULL); // Called InitHardwareMapping() ? assert(shared_mapper_ != NULL); // Storage should be provided by RGBMatrix. assert(rows_ == 8 || rows_ == 16 || rows_ == 32 || rows_ == 64); - assert(parallel >= 1 && parallel <= 3); - if (parallel > RGB_PARALLEL_CHAINS) { - fprintf(stderr, "Parallel of %d is higher than the supported " - "RGB_PARALLEL_CHAINS of %d\n", parallel, RGB_PARALLEL_CHAINS); - assert(parallel == 1); + if (parallel > hardware_mapping_->max_parallel_chains) { + fprintf(stderr, "The %s GPIO mapping only supports %d parallel chain%s, " + "but %d was requested.\n", hardware_mapping_->name, + hardware_mapping_->max_parallel_chains, + hardware_mapping_->max_parallel_chains > 1 ? "s" : "", parallel); + abort(); } + assert(parallel >= 1 && parallel <= 3); - bitplane_buffer_ = new IoBits [double_rows_ * columns_ * kBitPlanes]; + bitplane_buffer_ = new gpio_bits_t[double_rows_ * columns_ * kBitPlanes]; // If we're the first Framebuffer created, the shared PixelMapper is // still NULL, so create one. @@ -122,67 +112,81 @@ Framebuffer::~Framebuffer() { delete [] bitplane_buffer_; } +// TODO: this should also be parsed from some special formatted string, e.g. +// {addr={22,23,24,25,15},oe=18,clk=17,strobe=4, p0={11,27,7,8,9,10},...} +/* static */ void Framebuffer::InitHardwareMapping(const char *named_hardware) { + if (named_hardware == NULL || *named_hardware == '\0') { + named_hardware = "regular"; + } + + struct HardwareMapping *mapping = NULL; + for (HardwareMapping *it = matrix_hardware_mappings; it->name; ++it) { + if (strcasecmp(it->name, named_hardware) == 0) { + mapping = it; + break; + } + } + + if (!mapping) { + fprintf(stderr, "There is no hardware mapping named '%s'.\nAvailable: ", + named_hardware); + for (HardwareMapping *it = matrix_hardware_mappings; it->name; ++it) { + if (it != matrix_hardware_mappings) fprintf(stderr, ", "); + fprintf(stderr, "'%s'", it->name); + } + fprintf(stderr, "\n"); + abort(); + } + + if (mapping->max_parallel_chains == 0) { + // Auto determine. + struct HardwareMapping *h = mapping; + if ((h->p0_r1 | h->p0_g1 | h->p0_g1 | h->p0_r2 | h->p0_g2 | h->p0_g2) > 0) + ++mapping->max_parallel_chains; + if ((h->p1_r1 | h->p1_g1 | h->p1_g1 | h->p1_r2 | h->p1_g2 | h->p1_g2) > 0) + ++mapping->max_parallel_chains; + if ((h->p2_r1 | h->p2_g1 | h->p2_g1 | h->p2_r2 | h->p2_g2 | h->p2_g2) > 0) + ++mapping->max_parallel_chains; + } + hardware_mapping_ = mapping; +} + /* static */ void Framebuffer::InitGPIO(GPIO *io, int rows, int parallel, bool allow_hardware_pulsing, int pwm_lsb_nanoseconds) { if (sOutputEnablePulser != NULL) return; // already initialized. + const struct HardwareMapping &h = *hardware_mapping_; // Tell GPIO about all bits we intend to use. - IoBits b; - b.raw = 0; + gpio_bits_t all_used_bits = 0; -#ifdef PI_REV1_RGB_PINOUT - // This is only to be enabled with classic pinout. - b.bits.output_enable_rev1 = b.bits.output_enable_rev2 = 1; - b.bits.clock_rev1 = b.bits.clock_rev2 = 1; -#endif + all_used_bits |= h.output_enable | h.clock | h.strobe; - b.bits.output_enable = 1; - b.bits.clock = 1; - b.bits.strobe = 1; - - b.bits.p0_r1 = b.bits.p0_g1 = b.bits.p0_b1 = 1; - b.bits.p0_r2 = b.bits.p0_g2 = b.bits.p0_b2 = 1; - -#if RGB_PARALLEL_CHAINS >= 2 + all_used_bits |= h.p0_r1 | h.p0_g1 | h.p0_b1 | h.p0_r2 | h.p0_g2 | h.p0_b2; if (parallel >= 2) { - b.bits.p1_r1 = b.bits.p1_g1 = b.bits.p1_b1 = 1; - b.bits.p1_r2 = b.bits.p1_g2 = b.bits.p1_b2 = 1; + all_used_bits |= h.p1_r1 | h.p1_g1 | h.p1_b1 | h.p1_r2 | h.p1_g2 | h.p1_b2; } -#endif - -#if RGB_PARALLEL_CHAINS >= 3 if (parallel >= 3) { - b.bits.p2_r1 = b.bits.p2_g1 = b.bits.p2_b1 = 1; - b.bits.p2_r2 = b.bits.p2_g2 = b.bits.p2_b2 = 1; + all_used_bits |= h.p2_r1 | h.p2_g1 | h.p2_b1 | h.p2_r2 | h.p2_g2 | h.p2_b2; } -#endif const int double_rows = rows / 2; - if (double_rows >= 32) b.bits.e = 1; - if (double_rows >= 16) b.bits.d = 1; - if (double_rows >= 8) b.bits.c = 1; - if (double_rows >= 4) b.bits.b = 1; - b.bits.a = 1; + if (double_rows >= 32) all_used_bits |= h.e; + if (double_rows >= 16) all_used_bits |= h.d; + if (double_rows >= 8) all_used_bits |= h.c; + if (double_rows >= 4) all_used_bits |= h.b; + all_used_bits |= h.a; // Initialize outputs, make sure that all of these are supported bits. - const uint32_t result = io->InitOutputs(b.raw); - assert(result == b.raw); - - // Now, set up the PinPulser for output enable. - IoBits output_enable_bits; -#ifdef PI_REV1_RGB_PINOUT - output_enable_bits.bits.output_enable_rev1 - = output_enable_bits.bits.output_enable_rev2 = 1; -#endif - output_enable_bits.bits.output_enable = 1; + const uint32_t result = io->InitOutputs(all_used_bits); + assert(result == all_used_bits); // Impl: all bits declared in gpio.cc ? std::vector bitplane_timings; for (int b = 0; b < kBitPlanes; ++b) { bitplane_timings.push_back(pwm_lsb_nanoseconds << b); } - sOutputEnablePulser = PinPulser::Create(io, output_enable_bits.raw, + sOutputEnablePulser = PinPulser::Create(io, h.output_enable, allow_hardware_pulsing, bitplane_timings); } @@ -194,8 +198,7 @@ bool Framebuffer::SetPWMBits(uint8_t value) { return true; } -inline Framebuffer::IoBits *Framebuffer::ValueAt(int double_row, - int column, int bit) { +inline gpio_bits_t *Framebuffer::ValueAt(int double_row, int column, int bit) { return &bitplane_buffer_[ double_row * (columns_ * kBitPlanes) + bit * columns_ + column ]; @@ -272,27 +275,22 @@ void Framebuffer::Fill(uint8_t r, uint8_t g, uint8_t b) { } else { MapColors(r, g, b, &red, &blue, &green); } + const struct HardwareMapping &h = *hardware_mapping_; + gpio_bits_t all_r = h.p0_r1 | h.p0_r2 | h.p1_r1 | h.p1_r2 | h.p2_r1 | h.p2_r2; + gpio_bits_t all_g = h.p0_g1 | h.p0_g2 | h.p1_g1 | h.p1_g2 | h.p2_g1 | h.p2_g2; + gpio_bits_t all_b = h.p0_b1 | h.p0_b2 | h.p1_b1 | h.p1_b2 | h.p2_b1 | h.p2_b2; for (int b = kBitPlanes - pwm_bits_; b < kBitPlanes; ++b) { uint16_t mask = 1 << b; - IoBits plane_bits; - plane_bits.raw = 0; - plane_bits.bits.p0_r1 = plane_bits.bits.p0_r2 = (red & mask) == mask; - plane_bits.bits.p0_g1 = plane_bits.bits.p0_g2 = (green & mask) == mask; - plane_bits.bits.p0_b1 = plane_bits.bits.p0_b2 = (blue & mask) == mask; + gpio_bits_t plane_bits = 0; + plane_bits |= ((red & mask) == mask) ? all_r : 0; + plane_bits |= ((green & mask) == mask) ? all_g : 0; + plane_bits |= ((blue & mask) == mask) ? all_b : 0; -#if RGB_PARALLEL_CHAINS > 1 - plane_bits.bits.p1_r1 = plane_bits.bits.p1_r2 = - plane_bits.bits.p2_r1 = plane_bits.bits.p2_r2 = (red & mask) == mask; - plane_bits.bits.p1_g1 = plane_bits.bits.p1_g2 = - plane_bits.bits.p2_g1 = plane_bits.bits.p2_g2 = (green & mask) == mask; - plane_bits.bits.p1_b1 = plane_bits.bits.p1_b2 = - plane_bits.bits.p2_b1 = plane_bits.bits.p2_b2 = (blue & mask) == mask; -#endif for (int row = 0; row < double_rows_; ++row) { - IoBits *row_data = ValueAt(row, 0, b); + uint32_t *row_data = ValueAt(row, 0, b); for (int col = 0; col < columns_; ++col) { - (row_data++)->raw = plane_bits.raw; + *row_data++ = plane_bits; } } } @@ -314,7 +312,7 @@ void Framebuffer::SetPixel(int x, int y, uint8_t r, uint8_t g, uint8_t b) { MapColors(r, g, b, &red, &blue, &green); } - IoBits *bits = bitplane_buffer_ + pos; + uint32_t *bits = bitplane_buffer_ + pos; const int min_bit_plane = kBitPlanes - pwm_bits_; bits += (columns_ * min_bit_plane); const uint32_t r_bits = designator->r_bit; @@ -327,101 +325,69 @@ void Framebuffer::SetPixel(int x, int y, uint8_t r, uint8_t g, uint8_t b) { if (red & mask) color_bits |= r_bits; if (green & mask) color_bits |= g_bits; if (blue & mask) color_bits |= b_bits; - bits->raw = (bits->raw & designator_mask) | color_bits; + *bits = (*bits & designator_mask) | color_bits; bits += columns_; } } void Framebuffer::InitDefaultDesignator(int x, int y, PixelDesignator *d) { - IoBits *bits = ValueAt(y & row_mask_, x, 0); + const struct HardwareMapping &h = *hardware_mapping_; + uint32_t *bits = ValueAt(y & row_mask_, x, 0); d->gpio_word = bits - bitplane_buffer_; d->r_bit = d->g_bit = d->b_bit = 0; if (y < rows_) { if (y < double_rows_) { - color_bits(&d->r_bit).bits.p0_r1 = 1; - color_bits(&d->g_bit).bits.p0_g1 = 1; - color_bits(&d->b_bit).bits.p0_b1 = 1; + d->r_bit = h.p0_r1; + d->g_bit = h.p0_g1; + d->b_bit = h.p0_b1; } else { - color_bits(&d->r_bit).bits.p0_r2 = 1; - color_bits(&d->g_bit).bits.p0_g2 = 1; - color_bits(&d->b_bit).bits.p0_b2 = 1; + d->r_bit = h.p0_r2; + d->g_bit = h.p0_g2; + d->b_bit = h.p0_b2; } } -#if RGB_PARALLEL_CHAINS >= 2 else if (y >= rows_ && y < 2 * rows_) { if (y - rows_ < double_rows_) { - color_bits(&d->r_bit).bits.p1_r1 = 1; - color_bits(&d->g_bit).bits.p1_g1 = 1; - color_bits(&d->b_bit).bits.p1_b1 = 1; + d->r_bit = h.p1_r1; + d->g_bit = h.p1_g1; + d->b_bit = h.p1_b1; } else { - color_bits(&d->r_bit).bits.p1_r2 = 1; - color_bits(&d->g_bit).bits.p1_g2 = 1; - color_bits(&d->b_bit).bits.p1_b2 = 1; + d->r_bit = h.p1_r2; + d->g_bit = h.p1_g2; + d->b_bit = h.p1_b2; } } -#endif -#if RGB_PARALLEL_CHAINS >= 3 else { if (y - 2*rows_ < double_rows_) { - color_bits(&d->r_bit).bits.p2_r1 = 1; - color_bits(&d->g_bit).bits.p2_g1 = 1; - color_bits(&d->b_bit).bits.p2_b1 = 1; + d->r_bit = h.p2_r1; + d->g_bit = h.p2_g1; + d->b_bit = h.p2_b1; } else { - color_bits(&d->r_bit).bits.p2_r2 = 1; - color_bits(&d->g_bit).bits.p2_g2 = 1; - color_bits(&d->b_bit).bits.p2_b2 = 1; + d->r_bit = h.p2_r2; + d->g_bit = h.p2_g2; + d->b_bit = h.p2_b2; } } -#endif + d->mask = ~(d->r_bit | d->g_bit | d->b_bit); } void Framebuffer::DumpToMatrix(GPIO *io) { - IoBits color_clk_mask; // Mask of bits we need to set while clocking in. - color_clk_mask.bits.p0_r1 - = color_clk_mask.bits.p0_g1 - = color_clk_mask.bits.p0_b1 - = color_clk_mask.bits.p0_r2 - = color_clk_mask.bits.p0_g2 - = color_clk_mask.bits.p0_b2 = 1; - -#if RGB_PARALLEL_CHAINS >= 2 + const struct HardwareMapping &h = *hardware_mapping_; + gpio_bits_t color_clk_mask = 0; // Mask of bits while clocking in. + color_clk_mask |= h.p0_r1 | h.p0_g1 | h.p0_b1 | h.p0_r2 | h.p0_g2 | h.p0_b2; if (parallel_ >= 2) { - color_clk_mask.bits.p1_r1 - = color_clk_mask.bits.p1_g1 - = color_clk_mask.bits.p1_b1 - = color_clk_mask.bits.p1_r2 - = color_clk_mask.bits.p1_g2 - = color_clk_mask.bits.p1_b2 = 1; + color_clk_mask |= h.p1_r1 | h.p1_g1 | h.p1_b1 | h.p1_r2 | h.p1_g2 | h.p1_b2; } -#endif - -#if RGB_PARALLEL_CHAINS >= 3 if (parallel_ >= 3) { - color_clk_mask.bits.p2_r1 - = color_clk_mask.bits.p2_g1 - = color_clk_mask.bits.p2_b1 - = color_clk_mask.bits.p2_r2 - = color_clk_mask.bits.p2_g2 - = color_clk_mask.bits.p2_b2 = 1; + color_clk_mask |= h.p2_r1 | h.p2_g1 | h.p2_b1 | h.p2_r2 | h.p2_g2 | h.p2_b2; } -#endif -#ifdef PI_REV1_RGB_PINOUT - color_clk_mask.bits.clock_rev1 = color_clk_mask.bits.clock_rev2 = 1; -#endif - color_clk_mask.bits.clock = 1; + color_clk_mask |= h.clock; - IoBits row_mask; - row_mask.bits.a = row_mask.bits.b = row_mask.bits.c - = row_mask.bits.d = row_mask.bits.e = 1; + const gpio_bits_t row_mask = h.a | h.b | h.c | h.d | h.e; - IoBits clock, strobe, row_address; -#ifdef PI_REV1_RGB_PINOUT - clock.bits.clock_rev1 = clock.bits.clock_rev2 = 1; -#endif - clock.bits.clock = 1; - strobe.bits.strobe = 1; + gpio_bits_t row_address; // info needed for interlace mode. uint8_t rot_bits = 0; @@ -445,32 +411,32 @@ void Framebuffer::DumpToMatrix(GPIO *io) { d_row = ((row_loop << 1) | (row_loop >> rot_bits)) & row_mask_; } - row_address.bits.a = d_row; - row_address.bits.b = d_row >> 1; - row_address.bits.c = d_row >> 2; - row_address.bits.d = d_row >> 3; - row_address.bits.e = d_row >> 4; + row_address = (d_row & 0x01) ? h.a : 0; + row_address |= (d_row & 0x02) ? h.b : 0; + row_address |= (d_row & 0x04) ? h.c : 0; + row_address |= (d_row & 0x08) ? h.d : 0; + row_address |= (d_row & 0x10) ? h.e : 0; // Rows can't be switched very quickly without ghosting, so we do the // full PWM of one row before switching rows. for (int b = kBitPlanes - pwm_to_show; b < kBitPlanes; ++b) { - IoBits *row_data = ValueAt(d_row, 0, b); + gpio_bits_t *row_data = ValueAt(d_row, 0, b); // While the output enable is still on, we can already clock in the next // data. for (int col = 0; col < columns_; ++col) { - const IoBits &out = *row_data++; - io->WriteMaskedBits(out.raw, color_clk_mask.raw); // col + reset clock - io->SetBits(clock.raw); // Rising edge: clock color in. + const gpio_bits_t &out = *row_data++; + io->WriteMaskedBits(out, color_clk_mask); // col + reset clock + io->SetBits(h.clock); // Rising edge: clock color in. } - io->ClearBits(color_clk_mask.raw); // clock back to normal. + io->ClearBits(color_clk_mask); // clock back to normal. // OE of the previous row-data must be finished before strobe. sOutputEnablePulser->WaitPulseFinished(); // Setting address and strobing needs to happen in dark time. - io->WriteMaskedBits(row_address.raw, row_mask.raw); // Set row address - io->SetBits(strobe.raw); // Strobe in the previously clocked in row. - io->ClearBits(strobe.raw); + io->WriteMaskedBits(row_address, row_mask); // Set row address + io->SetBits(h.strobe); // Strobe in the previously clocked in row. + io->ClearBits(h.strobe); // Now switch on for the sleep time necessary for that bit-plane. sOutputEnablePulser->SendPulse(b); diff --git a/lib/hardware-mapping.c b/lib/hardware-mapping.c new file mode 100644 index 0000000..abc5038 --- /dev/null +++ b/lib/hardware-mapping.c @@ -0,0 +1,185 @@ +/* -*- mode: c; c-basic-offset: 2; indent-tabs-mode: nil; -*- + * Copyright (C) 2013, 2016 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 + */ + +/* + * We do this in plain C so that we can use designated initializers. + */ +#include "hardware-mapping.h" + +#define GPIO_BIT(b) (1<<(b)) + +struct HardwareMapping matrix_hardware_mappings[] = { + /* + * The regular hardware mapping described in the wiring.md and used + * by the adapter PCBs. + */ + { + .name = "regular", + + .output_enable = GPIO_BIT(18), + .clock = GPIO_BIT(17), + .strobe = GPIO_BIT(4), + + /* Address lines */ + .a = GPIO_BIT(22), + .b = GPIO_BIT(23), + .c = GPIO_BIT(24), + .d = GPIO_BIT(25), + .e = GPIO_BIT(15), /* RxD kept free unless 1:64 */ + + /* Parallel chain 0, RGB for both sub-panels */ + .p0_r1 = GPIO_BIT(11), /* masks: SPI0_SCKL */ + .p0_g1 = GPIO_BIT(27), /* Not on RPi1, Rev1 */ + .p0_b1 = GPIO_BIT(7), /* masks: SPI0_CE1 */ + .p0_r2 = GPIO_BIT(8), /* masks: SPI0_CE0 */ + .p0_g2 = GPIO_BIT(9), /* masks: SPI0_MISO */ + .p0_b2 = GPIO_BIT(10), /* masks: SPI0_MOSI */ + + /* All the following are only available with 40 GPIP pins, on A+/B+/Pi2,3 */ + /* Chain 1 */ + .p1_r1 = GPIO_BIT(12), + .p1_g1 = GPIO_BIT(5), + .p1_b1 = GPIO_BIT(6), + .p1_r2 = GPIO_BIT(19), + .p1_g2 = GPIO_BIT(13), + .p1_b2 = GPIO_BIT(20), + + /* Chain 2 */ + .p2_r1 = GPIO_BIT(14), /* masks TxD when parallel=3 */ + .p2_g1 = GPIO_BIT(2), /* masks SCL when parallel=3 */ + .p2_b1 = GPIO_BIT(3), /* masks SDA when parallel=3 */ + .p2_r2 = GPIO_BIT(26), + .p2_g2 = GPIO_BIT(16), + .p2_b2 = GPIO_BIT(21), + }, + + /* + * This is used if you have an Adafruit HAT in the default configuration + */ + { + .name = "adafruit-hat", + + .output_enable = GPIO_BIT(4), + .clock = GPIO_BIT(17), + .strobe = GPIO_BIT(21), + + .a = GPIO_BIT(22), + .b = GPIO_BIT(26), + .c = GPIO_BIT(27), + .d = GPIO_BIT(20), + .e = GPIO_BIT(24), /* Needs manual wiring, see README.md */ + + .p0_r1 = GPIO_BIT(5), + .p0_g1 = GPIO_BIT(13), + .p0_b1 = GPIO_BIT(6), + .p0_r2 = GPIO_BIT(12), + .p0_g2 = GPIO_BIT(16), + .p0_b2 = GPIO_BIT(23), + }, + + /* + * An Adafruit HAT with the PWM modification + */ + { + .name = "adafruit-hat-pwm", + + .output_enable = GPIO_BIT(18), /* The only change compared to above */ + .clock = GPIO_BIT(17), + .strobe = GPIO_BIT(21), + + .a = GPIO_BIT(22), + .b = GPIO_BIT(26), + .c = GPIO_BIT(27), + .d = GPIO_BIT(20), + .e = GPIO_BIT(24), + + .p0_r1 = GPIO_BIT(5), + .p0_g1 = GPIO_BIT(13), + .p0_b1 = GPIO_BIT(6), + .p0_r2 = GPIO_BIT(12), + .p0_g2 = GPIO_BIT(16), + .p0_b2 = GPIO_BIT(23), + }, + + /* + * Classic: Early forms of this library had this as default mapping, mostly + * derived from the 26 GPIO-header version so that it also can work + * on 40 Pin GPIO headers with more parallel chains. + * Not used anymore. + */ + { + .name = "classic", + + .output_enable = GPIO_BIT(27), /* Not available on RPi1, Rev 1 */ + .clock = GPIO_BIT(11), + .strobe = GPIO_BIT(4), + + .a = GPIO_BIT(7), + .b = GPIO_BIT(8), + .c = GPIO_BIT(9), + .d = GPIO_BIT(10), + + .p0_r1 = GPIO_BIT(17), + .p0_g1 = GPIO_BIT(18), + .p0_b1 = GPIO_BIT(22), + .p0_r2 = GPIO_BIT(23), + .p0_g2 = GPIO_BIT(24), + .p0_b2 = GPIO_BIT(25), + + .p1_r1 = GPIO_BIT(12), + .p1_g1 = GPIO_BIT(5), + .p1_b1 = GPIO_BIT(6), + .p1_r2 = GPIO_BIT(19), + .p1_g2 = GPIO_BIT(13), + .p1_b2 = GPIO_BIT(20), + + .p2_r1 = GPIO_BIT(14), /* masks TxD if parallel = 3 */ + .p2_g1 = GPIO_BIT(2), /* masks SDA if parallel = 3 */ + .p2_b1 = GPIO_BIT(3), /* masks SCL if parallel = 3 */ + .p2_r2 = GPIO_BIT(15), + .p2_g2 = GPIO_BIT(26), + .p2_b2 = GPIO_BIT(21), + }, + + /* + * Classic pin-out for Rev-A Raspberry Pi. + */ + { + .name = "classic-pi1", + + /* The Revision-1 and Revision-2 boards have different GPIO mappings + * on the P1-3 and P1-5. So we use both interpretations. + * To keep the I2C pins free, we avoid these in later mappings. + */ + .output_enable = GPIO_BIT(0) | GPIO_BIT(2), + .clock = GPIO_BIT(1) | GPIO_BIT(3), + .strobe = GPIO_BIT(4), + + .a = GPIO_BIT(7), + .b = GPIO_BIT(8), + .c = GPIO_BIT(9), + .d = GPIO_BIT(10), + + .p0_r1 = GPIO_BIT(17), + .p0_g1 = GPIO_BIT(18), + .p0_b1 = GPIO_BIT(22), + .p0_r2 = GPIO_BIT(23), + .p0_g2 = GPIO_BIT(24), + .p0_b2 = GPIO_BIT(25), + }, + + {0} +}; diff --git a/lib/hardware-mapping.h b/lib/hardware-mapping.h new file mode 100644 index 0000000..accce26 --- /dev/null +++ b/lib/hardware-mapping.h @@ -0,0 +1,53 @@ +/* -*- 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 + */ +#ifndef RPI_HARDWARE_MAPPING_H +#define RPI_HARDWARE_MAPPING_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +typedef uint32_t gpio_bits_t; /* this should probably come from gpio.h */ + +struct HardwareMapping { + const char *name; + int max_parallel_chains; + + gpio_bits_t output_enable; + gpio_bits_t clock; + gpio_bits_t strobe; + + gpio_bits_t a, b, c, d, e; + + gpio_bits_t p0_r1, p0_g1, p0_b1; + gpio_bits_t p0_r2, p0_g2, p0_b2; + + gpio_bits_t p1_r1, p1_g1, p1_b1; + gpio_bits_t p1_r2, p1_g2, p1_b2; + + gpio_bits_t p2_r1, p2_g1, p2_b1; + gpio_bits_t p2_r2, p2_g2, p2_b2; +}; + +extern struct HardwareMapping matrix_hardware_mappings[]; + +#ifdef __cplusplus +} // extern C +#endif + +#endif diff --git a/lib/hardware/README b/lib/hardware/README deleted file mode 100644 index 7c3897c..0000000 --- a/lib/hardware/README +++ /dev/null @@ -1,3 +0,0 @@ -These sub-directories contain include files that are specific for the -particular hardware used. -They are chosen at compile-time with the HARDWARE_DESC variable in lib/Makefile. diff --git a/lib/hardware/adafruit-hat/led-panel-pin-mapping.h b/lib/hardware/adafruit-hat/led-panel-pin-mapping.h deleted file mode 100644 index 77fbcb7..0000000 --- a/lib/hardware/adafruit-hat/led-panel-pin-mapping.h +++ /dev/null @@ -1,45 +0,0 @@ -// -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*- -// Adafruit made a HAT to work with this library, but it has a slightly -// different GPIO mapping. This is this mapping. A variant of this mapping -// allows using the Raspberry Pi PWM hardware. This requires modifying the -// HAT to connect GPIO 4 and 18. See #else for regular mapping. - -#define RGB_PARALLEL_CHAINS 1 -#define FYI_ADAFRUIT_HAT_PIN_MAPPING_INCLUDED_ 1 - -union IoBits { - struct { - // This bitset reflects the GPIO mapping. The naming of the - // pins of type 'p0_r1' means 'first parallel chain, red-bit one' - unsigned int unused_0_3 : 4; // 0..3 -#ifdef ADAFRUIT_RGBMATRIX_HAT_PWM - unsigned int unused_4 : 1; // 4 -#else - unsigned int output_enable : 1; // 4 -#endif - unsigned int p0_r1 : 1; // 5 - unsigned int p0_b1 : 1; // 6 - unsigned int unused_7_11 : 5; // 7..11 - unsigned int p0_r2 : 1; // 12 - unsigned int p0_g1 : 1; // 13 - unsigned int unused_14_15 : 2; // 14,15 - unsigned int p0_g2 : 1; // 16 - unsigned int clock : 1; // 17 -#ifdef ADAFRUIT_RGBMATRIX_HAT_PWM - unsigned int output_enable : 1; // 18 - unsigned int unused_19 : 1; // 19 -#else - unsigned int unused_18_19 : 2; // 18,19 -#endif - unsigned int d : 1; // 20 - unsigned int strobe : 1; // 21 - unsigned int a : 1; // 22 - unsigned int p0_b2 : 1; // 23 - unsigned int e : 1; // 24 // Needs manual wiring - unsigned int unused_25 : 1; // 25 - unsigned int b : 1; // 26 - unsigned int c : 1; // 27 - } bits; - uint32_t raw; - IoBits() : raw(0) {} -}; diff --git a/lib/hardware/classic/led-panel-pin-mapping.h b/lib/hardware/classic/led-panel-pin-mapping.h deleted file mode 100644 index 382c5d9..0000000 --- a/lib/hardware/classic/led-panel-pin-mapping.h +++ /dev/null @@ -1,54 +0,0 @@ -// -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*- -// Classic pinout before July 2015. Consider upgrading to the new pinout. -#define RGB_PARALLEL_CHAINS 3 -union IoBits { - struct { - // This bitset reflects the GPIO mapping. The naming of the - // pins of type 'p0_r1' means 'first parallel chain, red-bit one' -#ifdef PI_REV1_RGB_PINOUT - // TODO(hzeller): Maybe break out this rev1 mapping. I don't have a - // Rev1 Pi to test though, so this is it for now. -# undef RGB_PARALLEL_CHAINS -# define RGB_PARALLEL_CHAINS 1 - // The Revision1 and Revision2 boards have different GPIO mappings - // on the pins 2 and 3. Just use both interpretations. - // To keep the I2C pins free, we don't use these anymore. - unsigned int output_enable_rev1 : 1; // 0 (RPi 1, Revision 1) - unsigned int clock_rev1 : 1; // 1 (RPi 1, Revision 1) - unsigned int output_enable_rev2 : 1; // 2 (Pi1.Rev2; masks: I2C SDA) - unsigned int clock_rev2 : 1; // 3 (Pi1.Rev2; masks: I2C SCL) -#else - unsigned int unused_0_1 : 2; // 0..1 (only on RPi 1, Revision 1) - unsigned int p2_g1 : 1; // 2 (masks SDA when parallel=3) - unsigned int p2_b1 : 1; // 3 (masks SCL when parallel=3) -#endif - unsigned int strobe : 1; // 4 - unsigned int p1_g1 : 1; // 5 (only on A+/B+/Pi2) - unsigned int p1_b1 : 1; // 6 (only on A+/B+/Pi2) - // row: 7..10, but separated as seprate bits to make it easier to shuffle - // bits if needed. - unsigned int a : 1; // 7 (masks: SPI0_CE1) - unsigned int b : 1; // 8 (masks: SPI0_CE0) - unsigned int c : 1; // 9 (masks: SPI0_MISO) - unsigned int d : 1; // 10 (masks: SPI0_MOSI) - unsigned int clock : 1; // 11 (masks: SPI0_SCKL) - unsigned int p1_r1 : 1; // 12 (only on A+/B+/Pi2) - unsigned int p1_g2 : 1; // 13 (only on A+/B+/Pi2) - unsigned int p2_r1 : 1; // 14 (masks TxD when parallel=3) - unsigned int p2_r2 : 1; // 15 (masks RxD when parallel=3) - unsigned int e : 1; // 16 (only on A+/B+/Pi2) - unsigned int p0_r1 : 1; // 17 - unsigned int p0_g1 : 1; // 18 - unsigned int p1_r2 : 1; // 19 (only on A+/B+/Pi2) - unsigned int p1_b2 : 1; // 20 (only on A+/B+/Pi2) - unsigned int p2_b2 : 1; // 21 (only on A+/B+/Pi2) - unsigned int p0_b1 : 1; // 22 - unsigned int p0_r2 : 1; // 23 - unsigned int p0_g2 : 1; // 24 - unsigned int p0_b2 : 1; // 25 - unsigned int p2_g2 : 1; // 26 (only on A+/B+/Pi2) - unsigned int output_enable : 1; // 27 (Not on RPi1, Rev1) - } bits; - uint32_t raw; - IoBits() : raw(0) {} -}; diff --git a/lib/hardware/regular/led-panel-pin-mapping.h b/lib/hardware/regular/led-panel-pin-mapping.h deleted file mode 100644 index 0f4b866..0000000 --- a/lib/hardware/regular/led-panel-pin-mapping.h +++ /dev/null @@ -1,46 +0,0 @@ -// -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*- -// Standard pinout since July 2015 -// This uses the PWM pin to create the timing. -#define RGB_PARALLEL_CHAINS 3 -union IoBits { - struct { - // This bitset reflects the GPIO mapping. The naming of the - // pins of type 'p0_r1' means 'first parallel chain, red-bit one' - // GPIO Header-pos - unsigned int unused_0_1 : 2; // 0..1 (only on RPi 1, Revision 1) - unsigned int p2_g1 : 1; // 2 P1-03 (masks SDA when parallel=3) - unsigned int p2_b1 : 1; // 3 P1-05 (masks SCL when parallel=3) - unsigned int strobe : 1; // 4 P1-07 - unsigned int p1_g1 : 1; // 5 P1-29 (only on A+/B+/Pi2) - unsigned int p1_b1 : 1; // 6 P1-31 (only on A+/B+/Pi2) - // TODO: be able to disable chain 0 for higher-pin RPis to gain SPI back. - unsigned int p0_b1 : 1; // 7 P1-26 (masks: SPI0_CE1) - unsigned int p0_r2 : 1; // 8 P1-24 (masks: SPI0_CE0) - unsigned int p0_g2 : 1; // 9 P1-21 (masks: SPI0_MISO - unsigned int p0_b2 : 1; // 10 P1-19 (masks: SPI0_MOSI) - unsigned int p0_r1 : 1; // 11 P1-23 (masks: SPI0_SCKL) - - unsigned int p1_r1 : 1; // 12 P1-32 (only on A+/B+/Pi2) - unsigned int p1_g2 : 1; // 13 P1-33 (only on A+/B+/Pi2) - unsigned int p2_r1 : 1; // 14 P1-08 (masks TxD when parallel=3) - unsigned int e : 1; // 15 P1-10 (RxD) - kept free unless 1:64 - unsigned int p2_g2 : 1; // 16 P1-36 (only on A+/B+/Pi2) - - unsigned int clock : 1; // 17 P1-11 - - unsigned int output_enable : 1; // 18 P1-12 (PWM pin: our timing) - unsigned int p1_r2 : 1; // 19 P1-35 (only on A+/B+/Pi2) - unsigned int p1_b2 : 1; // 20 P1-38 (only on A+/B+/Pi2) - unsigned int p2_b2 : 1; // 21 P1-40 (only on A+/B+/Pi2) - - unsigned int a : 1; // 22 P1-15 // row bits. - unsigned int b : 1; // 23 P1-16 - unsigned int c : 1; // 24 P1-18 - unsigned int d : 1; // 25 P1-22 - - unsigned int p2_r2 : 1; // 26 P1-37 (only on A+/B+/Pi2) - unsigned int p0_g1 : 1; // 27 P1-13 (Not on RPi1, Rev1) - } bits; - uint32_t raw; - IoBits() : raw(0) {} -}; diff --git a/lib/led-matrix-c.cc b/lib/led-matrix-c.cc index f20c07f..1f0f246 100644 --- a/lib/led-matrix-c.cc +++ b/lib/led-matrix-c.cc @@ -53,6 +53,7 @@ struct RGBLedMatrix *led_matrix_create_from_options( // C-struct values if available. // We assume everything non-zero has an explicit value. #define OPT_COPY_IF_SET(o) if (opts->o) default_opts.o = opts->o + OPT_COPY_IF_SET(hardware_mapping); OPT_COPY_IF_SET(rows); OPT_COPY_IF_SET(chain_length); OPT_COPY_IF_SET(parallel); @@ -77,6 +78,7 @@ struct RGBLedMatrix *led_matrix_create_from_options( if (opts) { #define ACTUAL_VALUE_BACK_TO_OPT(o) opts->o = default_opts.o + ACTUAL_VALUE_BACK_TO_OPT(hardware_mapping); ACTUAL_VALUE_BACK_TO_OPT(rows); ACTUAL_VALUE_BACK_TO_OPT(chain_length); ACTUAL_VALUE_BACK_TO_OPT(parallel); diff --git a/lib/led-matrix.cc b/lib/led-matrix.cc index 86aa3fe..35eeca2 100644 --- a/lib/led-matrix.cc +++ b/lib/led-matrix.cc @@ -29,6 +29,22 @@ #include "thread.h" #include "framebuffer-internal.h" +// Leave this in here for a while. Setting things from old defines. +#if defined(ADAFRUIT_RGBMATRIX_HAT) +# warning "You are using an old way to select the Adafruit HAT by defining -DADAFRUIT_RGBMATRIX_HAT" +# warning "The new way to do this is to set HARDWARE_DESC=adafruit-hat" +# warning "Check out https://github.com/hzeller/rpi-rgb-led-matrix#switch-the-pinout" +# undef DEFAULT_HARDWARE +# define DEFAULT_HARDWARE "adafruit-hat" +#endif + +#if defined(ADAFRUIT_RGBMATRIX_HAT_PWM) +# warning "You are using an old way to select the Adafruit HAT with flicker mod by defining -DADAFRUIT_RGBMATRIX_HAT_PWM" +# warning "The new way to do this is to set HARDWARE_DESC=adafruit-hat-pwm" +# undef DEFAULT_HARDWARE +# define DEFAULT_HARDWARE "adafruit-hat-pwm" +#endif + namespace rgb_matrix { // Pump pixels to screen. Needs to be high priority real-time because jitter class RGBMatrix::UpdateThread : public Thread { @@ -110,11 +126,18 @@ private: }; // Some defaults. See options-initialize.cc for the command line parsing. -RGBMatrix::Options::Options() - : rows(32), chain_length(1), parallel(1), pwm_bits(11), - // Historically, we provided these options only as #defines. Make sure that - // things still behave as before if someone has set these. - // At some point: remove them from the Makefile. Later: remove them here. +RGBMatrix::Options::Options() : + // Historically, we provided these options only as #defines. Make sure that + // things still behave as before if someone has set these. + // At some point: remove them from the Makefile. Later: remove them here. +#ifdef DEFAULT_HARDWARE + hardware_mapping(DEFAULT_HARDWARE), +#else + hardware_mapping("regular"), +#endif + + rows(32), chain_length(1), parallel(1), pwm_bits(11), + #ifdef LSB_PWM_NANOSECONDS pwm_lsb_nanoseconds(LSB_PWM_NANOSECONDS), #else @@ -159,6 +182,7 @@ RGBMatrix::Options::Options() RGBMatrix::RGBMatrix(GPIO *io, const Options &options) : params_(options), io_(NULL), updater_(NULL), shared_pixel_mapper_(NULL) { assert(params_.Validate(NULL)); + internal::Framebuffer::InitHardwareMapping(params_.hardware_mapping); active_ = CreateFrameCanvas(); Clear(); SetGPIO(io, true); @@ -172,6 +196,7 @@ RGBMatrix::RGBMatrix(GPIO *io, int rows, int chained_displays, params_.chain_length = chained_displays; params_.parallel = parallel_displays; assert(params_.Validate(NULL)); + internal::Framebuffer::InitHardwareMapping(params_.hardware_mapping); active_ = CreateFrameCanvas(); Clear(); SetGPIO(io, true); diff --git a/lib/options-initialize.cc b/lib/options-initialize.cc index bd8fdf6..8aa9969 100644 --- a/lib/options-initialize.cc +++ b/lib/options-initialize.cc @@ -95,6 +95,33 @@ static bool ConsumeIntFlag(const char *flag_name, return true; // consumed. } +// The resulting value is allocated. +static bool ConsumeStringFlag(const char *flag_name, + argv_iterator &pos, const argv_iterator end, + const char **result_value, int *error) { + const char *option = *pos; + if (strncmp(option, OPTION_PREFIX, OPTION_PREFIX_LEN) != 0) + return false; + option += OPTION_PREFIX_LEN; + const size_t flag_len = strlen(flag_name); + if (strncmp(option, flag_name, flag_len) != 0) + return false; // not consumed. + const char *value; + if (option[flag_len] == '=') // --option=hello # value in same arg + value = option + flag_len + 1; + else if (pos + 1 < end) { // --option hello # value in next arg + value = *(++pos); + } else { + fprintf(stderr, "Parameter expected after %s%s\n", + OPTION_PREFIX, flag_name); + ++*error; + *result_value = NULL; + return true; // consumed, but error. + } + *result_value = strdup(value); // This will leak, but no big deal. + return true; +} + static bool FlagInit(int &argc, char **&argv, RGBMatrix::Options *mopts, RuntimeOptions *ropts) { @@ -110,6 +137,9 @@ static bool FlagInit(int &argc, char **&argv, for (/**/; it < end; ++it) { posix_end_option_seen |= (strcmp(*it, "--") == 0); if (!posix_end_option_seen) { + if (ConsumeStringFlag("gpio-mapping", it, end, + &mopts->hardware_mapping, &err)) + continue; if (ConsumeIntFlag("rows", it, end, &mopts->rows, &err)) continue; if (ConsumeIntFlag("chain", it, end, &mopts->chain_length, &err)) @@ -282,6 +312,7 @@ RGBMatrix *CreateMatrixFromFlags(int *argc, char ***argv, void PrintMatrixFlags(FILE *out, const RGBMatrix::Options &d, const RuntimeOptions &r) { fprintf(out, + "\t--led-gpio-mapping= : Name of GPIO mapping used. Default \"%s\"\n" "\t--led-rows= : Panel rows. 8, 16, 32 or 64. " "(Default: %d).\n" "\t--led-chain= : Number of daisy-chained panels. " @@ -300,6 +331,7 @@ void PrintMatrixFlags(FILE *out, const RGBMatrix::Options &d, "\t--led-pwm-lsb-nanoseconds : PWM Nanoseconds for LSB " "(Default: %d)\n" "\t--led-%shardware-pulse : %sse hardware pin-pulse generation.\n", + d.hardware_mapping, d.rows, d.chain_length, d.parallel, d.pwm_bits, d.brightness, d.scan_mode, d.show_refresh_rate ? "no-" : "", d.show_refresh_rate ? "Don't s" : "S", diff --git a/utils/README.md b/utils/README.md index eb02507..ce8e8c1 100644 --- a/utils/README.md +++ b/utils/README.md @@ -29,6 +29,7 @@ Options: -R : Rotate output; steps of 90 degrees General LED matrix options: + --led-gpio-mapping= : Name of GPIO mapping used. Default "regular" --led-rows= : Panel rows. 8, 16, 32 or 64. (Default: 32). --led-chain= : Number of daisy-chained panels. (Default: 1). --led-parallel= : For A/B+ models or RPi2,3b: parallel chains. range=1..3 (Default: 1). diff --git a/utils/led-image-viewer.cc b/utils/led-image-viewer.cc index 93ad50f..116a629 100644 --- a/utils/led-image-viewer.cc +++ b/utils/led-image-viewer.cc @@ -303,6 +303,9 @@ int main(int argc, char *argv[]) { matrix->ApplyStaticTransformer(rgb_matrix::RotateTransformer(angle)); } + printf("Size: %dx%d. Hardware gpio mapping: %s\n", + matrix->width(), matrix->height(), matrix_options.hardware_mapping); + // These parameters are needed once we do scrolling. const bool fill_width = false; const bool fill_height = false;