mirror of
https://github.com/Hopiu/rpi-rgb-led-matrix.git
synced 2026-03-16 22:10:27 +00:00
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.
This commit is contained in:
parent
9cbb4e116e
commit
b68d100ae0
21 changed files with 504 additions and 345 deletions
44
README.md
44
README.md
|
|
@ -84,8 +84,8 @@ This documentation is split into parts that help you through the process
|
|||
* <a href="wiring.md"><img src="img/wire-up-icon.png"></a>
|
||||
[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=<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=<rows> : Panel rows. 8, 16, 32 or 64. (Default: 32).
|
||||
--led-chain=<chained> : Number of daisy-chained panels. (Default: 1).
|
||||
--led-parallel=<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):
|
|||
|
||||
<a href="img/adafruit-mod.jpg"><img src="img/adafruit-mod.jpg" height="80px"></a>
|
||||
|
||||
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,
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ Options:
|
|||
in the middle in an U-arrangement to get more vertical space.
|
||||
-R <rotation> : Sets the rotation of matrix. Allowed: 0, 90, 180, 270. Default: 0.
|
||||
-t <seconds> : Run for these number of seconds, then exit.
|
||||
--led-gpio-mapping=<name> : Name of GPIO mapping used. Default "regular"
|
||||
--led-rows=<rows> : Panel rows. 8, 16, 32 or 64. (Default: 32).
|
||||
--led-chain=<chained> : Number of daisy-chained panels. (Default: 1).
|
||||
--led-parallel=<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;
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
*/
|
||||
#include "led-matrix-c.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
32
lib/Makefile
32
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
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@
|
|||
|
||||
#include <stdint.h>
|
||||
|
||||
#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<IoBits*>(val);
|
||||
}
|
||||
gpio_bits_t *bitplane_buffer_;
|
||||
inline gpio_bits_t *ValueAt(int double_row, int column, int bit);
|
||||
|
||||
PixelMapper **shared_mapper_; // Storage in RGBMatrix.
|
||||
};
|
||||
|
|
|
|||
|
|
@ -19,11 +19,12 @@
|
|||
|
||||
#include "framebuffer-internal.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#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<int> 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);
|
||||
|
|
|
|||
185
lib/hardware-mapping.c
Normal file
185
lib/hardware-mapping.c
Normal file
|
|
@ -0,0 +1,185 @@
|
|||
/* -*- mode: c; c-basic-offset: 2; indent-tabs-mode: nil; -*-
|
||||
* Copyright (C) 2013, 2016 Henner Zeller <h.zeller@acm.org>
|
||||
*
|
||||
* 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 <http: *gnu.org/licenses/gpl-2.0.txt>
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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}
|
||||
};
|
||||
53
lib/hardware-mapping.h
Normal file
53
lib/hardware-mapping.h
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
/* -*- mode: c; c-basic-offset: 2; indent-tabs-mode: nil; -*-
|
||||
* Copyright (C) 2013 Henner Zeller <h.zeller@acm.org>
|
||||
*
|
||||
* 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 <http: *gnu.org/licenses/gpl-2.0.txt>
|
||||
*/
|
||||
#ifndef RPI_HARDWARE_MAPPING_H
|
||||
#define RPI_HARDWARE_MAPPING_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
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
|
||||
|
|
@ -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.
|
||||
|
|
@ -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) {}
|
||||
};
|
||||
|
|
@ -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) {}
|
||||
};
|
||||
|
|
@ -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) {}
|
||||
};
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -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> : Name of GPIO mapping used. Default \"%s\"\n"
|
||||
"\t--led-rows=<rows> : Panel rows. 8, 16, 32 or 64. "
|
||||
"(Default: %d).\n"
|
||||
"\t--led-chain=<chained> : 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",
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ Options:
|
|||
-R<angle> : Rotate output; steps of 90 degrees
|
||||
|
||||
General LED matrix options:
|
||||
--led-gpio-mapping=<name> : Name of GPIO mapping used. Default "regular"
|
||||
--led-rows=<rows> : Panel rows. 8, 16, 32 or 64. (Default: 32).
|
||||
--led-chain=<chained> : Number of daisy-chained panels. (Default: 1).
|
||||
--led-parallel=<parallel> : For A/B+ models or RPi2,3b: parallel chains. range=1..3 (Default: 1).
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Reference in a new issue