mirror of
https://github.com/Hopiu/rpi-rgb-led-matrix.git
synced 2026-03-16 22:10:27 +00:00
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:
parent
c089366c12
commit
d28b2a23b4
13 changed files with 538 additions and 464 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -1,6 +1,7 @@
|
||||||
*.o
|
*.o
|
||||||
*.a
|
*.a
|
||||||
|
*~
|
||||||
led-image-viewer
|
led-image-viewer
|
||||||
led-matrix
|
demo
|
||||||
minimal-example
|
minimal-example
|
||||||
text-example
|
text-example
|
||||||
|
|
|
||||||
26
Makefile
26
Makefile
|
|
@ -5,8 +5,9 @@ ALL_BINARIES=$(BINARIES) led-image-viewer
|
||||||
|
|
||||||
# Where our library resides. It is split between includes and the binary
|
# Where our library resides. It is split between includes and the binary
|
||||||
# library in lib
|
# library in lib
|
||||||
RGB_INCDIR=include
|
RGB_LIB_DISTRIBUTION=.
|
||||||
RGB_LIBDIR=lib
|
RGB_INCDIR=$(RGB_LIB_DISTRIBUTION)/include
|
||||||
|
RGB_LIBDIR=$(RGB_LIB_DISTRIBUTION)/lib
|
||||||
RGB_LIBRARY_NAME=rgbmatrix
|
RGB_LIBRARY_NAME=rgbmatrix
|
||||||
RGB_LIBRARY=$(RGB_LIBDIR)/lib$(RGB_LIBRARY_NAME).a
|
RGB_LIBRARY=$(RGB_LIBDIR)/lib$(RGB_LIBRARY_NAME).a
|
||||||
LDFLAGS+=-L$(RGB_LIBDIR) -l$(RGB_LIBRARY_NAME) -lrt -lm -lpthread
|
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_CXXFLAGS=`GraphicsMagick++-config --cppflags --cxxflags`
|
||||||
MAGICK_LDFLAGS=`GraphicsMagick++-config --ldflags --libs`
|
MAGICK_LDFLAGS=`GraphicsMagick++-config --ldflags --libs`
|
||||||
|
|
||||||
all : $(BINARIES)
|
all : $(RGB_LIBRARY)
|
||||||
|
|
||||||
$(RGB_LIBRARY): FORCE
|
$(RGB_LIBRARY): FORCE
|
||||||
$(MAKE) -C $(RGB_LIBDIR)
|
$(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:
|
clean:
|
||||||
rm -f $(OBJECTS) $(ALL_BINARIES)
|
|
||||||
$(MAKE) -C lib clean
|
$(MAKE) -C lib clean
|
||||||
$(MAKE) -C $(PYTHON_LIB_DIR) clean
|
$(MAKE) -C $(PYTHON_LIB_DIR) clean
|
||||||
|
|
||||||
|
|
|
||||||
484
README.md
484
README.md
|
|
@ -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).
|
there might likely be timing problems with the panels that will creep up then).
|
||||||
With fewer colors you can control even more, faster.
|
With fewer colors you can control even more, faster.
|
||||||
|
|
||||||
The LED-matrix **library** is (c) Henner Zeller <h.zeller@acm.org> with
|
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>
|
[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
|
||||||
The demo-main.cc **example code** using this library is released to the
|
source and all your modifications available to the receiver of such product so
|
||||||
public domain.
|
that they have the freedom to adapt and improve).
|
||||||
|
|
||||||
Overview
|
Overview
|
||||||
--------
|
--------
|
||||||
The 32x32 or 16x32 RGB LED matrix panels can be scored at [Sparkfun][sparkfun],
|
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
|
[AdaFruit][ada] or eBay and Aliexpress. If you are in China, I'd try to get
|
||||||
from some manufacturer, Taobao or Alibaba.
|
them directly from some manufacturer, Taobao or Alibaba.
|
||||||
|
|
||||||
The `RGBMatrix` class provided in `include/led-matrix.h` does what is needed
|
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
|
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.
|
use the demo binary provided here which provides some useful examples.
|
||||||
|
|
||||||
Check out the [minimal-example.cc](./minimal-example.cc) to get started using
|
Check out [utils/ directory for some ready-made tools](./utils) to get started
|
||||||
this library.
|
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
|
All Raspberry Pi versions supported
|
||||||
-----------------------------------
|
-----------------------------------
|
||||||
|
|
@ -49,7 +50,7 @@ The [Raspbian Lite][raspbian-lite] distribution is recommended.
|
||||||
Types of Displays
|
Types of Displays
|
||||||
-----------------
|
-----------------
|
||||||
There are various types of displays that come all with the same Hub75 connector.
|
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
|
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
|
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.
|
is A and B), but I have not had these in my lab yet to test.
|
||||||
|
|
||||||
Connection
|
Let's do it
|
||||||
----------
|
------------
|
||||||
You need a separate power supply for the panel. There is a connector for that
|
This documentation is split into parts that help you through the process
|
||||||
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
|
1. [Wire up the matrix to your Pi](./wiring.md). This document describes what
|
||||||
typically has two ports, one is the input and the other is the output to
|
goes where. You might also be interested in [breakout boards](./adapter)
|
||||||
chain additional panels. Usually an arrow shows which of the connectors is
|
for that. If you have an [Adafruit HAT], necessary steps are
|
||||||
the input.
|
[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
|
Chaining panels
|
||||||
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
|
We might only have a limited amount of GPIOs on the Raspberry Pi, but luckily,
|
||||||
`R0`, `G0`, `B0`, `R1`, `G1`, `B1`, they start the index with one and name these
|
the RGB matrices can be chained. The display panels have an input connector,
|
||||||
`R1`, `G1`, `B1`, `R2`, `G2`, `B2`; the functionality is identical.
|
and also have an output port, that you can connect to the next display in a
|
||||||
![Hub 75 interface][hub75]
|
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
|
The [wiring.md](./wiring.md#chaining-parallel-chains-and-coordinate-system)
|
||||||
signals `R1`, `G1`, `B1`, `R2`, `G2`, `B2` below.
|
document explains the details.
|
||||||
|
|
||||||
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]
|
|
||||||
|
|
||||||
|
<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>
|
<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
|
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
|
There are lots of parameters in [lib/Makefile](./lib/Makefile) that you might
|
||||||
be interested in tweaking.
|
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
|
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
|
ready-made vs. single-chain tradeoff is worthwhile, then you might go for that
|
||||||
(I am not affiliated with Adafruit).
|
(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
|
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
|
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)
|
It might be more convienent at this point to consider the [Active3 adapter](./adapter/active-3)
|
||||||
that has that covered already.
|
that has that covered already.
|
||||||
|
|
||||||
Technical details
|
CPU use
|
||||||
-----------------
|
-------
|
||||||
|
|
||||||
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**
|
|
||||||
|
|
||||||
These displays need to be updated constantly to show an image with PWMed
|
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,
|
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).
|
(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,
|
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
|
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
|
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
|
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
|
Limitations
|
||||||
-----------
|
-----------
|
||||||
If you are using the RGB_CLASSIC_PINOUT, then we can't make use of the PWM
|
If you are using the RGB_CLASSIC_PINOUT, or Adafruit Hat in the default
|
||||||
hardware (which only outputs to a particular pin), so you'll see random
|
configuration, then we can't make use of the PWM hardware (which only outputs
|
||||||
brightness glitches. I strongly suggest to change to the now default pinout.
|
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
|
The system needs constant CPU to update the display. Using the DMA controller
|
||||||
was considered but after extensive experiments
|
was considered but after extensive experiments
|
||||||
|
|
@ -697,19 +308,10 @@ things, like this installation by Dirk in Scharbeutz, Germany:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
[hub75]: ./img/hub75.jpg
|
|
||||||
[hub75-arrow]: ./img/hub75-other.jpg
|
|
||||||
[hub75-idc]: ./img/idc-hub75-connector.jpg
|
|
||||||
[matrix64]: ./img/chained-64x64.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
|
[sparkfun]: https://www.sparkfun.com/products/12584
|
||||||
[ada]: http://www.adafruit.com/product/1484
|
[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
|
[rt-paper]: https://www.osadl.org/fileadmin/dam/rtlws/12/Brown.pdf
|
||||||
[adafruit-hat]: https://www.adafruit.com/products/2345
|
[adafruit-hat]: https://www.adafruit.com/products/2345
|
||||||
[raspbian-lite]: https://downloads.raspberrypi.org/raspbian_lite_latest
|
[raspbian-lite]: https://downloads.raspberrypi.org/raspbian_lite_latest
|
||||||
|
[Adafruit HAT]: https://www.adafruit.com/products/2345
|
||||||
35
examples-api-use/Makefile
Normal file
35
examples-api-use/Makefile
Normal 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
178
examples-api-use/README.md
Normal 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
7
fonts/README.md
Normal 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
36
utils/Makefile
Normal 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
16
utils/README.md
Normal 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
217
wiring.md
Normal 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
|
||||||
Loading…
Reference in a new issue