From 7160a8ade3dcecabe25ce0de73232b365f7587be Mon Sep 17 00:00:00 2001 From: Henner Zeller Date: Mon, 2 Sep 2019 19:44:05 -0700 Subject: [PATCH] Add initialization for FM6126A panels. Use --led-panel-type=FM6126A This is mostly experimental at this point (I only have a single panel to test, thanks @esden), so please let me know if this works for you. It is modelled after the Python script that was shared on #746. Right now, this is not abstracted yet for potential other panel types, this will be added as we go. (This works in C++ now. If someone needs this using Python, please update the Python bindings and send a pull request for the new option). Fixes #746 #807 --- README.md | 14 +++++++++++ include/led-matrix-c.h | 6 +++++ include/led-matrix.h | 4 +++ lib/framebuffer-internal.h | 1 + lib/framebuffer.cc | 51 ++++++++++++++++++++++++++++++++++++++ lib/led-matrix-c.cc | 14 ++++++----- lib/led-matrix.cc | 5 +++- lib/options-initialize.cc | 6 ++++- 8 files changed, 93 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 9f8315059..6cca3031e 100644 --- a/README.md +++ b/README.md @@ -80,6 +80,10 @@ faster refresh-rate for the same size, but do some multiplexing internally of which there are a few types out there; they can be chosen with the `--led-multiplexing` parameter. +There are some panels that have a different chip-set than the default HUB75. +These require some initialization sequence. The current supported one is +`--led-panel-type=FM6126A`. + Generally, the higher scan-rate (e.g. 1:8), a.k.a. outdoor panels generally allow faster refresh rate, but you might need to figure out the multiplexing mapping if one of the three provided does not work. @@ -185,6 +189,16 @@ This illustrates what each of these parameters mean: +##### Panel Type + +Typically, panels should just work out of the box, but some panels use a +different chip-set that requires some initialization. If you don't see any +output on your panel, try setting: + +``` +--led-panel-type=FM6126A +``` + ##### Multiplexing If you have some 'outdoor' panels or panels with different multiplexing, the following will be useful: diff --git a/include/led-matrix-c.h b/include/led-matrix-c.h index 081d22d20..1335e0ae9 100644 --- a/include/led-matrix-c.h +++ b/include/led-matrix-c.h @@ -131,6 +131,12 @@ struct RGBLedMatrixOptions { */ const char *pixel_mapper_config; /* Corresponding flag: --led-pixel-mapper */ + /* + * Panel type. Typically just NULL, but certain panels (AM6126) require + * an initialization sequence + */ + const char *panel_type; /* Corresponding flag: --led-panel-type */ + /** The following are boolean flags, all off by default **/ /* Allow to use the hardware subsystem to create pulses. This won't do diff --git a/include/led-matrix.h b/include/led-matrix.h index c94013cdf..9fb2132a6 100644 --- a/include/led-matrix.h +++ b/include/led-matrix.h @@ -144,6 +144,10 @@ class RGBMatrix : public Canvas { // to this matrix. A semicolon-separated list of pixel-mappers with optional // parameter. const char *pixel_mapper_config; // Flag: --led-pixel-mapper + + // Panel type. Typically an empty string or NULL, but some panels need + // a particular initialization sequence, so this is used for that. + const char *panel_type; // Flag: --led-panel-type }; // Create an RGBMatrix. diff --git a/lib/framebuffer-internal.h b/lib/framebuffer-internal.h index 15d6f977f..9a68ed6d4 100644 --- a/lib/framebuffer-internal.h +++ b/lib/framebuffer-internal.h @@ -78,6 +78,7 @@ class Framebuffer { int pwm_lsb_nanoseconds, int dither_bits, int row_address_type); + static void InitializePanels(GPIO *io, const char *panel_type, int columns); // Set PWM bits used for output. Default is 11, but if you only deal with // simple comic-colors, 1 might be sufficient. Lower require less CPU. diff --git a/lib/framebuffer.cc b/lib/framebuffer.cc index 465bd79e3..a878aa48a 100644 --- a/lib/framebuffer.cc +++ b/lib/framebuffer.cc @@ -352,6 +352,57 @@ Framebuffer::~Framebuffer() { bitplane_timings); } +// NOTE: first version for panel initialization sequence, need to refine +// until it is more clear how different panel types are initialized to be +// able to abstract this more. + +static void InitFM6126(GPIO *io, const struct HardwareMapping &h, int columns) { + const uint32_t bits_on + = h.p0_r1 | h.p0_g1 | h.p0_b1 | h.p0_r2 | h.p0_g2 | h.p0_b2 + | h.p1_r1 | h.p1_g1 | h.p1_b1 | h.p1_r2 | h.p1_g2 | h.p1_b2 + | h.p2_r1 | h.p2_g1 | h.p2_b1 | h.p2_r2 | h.p2_g2 | h.p2_b2 + | h.a; // Address bit 'A' is always on. + const uint32_t bits_off = h.a; + + // Init bits. TODO: customize, as we can do things such as brightness here, + // which would allow more higher quality output. + static const char* init_b12 = "0111111111111111"; // full bright + static const char* init_b13 = "0000000001000000"; // panel on. + + io->ClearBits(h.clock | h.strobe); + + for (int i = 0; i < columns; ++i) { + uint32_t value = init_b12[i % 16] == '0' ? bits_off : bits_on; + if (i > columns - 12) value |= h.strobe; + io->Write(value); + io->SetBits(h.clock); + io->ClearBits(h.clock); + } + io->ClearBits(h.strobe); + + for (int i = 0; i < columns; ++i) { + uint32_t value = init_b13[i % 16] == '0' ? bits_off : bits_on; + if (i > columns - 13) value |= h.strobe; + io->Write(value); + io->SetBits(h.clock); + io->ClearBits(h.clock); + } + io->ClearBits(h.strobe); +} + +/*static*/ void Framebuffer::InitializePanels(GPIO *io, + const char *panel_type, + int columns) { + if (!panel_type || panel_type[0] == '\0') return; + if (strncasecmp(panel_type, "fm6126", 6) == 0) { + InitFM6126(io, *hardware_mapping_, columns); + } + // else if (strncasecmp(...)) // more init types + else { + fprintf(stderr, "Unknown panel type '%s'; typo ?\n", panel_type); + } +} + bool Framebuffer::SetPWMBits(uint8_t value) { if (value < 1 || value > kBitPlanes) return false; diff --git a/lib/led-matrix-c.cc b/lib/led-matrix-c.cc index 791503671..7b5f953fd 100644 --- a/lib/led-matrix-c.cc +++ b/lib/led-matrix-c.cc @@ -70,18 +70,19 @@ struct RGBLedMatrix *led_matrix_create_from_options( OPT_COPY_IF_SET(cols); OPT_COPY_IF_SET(chain_length); OPT_COPY_IF_SET(parallel); - OPT_COPY_IF_SET(multiplexing); OPT_COPY_IF_SET(pwm_bits); OPT_COPY_IF_SET(pwm_lsb_nanoseconds); OPT_COPY_IF_SET(pwm_dither_bits); OPT_COPY_IF_SET(brightness); OPT_COPY_IF_SET(scan_mode); + OPT_COPY_IF_SET(row_address_type); + OPT_COPY_IF_SET(multiplexing); OPT_COPY_IF_SET(disable_hardware_pulsing); OPT_COPY_IF_SET(show_refresh_rate); + OPT_COPY_IF_SET(inverse_colors); OPT_COPY_IF_SET(led_rgb_sequence); OPT_COPY_IF_SET(pixel_mapper_config); - OPT_COPY_IF_SET(inverse_colors); - OPT_COPY_IF_SET(row_address_type); + OPT_COPY_IF_SET(panel_type); #undef OPT_COPY_IF_SET } @@ -101,18 +102,19 @@ struct RGBLedMatrix *led_matrix_create_from_options( ACTUAL_VALUE_BACK_TO_OPT(cols); ACTUAL_VALUE_BACK_TO_OPT(chain_length); ACTUAL_VALUE_BACK_TO_OPT(parallel); - ACTUAL_VALUE_BACK_TO_OPT(multiplexing); ACTUAL_VALUE_BACK_TO_OPT(pwm_bits); ACTUAL_VALUE_BACK_TO_OPT(pwm_lsb_nanoseconds); ACTUAL_VALUE_BACK_TO_OPT(pwm_dither_bits); ACTUAL_VALUE_BACK_TO_OPT(brightness); ACTUAL_VALUE_BACK_TO_OPT(scan_mode); + ACTUAL_VALUE_BACK_TO_OPT(row_address_type); + ACTUAL_VALUE_BACK_TO_OPT(multiplexing); ACTUAL_VALUE_BACK_TO_OPT(disable_hardware_pulsing); ACTUAL_VALUE_BACK_TO_OPT(show_refresh_rate); + ACTUAL_VALUE_BACK_TO_OPT(inverse_colors); ACTUAL_VALUE_BACK_TO_OPT(led_rgb_sequence); ACTUAL_VALUE_BACK_TO_OPT(pixel_mapper_config); - ACTUAL_VALUE_BACK_TO_OPT(inverse_colors); - ACTUAL_VALUE_BACK_TO_OPT(row_address_type); + ACTUAL_VALUE_BACK_TO_OPT(panel_type); #undef ACTUAL_VALUE_BACK_TO_OPT } diff --git a/lib/led-matrix.cc b/lib/led-matrix.cc index b60df33c1..d81be00be 100644 --- a/lib/led-matrix.cc +++ b/lib/led-matrix.cc @@ -234,7 +234,8 @@ RGBMatrix::Options::Options() : inverse_colors(false), #endif led_rgb_sequence("RGB"), - pixel_mapper_config(NULL) + pixel_mapper_config(NULL), + panel_type(NULL) { // Nothing to see here. } @@ -258,6 +259,7 @@ RGBMatrix::RGBMatrix(GPIO *io, const Options &options) } Framebuffer::InitHardwareMapping(params_.hardware_mapping); + active_ = CreateFrameCanvas(); Clear(); SetGPIO(io, true); @@ -332,6 +334,7 @@ void RGBMatrix::SetGPIO(GPIO *io, bool start_thread) { !params_.disable_hardware_pulsing, params_.pwm_lsb_nanoseconds, params_.pwm_dither_bits, params_.row_address_type); + Framebuffer::InitializePanels(io_, params_.panel_type, params_.cols); } if (start_thread) { StartRefresh(); diff --git a/lib/options-initialize.cc b/lib/options-initialize.cc index 688c2f5ea..1f559dceb 100644 --- a/lib/options-initialize.cc +++ b/lib/options-initialize.cc @@ -150,6 +150,9 @@ static bool FlagInit(int &argc, char **&argv, if (ConsumeStringFlag("pixel-mapper", it, end, &mopts->pixel_mapper_config, &err)) continue; + if (ConsumeStringFlag("panel-type", it, end, + &mopts->panel_type, &err)) + continue; if (ConsumeIntFlag("rows", it, end, &mopts->rows, &err)) continue; if (ConsumeIntFlag("cols", it, end, &mopts->cols, &err)) @@ -396,7 +399,8 @@ void PrintMatrixFlags(FILE *out, const RGBMatrix::Options &d, "(Default: %d)\n" "\t--led-pwm-dither-bits=<0..2> : Time dithering of lower bits " "(Default: 0)\n" - "\t--led-%shardware-pulse : %sse hardware pin-pulse generation.\n", + "\t--led-%shardware-pulse : %sse hardware pin-pulse generation.\n" + "\t--led-panel-type= : Needed to initialize special panels. Supported: 'FM6126A'\n", d.hardware_mapping, d.rows, d.cols, d.chain_length, d.parallel, (int) muxers.size(), CreateAvailableMultiplexString(muxers).c_str(),