o First step in separating the documentation in more smaller

chunks.
o Create sub-directory for api examples and ready utilities.
This commit is contained in:
Henner Zeller 2016-08-20 09:57:27 -07:00
parent c089366c12
commit d28b2a23b4
13 changed files with 538 additions and 464 deletions

3
.gitignore vendored
View file

@ -1,6 +1,7 @@
*.o
*.a
*~
led-image-viewer
led-matrix
demo
minimal-example
text-example

View file

@ -5,8 +5,9 @@ ALL_BINARIES=$(BINARIES) led-image-viewer
# Where our library resides. It is split between includes and the binary
# library in lib
RGB_INCDIR=include
RGB_LIBDIR=lib
RGB_LIB_DISTRIBUTION=.
RGB_INCDIR=$(RGB_LIB_DISTRIBUTION)/include
RGB_LIBDIR=$(RGB_LIB_DISTRIBUTION)/lib
RGB_LIBRARY_NAME=rgbmatrix
RGB_LIBRARY=$(RGB_LIBDIR)/lib$(RGB_LIBRARY_NAME).a
LDFLAGS+=-L$(RGB_LIBDIR) -l$(RGB_LIBRARY_NAME) -lrt -lm -lpthread
@ -17,31 +18,12 @@ PYTHON_LIB_DIR=python
MAGICK_CXXFLAGS=`GraphicsMagick++-config --cppflags --cxxflags`
MAGICK_LDFLAGS=`GraphicsMagick++-config --ldflags --libs`
all : $(BINARIES)
all : $(RGB_LIBRARY)
$(RGB_LIBRARY): FORCE
$(MAKE) -C $(RGB_LIBDIR)
led-matrix : demo-main.o $(RGB_LIBRARY)
$(CXX) $(CXXFLAGS) demo-main.o -o $@ $(LDFLAGS)
minimal-example : minimal-example.o $(RGB_LIBRARY)
$(CXX) $(CXXFLAGS) minimal-example.o -o $@ $(LDFLAGS)
text-example : text-example.o $(RGB_LIBRARY)
$(CXX) $(CXXFLAGS) text-example.o -o $@ $(LDFLAGS)
led-image-viewer: led-image-viewer.o $(RGB_LIBRARY)
$(CXX) $(CXXFLAGS) led-image-viewer.o -o $@ $(LDFLAGS) $(MAGICK_LDFLAGS)
%.o : %.cc
$(CXX) -I$(RGB_INCDIR) $(CXXFLAGS) -c -o $@ $<
led-image-viewer.o : led-image-viewer.cc
$(CXX) -I$(RGB_INCDIR) $(CXXFLAGS) $(MAGICK_CXXFLAGS) -c -o $@ $<
clean:
rm -f $(OBJECTS) $(ALL_BINARIES)
$(MAKE) -C lib clean
$(MAKE) -C $(PYTHON_LIB_DIR) clean

484
README.md
View file

