小伙伴们的智能之旅

RPi 2B: 使用树莓派驱动LED彩色点阵屏 - 介绍

这里提到的LED彩色点阵屏是由一个或者多个LED彩色单元板,这种单元板没有驱动电路,需要外接相关的控制卡。对于单色或者是双色LED点阵屏/单元板,只需要使用基于STM32 MCU的控制卡。但是对于彩色单元板或者是想对单色/双色单元板实现灰阶效果,那么就需要基于FPGA/CPLD芯片的控制卡。

而由Henner Zeller开发的rpi-rgb-led-matrix却是可以通过树莓派来控制LED彩色单元板(https://github.com/hzeller/rpi-rgb-led-matrix/blob/master/README.md):

Controlling RGB LED display with Raspberry Pi GPIO

A library to control commonly available 32×32 or 16×32 RGB LED panels with the Raspberry Pi. Can support PWM up to 11Bit per channel, providing true 24bpp color with CIE1931 profile.

Supports 3 chains with many 32×32-panels each. On a Raspberry Pi 2, you can easily chain 12 panels in that chain (so 36 panels total), but you can stretch that to up to 96-ish panels (32 chain length) and still reach around 100Hz refresh rate with full 24Bit color (theoretical – never tested this; there might likely be timing problems with the panels that will creep up then). With fewer colors you can control even more, faster.

The LED-matrix library is (c) Henner Zeller h.zeller@acm.org with GNU General Public License Version 2.0 http://www.gnu.org/licenses/gpl-2.0.txt

The demo-main.cc example code using this library is released to the public domain.

显示屏更新的代码在这里:https://github.com/hzeller/rpi-rgb-led-matrix/blob/master/lib/framebuffer.cc:

void Framebuffer::DumpToMatrix(GPIO *io) {
  IoBits color_clk_mask;   // Mask of bits we need to set while clocking in.
  color_clk_mask.bits.p0_r1
    = color_clk_mask.bits.p0_g1
    = color_clk_mask.bits.p0_b1
    = color_clk_mask.bits.p0_r2
    = color_clk_mask.bits.p0_g2
    = color_clk_mask.bits.p0_b2 = 1;

#ifndef ONLY_SINGLE_CHAIN
  if (parallel_ >= 2) {
    color_clk_mask.bits.p1_r1
      = color_clk_mask.bits.p1_g1
      = color_clk_mask.bits.p1_b1
      = color_clk_mask.bits.p1_r2
      = color_clk_mask.bits.p1_g2
      = color_clk_mask.bits.p1_b2 = 1;
  }

  if (parallel_ >= 3) {
    color_clk_mask.bits.p2_r1
      = color_clk_mask.bits.p2_g1
      = color_clk_mask.bits.p2_b1
      = color_clk_mask.bits.p2_r2
      = color_clk_mask.bits.p2_g2
      = color_clk_mask.bits.p2_b2 = 1;
  }
#endif

#ifdef PI_REV1_RGB_PINOUT_
  color_clk_mask.bits.clock_rev1 = color_clk_mask.bits.clock_rev2 = 1;
#endif
  color_clk_mask.bits.clock = 1;

  IoBits row_mask;
  row_mask.bits.a = row_mask.bits.b = row_mask.bits.c
    = row_mask.bits.d = row_mask.bits.e = 1;

  IoBits clock, strobe, row_address;
#ifdef PI_REV1_RGB_PINOUT_
  clock.bits.clock_rev1 = clock.bits.clock_rev2 = 1;
#endif
  clock.bits.clock = 1;
  strobe.bits.strobe = 1;

  const int pwm_to_show = pwm_bits_;  // Local copy, might change in process.
  for (uint8_t d_row = 0; d_row < double_rows_; ++d_row) {
    row_address.bits.a = d_row;
    row_address.bits.b = d_row >> 1;
    row_address.bits.c = d_row >> 2;
    row_address.bits.d = d_row >> 3;
    row_address.bits.e = d_row >> 4;

    io->WriteMaskedBits(row_address.raw, row_mask.raw);  // Set row address

    // Rows can't be switched very quickly without ghosting, so we do the
    // full PWM of one row before switching rows.
    for (int b = kBitPlanes - pwm_to_show; b < kBitPlanes; ++b) {
      IoBits *row_data = ValueAt(d_row, 0, b);
      // While the output enable is still on, we can already clock in the next
      // data.
      for (int col = 0; col < columns_; ++col) {
        const IoBits &out = *row_data++;
        io->WriteMaskedBits(out.raw, color_clk_mask.raw);  // col + reset clock
        io->SetBits(clock.raw);               // Rising edge: clock color in.
      }
      io->ClearBits(color_clk_mask.raw);    // clock back to normal.

      // OE of the previous row-data must be finished before strobe.
      sOutputEnablePulser->WaitPulseFinished();

      io->SetBits(strobe.raw);   // Strobe in the previously clocked in row.
      io->ClearBits(strobe.raw);

      // Now switch on for the sleep time necessary for that bit-plane.
      sOutputEnablePulser->SendPulse(b);
    }
    sOutputEnablePulser->WaitPulseFinished();
  }
}

从这里可以看到,对于显示屏中的每一行,先做11位的PWM控制,做完之后,再更新下一行。由于在PWM更新时,最短一次可能只需要100ns, 所以想使用STM32实现这样的效果根本是不可能的。(假设STM32核心频率为72MHz, 执行一条指令需要0.000,000,0139s = 13.9ns,同时可以看出这种方式需要的内存也是很大的,对于32×32 x rgb的单元板,光显示就需要32 * 32 * 3 * 11/8 = 4224 byte, 也许今后可以试试使用4/5位的PWM控制)。

NOTE:

使用硬件PWM是为了提供精确的控制时间。

哎,神人啊!!

Keep in mind that these displays are normally designed to be driven by FPGAs or other high speed processors; they do not have built in PWM control of any kind. Instead, you’re supposed to redraw the screen over and over to ‘manually’ PWM the whole thing. On a 16 MHz Arduino Uno, we managed to squeeze 12-bit color (4096 colors) but this display would really shine if driven by an FPGA, CPLD, Propeller, XMOS or other high speed multi-processor controller.

(图片来自:https://learn.adafruit.com/assets/2937)

  1. https://github.com/hzeller/rpi-rgb-led-matrix
  2. https://learn.adafruit.com/32×16-32×32-rgb-led-matrix/overview