Initial commit.
This commit is contained in:
parent
a745890acb
commit
886c692b5a
14 changed files with 992 additions and 0 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -1,3 +1,5 @@
|
|||
display-test
|
||||
rpi-fb-matrix
|
||||
# Compiled Object files
|
||||
*.slo
|
||||
*.lo
|
||||
|
|
|
|||
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
[submodule "rpi-rgb-led-matrix"]
|
||||
path = rpi-rgb-led-matrix
|
||||
url = https://github.com/hzeller/rpi-rgb-led-matrix.git
|
||||
104
Config.cpp
Normal file
104
Config.cpp
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
// Matrix configuration parsing class implementation.
|
||||
// Author: Tony DiCola
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
|
||||
#include <libconfig.h++>
|
||||
|
||||
#include "Config.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
||||
Config::Config(const string& filename) {
|
||||
try {
|
||||
// Load config file with libconfig.
|
||||
libconfig::Config cfg;
|
||||
cfg.readFile(filename.c_str());
|
||||
libconfig::Setting& root = cfg.getRoot();
|
||||
// Parse out the matrix configuration values.
|
||||
_display_width = root["display_width"];
|
||||
_display_height = root["display_height"];
|
||||
_panel_width = root["panel_width"];
|
||||
_panel_height = root["panel_height"];
|
||||
_chain_length = root["chain_length"];
|
||||
_parallel_count = root["parallel_count"];
|
||||
// Do basic validation of configuration.
|
||||
if (_display_width % _panel_width != 0) {
|
||||
throw invalid_argument("display_width must be a multiple of panel_width!");
|
||||
}
|
||||
if (_display_height % _panel_height != 0) {
|
||||
throw invalid_argument("display_height must be a multiple of panel_height!");
|
||||
}
|
||||
if ((_parallel_count < 1) || (_parallel_count > 3)) {
|
||||
throw invalid_argument("parallel_count must be between 1 and 3!");
|
||||
}
|
||||
// Parse out the individual panel configurations.
|
||||
libconfig::Setting& panels_config = root["panels"];
|
||||
for (int i = 0; i < panels_config.getLength(); ++i) {
|
||||
libconfig::Setting& row = panels_config[i];
|
||||
for (int j = 0; j < row.getLength(); ++j) {
|
||||
GridTransformer::Panel panel;
|
||||
// Read panel order (required setting for each panel).
|
||||
panel.order = row[j]["order"];
|
||||
// Set default values for rotation and parallel chain, then override
|
||||
// them with any panel-specific configuration values.
|
||||
panel.rotate = 0;
|
||||
panel.parallel = 0;
|
||||
row[j].lookupValue("rotate", panel.rotate);
|
||||
row[j].lookupValue("parallel", panel.parallel);
|
||||
// Perform validation of panel values.
|
||||
// If panels are square allow rotations that are a multiple of 90, otherwise
|
||||
// only allow a rotation of 180 degrees.
|
||||
if ((_panel_width == _panel_height) && (panel.rotate % 90 != 0)) {
|
||||
stringstream error;
|
||||
error << "Panel " << i << "," << j << " rotation must be a multiple of 90 degrees!";
|
||||
throw invalid_argument(error.str());
|
||||
}
|
||||
else if ((_panel_width != _panel_height) && (panel.rotate % 180 != 0)) {
|
||||
stringstream error;
|
||||
error << "Panel row " << j << ", column " << i << " can only be rotated 180 degrees!";
|
||||
throw invalid_argument(error.str());
|
||||
}
|
||||
// Check that parallel is value between 0 and 2 (up to 3 parallel chains).
|
||||
if ((panel.parallel < 0) || (panel.parallel > 2)) {
|
||||
stringstream error;
|
||||
error << "Panel row " << j << ", column " << i << " parallel value must be 0, 1, or 2!";
|
||||
throw invalid_argument(error.str());
|
||||
}
|
||||
// Add the panel to the list of panel configurations.
|
||||
_panels.push_back(panel);
|
||||
}
|
||||
}
|
||||
// Check the number of configured panels matches the expected number
|
||||
// of panels (# of panel columns * # of panel rows).
|
||||
int expected = (_display_width / _panel_width) * (_display_height / _panel_height);
|
||||
if (_panels.size() != (unsigned int)expected) {
|
||||
stringstream error;
|
||||
error << "Expected " << expected << " panels in configuration but found " << _panels.size() << "!";
|
||||
throw invalid_argument(error.str());
|
||||
}
|
||||
}
|
||||
catch (const libconfig::FileIOException& fioex)
|
||||
{
|
||||
throw runtime_error("IO error while reading configuration file. Does the file exist?");
|
||||
}
|
||||
catch (const libconfig::ParseException& pex)
|
||||
{
|
||||
stringstream error;
|
||||
error << "Config file error at " << pex.getFile() << ":" << pex.getLine()
|
||||
<< " - " << pex.getError();
|
||||
throw invalid_argument(error.str());
|
||||
}
|
||||
catch (const libconfig::SettingNotFoundException& nfex)
|
||||
{
|
||||
stringstream error;
|
||||
error << "Expected to find setting: " << nfex.getPath();
|
||||
throw invalid_argument(error.str());
|
||||
}
|
||||
catch (const libconfig::ConfigException& ex) {
|
||||
throw runtime_error("Error loading configuration!");
|
||||
}
|
||||
}
|
||||
49
Config.h
Normal file
49
Config.h
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
// Matrix configuration parsing class declaration.
|
||||
// Author: Tony DiCola
|
||||
#ifndef CONFIG_H
|
||||
#define CONFIG_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "GridTransformer.h"
|
||||
|
||||
class Config {
|
||||
public:
|
||||
Config(const std::string& filename);
|
||||
|
||||
// Attribute accessors:
|
||||
int getDisplayWidth() const {
|
||||
return _display_width;
|
||||
}
|
||||
int getDisplayHeight() const {
|
||||
return _display_height;
|
||||
}
|
||||
int getPanelWidth() const {
|
||||
return _panel_width;
|
||||
}
|
||||
int getPanelHeight() const {
|
||||
return _panel_height;
|
||||
}
|
||||
int getChainLength() const {
|
||||
return _chain_length;
|
||||
}
|
||||
int getParallelCount() const {
|
||||
return _parallel_count;
|
||||
}
|
||||
GridTransformer getGridTransformer() const {
|
||||
return GridTransformer(_display_width, _display_height, _panel_width,
|
||||
_panel_height, _chain_length, _panels);
|
||||
}
|
||||
|
||||
private:
|
||||
int _display_width,
|
||||
_display_height,
|
||||
_panel_width,
|
||||
_panel_height,
|
||||
_chain_length,
|
||||
_parallel_count;
|
||||
std::vector<GridTransformer::Panel> _panels;
|
||||
};
|
||||
|
||||
#endif
|
||||
87
GridTransformer.cpp
Normal file
87
GridTransformer.cpp
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
// LED matrix library transformer to map a rectangular canvas onto a complex
|
||||
// chain of matrices.
|
||||
// Author: Tony DiCola
|
||||
#include "GridTransformer.h"
|
||||
|
||||
using namespace rgb_matrix;
|
||||
using namespace std;
|
||||
|
||||
GridTransformer::GridTransformer(int width, int height, int panel_width, int panel_height,
|
||||
int chain_length, const std::vector<Panel>& panels):
|
||||
_width(width),
|
||||
_height(height),
|
||||
_panel_width(panel_width),
|
||||
_panel_height(panel_height),
|
||||
_chain_length(chain_length),
|
||||
_source(NULL),
|
||||
_panels(panels)
|
||||
{
|
||||
// Display width must be a multiple of the panel pixel column count.
|
||||
assert(_width % _panel_width == 0);
|
||||
// Display height must be a multiple of the panel pixel row count.
|
||||
assert(_height % _panel_height == 0);
|
||||
// Compute number of rows and columns of panels.
|
||||
_rows = _height / _panel_height;
|
||||
_cols = _width / _panel_width;
|
||||
// Check panel definition list has exactly the expected number of panels.
|
||||
assert((_rows * _cols) == (int)_panels.size());
|
||||
}
|
||||
|
||||
void GridTransformer::SetPixel(int x, int y, uint8_t red, uint8_t green, uint8_t blue) {
|
||||
assert(_source != NULL);
|
||||
if ((x < 0) || (y < 0) || (x >= _width) || (y >= _height)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Figure out what row and column panel this pixel is within.
|
||||
int row = y / _panel_height;
|
||||
int col = x / _panel_width;
|
||||
|
||||
// Get the panel information for this pixel.
|
||||
Panel panel = _panels[_cols*row + col];
|
||||
|
||||
// Compute location of the pixel within the panel.
|
||||
x = x % _panel_width;
|
||||
y = y % _panel_height;
|
||||
|
||||
// Perform any panel rotation to the pixel.
|
||||
// NOTE: 90 and 270 degree rotation only possible on 32 row (square) panels.
|
||||
if (panel.rotate == 90) {
|
||||
assert(_panel_height == _panel_width);
|
||||
int old_x = x;
|
||||
x = (_panel_height-1)-y;
|
||||
y = old_x;
|
||||
}
|
||||
else if (panel.rotate == 180) {
|
||||
x = (_panel_width-1)-x;
|
||||
y = (_panel_height-1)-y;
|
||||
}
|
||||
else if (panel.rotate == 270) {
|
||||
assert(_panel_height == _panel_width);
|
||||
int old_y = y;
|
||||
y = (_panel_width-1)-x;
|
||||
x = old_y;
|
||||
}
|
||||
|
||||
// Determine x offset into the source panel based on its order along the chain.
|
||||
// The order needs to be inverted because the matrix library starts with the
|
||||
// origin of an image at the end of the chain and not at the start (where
|
||||
// ordering begins for this transformer).
|
||||
int x_offset = ((_chain_length-1)-panel.order)*_panel_width;
|
||||
|
||||
// Determine y offset into the source panel based on its parrallel chain value.
|
||||
int y_offset = panel.parallel*_panel_height;
|
||||
|
||||
_source->SetPixel(x_offset + x,
|
||||
y_offset + y,
|
||||
red, green, blue);
|
||||
}
|
||||
|
||||
Canvas* GridTransformer::Transform(Canvas* source) {
|
||||
assert(source != NULL);
|
||||
int swidth = source->width();
|
||||
int sheight = source->height();
|
||||
assert((_width * _height) == (swidth * sheight));
|
||||
_source = source;
|
||||
return this;
|
||||
}
|
||||
65
GridTransformer.h
Normal file
65
GridTransformer.h
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
// LED matrix library transformer to map a rectangular canvas onto a complex
|
||||
// chain of matrices.
|
||||
// Author: Tony DiCola
|
||||
#ifndef GRIDTRANSFORMER_H
|
||||
#define GRIDTRANSFORMER_H
|
||||
|
||||
#include <cassert>
|
||||
#include <vector>
|
||||
|
||||
#include "led-matrix.h"
|
||||
|
||||
|
||||
class GridTransformer: public rgb_matrix::Canvas, public rgb_matrix::CanvasTransformer {
|
||||
public:
|
||||
struct Panel {
|
||||
int order;
|
||||
int rotate;
|
||||
int parallel;
|
||||
};
|
||||
|
||||
GridTransformer(int width, int height, int panel_width, int panel_height,
|
||||
int chain_length, const std::vector<Panel>& panels);
|
||||
virtual ~GridTransformer() {}
|
||||
|
||||
// Canvas interface implementation:
|
||||
virtual int width() const {
|
||||
return _width;
|
||||
}
|
||||
virtual int height() const {
|
||||
return _height;
|
||||
}
|
||||
virtual void Clear() {
|
||||
assert(_source != NULL);
|
||||
_source->Clear();
|
||||
}
|
||||
virtual void Fill(uint8_t red, uint8_t green, uint8_t blue) {
|
||||
assert(_source != NULL);
|
||||
_source->Fill(red, green, blue);
|
||||
}
|
||||
virtual void SetPixel(int x, int y, uint8_t red, uint8_t green, uint8_t blue);
|
||||
|
||||
// Transformer interface implementation:
|
||||
virtual rgb_matrix::Canvas* Transform(rgb_matrix::Canvas* source);
|
||||
|
||||
// Other attribute accessors.
|
||||
int getRows() const {
|
||||
return _rows;
|
||||
}
|
||||
int getColumns() const {
|
||||
return _cols;
|
||||
}
|
||||
|
||||
private:
|
||||
int _width,
|
||||
_height,
|
||||
_panel_width,
|
||||
_panel_height,
|
||||
_chain_length,
|
||||
_rows,
|
||||
_cols;
|
||||
rgb_matrix::Canvas* _source;
|
||||
std::vector<Panel> _panels;
|
||||
};
|
||||
|
||||
#endif
|
||||
36
Makefile
Normal file
36
Makefile
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
# Configure the rpi-rgb-led-matrix library here:
|
||||
# The -DADAFRUIT_RGBMATRIX_HAT value configures the library to use the Adafruit
|
||||
# LED matrix HAT wiring, and the -DRGB_SLOWDOWN_GPIO=1 value configures the
|
||||
# library to work with a Raspberry Pi 2. For a Pi 1 (or perhaps even on a Pi 2,
|
||||
# but I found it necessary in my testing) you can remove the -DRGB_SLOWDOWN_GPIO=1
|
||||
# option. You can also add any other rpi-rgb-led-matrix library defines here
|
||||
# to configure the library for more special needs. See the library's docs for
|
||||
# details on options:
|
||||
# https://github.com/hzeller/rpi-rgb-led-matrix/blob/master/lib/Makefile
|
||||
export DEFINES = -DADAFRUIT_RGBMATRIX_HAT -DRGB_SLOWDOWN_GPIO=1
|
||||
|
||||
# Configure compiler and libraries:
|
||||
CXX = g++
|
||||
CXXFLAGS = -Wall -std=c++11 -I. -I./rpi-rgb-led-matrix/include -I/opt/vc/include -I/opt/vc/include/interface/vcos/pthreads -I/opt/vc/include/interface/vmcs_host -I/opt/vc/include/interface/vmcs_host/linux -L./rpi-rgb-led-matrix/lib -L/opt/vc/lib
|
||||
LIBS = -lrgbmatrix -lrt -lm -lpthread -lbcm_host -lconfig++
|
||||
|
||||
# Makefile rules:
|
||||
all: rpi-fb-matrix display-test
|
||||
|
||||
rpi-fb-matrix: rpi-fb-matrix.o GridTransformer.o Config.o ./rpi-rgb-led-matrix/lib/librgbmatrix.a
|
||||
$(CXX) -o $@ $^ $(CXXFLAGS) $(LIBS)
|
||||
|
||||
display-test: display-test.o GridTransformer.o Config.o glcdfont.o ./rpi-rgb-led-matrix/lib/librgbmatrix.a
|
||||
$(CXX) -o $@ $^ $(CXXFLAGS) $(LIBS)
|
||||
|
||||
%.o: %.cpp $(DEPS)
|
||||
$(CXX) -c -o $@ $< $(CXXFLAGS)
|
||||
|
||||
./rpi-rgb-led-matrix/lib/librgbmatrix.a:
|
||||
$(MAKE) -C ./rpi-rgb-led-matrix/lib
|
||||
|
||||
.PHONY: clean
|
||||
|
||||
clean:
|
||||
rm -f *.o rpi-fb-matrix display-test
|
||||
$(MAKE) -C ./rpi-rgb-led-matrix/lib clean
|
||||
43
README.md
43
README.md
|
|
@ -1,2 +1,45 @@
|
|||
# rpi-fb-matrix
|
||||
Raspberry Pi framebuffer copy tool for RGB LED matrices. Show what's on a Pi HDMI output on a big RGB LED matrix chain!
|
||||
|
||||
## Setup
|
||||
|
||||
You **must** clone this repository with the recursive option so that necessary
|
||||
submodules are also cloned. Run this command:
|
||||
|
||||
git clone --recursive https://github.com/adafruit/rpi-fb-matrix.git
|
||||
|
||||
Next you will need a few dependencies (libconfig++) to compile the code. Run
|
||||
these commands to install the dependencies:
|
||||
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y build-essential libconfig++-dev
|
||||
|
||||
Now if necessary you can change how the RGB LED matrix library is configured
|
||||
by editing the variables set in the Makefile. By default the Makefile is
|
||||
configured to work with the Adafruit LED matrix HAT and a Raspberry Pi 2. If
|
||||
you're using a different configuration open the Makefile and edit the `export DEFINES=...`
|
||||
line at the top.
|
||||
|
||||
Build the project by running make:
|
||||
|
||||
make
|
||||
|
||||
Once compiled there will be two executables:
|
||||
|
||||
* rpi-fb-matrix: The main program that will copy the contents of the primary
|
||||
display (HDMI output) to attached LED matrices.
|
||||
* display-test: A program to display the order and orientation of chained
|
||||
together LED matrices. Good for building complex display chains.
|
||||
|
||||
## Acknowledgments
|
||||
|
||||
This program makes use of the following excellent libraries:
|
||||
|
||||
* [rpi-rgb-led-matrix](https://github.com/hzeller/rpi-rgb-led-matrix)
|
||||
* [libconfig](http://www.hyperrealm.com/libconfig/)
|
||||
|
||||
Framebuffer capture code based on information from the [rpi-fbcp](https://github.com/tasanakorn/rpi-fbcp) tool.
|
||||
|
||||
## License
|
||||
|
||||
Released under the GPL v2.0 license, see LICENSE.txt for details.
|
||||
|
|
|
|||
107
display-test.cpp
Normal file
107
display-test.cpp
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
// Program to aid in the testing of LED matrix chains.
|
||||
// Author: Tony DiCola
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <led-matrix.h>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "Config.h"
|
||||
#include "glcdfont.h"
|
||||
#include "GridTransformer.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace rgb_matrix;
|
||||
|
||||
|
||||
bool running = true; // Global to keep track of if the program should run.
|
||||
// Will be set false by a SIGINT handler when ctrl-c is
|
||||
// pressed, then the main loop will cleanly exit.
|
||||
|
||||
|
||||
void printCanvas(Canvas& canvas, int x, int y, const string& message,
|
||||
int r = 255, int g = 255, int b = 255) {
|
||||
// Loop through all the characters and print them starting at the provided
|
||||
// coordinates.
|
||||
for (auto c: message) {
|
||||
// Loop through each column of the character.
|
||||
for (int i=0; i<5; ++i) {
|
||||
unsigned char col = glcdfont[c*5+i];
|
||||
x += 1;
|
||||
// Loop through each row of the column.
|
||||
for (int j=0; j<8; ++j) {
|
||||
// Put a pixel for each 1 in the column byte.
|
||||
if ((col >> j) & 0x01) {
|
||||
canvas.SetPixel(x, y+j, r, g, b);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Add a column of padding between characters.
|
||||
x += 1;
|
||||
}
|
||||
}
|
||||
|
||||
void sigintHandler(int s) {
|
||||
running = false;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
try
|
||||
{
|
||||
// Expect one command line parameter with display configuration filename.
|
||||
if (argc != 2) {
|
||||
throw runtime_error("Expected configuration file name as only command line parameter!\r\nUsage: display-test /path/to/display/config.cfg");
|
||||
}
|
||||
|
||||
// Load the configuration.
|
||||
Config config(argv[1]);
|
||||
cout << "Using config values: " << endl
|
||||
<< " display_width: " << config.getDisplayWidth() << endl
|
||||
<< " display_height: " << config.getDisplayHeight() << endl
|
||||
<< " panel_width: " << config.getPanelWidth() << endl
|
||||
<< " panel_height: " << config.getPanelHeight() << endl
|
||||
<< " chain_length: " << config.getChainLength() << endl
|
||||
<< " parallel_count: " << config.getParallelCount() << endl;
|
||||
|
||||
// Initialize matrix library.
|
||||
GPIO io;
|
||||
if (!io.Init()) {
|
||||
throw runtime_error("Failed to initialize rpi-led-matrix library! Make sure to run as root with sudo.");
|
||||
}
|
||||
|
||||
// Create canvas and apply GridTransformer.
|
||||
RGBMatrix canvas(&io, config.getPanelHeight(), config.getChainLength(),
|
||||
config.getParallelCount());
|
||||
GridTransformer grid = config.getGridTransformer();
|
||||
canvas.SetTransformer(&grid);
|
||||
|
||||
// Clear the canvas, then draw on each panel.
|
||||
canvas.Fill(0, 0, 0);
|
||||
for (int i=0; i<grid.getRows(); ++i) {
|
||||
for (int j=0; j<grid.getColumns(); ++j) {
|
||||
// Compute panel origin position.
|
||||
int x = i*config.getPanelWidth();
|
||||
int y = j*config.getPanelHeight();
|
||||
// Print the current grid position to the top left (origin) of the panel.
|
||||
stringstream pos;
|
||||
pos << i << "," << j;
|
||||
printCanvas(canvas, x, y, pos.str());
|
||||
}
|
||||
}
|
||||
// Loop forever waiting for Ctrl-C signal to quit.
|
||||
signal(SIGINT, sigintHandler);
|
||||
cout << "Press Ctrl-C to quit..." << endl;
|
||||
while (running) {
|
||||
sleep(1);
|
||||
}
|
||||
canvas.Clear();
|
||||
}
|
||||
catch (const exception& ex) {
|
||||
cerr << ex.what() << endl;
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
259
glcdfont.c
Normal file
259
glcdfont.c
Normal file
|
|
@ -0,0 +1,259 @@
|
|||
// Standard ASCII 5x7 font
|
||||
const unsigned char glcdfont[] = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x3E, 0x5B, 0x4F, 0x5B, 0x3E,
|
||||
0x3E, 0x6B, 0x4F, 0x6B, 0x3E,
|
||||
0x1C, 0x3E, 0x7C, 0x3E, 0x1C,
|
||||
0x18, 0x3C, 0x7E, 0x3C, 0x18,
|
||||
0x1C, 0x57, 0x7D, 0x57, 0x1C,
|
||||
0x1C, 0x5E, 0x7F, 0x5E, 0x1C,
|
||||
0x00, 0x18, 0x3C, 0x18, 0x00,
|
||||
0xFF, 0xE7, 0xC3, 0xE7, 0xFF,
|
||||
0x00, 0x18, 0x24, 0x18, 0x00,
|
||||
0xFF, 0xE7, 0xDB, 0xE7, 0xFF,
|
||||
0x30, 0x48, 0x3A, 0x06, 0x0E,
|
||||
0x26, 0x29, 0x79, 0x29, 0x26,
|
||||
0x40, 0x7F, 0x05, 0x05, 0x07,
|
||||
0x40, 0x7F, 0x05, 0x25, 0x3F,
|
||||
0x5A, 0x3C, 0xE7, 0x3C, 0x5A,
|
||||
0x7F, 0x3E, 0x1C, 0x1C, 0x08,
|
||||
0x08, 0x1C, 0x1C, 0x3E, 0x7F,
|
||||
0x14, 0x22, 0x7F, 0x22, 0x14,
|
||||
0x5F, 0x5F, 0x00, 0x5F, 0x5F,
|
||||
0x06, 0x09, 0x7F, 0x01, 0x7F,
|
||||
0x00, 0x66, 0x89, 0x95, 0x6A,
|
||||
0x60, 0x60, 0x60, 0x60, 0x60,
|
||||
0x94, 0xA2, 0xFF, 0xA2, 0x94,
|
||||
0x08, 0x04, 0x7E, 0x04, 0x08,
|
||||
0x10, 0x20, 0x7E, 0x20, 0x10,
|
||||
0x08, 0x08, 0x2A, 0x1C, 0x08,
|
||||
0x08, 0x1C, 0x2A, 0x08, 0x08,
|
||||
0x1E, 0x10, 0x10, 0x10, 0x10,
|
||||
0x0C, 0x1E, 0x0C, 0x1E, 0x0C,
|
||||
0x30, 0x38, 0x3E, 0x38, 0x30,
|
||||
0x06, 0x0E, 0x3E, 0x0E, 0x06,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x5F, 0x00, 0x00,
|
||||
0x00, 0x07, 0x00, 0x07, 0x00,
|
||||
0x14, 0x7F, 0x14, 0x7F, 0x14,
|
||||
0x24, 0x2A, 0x7F, 0x2A, 0x12,
|
||||
0x23, 0x13, 0x08, 0x64, 0x62,
|
||||
0x36, 0x49, 0x56, 0x20, 0x50,
|
||||
0x00, 0x08, 0x07, 0x03, 0x00,
|
||||
0x00, 0x1C, 0x22, 0x41, 0x00,
|
||||
0x00, 0x41, 0x22, 0x1C, 0x00,
|
||||
0x2A, 0x1C, 0x7F, 0x1C, 0x2A,
|
||||
0x08, 0x08, 0x3E, 0x08, 0x08,
|
||||
0x00, 0x80, 0x70, 0x30, 0x00,
|
||||
0x08, 0x08, 0x08, 0x08, 0x08,
|
||||
0x00, 0x00, 0x60, 0x60, 0x00,
|
||||
0x20, 0x10, 0x08, 0x04, 0x02,
|
||||
0x3E, 0x51, 0x49, 0x45, 0x3E,
|
||||
0x00, 0x42, 0x7F, 0x40, 0x00,
|
||||
0x72, 0x49, 0x49, 0x49, 0x46,
|
||||
0x21, 0x41, 0x49, 0x4D, 0x33,
|
||||
0x18, 0x14, 0x12, 0x7F, 0x10,
|
||||
0x27, 0x45, 0x45, 0x45, 0x39,
|
||||
0x3C, 0x4A, 0x49, 0x49, 0x31,
|
||||
0x41, 0x21, 0x11, 0x09, 0x07,
|
||||
0x36, 0x49, 0x49, 0x49, 0x36,
|
||||
0x46, 0x49, 0x49, 0x29, 0x1E,
|
||||
0x00, 0x00, 0x14, 0x00, 0x00,
|
||||
0x00, 0x40, 0x34, 0x00, 0x00,
|
||||
0x00, 0x08, 0x14, 0x22, 0x41,
|
||||
0x14, 0x14, 0x14, 0x14, 0x14,
|
||||
0x00, 0x41, 0x22, 0x14, 0x08,
|
||||
0x02, 0x01, 0x59, 0x09, 0x06,
|
||||
0x3E, 0x41, 0x5D, 0x59, 0x4E,
|
||||
0x7C, 0x12, 0x11, 0x12, 0x7C,
|
||||
0x7F, 0x49, 0x49, 0x49, 0x36,
|
||||
0x3E, 0x41, 0x41, 0x41, 0x22,
|
||||
0x7F, 0x41, 0x41, 0x41, 0x3E,
|
||||
0x7F, 0x49, 0x49, 0x49, 0x41,
|
||||
0x7F, 0x09, 0x09, 0x09, 0x01,
|
||||
0x3E, 0x41, 0x41, 0x51, 0x73,
|
||||
0x7F, 0x08, 0x08, 0x08, 0x7F,
|
||||
0x00, 0x41, 0x7F, 0x41, 0x00,
|
||||
0x20, 0x40, 0x41, 0x3F, 0x01,
|
||||
0x7F, 0x08, 0x14, 0x22, 0x41,
|
||||
0x7F, 0x40, 0x40, 0x40, 0x40,
|
||||
0x7F, 0x02, 0x1C, 0x02, 0x7F,
|
||||
0x7F, 0x04, 0x08, 0x10, 0x7F,
|
||||
0x3E, 0x41, 0x41, 0x41, 0x3E,
|
||||
0x7F, 0x09, 0x09, 0x09, 0x06,
|
||||
0x3E, 0x41, 0x51, 0x21, 0x5E,
|
||||
0x7F, 0x09, 0x19, 0x29, 0x46,
|
||||
0x26, 0x49, 0x49, 0x49, 0x32,
|
||||
0x03, 0x01, 0x7F, 0x01, 0x03,
|
||||
0x3F, 0x40, 0x40, 0x40, 0x3F,
|
||||
0x1F, 0x20, 0x40, 0x20, 0x1F,
|
||||
0x3F, 0x40, 0x38, 0x40, 0x3F,
|
||||
0x63, 0x14, 0x08, 0x14, 0x63,
|
||||
0x03, 0x04, 0x78, 0x04, 0x03,
|
||||
0x61, 0x59, 0x49, 0x4D, 0x43,
|
||||
0x00, 0x7F, 0x41, 0x41, 0x41,
|
||||
0x02, 0x04, 0x08, 0x10, 0x20,
|
||||
0x00, 0x41, 0x41, 0x41, 0x7F,
|
||||
0x04, 0x02, 0x01, 0x02, 0x04,
|
||||
0x40, 0x40, 0x40, 0x40, 0x40,
|
||||
0x00, 0x03, 0x07, 0x08, 0x00,
|
||||
0x20, 0x54, 0x54, 0x78, 0x40,
|
||||
0x7F, 0x28, 0x44, 0x44, 0x38,
|
||||
0x38, 0x44, 0x44, 0x44, 0x28,
|
||||
0x38, 0x44, 0x44, 0x28, 0x7F,
|
||||
0x38, 0x54, 0x54, 0x54, 0x18,
|
||||
0x00, 0x08, 0x7E, 0x09, 0x02,
|
||||
0x18, 0xA4, 0xA4, 0x9C, 0x78,
|
||||
0x7F, 0x08, 0x04, 0x04, 0x78,
|
||||
0x00, 0x44, 0x7D, 0x40, 0x00,
|
||||
0x20, 0x40, 0x40, 0x3D, 0x00,
|
||||
0x7F, 0x10, 0x28, 0x44, 0x00,
|
||||
0x00, 0x41, 0x7F, 0x40, 0x00,
|
||||
0x7C, 0x04, 0x78, 0x04, 0x78,
|
||||
0x7C, 0x08, 0x04, 0x04, 0x78,
|
||||
0x38, 0x44, 0x44, 0x44, 0x38,
|
||||
0xFC, 0x18, 0x24, 0x24, 0x18,
|
||||
0x18, 0x24, 0x24, 0x18, 0xFC,
|
||||
0x7C, 0x08, 0x04, 0x04, 0x08,
|
||||
0x48, 0x54, 0x54, 0x54, 0x24,
|
||||
0x04, 0x04, 0x3F, 0x44, 0x24,
|
||||
0x3C, 0x40, 0x40, 0x20, 0x7C,
|
||||
0x1C, 0x20, 0x40, 0x20, 0x1C,
|
||||
0x3C, 0x40, 0x30, 0x40, 0x3C,
|
||||
0x44, 0x28, 0x10, 0x28, 0x44,
|
||||
0x4C, 0x90, 0x90, 0x90, 0x7C,
|
||||
0x44, 0x64, 0x54, 0x4C, 0x44,
|
||||
0x00, 0x08, 0x36, 0x41, 0x00,
|
||||
0x00, 0x00, 0x77, 0x00, 0x00,
|
||||
0x00, 0x41, 0x36, 0x08, 0x00,
|
||||
0x02, 0x01, 0x02, 0x04, 0x02,
|
||||
0x3C, 0x26, 0x23, 0x26, 0x3C,
|
||||
0x1E, 0xA1, 0xA1, 0x61, 0x12,
|
||||
0x3A, 0x40, 0x40, 0x20, 0x7A,
|
||||
0x38, 0x54, 0x54, 0x55, 0x59,
|
||||
0x21, 0x55, 0x55, 0x79, 0x41,
|
||||
0x22, 0x54, 0x54, 0x78, 0x42, // a-umlaut
|
||||
0x21, 0x55, 0x54, 0x78, 0x40,
|
||||
0x20, 0x54, 0x55, 0x79, 0x40,
|
||||
0x0C, 0x1E, 0x52, 0x72, 0x12,
|
||||
0x39, 0x55, 0x55, 0x55, 0x59,
|
||||
0x39, 0x54, 0x54, 0x54, 0x59,
|
||||
0x39, 0x55, 0x54, 0x54, 0x58,
|
||||
0x00, 0x00, 0x45, 0x7C, 0x41,
|
||||
0x00, 0x02, 0x45, 0x7D, 0x42,
|
||||
0x00, 0x01, 0x45, 0x7C, 0x40,
|
||||
0x7D, 0x12, 0x11, 0x12, 0x7D, // A-umlaut
|
||||
0xF0, 0x28, 0x25, 0x28, 0xF0,
|
||||
0x7C, 0x54, 0x55, 0x45, 0x00,
|
||||
0x20, 0x54, 0x54, 0x7C, 0x54,
|
||||
0x7C, 0x0A, 0x09, 0x7F, 0x49,
|
||||
0x32, 0x49, 0x49, 0x49, 0x32,
|
||||
0x3A, 0x44, 0x44, 0x44, 0x3A, // o-umlaut
|
||||
0x32, 0x4A, 0x48, 0x48, 0x30,
|
||||
0x3A, 0x41, 0x41, 0x21, 0x7A,
|
||||
0x3A, 0x42, 0x40, 0x20, 0x78,
|
||||
0x00, 0x9D, 0xA0, 0xA0, 0x7D,
|
||||
0x3D, 0x42, 0x42, 0x42, 0x3D, // O-umlaut
|
||||
0x3D, 0x40, 0x40, 0x40, 0x3D,
|
||||
0x3C, 0x24, 0xFF, 0x24, 0x24,
|
||||
0x48, 0x7E, 0x49, 0x43, 0x66,
|
||||
0x2B, 0x2F, 0xFC, 0x2F, 0x2B,
|
||||
0xFF, 0x09, 0x29, 0xF6, 0x20,
|
||||
0xC0, 0x88, 0x7E, 0x09, 0x03,
|
||||
0x20, 0x54, 0x54, 0x79, 0x41,
|
||||
0x00, 0x00, 0x44, 0x7D, 0x41,
|
||||
0x30, 0x48, 0x48, 0x4A, 0x32,
|
||||
0x38, 0x40, 0x40, 0x22, 0x7A,
|
||||
0x00, 0x7A, 0x0A, 0x0A, 0x72,
|
||||
0x7D, 0x0D, 0x19, 0x31, 0x7D,
|
||||
0x26, 0x29, 0x29, 0x2F, 0x28,
|
||||
0x26, 0x29, 0x29, 0x29, 0x26,
|
||||
0x30, 0x48, 0x4D, 0x40, 0x20,
|
||||
0x38, 0x08, 0x08, 0x08, 0x08,
|
||||
0x08, 0x08, 0x08, 0x08, 0x38,
|
||||
0x2F, 0x10, 0xC8, 0xAC, 0xBA,
|
||||
0x2F, 0x10, 0x28, 0x34, 0xFA,
|
||||
0x00, 0x00, 0x7B, 0x00, 0x00,
|
||||
0x08, 0x14, 0x2A, 0x14, 0x22,
|
||||
0x22, 0x14, 0x2A, 0x14, 0x08,
|
||||
0x55, 0x00, 0x55, 0x00, 0x55, // #176 (25% block) missing in old code
|
||||
0xAA, 0x55, 0xAA, 0x55, 0xAA, // 50% block
|
||||
0xFF, 0x55, 0xFF, 0x55, 0xFF, // 75% block
|
||||
0x00, 0x00, 0x00, 0xFF, 0x00,
|
||||
0x10, 0x10, 0x10, 0xFF, 0x00,
|
||||
0x14, 0x14, 0x14, 0xFF, 0x00,
|
||||
0x10, 0x10, 0xFF, 0x00, 0xFF,
|
||||
0x10, 0x10, 0xF0, 0x10, 0xF0,
|
||||
0x14, 0x14, 0x14, 0xFC, 0x00,
|
||||
0x14, 0x14, 0xF7, 0x00, 0xFF,
|
||||
0x00, 0x00, 0xFF, 0x00, 0xFF,
|
||||
0x14, 0x14, 0xF4, 0x04, 0xFC,
|
||||
0x14, 0x14, 0x17, 0x10, 0x1F,
|
||||
0x10, 0x10, 0x1F, 0x10, 0x1F,
|
||||
0x14, 0x14, 0x14, 0x1F, 0x00,
|
||||
0x10, 0x10, 0x10, 0xF0, 0x00,
|
||||
0x00, 0x00, 0x00, 0x1F, 0x10,
|
||||
0x10, 0x10, 0x10, 0x1F, 0x10,
|
||||
0x10, 0x10, 0x10, 0xF0, 0x10,
|
||||
0x00, 0x00, 0x00, 0xFF, 0x10,
|
||||
0x10, 0x10, 0x10, 0x10, 0x10,
|
||||
0x10, 0x10, 0x10, 0xFF, 0x10,
|
||||
0x00, 0x00, 0x00, 0xFF, 0x14,
|
||||
0x00, 0x00, 0xFF, 0x00, 0xFF,
|
||||
0x00, 0x00, 0x1F, 0x10, 0x17,
|
||||
0x00, 0x00, 0xFC, 0x04, 0xF4,
|
||||
0x14, 0x14, 0x17, 0x10, 0x17,
|
||||
0x14, 0x14, 0xF4, 0x04, 0xF4,
|
||||
0x00, 0x00, 0xFF, 0x00, 0xF7,
|
||||
0x14, 0x14, 0x14, 0x14, 0x14,
|
||||
0x14, 0x14, 0xF7, 0x00, 0xF7,
|
||||
0x14, 0x14, 0x14, 0x17, 0x14,
|
||||
0x10, 0x10, 0x1F, 0x10, 0x1F,
|
||||
0x14, 0x14, 0x14, 0xF4, 0x14,
|
||||
0x10, 0x10, 0xF0, 0x10, 0xF0,
|
||||
0x00, 0x00, 0x1F, 0x10, 0x1F,
|
||||
0x00, 0x00, 0x00, 0x1F, 0x14,
|
||||
0x00, 0x00, 0x00, 0xFC, 0x14,
|
||||
0x00, 0x00, 0xF0, 0x10, 0xF0,
|
||||
0x10, 0x10, 0xFF, 0x10, 0xFF,
|
||||
0x14, 0x14, 0x14, 0xFF, 0x14,
|
||||
0x10, 0x10, 0x10, 0x1F, 0x00,
|
||||
0x00, 0x00, 0x00, 0xF0, 0x10,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xF0, 0xF0, 0xF0, 0xF0, 0xF0,
|
||||
0xFF, 0xFF, 0xFF, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0xFF, 0xFF,
|
||||
0x0F, 0x0F, 0x0F, 0x0F, 0x0F,
|
||||
0x38, 0x44, 0x44, 0x38, 0x44,
|
||||
0xFC, 0x4A, 0x4A, 0x4A, 0x34, // sharp-s or beta
|
||||
0x7E, 0x02, 0x02, 0x06, 0x06,
|
||||
0x02, 0x7E, 0x02, 0x7E, 0x02,
|
||||
0x63, 0x55, 0x49, 0x41, 0x63,
|
||||
0x38, 0x44, 0x44, 0x3C, 0x04,
|
||||
0x40, 0x7E, 0x20, 0x1E, 0x20,
|
||||
0x06, 0x02, 0x7E, 0x02, 0x02,
|
||||
0x99, 0xA5, 0xE7, 0xA5, 0x99,
|
||||
0x1C, 0x2A, 0x49, 0x2A, 0x1C,
|
||||
0x4C, 0x72, 0x01, 0x72, 0x4C,
|
||||
0x30, 0x4A, 0x4D, 0x4D, 0x30,
|
||||
0x30, 0x48, 0x78, 0x48, 0x30,
|
||||
0xBC, 0x62, 0x5A, 0x46, 0x3D,
|
||||
0x3E, 0x49, 0x49, 0x49, 0x00,
|
||||
0x7E, 0x01, 0x01, 0x01, 0x7E,
|
||||
0x2A, 0x2A, 0x2A, 0x2A, 0x2A,
|
||||
0x44, 0x44, 0x5F, 0x44, 0x44,
|
||||
0x40, 0x51, 0x4A, 0x44, 0x40,
|
||||
0x40, 0x44, 0x4A, 0x51, 0x40,
|
||||
0x00, 0x00, 0xFF, 0x01, 0x03,
|
||||
0xE0, 0x80, 0xFF, 0x00, 0x00,
|
||||
0x08, 0x08, 0x6B, 0x6B, 0x08,
|
||||
0x36, 0x12, 0x36, 0x24, 0x36,
|
||||
0x06, 0x0F, 0x09, 0x0F, 0x06,
|
||||
0x00, 0x00, 0x18, 0x18, 0x00,
|
||||
0x00, 0x00, 0x10, 0x10, 0x00,
|
||||
0x30, 0x40, 0xFF, 0x01, 0x01,
|
||||
0x00, 0x1F, 0x01, 0x01, 0x1E,
|
||||
0x00, 0x19, 0x1D, 0x17, 0x12,
|
||||
0x00, 0x3C, 0x3C, 0x3C, 0x3C,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00 // #255 NBSP
|
||||
};
|
||||
6
glcdfont.h
Normal file
6
glcdfont.h
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
#ifndef GLCDFONT_H
|
||||
#define GLCDFONT_H
|
||||
|
||||
extern const unsigned char glcdfont[];
|
||||
|
||||
#endif
|
||||
63
matrix.cfg
Normal file
63
matrix.cfg
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
// LED Matrix Display Configuration
|
||||
|
||||
// Define the entire width and height of the display in pixels.
|
||||
// This is the _total_ width and height of the rectangle defined by all the
|
||||
// chained panels. The width should be a multiple of the panel pixel width (32),
|
||||
// and the height should be a multiple of the panel pixel height (8, 16, or 32).
|
||||
display_width = 64;
|
||||
display_height = 64;
|
||||
|
||||
// Define the width of each panel in pixels. This should always be 32 (but can
|
||||
// in theory be changed).
|
||||
panel_width = 32;
|
||||
|
||||
// Define the height of each panel in pixels. This is typically 8, 16, or 32.
|
||||
// NOTE: Each panel in the display _must_ be the same height! You cannot mix
|
||||
// 16 and 32 pixel high panels for example.
|
||||
panel_height = 32;
|
||||
|
||||
// Define the total number of panels in each chain. Count up however many
|
||||
// panels are connected together and put that value here. If you're using
|
||||
// multiple parallel chains count each one up separately and pick the largest
|
||||
// value for this configuration.
|
||||
chain_length = 4;
|
||||
|
||||
// Define the total number of parallel chains. If using the Adafruit HAT you
|
||||
// can only have one chain so stick with the value 1. The Pi 2 can support up
|
||||
// to 3 parallel chains, see the rpi-rgb-led-matrix library for more information:
|
||||
// https://github.com/hzeller/rpi-rgb-led-matrix#chaining-parallel-chains-and-coordinate-system
|
||||
parallel_count = 1;
|
||||
|
||||
// Configure each LED matrix panel.
|
||||
// This is a two-dimensional array with an entry for each panel. The array
|
||||
// defines the grid that will subdivide the display, so for example a 64x64 size
|
||||
// display with 32x32 pixel panels would be a 2x2 array of panel configurations.
|
||||
//
|
||||
// For each panel you must set the order that it is within its chain, i.e. the
|
||||
// first panel in a chain is order = 0, the next one is order = 1, etc. You can
|
||||
// also set a rotation for each panel to account for changes in panel orientation
|
||||
// (like when 'snaking' a series of panels end to end for shorter wire runs).
|
||||
//
|
||||
// For example the configuration below defines this grid display of panels and
|
||||
// their wiring (starting from the upper right panel and snaking left, down, and
|
||||
// right to the bottom right panel):
|
||||
// ______________ ______________
|
||||
// | Panel | | Panel |
|
||||
// /==| order = 1 |<=| order = 0 |<= Chain start (from Pi)
|
||||
// | | rotate = 0 | | rotate = 0 |
|
||||
// | |______________| |______________|
|
||||
// | ______________ ______________
|
||||
// | | Panel | | Panel |
|
||||
// \==| order = 2 |=>| order = 3 |
|
||||
// | rotate = 180 | | rotate = 180 |
|
||||
// |______________| |______________|
|
||||
//
|
||||
// Notice the chain starts in the upper right and snakes around to the bottom
|
||||
// right. The order of each panel is set as its position along the chain,
|
||||
// and rotation is applied to the lower panels that are flipped around relative
|
||||
// to the panels above them.
|
||||
//
|
||||
panels = (
|
||||
( { order = 1; rotate = 0; }, { order = 0; rotate = 0; } ),
|
||||
( { order = 2; rotate = 180; }, { order = 3; rotate = 180; } )
|
||||
)
|
||||
167
rpi-fb-matrix.cpp
Normal file
167
rpi-fb-matrix.cpp
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
// Program to copy the contents of the Raspberry Pi primary display to LED matrices.
|
||||
// Author: Tony DiCola
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <bcm_host.h>
|
||||
#include <fcntl.h>
|
||||
#include <led-matrix.h>
|
||||
#include <linux/fb.h>
|
||||
#include <signal.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "Config.h"
|
||||
#include "GridTransformer.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace rgb_matrix;
|
||||
|
||||
|
||||
bool running = true; // Global to keep track of if the program should run.
|
||||
// Will be set false by a SIGINT handler when ctrl-c is
|
||||
// pressed, then the main loop will cleanly exit.
|
||||
|
||||
|
||||
// Class to encapsulate all the logic for capturing an image of the Pi's primary
|
||||
// display. Manages all the BCM GPU and CPU resources automatically while in scope.
|
||||
class BCMDisplayCapture {
|
||||
public:
|
||||
BCMDisplayCapture(int width, int height):
|
||||
_width(width),
|
||||
_height(height),
|
||||
_display(0),
|
||||
_screen_resource(0),
|
||||
_screen_data(NULL)
|
||||
{
|
||||
// Get information about primary/HDMI display.
|
||||
_display = vc_dispmanx_display_open(0);
|
||||
if (!_display) {
|
||||
throw runtime_error("Unable to open primary display!");
|
||||
}
|
||||
DISPMANX_MODEINFO_T display_info;
|
||||
if (vc_dispmanx_display_get_info(_display, &display_info)) {
|
||||
throw runtime_error("Unable to get primary display information!");
|
||||
}
|
||||
cout << "Primary display:" << endl
|
||||
<< " resolution: " << display_info.width << "x" << display_info.height << endl
|
||||
<< " format: " << display_info.input_format << endl;
|
||||
// Create a GPU image surface to hold the captured screen.
|
||||
uint32_t image_prt;
|
||||
_screen_resource = vc_dispmanx_resource_create(VC_IMAGE_RGB888, width, height, &image_prt);
|
||||
if (!_screen_resource) {
|
||||
throw runtime_error("Unable to create screen surface!");
|
||||
}
|
||||
// Create a rectangular region of the captured screen size.
|
||||
vc_dispmanx_rect_set(&_rect, 0, 0, _width, _height);
|
||||
// Allocate CPU memory for copying out the captured screen. Must be aligned
|
||||
// to a larger size because of GPU surface memory size constraints.
|
||||
_pitch = ALIGN_UP(_width*3, 32);
|
||||
_screen_data = new uint8_t[_pitch*_height];
|
||||
}
|
||||
|
||||
void capture() {
|
||||
// Capture the primary display and copy it from GPU to CPU memory.
|
||||
vc_dispmanx_snapshot(_display, _screen_resource, (DISPMANX_TRANSFORM_T)0);
|
||||
vc_dispmanx_resource_read_data(_screen_resource, &_rect, _screen_data, _pitch);
|
||||
}
|
||||
|
||||
void getPixel(int x, int y, uint8_t* r, uint8_t* g, uint8_t* b) {
|
||||
// Grab the requested pixel from the last captured display image.
|
||||
uint8_t* row = _screen_data + (y*_pitch);
|
||||
*r = row[x*3];
|
||||
*g = row[x*3+1];
|
||||
*b = row[x*3+2];
|
||||
}
|
||||
|
||||
~BCMDisplayCapture() {
|
||||
// Clean up BCM and other resources.
|
||||
if (_screen_resource != 0) {
|
||||
vc_dispmanx_resource_delete(_screen_resource);
|
||||
}
|
||||
if (_display != 0) {
|
||||
vc_dispmanx_display_close(_display);
|
||||
}
|
||||
if (_screen_data != NULL) {
|
||||
delete[] _screen_data;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
int _width,
|
||||
_height,
|
||||
_pitch;
|
||||
DISPMANX_DISPLAY_HANDLE_T _display;
|
||||
DISPMANX_RESOURCE_HANDLE_T _screen_resource;
|
||||
VC_RECT_T _rect;
|
||||
uint8_t* _screen_data;
|
||||
};
|
||||
|
||||
|
||||
void sigintHandler(int s) {
|
||||
running = false;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
try
|
||||
{
|
||||
// Expect one command line parameter with display configuration filename.
|
||||
if (argc != 2) {
|
||||
throw runtime_error("Expected configuration file name as only command line parameter!\r\nUsage: rpi-fb-matrix /path/to/display/config.cfg");
|
||||
}
|
||||
|
||||
// Load the configuration.
|
||||
Config config(argv[1]);
|
||||
cout << "Using config values: " << endl
|
||||
<< " display_width: " << config.getDisplayWidth() << endl
|
||||
<< " display_height: " << config.getDisplayHeight() << endl
|
||||
<< " panel_width: " << config.getPanelWidth() << endl
|
||||
<< " panel_height: " << config.getPanelHeight() << endl
|
||||
<< " chain_length: " << config.getChainLength() << endl
|
||||
<< " parallel_count: " << config.getParallelCount() << endl;
|
||||
|
||||
// Initialize matrix library.
|
||||
GPIO io;
|
||||
if (!io.Init()) {
|
||||
throw runtime_error("Failed to initialize rpi-led-matrix library! Make sure to run as root with sudo.");
|
||||
}
|
||||
|
||||
// Create canvas and apply GridTransformer.
|
||||
RGBMatrix canvas(&io, config.getPanelHeight(), config.getChainLength(),
|
||||
config.getParallelCount());
|
||||
GridTransformer grid = config.getGridTransformer();
|
||||
canvas.SetTransformer(&grid);
|
||||
canvas.Clear();
|
||||
|
||||
// Initialize BCM functions and display capture class.
|
||||
bcm_host_init();
|
||||
BCMDisplayCapture displayCapture(config.getDisplayWidth(),
|
||||
config.getDisplayHeight());
|
||||
|
||||
// Loop forever waiting for Ctrl-C signal to quit.
|
||||
signal(SIGINT, sigintHandler);
|
||||
cout << "Press Ctrl-C to quit..." << endl;
|
||||
while (running) {
|
||||
// Capture the current display image.
|
||||
displayCapture.capture();
|
||||
// Loop through the frame data and set the pixels on the matrix canvas.
|
||||
for (int y=0; y<config.getDisplayHeight(); ++y) {
|
||||
for (int x=0; x<config.getDisplayWidth(); ++x) {
|
||||
uint8_t red, green, blue;
|
||||
displayCapture.getPixel(x, y, &red, &green, &blue);
|
||||
canvas.SetPixel(x, y, red, green, blue);
|
||||
}
|
||||
}
|
||||
// Sleep for 25 milliseconds.
|
||||
usleep(25 * 1000);
|
||||
}
|
||||
canvas.Clear();
|
||||
}
|
||||
catch (const exception& ex) {
|
||||
cerr << ex.what() << endl;
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
1
rpi-rgb-led-matrix
Submodule
1
rpi-rgb-led-matrix
Submodule
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 5cdf1b4954aed4ea8b9d7fb57f3d1f410201408d
|
||||
Loading…
Reference in a new issue