@ -12,24 +12,25 @@ around 100Hz refresh rate with full 24Bit color (theoretical - never tested this
there might likely be timing problems with the panels that will creep up then).
With fewer colors you can control even more, faster.
The LED-matrix **library** is (c) Henner Zeller <h.zeller@acm.org> with
GNU General Public License Version 2.0 <http://www.gnu.org/licenses/gpl-2.0.txt>
The demo-main.cc **example code** using this library is released to the
public domain.
The LED-matrix library is (c) Henner Zeller <h.zeller@acm.org>, licensed with
[GNU General Public License Version 2.0](http://www.gnu.org/licenses/gpl-2.0.txt)
(which means, if you use it in a product somewhere, you need to make the
source and all your modifications available to the receiver of such product so
that they have the freedom to adapt and improve).
Overview
--------
The 32x32 or 16x32 RGB LED matrix panels can be scored at [Sparkfun][sparkfun],
[AdaFruit][ada] or eBay. If you are in China, I'd try to get them directly
from some manufacturer, Taobao or Alibaba.
[AdaFruit][ada] or eBay and Aliexpress. If you are in China, I'd try to get
them directly from some manufacturer, Taobao or Alibaba.
The `RGBMatrix` class provided in `include/led-matrix.h` does what is needed
to control these. You can use this as a library in your own projects or just
use the demo binary provided here which provides some useful examples.
Check out the [minimal-example.cc](./minimal-example.cc) to get started using
this library.
Check out [utils/ directory for some ready-made tools](./utils) to get started
using the library, or the [example-api-use/](./example-api-use) directory if
you want to get started programming your own utils.
All Raspberry Pi versions supported
-----------------------------------
@ -49,7 +50,7 @@ The [Raspbian Lite][raspbian-lite] distribution is recommended.
Types of Displays
-----------------
There are various types of displays that come all with the same Hub75 connector.
They vary in the way the multiplexing is happening or sometimes they are
They vary in the way the multiplexing is happening.
Type | Scan Multiplexing | Program Option | Remark
-----:|:-----------------:|:----------------|-------
@ -66,335 +67,35 @@ The 64x64 matrixes typically have 5 address lines (A, B, C, D, E). There are
also 64x64 panels out there that only seem to have 1:4 multiplexing (there
is A and B), but I have not had these in my lab yet to test.
Connection
----------
You need a separate power supply for the panel. There is a connector for that
separate from the logic connector, typically a big one in the center of the
board. The board requires 5V (double check the polarity: what is printed
on the board is correct - I once got boards with supplied cables that had red
(suggesting `+`) and black (suggesting `GND`) reversed!). This power supply is
used to light the LEDs; plan for ~3.5 Ampere per 32x32 panel.
Let's do it
------------
This documentation is split into parts that help you through the process
The connector on the RGB panels is called a Hub75 interface. Each panel
typically has two ports, one is the input and the other is the output to
chain additional panels. Usually an arrow shows which of the connectors is
the input.
1. [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)
2. Run a demo. You find that in the
[examples-api-use/](./examples-api-use#running-some-demos) directory.
3. Use the utilities. The [utils](./utils) directory has some ready-made
useful utilities to show image or text.
Here you see a Hub75 connector to be seen at the bottom of the RGB panel
board including the arrow indicating the input direction:
![Hub 75 interface][hub75-arrow]
Chaining panels
---------------
Other boards are very similar, but instead of zero-indexed color bits
`R0`, `G0`, `B0`, `R1`, `G1`, `B1`, they start the index with one and name these
`R1`, `G1`, `B1`, `R2`, `G2`, `B2`; the functionality is identical.
![Hub 75 interface][hub75]
We might only have a limited amount of GPIOs on the Raspberry Pi, but luckily,
the RGB matrices can be chained. The display panels have an input connector,
and also have an output port, that you can connect to the next display in a
daisy-chain manner. There is the flag `-c` in the demo program to give number
of displays that are chained.
You end up with a very wide display (chain * 32 pixels).
Throughout this document, we will use the one-index base, so we will call these
signals `R1`, `G1`, `B1`, `R2`, `G2`, `B2` below.
The `strobe` signals is sometimes also called `latch` or `lat`. We'll call it
`strobe` here.
If you plug an IDC-cable into your RGB panel to the input connector, this is
how the signal positions are on the other end of the cable (imagine the LED
panels somewhere outside the picture on the left); note the notch on the right
side of the connector:
![Hub 75 IDC connector][hub75-idc]
The RPi only has 3.3V logic output level, but many displays operated at 5V
interprets these logic levels fine, just make sure to run a short
cable to the board (if you see problems, see [troubleshouting paragraph](#troubleshooting)).
If you do run into glitches or erratic pixels, consider some line-buffering,
e.g. using the [active adapter PCB](./adapter/).
Since we only need output pins on the RPi, we don't need to worry about level
conversion back.
For a single chain of LED-panels, we need 13 IO lines, which fit all in the
header of the old Raspberry Pis. Newer Raspberry Pis with 40 pins have more
GPIO lines which allows us to connect three parallel chains of RGB panels.
For reference, this is how the numbering on the Raspberry Pi looks like:
<a href="img/raspberry-gpio.jpg"><img src="img/raspberry-gpio.jpg" width="600px"></a>
This is the same representation used in the table below, which helps for
visual inspection.
### Wiring diagram
For each of the up to three chains, you have to connect `GND`, `strobe`,
`clock`, `OE-`, `A`, `B`, `C`, `D` to all of these (the `D` line is needed
for 32x32 displays; 32x16 displays don't need it); you find the positions
below (there are more GND pins on the Raspberry Pi, but they are left out
for simplicity).
Then for each panel, there is a set of (R1, G1, B1, R2, G2, B2) that you have
to connect to the corresponding pins that are marked `[1]`, `[2]` and `[3]` for
chain 1, 2, and 3 below.
If you only connect one panel or have one chain, connect it to `[1]` (:smile:); if you
use parallel chains, add the other `[2]` and `[3]`.
To make things quicker to navigate visually, each chain is marked with a separate
icon:
`[1]`=:smile:, `[2]`=:boom: and `[3]`=:droplet: ; signals that go to all
chains have all icons.
Connection | Pin | Pin | Connection
---------------------------------:|:---:|:---:|:-----------------------------
- | 1 | 2 | -
:droplet: **[3] G1** | 3 | 4 | -
:droplet: **[3] B1** | 5 | 6 | **GND** :smile::boom::droplet:
:smile::boom::droplet: **strobe** | 7 | 8 | **[3] R1** :droplet:
- | 9 | 10 | **E** :smile::boom::droplet: (for 64 row matrix, 1:32)
:smile::boom::droplet: **clock** | 11 | 12 | **OE-** :smile::boom::droplet:
:smile: **[1] G1** | 13 | 14 | -
:smile::boom::droplet: **A** | 15 | 16 | **B** :smile::boom::droplet:
- | 17 | 18 | **C** :smile::boom::droplet:
:smile: **[1] B2** | 19 | 20 | -
:smile: **[1] G2** | 21 | 22 | **D** :smile::boom::droplet: (for 32 row matrix, 1:16)
:smile: **[1] R1** | 23 | 24 | **[1] R2** :smile:
- | 25 | 26 | **[1] B1** :smile:
- | 27 | 28 | -
:boom: **[2] G1** | 29 | 30 | -
:boom: **[2] B1** | 31 | 32 | **[2] R1** :boom:
:boom: **[2] G2** | 33 | 34 | -
:boom: **[2] R2** | 35 | 36 | **[3] G2** :droplet:
:droplet:**[3] R2** | 37 | 38 | **[2] B2** :boom:
- | 39 | 40 | **[3] B2** :droplet:
In the [adapter/](./adapter) directory, there are some boards that make
the wiring task simpler.
Running
-------
The demo-main.cc has some testing demos. Via command line flags, you can choose
the display type you have (16x32 or 32x32), and how many you have chained.
```
$ make
$ ./led-matrix
Expected required option -D <demo>
usage: ./led-matrix <options> -D <demo-nr> [optional parameter]
Options:
-r <rows> : Panel rows. '16' for 16x32 (1:8 multiplexing),
'32' for 32x32 (1:16), '8' for 1:4 multiplexing; Default: 32
-P <parallel> : For Plus-models or RPi2: parallel chains. 1..3. Default: 1
-c <chained> : Daisy-chained boards. Default: 1.
-L : 'Large' display, composed out of 4 times 32x32
-p <pwm-bits> : Bits used for PWM. Something between 1..11
-l : Don't do luminance correction (CIE1931)
-D <demo-nr> : Always needs to be set
-d : run as daemon. Use this when starting in
/etc/init.d, but also when running without
terminal (e.g. cron).
-t <seconds> : Run for these number of seconds, then exit.
(if neither -d nor -t are supplied, waits for <RETURN>)
-b <brightnes>: Sets brightness percent. Default: 100.
-R <rotation> : Sets the rotation of matrix. Allowed: 0, 90, 180, 270. Default: 0.
Demos, choosen with -D
0 - some rotating square
1 - forward scrolling an image (-m <scroll-ms>)
2 - backward scrolling an image (-m <scroll-ms>)
3 - test image: a square
4 - Pulsing color
5 - Grayscale Block
6 - Abelian sandpile model (-m <time-step-ms>)
7 - Conway's game of life (-m <time-step-ms>)
8 - Langton's ant (-m <time-step-ms>)
9 - Volume bars (-m <time-step-ms>)
10 - Evolution of color (-m <time-step-ms>)
11 - Brightness pulse generator
Example:
./led-matrix -t 10 -D 1 runtext.ppm
Scrolls the runtext for 10 seconds
```
To run the actual demos, you need to run this as root so that the
GPIO pins can be accessed.
The most interesting one is probably the demo '1' which requires a ppm (type
raw) with a height of 32 pixel - it is infinitely scrolled over the screen; for
convenience, there is a little runtext.ppm example included:
$ sudo ./led-matrix -D 1 runtext.ppm
Here is a video of how it looks
[![Runtext][run-vid]](http://youtu.be/OJvEWyvO4ro)
There are also two examples [minimal-example.cc](./minimal-example.c) and
[text-example.cc](./text-example.cc) that show use of the API.
The text example allows for some interactive output of text (using a bitmap-font
found in the `fonts/` directory). Even though it is just an example, it can
be useful in its own right. For instance, you can connect to its input with a
pipe and simply feed text from a shell-script or other program that wants to
output something. Let's display the time in blue:
(while :; do date +%T ; sleep 0.2 ; done) | sudo ./text-example -f fonts/8x13B.bdf -y8 -c2 -C0,0,255
You could connect this via a pipe to any process that just outputs new
information on standard-output every now and then. The screen is filled with
text until it overflows which then clears it. Or sending an empty line explicitly
clears the screen (if you want to display an empty line, just send a space).
![Time][time]
### Image Viewer ###
One of the possibly useful demo applications is an image viewer that
reads all kinds of image formats, including animated gifs. It is not compiled
by default, as you need to install the GraphicsMagick dependencies first:
sudo apt-get update
sudo apt-get install libgraphicsmagick++-dev libwebp-dev
make led-image-viewer
Then, you can run it with any common image format, including animated gifs:
sudo ./led-image-viewer myimage.gif
It also supports the standard options to specify the connected
displays (`-r`, `-c`, `-P`).
Chaining, parallel chains and coordinate system
------------------------------------------------
Displays panels have an input connector, but also have an output port, that
you can connect to the next display in a daisy-chain manner. There is the
flag `-c` in the demo program to give number of displays that are chained.
You end up with a very wide
display (chain * 32 pixels). Longer chains affect the refresh rate negatively,
so if you want to stay above 100Hz with full color, don't chain more than
12 panels.
If you use a PWM depth of 1 bit (`-p`), the chain can be much longer.
The original Raspberry Pis with 26 GPIO pins just had enough connector pins
to drive one chain of LED panels. Newer Raspberry Pis have 40 GPIO pins that
allows to add two additional chains of panels in parallel - the nice thing is,
that this doesn't require more CPU and allows you to keep your refresh-rate high,
because you can shorten your chains.
So with that, we have a couple of parameters to keep track of. The **rows** are
the number of LED rows on a particular module; typically these are 16 for a 16x32
display or 32 for 32x32 displays.
Then there is the **chain length**, which is the number of panels that are
daisy chained together.
Finally, there is a parameter how many **parallel** chains we have connected to
the Pi -- limited to 1 on old Raspberry Pis, up to three on newer Raspberry Pis.
For a single Panel, the chain and parallel parameters are both just one: a single
chain (with no else in parallel) with a chain length of 1.
The `RGBMatrix` class constructor has parameters for number of rows,
chain-length and number of parallel. For the demo programs and the image view,
there are command line options for that: `-r` gives rows,
`-c` the chain-length and `-P` the number of parallel chains.
The coordinate system starts at (0,0) at the top of the first parallel chain,
furthest away from the Pi. The following picture gives an overview of various
parameters and the coordinate system.
![Coordinate overview][coordinates]
The [wiring.md](./wiring.md#chaining-parallel-chains-and-coordinate-system)
document explains the details.
<a href="wiring.md#chaining-parallel-chains-and-coordinate-system"><img src="img/coordinates.png"></a>
<a href="adapter/"><img src="img/three-parallel-panels-soic.jpg" width="300px"></a>
## Remapping coordinates ##
You might choose a different physical layout than the wiring provides.
Say you have 4 displays with 32x32 and only a single output
like with a Raspberry Pi 1 or the Adafruit HAT -- if we chain
them, we get a display 32 pixel high, (4*32)=128 pixel long. If we arrange
the boards in a square, we get a logical display of 64x64 pixels:
<img src="img/chained-64x64.jpg" width="400px"> In action:
[![PixelPusher video][pp-vid]](http://youtu.be/ZglGuMaKvpY)
How can we make this 'folded' 128x32 screen behave like a 64x64 screen ?
In the API, there is an interface to implement,
a [`CanvasTransformer`](./include/canvas.h) that allows to program re-arrangements
of pixels in any way. You can plug such a `CanvasTransformer` into the RGBMatrix
to use the new layout (`void RGBMatrix::SetTransformer(CanvasTransformer *transformer)`).
Sometimes you even need this for the panel itself: In newer panels
(often with 1:4 multiplexing) the pixels are often not mapped in
a straight-forward way, but in a snake arrangement for instance. The CanvasTransformer
allows you to work around that (sorry, I have not seen these panels myself so that
I couldn't test that; but if you come accross one, you might want to send a pull-request
with a new CanvasTransformer).
Back to the 64x64 arrangement:
There is a sample implementation `class LargeSquare64x64Transformer` that maps
the 128x32 pixel logical arrangement into the 64x64 arrangement doing
the coordinate mapping. In the demo program and the `led-image-viewer`, you
can activate this with the `-L` option.
Using the API
-------------
While there is the demo program, the matrix code can be used independently as
a library. The includes are in `include/`, the library to link is built
in `lib/`. So if you are proficient in C++, then use it in your code.
Due to the wonders of github, it is pretty easy to be up-to-date.
I suggest to add this code as a sub-module in your git repository. That way
you can use that particular version and easily update it if there are changes:
git submodule add https://github.com/hzeller/rpi-rgb-led-matrix.git matrix
(Read more about how to use [submodules in git][git-submodules])
This will check out the repository in a subdirectory `matrix/`.
The library to build would be in directory `matrix/lib`, so let's hook that
into your toplevel Makefile.
I suggest to set up some variables like this:
RGB_INCDIR=matrix/include
RGB_LIBDIR=matrix/lib
RGB_LIBRARY_NAME=rgbmatrix
RGB_LIBRARY=$(RGB_LIBDIR)/lib$(RGB_LIBRARY_NAME).a
LDFLAGS+=-L$(RGB_LIBDIR) -l$(RGB_LIBRARY_NAME) -lrt -lm -lpthread
Also, you want to add a target to build the libary in your sub-module
# (FYI: Make sure, there is a TAB-character in front of the $(MAKE))
$(RGB_LIBRARY):
$(MAKE) -C $(RGB_LIBDIR)
Now, your final binary needs to depend on your objects and also the
`$(RGB_LIBRARY)`
my-binary : $(OBJECTS) $(RGB_LIBRARY)
$(CXX) $(CXXFLAGS) $(OBJECTS) -o $@ $(LDFLAGS)
As an example, see the [PixelPusher implementation][pixelpush] which is using
this library in a git sub-module.
If you are writing your own Makefile, make sure to pass the `-O3` option to
the compiler to make sure to generate fast code.
Note, all the types provided are in the `rgb_matrix` namespace. That way, they
won't clash with other types you might use in your code; in particular pretty
common names such as `GPIO` or `Canvas` might run into clashing trouble.
Anyway, for convenience you just might add using-declarations in your
code:
// Types exported by the RGB-Matrix library.
using rgb_matrix::Canvas;
using rgb_matrix::GPIO;
using rgb_matrix::RGBMatrix;
using rgb_matrix::ThreadedCanvasManipulator;
Or, if you are lazy, just import the whole namespace:
using namespace rgb_matrix;
Read the [`minimal-example.cc`](./minimal-example.cc) to get started, then
have a look into [`demo-main.cc`](./demo-main.cc).
Troubleshooting
---------------
@ -491,75 +192,6 @@ notice that your image looks like a 'negative'. The parameter to tweak is
There are lots of parameters in [lib/Makefile](./lib/Makefile) that you might
be interested in tweaking.
A word about power
------------------
These displays suck a lot of current. At 5V, when all LEDs are on (full white),
my LED panel draws about 3.4A. That means, you need a beefy power supply to
drive these panels; a 2A USB charger or similar is not enough for a
32x32 panel; it might be for a 16x32.
If you connect multiple boards together, you needs a power supply that can
keep up with 3.5A / panel. Good are old PC power supplies that often
provide > 20A on the 5V rail. Also you can get dedicated 5V high current
switching power supplies for these kind of applications (check eBay).
The current draw is pretty spiky. Due to the PWM of the LEDs, there are very
short peaks of a couple of 100ns to about 1ms of full current draw.
Often, the power cable can't support these very short spikes due to inherent
inductance. This can result in 'noisy' outputs, with random pixels not behaving
as they should. A low ESR capacitor close to the input is good in these cases.
On some displays, the quality of the output quickly gets erratic
when voltage drops below 4.5V. Some even need a little bit higher voltage around
5.5V to work reliably.
When you connect these boards to a power source, the following are good
guidelines:
- Have fairly thick cables connecting the power to the board.
Plan not to loose more than 50mV from the source to the LED matrix.
So that would be 50mV / 3.5A = 14 mΩ. For both supply wires, so 7mΩ
each trace.
A 1mm² copper cable has about 17.5mΩ/meter, so you'd need a **2.5mm²
copper cable per meter and panel**. Multiply by meter and number of
panels to get the needed cross-section.
(For Americans: that would be ~13 gauge wire for 3 ft and one panel)
- While a star configuration for the cabeling would be optimal (each panel gets
an individual wire from the power supply), it is typically sufficient
using aluminum mounting brackets or bars as part of
your power solution. With aluminum of 1mm² specific resistivity of
about 28mΩ/meter, you'd need a cross sectional area of about 4mm² per panel
and meter.
In the following example you see the structural aluminum bars in the middle
(covered in colored vinyl) dualing as power bars. The 60A/5V power supply is connected
to the center bolts (display uses about 42A all LEDs on):
![Powerbar][powerbar]
- Often these boards come with cables that have connectors crimped on.
Some cheap cables are typically too thin; you might want to clip them close to
the connector solder your proper, thick cable to it.
- It is good to buffer the current spikes directly at the panel. The most
spikes happen while PWM-ing a single line.
So let's say we want to buffer the energy to power a single line without
dropping more than 50mV. We use 3.5A which is 3.5Joule/second. We do
about 140Hz refresh rate and divide that in 16 lines, so we need
3.5 Joule/140/16 = ~1.6mJoule in the time period to display one line.
We want to get the energy out of the voltage drop of 50mV; so with
W = 1/2*C*U², we can calculate the capacitance needed:
C = 2 * 1.6mJoule / ((5V)² - (5V - 50mV)²) = ~6400µF.
So, **2 x 3300µF** low-ESR capacitors in parallel directly
at the board are a good choice (two, because lower parallel ESR; also
fits easier under board).
(In reality, we need of course less, as the highest ripple comes with
50% duty cyle thus half the current; also the input is recharching all
the time. But: as engineer plan for maximum and then some; in the picture
above I am using 1x3300uF per panel and it works fine).
Now welcome your over-engineered power solution :)
If you have an Adafruit HAT
---------------------------
@ -572,7 +204,7 @@ but it only allows for a single chain. If the
ready-made vs. single-chain tradeoff is worthwhile, then you might go for that
(I am not affiliated with Adafruit).
### Getting it to work
### Switch the Pinout
The Adafruit HAT uses a modified pinout, so they forked this library and
modified the pinout there. However, that fork is _ancient_, so I strongly
@ -626,30 +258,8 @@ above makes sense to you, you have the Ninja level to do it!
It might be more convienent at this point to consider the [Active3 adapter](./adapter/active-3)
that has that covered already.
Technical details
-----------------
The matrix modules available on the market all seem to have the same
standard interface, essentially controlling
two banks of 16 rows (0..15 and 16..31) There are always two rows (n and n+16),
that are controlled in parallel
(These displays are also available in 16x32; in that case, it is two banks of 8).
The data for each row needs to be clocked in serially using one bit for red,
green and blue for both rows that are controlled in parallel (= 6 bits), then
a positive clock edge to shift them in - 32 pixels for one row are clocked in
like this (or more: you can chain these displays).
With 'strobe', the data is transferred to the output buffers for the row.
There are four bits that select the current row(-pair) to be displayed.
Then, with 'output enable', we switch the LEDs on for the amount of time for
that bit plane. This is using some hardware support from the Pi to generate
precise timings (but not if you use an old pinout).
Since LEDs can only be on or off, we have to do our own PWM by constantly
clocking in pixels.
**CPU use**
CPU use
-------
These displays need to be updated constantly to show an image with PWMed
LEDs. This is dependent on the length of the chain: for each chain element,
@ -657,9 +267,9 @@ about 1'000'000 write operations have to happen every second!
(chain_length * 32 pixel long * 16 rows * 11 bit planes * 180 Hz refresh rate).
We can't use hardware support for writing these as DMA is too slow,
thus the constant CPU use on an RPi is roughly 30-40%.
thus the constant CPU use on an RPi is roughly 30-40% of one core.
Keep that in mind if you plan to run other things on this computer (This
is less noticable on Raspberry Pi, Version 2 that has more cores).
is less noticable on Raspberry Pi, Version 2 or 3 that has more cores).
Also, the output quality is suceptible to other heavy tasks running on that
computer - there might be changes in the overall brigthness when this affects
@ -678,9 +288,10 @@ utilize it then. Still, I'd typically recommend it.
Limitations
-----------
If you are using the RGB_CLASSIC_PINOUT, then we can't make use of the PWM
hardware (which only outputs to a particular pin), so you'll see random
brightness glitches. I strongly suggest to change to the now default pinout.
If you are using the RGB_CLASSIC_PINOUT, or Adafruit Hat in the default
configuration, then we can't make use of the PWM hardware (which only outputs
to a particular pin), so you'll see random brightness glitches. I strongly
suggest to change the pinout.
The system needs constant CPU to update the display. Using the DMA controller
was considered but after extensive experiments
@ -697,19 +308,10 @@ things, like this installation by Dirk in Scharbeutz, Germany:
![](./img/user-action-shot.jpg)
[hub75]: ./img/hub75.jpg
[hub75-arrow]: ./img/hub75-other.jpg
[hub75-idc]: ./img/idc-hub75-connector.jpg
[matrix64]: ./img/chained-64x64.jpg
[coordinates]: ./img/coordinates.png
[time]: ./img/time-display.jpg
[pp-vid]: ./img/pixelpusher-vid.jpg
[run-vid]: ./img/running-vid.jpg
[powerbar]: ./img/powerbar.jpg
[pixelpush]: https://github.com/hzeller/rpi-matrix-pixelpusher
[sparkfun]: https://www.sparkfun.com/products/12584
[ada]: http://www.adafruit.com/product/1484
[git-submodules]: http://git-scm.com/book/en/Git-Tools-Submodules
[rt-paper]: https://www.osadl.org/fileadmin/dam/rtlws/12/Brown.pdf
[adafruit-hat]: https://www.adafruit.com/products/2345
[raspbian-lite]: https://downloads.raspberrypi.org/raspbian_lite_latest
[Adafruit HAT]: https://www.adafruit.com/products/2345

35
examples-api-use/Makefile Normal file
View file

@ -0,0 +1,35 @@
CXXFLAGS=-Wall -O3 -g
OBJECTS=demo-main.o minimal-example.o text-example.o
BINARIES=demo minimal-example text-example
# Where our library resides. You mostly only need to change the
# RGB_LIB_DISTRIBUTION, this is where the library is checked out.
RGB_LIB_DISTRIBUTION=..
RGB_INCDIR=$(RGB_LIB_DISTRIBUTION)/include
RGB_LIBDIR=$(RGB_LIB_DISTRIBUTION)/lib
RGB_LIBRARY_NAME=rgbmatrix
RGB_LIBRARY=$(RGB_LIBDIR)/lib$(RGB_LIBRARY_NAME).a
LDFLAGS+=-L$(RGB_LIBDIR) -l$(RGB_LIBRARY_NAME) -lrt -lm -lpthread
all : $(BINARIES)
$(RGB_LIBRARY): FORCE
$(MAKE) -C $(RGB_LIBDIR)
demo : demo-main.o $(RGB_LIBRARY)
$(CXX) $(CXXFLAGS) demo-main.o -o $@ $(LDFLAGS)
minimal-example : minimal-example.o $(RGB_LIBRARY)
$(CXX) $(CXXFLAGS) minimal-example.o -o $@ $(LDFLAGS)
text-example : text-example.o $(RGB_LIBRARY)
$(CXX) $(CXXFLAGS) text-example.o -o $@ $(LDFLAGS)
%.o : %.cc
$(CXX) -I$(RGB_INCDIR) $(CXXFLAGS) -c -o $@ $<
clean:
rm -f $(OBJECTS) $(BINARIES)
FORCE:
.PHONY: FORCE

178
examples-api-use/README.md Normal file
View file

@ -0,0 +1,178 @@
Running some demos
------------------
The demo-main.cc has some testing demos. Via command line flags, you can choose
the display type you have (16x32 or 32x32), and how many you have chained.
```
$ make
$ ./demo
Expected required option -D <demo>
usage: ./demo <options> -D <demo-nr> [optional parameter]
Options:
-r <rows> : Panel rows. '16' for 16x32 (1:8 multiplexing),
'32' for 32x32 (1:16), '8' for 1:4 multiplexing; Default: 32
-P <parallel> : For Plus-models or RPi2: parallel chains. 1..3. Default: 1
-c <chained> : Daisy-chained boards. Default: 1.
-L : 'Large' display, composed out of 4 times 32x32
-p <pwm-bits> : Bits used for PWM. Something between 1..11
-l : Don't do luminance correction (CIE1931)
-D <demo-nr> : Always needs to be set
-d : run as daemon. Use this when starting in
/etc/init.d, but also when running without
terminal (e.g. cron).
-t <seconds> : Run for these number of seconds, then exit.
(if neither -d nor -t are supplied, waits for <RETURN>)
-b <brightnes>: Sets brightness percent. Default: 100.
-R <rotation> : Sets the rotation of matrix. Allowed: 0, 90, 180, 270. Default: 0.
Demos, choosen with -D
0 - some rotating square
1 - forward scrolling an image (-m <scroll-ms>)
2 - backward scrolling an image (-m <scroll-ms>)
3 - test image: a square
4 - Pulsing color
5 - Grayscale Block
6 - Abelian sandpile model (-m <time-step-ms>)
7 - Conway's game of life (-m <time-step-ms>)
8 - Langton's ant (-m <time-step-ms>)
9 - Volume bars (-m <time-step-ms>)
10 - Evolution of color (-m <time-step-ms>)
11 - Brightness pulse generator
Example:
./demo -t 10 -D 1 runtext.ppm
Scrolls the runtext for 10 seconds
```
To run the actual demos, you need to run this as root so that the
GPIO pins can be accessed.
The most interesting one is probably the demo '1' which requires a ppm (type
raw) with a height of 32 pixel - it is infinitely scrolled over the screen; for
convenience, there is a little runtext.ppm example included:
$ sudo ./demo -D 1 runtext.ppm
Here is a video of how it looks
[![Runtext][run-vid]](http://youtu.be/OJvEWyvO4ro)
There are also two examples [minimal-example.cc](./minimal-example.cc) and
[text-example.cc](./text-example.cc) that show use of the API.
The text example allows for some interactive output of text (using a bitmap-font
found in the `fonts/` directory). Even though it is just an example, it can
be useful in its own right. For instance, you can connect to its input with a
pipe and simply feed text from a shell-script or other program that wants to
output something. Let's display the time in blue:
(while :; do date +%T ; sleep 0.2 ; done) | sudo ./text-example -f ../fonts/8x13B.bdf -y8 -c2 -C0,0,255
You could connect this via a pipe to any process that just outputs new
information on standard-output every now and then. The screen is filled with
text until it overflows which then clears it. Or sending an empty line explicitly
clears the screen (if you want to display an empty line, just send a space).
![Time][time]
Using the API
-------------
While there is the demo program, the matrix code can be used independently as
a library. The includes are in `include/`, the library to link is built
in `lib/`. So if you are proficient in C++, then use it in your code.
Due to the wonders of github, it is pretty easy to be up-to-date.
I suggest to add this code as a sub-module in your git repository. That way
you can use that particular version and easily update it if there are changes:
git submodule add https://github.com/hzeller/rpi-rgb-demo.git matrix
(Read more about how to use [submodules in git][git-submodules])
This will check out the repository in a subdirectory `matrix/`.
The library to build would be in directory `matrix/lib`, so let's hook that
into your toplevel Makefile.
I suggest to set up some variables like this; you only need to change the
location `RGB_LIB_DISTRIBUTION` is pointing to; in the sub-module example, this
was the `matrix` directory:
RGB_LIB_DISTRIBUTION=matrix
RGB_INCDIR=$(RGB_LIB_DISTRIBUTION)/include
RGB_LIBDIR=$(RGB_LIB_DISTRIBUTION)/lib
RGB_LIBRARY_NAME=rgbmatrix
RGB_LIBRARY=$(RGB_LIBDIR)/lib$(RGB_LIBRARY_NAME).a
LDFLAGS+=-L$(RGB_LIBDIR) -l$(RGB_LIBRARY_NAME) -lrt -lm -lpthread
Also, you want to add a target to build the libary in your sub-module
# (FYI: Make sure, there is a TAB-character in front of the $(MAKE))
$(RGB_LIBRARY):
$(MAKE) -C $(RGB_LIBDIR)
Now, your final binary needs to depend on your objects and also the
`$(RGB_LIBRARY)`
my-binary : $(OBJECTS) $(RGB_LIBRARY)
$(CXX) $(CXXFLAGS) $(OBJECTS) -o $@ $(LDFLAGS)
As an example, see the [PixelPusher implementation][pixelpush] which is using
this library in a git sub-module.
If you are writing your own Makefile, make sure to pass the `-O3` option to
the compiler to make sure to generate fast code.
Note, all the types provided are in the `rgb_matrix` namespace. That way, they
won't clash with other types you might use in your code; in particular pretty
common names such as `GPIO` or `Canvas` might run into clashing trouble.
Anyway, for convenience you just might add using-declarations in your
code:
// Types exported by the RGB-Matrix library.
using rgb_matrix::Canvas;
using rgb_matrix::GPIO;
using rgb_matrix::RGBMatrix;
using rgb_matrix::ThreadedCanvasManipulator;
Or, if you are lazy, just import the whole namespace:
using namespace rgb_matrix;
Read the [`minimal-example.cc`](./minimal-example.cc) to get started, then
have a look into [`demo-main.cc`](./demo-main.cc).
## Remapping coordinates ##
You might choose a different physical layout than the wiring provides.
Say you have 4 displays with 32x32 and only a single output
like with a Raspberry Pi 1 or the Adafruit HAT -- if we chain
them, we get a display 32 pixel high, (4*32)=128 pixel long. If we arrange
the boards in a square, we get a logical display of 64x64 pixels:
<img src="../img/chained-64x64.jpg" width="400px"> In action:
[![PixelPusher video][pp-vid]](http://youtu.be/ZglGuMaKvpY)
How can we make this 'folded' 128x32 screen behave like a 64x64 screen ?
In the API, there is an interface to implement,
a [`CanvasTransformer`](./include/canvas.h) that allows to program re-arrangements
of pixels in any way. You can plug such a `CanvasTransformer` into the RGBMatrix
to use the new layout (`void RGBMatrix::SetTransformer(CanvasTransformer *transformer)`).
Sometimes you even need this for the panel itself: In newer panels
(often with 1:4 multiplexing) the pixels are often not mapped in
a straight-forward way, but in a snake arrangement for instance. The CanvasTransformer
allows you to work around that (sorry, I have not seen these panels myself so that
I couldn't test that; but if you come accross one, you might want to send a pull-request
with a new CanvasTransformer).
Back to the 64x64 arrangement:
There is a sample implementation `class LargeSquare64x64Transformer` that maps
the 128x32 pixel logical arrangement into the 64x64 arrangement doing
the coordinate mapping. In the demo program and the
[`led-image-viewer`](../utils#image-viewer), you can activate this with
the `-L` option.
[time]: ../img/time-display.jpg
[run-vid]: ../img/running-vid.jpg
[git-submodules]: http://git-scm.com/book/en/Git-Tools-Submodules
[pixelpush]: https://github.com/hzeller/rpi-matrix-pixelpusher
[pp-vid]: ../img/pixelpusher-vid.jpg

7
fonts/README.md Normal file
View file

@ -0,0 +1,7 @@
These are BDF fonts, a simple bitmap font-format that can be created
by many font tools. Given that these are bitmap fonts, they will look good on
very low resolution screens such as the LED displays.
Fonts in this directory are public domain (see the [README](./README)) and
help you to get started with the font support in the API or the `text-util`
from the utils/ directory.

36
utils/Makefile Normal file
View file

@ -0,0 +1,36 @@
CXXFLAGS=-Wall -O3 -g
OBJECTS=led-image-viewer.o
BINARIES=led-image-viewer
# Where our library resides. You mostly only need to change the
# RGB_LIB_DISTRIBUTION, this is where the library is checked out.
RGB_LIB_DISTRIBUTION=..
RGB_INCDIR=$(RGB_LIB_DISTRIBUTION)/include
RGB_LIBDIR=$(RGB_LIB_DISTRIBUTION)/lib
RGB_LIBRARY_NAME=rgbmatrix
RGB_LIBRARY=$(RGB_LIBDIR)/lib$(RGB_LIBRARY_NAME).a
LDFLAGS+=-L$(RGB_LIBDIR) -l$(RGB_LIBRARY_NAME) -lrt -lm -lpthread
# Imagemagic flags, only needed if actually compiled.
MAGICK_CXXFLAGS=`GraphicsMagick++-config --cppflags --cxxflags`
MAGICK_LDFLAGS=`GraphicsMagick++-config --ldflags --libs`
all : $(BINARIES)
$(RGB_LIBRARY): FORCE
$(MAKE) -C $(RGB_LIBDIR)
led-image-viewer: led-image-viewer.o $(RGB_LIBRARY)
$(CXX) $(CXXFLAGS) led-image-viewer.o -o $@ $(LDFLAGS) $(MAGICK_LDFLAGS)
%.o : %.cc
$(CXX) -I$(RGB_INCDIR) $(CXXFLAGS) -c -o $@ $<
led-image-viewer.o : led-image-viewer.cc
$(CXX) -I$(RGB_INCDIR) $(CXXFLAGS) $(MAGICK_CXXFLAGS) -c -o $@ $<
clean:
rm -f $(OBJECTS) $(BINARIES)
FORCE:
.PHONY: FORCE

16
utils/README.md Normal file
View file

@ -0,0 +1,16 @@
### Image Viewer ###
The image viewer reads all kinds of image formats, including animated gifs.
It is not compiled by default, as you need to install the GraphicsMagick
dependencies first:
sudo apt-get update
sudo apt-get install libgraphicsmagick++-dev libwebp-dev
make led-image-viewer
Then, you can run it with any common image format, including animated gifs:
sudo ./led-image-viewer myimage.gif
It also supports the standard options to specify the connected
displays (`-r`, `-c`, `-P`).

217
wiring.md Normal file
View file

@ -0,0 +1,217 @@
Connection
----------
You need a separate power supply for the panel. There is a connector for that
separate from the logic connector, typically a big one in the center of the
board. The board requires 5V (double check the polarity: what is printed
on the board is correct - I once got boards with supplied cables that had red
(suggesting `+`) and black (suggesting `GND`) reversed!). This power supply is
used to light the LEDs; plan for ~3.5 Ampere per 32x32 panel.
The connector on the RGB panels is called a Hub75 interface. Each panel
typically has two ports, one is the input and the other is the output to
chain additional panels. Usually an arrow shows which of the connectors is
the input.
Here you see a Hub75 connector to be seen at the bottom of the RGB panel
board including the arrow indicating the input direction:
![Hub 75 interface][hub75-arrow]
Other boards are very similar, but instead of zero-indexed color bits
`R0`, `G0`, `B0`, `R1`, `G1`, `B1`, they start the index with one and name these
`R1`, `G1`, `B1`, `R2`, `G2`, `B2`; the functionality is identical.
![Hub 75 interface][hub75]
Throughout this document, we will use the one-index base, so we will call these
signals `R1`, `G1`, `B1`, `R2`, `G2`, `B2` below.
The `strobe` signals is sometimes also called `latch` or `lat`. We'll call it
`strobe` here.
If you plug an IDC-cable into your RGB panel to the input connector, this is
how the signal positions are on the other end of the cable (imagine the LED
panels somewhere outside the picture on the left); note the notch on the right
side of the connector:
![Hub 75 IDC connector][hub75-idc]
The RPi only has 3.3V logic output level, but many displays operated at 5V
interprets these logic levels fine, just make sure to run a short
cable to the board (if you see problems, see [troubleshouting paragraph](#troubleshooting)).
If you do run into glitches or erratic pixels, consider some line-buffering,
e.g. using the [active adapter PCB](./adapter/).
Since we only need output pins on the RPi, we don't need to worry about level
conversion back.
For a single chain of LED-panels, we need 13 IO lines, which fit all in the
header of the old Raspberry Pis. Newer Raspberry Pis with 40 pins have more
GPIO lines which allows us to connect three parallel chains of RGB panels.
For reference, this is how the numbering on the Raspberry Pi looks like:
<a href="img/raspberry-gpio.jpg"><img src="img/raspberry-gpio.jpg" width="600px"></a>
This is the same representation used in the table below, which helps for
visual inspection.
### Wiring diagram
For each of the up to three chains, you have to connect `GND`, `strobe`,
`clock`, `OE-`, `A`, `B`, `C`, `D` to all of these (the `D` line is needed
for 32x32 displays; 32x16 displays don't need it); you find the positions
below (there are more GND pins on the Raspberry Pi, but they are left out
for simplicity).
Then for each panel, there is a set of (R1, G1, B1, R2, G2, B2) that you have
to connect to the corresponding pins that are marked `[1]`, `[2]` and `[3]` for
chain 1, 2, and 3 below.
If you only connect one panel or have one chain, connect it to `[1]` (:smile:); if you
use parallel chains, add the other `[2]` and `[3]`.
To make things quicker to navigate visually, each chain is marked with a separate
icon:
`[1]`=:smile:, `[2]`=:boom: and `[3]`=:droplet: ; signals that go to all
chains have all icons.
Connection | Pin | Pin | Connection
---------------------------------:|:---:|:---:|:-----------------------------
- | 1 | 2 | -
:droplet: **[3] G1** | 3 | 4 | -
:droplet: **[3] B1** | 5 | 6 | **GND** :smile::boom::droplet:
:smile::boom::droplet: **strobe** | 7 | 8 | **[3] R1** :droplet:
- | 9 | 10 | **E** :smile::boom::droplet: (for 64 row matrix, 1:32)
:smile::boom::droplet: **clock** | 11 | 12 | **OE-** :smile::boom::droplet:
:smile: **[1] G1** | 13 | 14 | -
:smile::boom::droplet: **A** | 15 | 16 | **B** :smile::boom::droplet:
- | 17 | 18 | **C** :smile::boom::droplet:
:smile: **[1] B2** | 19 | 20 | -
:smile: **[1] G2** | 21 | 22 | **D** :smile::boom::droplet: (for 32 row matrix, 1:16)
:smile: **[1] R1** | 23 | 24 | **[1] R2** :smile:
- | 25 | 26 | **[1] B1** :smile:
- | 27 | 28 | -
:boom: **[2] G1** | 29 | 30 | -
:boom: **[2] B1** | 31 | 32 | **[2] R1** :boom:
:boom: **[2] G2** | 33 | 34 | -
:boom: **[2] R2** | 35 | 36 | **[3] G2** :droplet:
:droplet:**[3] R2** | 37 | 38 | **[2] B2** :boom:
- | 39 | 40 | **[3] B2** :droplet:
In the [adapter/](./adapter) directory, there are some boards that make
the wiring task simpler.
Chaining, parallel chains and coordinate system
------------------------------------------------
Displays panels have an input connector, but also have an output port, that
you can connect to the next display in a daisy-chain manner. There is the
flag `-c` in the demo program to give number of displays that are chained.
You end up with a very wide
display (chain * 32 pixels). Longer chains affect the refresh rate negatively,
so if you want to stay above 100Hz with full color, don't chain more than
12 panels.
If you use a PWM depth of 1 bit (`-p`), the chain can be much longer.
The original Raspberry Pis with 26 GPIO pins just had enough connector pins
to drive one chain of LED panels. Newer Raspberry Pis have 40 GPIO pins that
allows to add two additional chains of panels in parallel - the nice thing is,
that this doesn't require more CPU and allows you to keep your refresh-rate high,
because you can shorten your chains.
So with that, we have a couple of parameters to keep track of. The **rows** are
the number of LED rows on a particular module; typically these are 16 for a 16x32
display or 32 for 32x32 displays.
Then there is the **chain length**, which is the number of panels that are
daisy chained together.
Finally, there is a parameter how many **parallel** chains we have connected to
the Pi -- limited to 1 on old Raspberry Pis, up to three on newer Raspberry Pis.
For a single Panel, the chain and parallel parameters are both just one: a single
chain (with no else in parallel) with a chain length of 1.
The `RGBMatrix` class constructor has parameters for number of rows,
chain-length and number of parallel. For the demo programs and the image view,
there are command line options for that: `-r` gives rows,
`-c` the chain-length and `-P` the number of parallel chains.
The coordinate system starts at (0,0) at the top of the first parallel chain,
furthest away from the Pi. The following picture gives an overview of various
parameters and the coordinate system.
![Coordinate overview][coordinates]
<a href="adapter/"><img src="img/three-parallel-panels-soic.jpg" width="300px"></a>
A word about power
------------------
These displays suck a lot of current. At 5V, when all LEDs are on (full white),
my LED panel draws about 3.4A. That means, you need a beefy power supply to
drive these panels; a 2A USB charger or similar is not enough for a
32x32 panel; it might be for a 16x32.
If you connect multiple boards together, you needs a power supply that can
keep up with 3.5A / panel. Good are old PC power supplies that often
provide > 20A on the 5V rail. Also you can get dedicated 5V high current
switching power supplies for these kind of applications (check eBay).
The current draw is pretty spiky. Due to the PWM of the LEDs, there are very
short peaks of a couple of 100ns to about 1ms of full current draw.
Often, the power cable can't support these very short spikes due to inherent
inductance. This can result in 'noisy' outputs, with random pixels not behaving
as they should. A low ESR capacitor close to the input is good in these cases.
On some displays, the quality of the output quickly gets erratic
when voltage drops below 4.5V. Some even need a little bit higher voltage around
5.5V to work reliably.
When you connect these boards to a power source, the following are good
guidelines:
- Have fairly thick cables connecting the power to the board.
Plan not to loose more than 50mV from the source to the LED matrix.
So that would be 50mV / 3.5A = 14 mΩ. For both supply wires, so 7mΩ
each trace.
A 1mm² copper cable has about 17.5mΩ/meter, so you'd need a **2.5mm²
copper cable per meter and panel**. Multiply by meter and number of
panels to get the needed cross-section.
(For Americans: that would be ~13 gauge wire for 3 ft and one panel)
- While a star configuration for the cabeling would be optimal (each panel gets
an individual wire from the power supply), it is typically sufficient
using aluminum mounting brackets or bars as part of
your power solution. With aluminum of 1mm² specific resistivity of
about 28mΩ/meter, you'd need a cross sectional area of about 4mm² per panel
and meter.
In the following example you see the structural aluminum bars in the middle
(covered in colored vinyl) dualing as power bars. The 60A/5V power supply is connected
to the center bolts (display uses about 42A all LEDs on):
![Powerbar][powerbar]
- Often these boards come with cables that have connectors crimped on.
Some cheap cables are typically too thin; you might want to clip them close to
the connector solder your proper, thick cable to it.
- It is good to buffer the current spikes directly at the panel. The most
spikes happen while PWM-ing a single line.
So let's say we want to buffer the energy to power a single line without
dropping more than 50mV. We use 3.5A which is 3.5Joule/second. We do
about 140Hz refresh rate and divide that in 16 lines, so we need
3.5 Joule/140/16 = ~1.6mJoule in the time period to display one line.
We want to get the energy out of the voltage drop of 50mV; so with
W = 1/2*C*U², we can calculate the capacitance needed:
C = 2 * 1.6mJoule / ((5V)² - (5V - 50mV)²) = ~6400µF.
So, **2 x 3300µF** low-ESR capacitors in parallel directly
at the board are a good choice (two, because lower parallel ESR; also
fits easier under board).
(In reality, we need of course less, as the highest ripple comes with
50% duty cyle thus half the current; also the input is recharching all
the time. But: as engineer plan for maximum and then some; in the picture
above I am using 1x3300uF per panel and it works fine).
Now welcome your over-engineered power solution :)
[hub75]: ./img/hub75.jpg
[hub75-arrow]: ./img/hub75-other.jpg
[hub75-idc]: ./img/idc-hub75-connector.jpg
[coordinates]: ./img/coordinates.png
[powerbar]: ./img/powerbar.jpg