Understanding LED matrix scanning method

This entry is for someone that having basic electronics knowledge and want to understand how to control led matrix using Arduino UNO.

How you control a led matrix?

Let start with the basic: controlling 1 row of single-color LEDs


Easy stuff, right? Just put D5 to HIGH and D8-D11 to LOW to light up each individual LED.

The only problem is Arduino UNO digital pins only capable of driving 40mA so that without current limiter R1 you probably fry your Arduino. R1 can be calculated roughly = (Vcc - V_led) / max_current. Let’s play safe, just use half of maximum current of IO pins and ignore V_led, which results in higher R1 and lower current through the LEDs. Say 5v/20mA = 250ohm, you can choose 330ohm or 470ohm which won’t make a noticeable difference. But hey, when you light up the whole row, each LED will become dimmer as each of them gets only 1/4 of the total current (assuming you are using the same LED).

This configuration is similar to common anode you probably see in 7-seg LED.

You will also need a buffer, like a screen image, to store the LED status data. For 4 Led array like that, you will need just about 4 bits (half of a byte).

Okay, let’s add another row.


Thing gets a little bit complex now, but still easy stuff as long as you light up one ROW at a time. You might ask, if I light up one row at a time, I can only see one row which won’t make up an image I want to display. Well that is true, if you are switching row pretty slow. But if you switch row fast enough, like 24 frame/s, you will see a full image without flickering. That is 1 frame for 1/24 second and each row is 1/48 second or roughly 21ms.

If you want LED to be brighter, then considering a mosfet or a transistor (high-side switch pnp, p-channel…) for each row and a current limiter resistor for each column if you are doing row scanning. People usually do row scanning rather than column scanning.


In case you want to use high current LED, you should put mosfet on each column too (low-side switch npn, n channel…). Or you can use something like Darlington transistor array ULN2803 for example.

Again, you will need a buffer of 4bit x 2 = 1byte.

That is the basic of LED scanning method.

So, you have 8×8 LED matrix, how do you use it?

Well, you do the same, hook 8 rows to 8 IO pins and 8 columns to another 8 IO pins. You have to put a current limit resistor for each row, and/or mosfet if needed.

A buffer of 8×8bit = 8 bytes is needed.

However, Arduino UNO has only 16 available IO pins, which left nothing for button or such. So, we can use 3 to 8 decoder IC to free up some pins.

74hc138 truth table



You will use input A, B, C to control output Y0 - Y7. Let say D3, D4, D5 hooked to input A, B, C for select one of 8 rows and Y0 - Y7 to control 8 columns.

To free up more pins or, more importantly, expand the LED matrix to 16 or 64 columns or much more than that, you have to replace parallel connection with serial connection by using shift register IC, unless you have luxury of 64 spare pins to use.


For this panel 32×16, you will see pin A, B, C, D for selecting row, which means 2^4 = 16 and hence the term 16s scan rate or 1/16 scan rate. Similarly, for panels with 1/8 scan rate you will see only A, B, C and for 1/32 panels there will be A, B, C, D, E

You can bitbang the output data to shift register like this example below for 32×16 panel

// 32 width x 16 height
// 32x16 bits = 4x16 bytes  = 64 bytes
uint8_t display_buffer [64]; // pixel data for the whole display

//each row
for (uint8_t row = 0; row <height; row++)
  //each byte in one row
  for (uint8_t byte_in_row = 0; byte_in_row < 4 ; byte_in_row++)
    uint8_t pixel_index = row * 4 + byte_in_row;
    // copy byte data, reverse if needed
    uint8_t pixels_data = display_buffer[pixel_index];

    // bitbang each byte
    for (uint8_t bit = 0; bit < 8; bit++)  
      digitalWrite(clk_pin, LOW);
      digitalWrite(data_pin, pixels_data & (0b10000000 >> bit));
      digitalWrite(clk_pin, HIGH);

  // disable display
  digitalWrite(oe_pin, HIGH); 
  // select row
  digitalWrite(a_pin, (row & 0b00000001));
  digitalWrite(b_pin, (row & 0b00000010);
  digitalWrite(c_pin, (row & 0b00000100));
  digitalWrite(d_pin, (row & 0b00001000));

  // latch data
  digitalWrite(stb_pin, LOW);
  digitalWrite(stb_pin, HIGH);
  digitalWrite(stb_pin, LOW);

  // enable display
  digitalWrite(oe_pin, LOW);
// end of row loop

A buffer of 64 bytes is needed for this panel.

How’s about the brightness of individual LED?

At this point, you know how to control single-color LED matrix by turning on/off individual LED. But to make it dimly you need to use PWM.

Let assume, you need 256 clock cycles for each PWM period and about 24 periods for each single LED to be seen brightness smoothly, not just as blinking LED. For each period you compare data color bytes of each pixel 256 times, say consuming 255 clock cycles. Using arduino standard 16mhz, you will have 16*10^6 / (256*24*256) = 10.25, meaning you can drive maximum of 10.25 LEDs without flickering. That is not true. The number would be lower than that since the MCU will spend many many clock cycles for calculating, mem reading, register writing… For that matter, you will be lucky if you are able to drive 10 LEDs smoothly.

Of course you can reduce to 4 bit greyscale and use 4 bit PWM which reduce a huge amount of CPU power then maybe you can drive 32×16 led panel.

There is an alternative to PWM, which is Binary Code Modulation. There’s a good article about BCM http://www.batsocks. … readme/art_bcm_3.htm

However, with 8 bit arduino PWM, you’ll need 1 byte for each pixel, that gives 255 levels of brightness. For 32×16 panel, you will need 512 bytes just for screen buffer. For 64×32 panel, it will be 2048 bytes. Arduino UNO only has 2k of memory, oops!!!

How’s about dual color panel or RGB panel?

Most of dual color panels are simply made by 2 single color panels in daisy chain. Not really but quite close. A 32×16 R/G panel is pretty much the same as one 64×16 Red panel. Similar thing with RGB panels. This yields the buffer size to 1024 bytes for R/G panel and 1.5kb for RGB panel. You also need to send more data in a short time frame to keep the frame rate high enough.

For anything larger than 32×16 panel, you will need better microcontroller, STM32F4 or ESP32 for example. Other faster microcontroller would work just fine but much more expensive.

Existing solutions?

- This guy successfully drives 96×32 RGB panel using STM32F4 discovery board https://fw.hardijzer.nl/?p=223

- You can also use Single Board Computer - Beaglebone Black to do this job https://bikerglen.com/projects/lighting/led-panel-1up/

- Or use relatively cheap ESP8266 dev board such as NodeMCU to drive RGB LED panels (HUB75 connection) https://github.com/2dom/PxMatrix

- This guy, http://openhardware.ro/mymatrix/, wrote a working arduino library for 32×16 red/green panel. The panel has 2 separate data inputs for Red and Green that must be sent data synchronously.


Add comment

Fill out the form below to add your own comments

User data

Add your comment

[color=red] TEXT [/color]
[quote] TEXT [/quote]
[code=arduino avrasm cpp css java javascript nginx perl python] TEXT [/code]
Clear format