Multiplexing using a 40X2

Hey guys,

I'm looking to multiplex a 14x14 LED matrix using a 40X2, and was just wondering if you guys could confirm a couple of things.

Firstly, I'm guessing this would make some sense - if I scan the rows, then the columns which are to be activated could be stored in variables, with a command such as pinsb = myVariable. This would allow me to store the status of all the LEDs in 14 wordvariables, which would make altering the status easy... in theory? I'd store these in w1-w14.

Next, the 40X2 would be receiving commands over a serial connection using the hardware serial pins, set to receive in the background. Once per cycle of the matrix, the chip would check if the hserinflag is set - if not it'll just continue pulsing the matrix, and if data has been received it would look up the "position" of the LED in the matrix using a lookup or lookdown command, and update the appropriate variable. To update, I'd copy the variable in question to w0, allowing me to easily set or clear the individual bit for the LED. Does this make sense?

Finally, would the processing time for the updating of LEDs be particularly noticeable? Especially if I overclocked the chip to 16MHz?

And yes, I know I could use chips like the MAX7219, but I simply prefer doing it like this - it serves as a learning experience, and allows me to completely customise everything in theory :)


Well-known member
I'm not trying to be difficult or make this insulting to you, because I don't know your skill level.

Can one of your LEDs be anything but ON or OFF? In other words, are they operated in a binary manner?

Consider that a BIT is either HIGH or LOW.

The X2 processors have 32 available BIT variables that you can use. (Manual Page 13)

A schematic would help everyone visualize what you are trying to do.
Cheers for replying :)

No bother asking that - perfectly valid question. I'm currently a systems apprentice, and have worked with PICAXE for a number of years now, and in programming specifically for slightly longer.

Aye, the LEDs are designed to be binary, I've no need to dim them or fade them - I literally just need them on or off.

I can try and get a schematic designed when I get home later tonight, but for now I can really only try and describe my "vision" - it really is a simple 14x14 matrix of LEDs, using MOSFETs on the high and low sides for switching, and ON Semiconductor NSI45020AT1G LED drivers for current limiting (these are constant current source drivers, 20mA).
The idea is that I turn "on" each row in turn, and for each row have a wordvariable containing the statuses of the columns. This is loaded with a let pins command.

I'll get some sample code and a diagram done later today when I'm at home with decent software :)


Senior Member
Should be fairly easy to do. I wouldn't worry about reserving w0 because you won't actually gain anything from that. I would use w0-w13 and consider the 14x14 as having rows 0-13 and columns 0-13 because it can save a lot of brain ache, confusion and mistakes when 1 means 0 and 0 is 1.

One issue is how much serial you will be sending because that has to be handled and the time taken can affect the display, causing flicker etc.

I would use a 16MHz resonator and run the PICAXE at 64MHz. The gains will almost certainly outweigh the small extra cost.

Untested, and the signal polarities may require inversion ...

#Picaxe 40X2

;  Col Col Col Col
;   13  8   7   0
;  A.5-A.0 D.7-D.0
; .---------------.
; | O - O   O - O | B.0   Row 0
; | :   :   :   : |  :     :
; | O - O   O - O | B.7   Row 7
; | O - O   O - O | C.0   Row 8
; | :   :   :   : |  :     :
; | O - O   O - O | C.5   Row 13
; `---------------'

#Macro Row(pinsRow,rowBits)
  pinsRow = 0
  pinsD   = @bPtrInc
  pinsA   = @bPtrInc
  pinsRow = rowBits
  Pause 1

#Macro InitDisplayFrame
  w0  = %11111111111111 ; Row 0
  w1  = %10000000000001 ; Row 1
  w2  = %11000000000001 ; Row 2
  w3  = %11100000000001 ; Row 3
  w4  = %11110000000001 ; Row 4
  w5  = %11111000000001 ; Row 5
  w6  = %11111100000001 ; Row 6
  w7  = %11111110000001 ; Row 7
  w8  = %11111111000001 ; Row 8
  w9  = %11111111100001 ; Row 9
  w10 = %11111111110001 ; Row 10
  w11 = %11111111111001 ; Row 11
  w12 = %11111111111101 ; Row 12
  w13 = %11111111111111 ; Row 13

#Macro InitSerial
  ; Not implemented in this version

#Macro CheckSerial
  ; Not implemented in this version

SetFreq M16
;        --1111
;        --321098            76543210
dirsC = %00111111 : dirsB = %11111111 ; Rows
dirsA = %00111111 : dirsD = %11111111 ; Cols
  bPtr = 0
  Row(pinsB,%00000001)  ; Row 0
  Row(pinsB,%00000010)  ; Row 1
  Row(pinsB,%00000100)  ; Row 2 
  Row(pinsB,%00001000)  ; Row 3 
  Row(pinsB,%00010000)  ; Row 4 
  Row(pinsB,%00100000)  ; Row 5 
  Row(pinsB,%01000000)  ; Row 6 
  Row(pinsB,%10000000)  ; Row 7
  Row(pinsC,%00000001)  ; Row 8
  Row(pinsC,%00000010)  ; Row 9
  Row(pinsC,%00000100)  ; Row 10 
  Row(pinsC,%00001000)  ; Row 11 
  Row(pinsC,%00010000)  ; Row 12 
  Row(pinsC,%00100000)  ; Row 13
Last edited:
The reason I reserved w0 is so that when I had to update an LED status (turn on/off), I could do it simply by setting or clearing an individual bit, instead of rewriting the whole byte - again to save brain ache. I'd probably just use symbols to represent w1-14 anyway, or just move them to w10-w24 :)

Also, how would you use an external resonator? That's one particular thing I have never yet done...

Oh, and it would be 2x 8-bit bytes being sent over serial - LED number + status (the latter only needs a single bit, I know - but it'll be sent by another PICAXE, and I suppose it lets me implement things like flashing later)

Just scanning through the code, you've introduced me to macros there... So I'm definitely interested about learning about them now... Where's my Manual 2... :geek:


Senior Member
Setting bits by moving things into 'w0' and twiddling 'bitX' variables is not an issue normally but it's a 'mugs game' when it comes to speed of execution and the speed of execution being important as here.

To set row 10, column 9, when 'w0-w13' are used for LED bitmap storage, it is as simple as -
row = 10
col = 9

bPtr  = row << 4 | col >> 3
col   = col & 7
@bPtr = DCD col | @bPtr
Using an external resonator is as simple as finding a 3-pin one, connecting the centre pin to 0V and the outer pins to legs 13 and 14 on the 40X2, either way round -

If you can get your serial protocol down to single bytes, 0-195 for LED, and other values to set flashing or whatnot, you will probably be able to improve the speed of serial handling and reduce the impact on LED multiplexing. But that's for later; just start with being able to show a pre-loaded LED pattern.

We don't have a detailed description of MACRO use but I have created some introductory documentation on those -

LED multiplexing is one of the cases where MACRO commands are really useful. They save a lot of typing, such as where we are doing the Row(...) outputting, so it's easier to see what's being done and we don't have to worry about the detail.

Those could have been assignments to variables with calls to GOSUB routines to do the work, but we don't want any GOSUBs or other overhead slowing anything down and we aren't worried about the size of program so long as it fits into memory.
Last edited:
I'm home :D

As requested, here's a couple of diagrams showing the planned connections (they may differ from your example, hippy - I just connected them as seemed sensible :) )
led matrix connections.PNG led matrix.PNG


Senior Member
You circuit pretty much matches the code I gave. A couple of issues -

For C.6, HSerOut, you have it to 0V. That should be left unconnected or damage may occur.

You have gained an extra 'col14' connection to A.7 which isn't required.

You have 'col11' on A.3 then 'col12' on A.5, skipping the Download Serial Out pin on A.4. That will complicate either the way LED memory is interacted with or the way row data is output. It would be better to use A.4 as 'col12', A.5 as 'col13', to simplify the code and ensure maximum execution speed.

Added: Maybe not that complicated or a major impact if running at a high enough speed -
#Macro Row(pinsRow,rowBits)
  pinsRow = 0
  pinsD   = @bPtrInc
  b0      = @bPtr    & %00110000 << 1
  pinsA   = @bPtrInc & %00001111 | b0
  pinsRow = rowBits
  Pause 1
On the LED matrix circuit, assuming you will have multiple columns active but only a single row active at a time; if you only have single resistors on the row lines the LED intensity can vary depending on how many LEDs are lit. It would seem best to move them to the column lines.
Last edited:
Those are all suspiciously valid points... Do you work for them or sommat?? 😜😜

I'll take a look at the macros, and give everything a test, see what happens :)


New Member
Sorry about the size of the picture ...

This is a central heating controller with a multiplexed 12 row * 8 column display (the pumps are "rows" 10-12 of columns 1-2). It's driven by a 20X2 running at 64MHz. However, rather than having each row/column tied to a separate pin, the rows are connected to the outputs of two 8-bit shift registers (74HC595), and the columns to a ULN2803A Darlington array via a third shift register. The shift registers are daisy-chained together, so this only uses three Picaxe pins, but there is - obviously - an overhead in pulsing the shift registers to update the display. Nevertheless, there is no flicker at 64MHz. Communication is via I2C. The resistors are on the rows (different values for the different colours), and the multiplexing is by column. The shift registers can supply the current for one row, and the Darlington can comfortably sink all 12 LEDs (not that they should ever be all on together).

DSCN3453 (3).JPG


Quite some time ago (read years) I built an 8 x 8 RGB display so that equates to 24 x 8 matrix. Driven by a 40X1 at 8 MHz

Some speed tests for interfacing IC’s found that i2c comms and using MCP23017 chips was way faster than shift register based schemes.

There was some slight flicker at 8 MHz so as Hippy suggests operating the 40X2 at maximum speed (64 MHz) will better avoid flicker AND more time to read the incoming serial data.
OK guys, I've been doing some work overnight, and came up with some ideas. Most importantly, I found what I think would be a sensible way to reduce the command length to a single byte - if I group individual LEDs into commonly used groups, for example LEDs XYZ would always be controlled together, then I can assign pseudonyms so these LED groups, limiting myself to 128 pseudonyms. That allows me to send 7 bits of LED address, and 1 bit for status.

The above would also make system structuring more simple, especially on something like a large model railway. Instead of running hundreds of wires for LEDs from a central circuit board, I can have almost unlimited modules scattered about the layout, connected only by power & serial connections. The main controller sending the commands would only have to know the pseudonyms for the LEDs it's wanting to control, with each individual driver module then reacting as appropriate if the group implied by the command affects any LEDs attached to that module.

To make this work efficiently, however, obviously I'm gonna have to dive head first into the world of lookup tables - is there a decent tutorial out there somewhere which I could use?

Cheers guys!
As an additional to the above, I've just come across another conundrum. The application this is to be used in contains multiple "groups" of LEDs, where anything up to about 25 LEDs need to be updated at the same time, hence the pseudonyms seeming suitable. However, I've come across a brick wall when trying to work out how to "decode" the pseudonyms, to reveal to the processor which individual LEDs need updating - short of simply having each group connected linearly, I'm completely stuck here... perhaps I just need another caffeine injection...


Senior Member
The protocol doesn't have to be single bytes; it's just that the fewer bytes there are the less overhead that will create, the less impact from processing there will be.

You could for example send 0-195 to cause an operation on a single LED. Use 250, 251 and 252 which indicates that subsequent operations will be to set, clear or toggle the LEDs specified.

You could use 253 then send two 0-192 values to operate on all LEDs in that range; thus "250 253 0 195" would clear all LEDs. You could also use 255 as a quick clear everything. Not tested ...

Symbol rxd  = b2
Symbol from = b3
Symbol onto = b4
Symbol mode = b5
Symbol led  = b6
Symbol msk  = b7

  If ptr <> hSerPtr Then
     rxd = @ptrInc
     Select Case mode
       Case < $40
         Select Case rxd
           Case <= 195         ; 000-195 Individual LED
             from = rxd
             onto = rxd
             Gosub DoIt
           Case <= 249         ; 196-249 Not currently used
           Case <= 252         ; 250-252 mode
             mode = rxd - 250
           Case 253            ; 253 Range
             mode = mode | $40
           Case 254            ; 254 Not currently used
           Else                ; 255 Clear all
             from = 0
             onto = 195
             Gosub DoIt
         End Select
       Case < $80
         mode = mode | $80
         from = rxd
         mode = mode & 3
         onto = rxd
         Gosub DoIt
     End Select
   Loop While ptr <> hSerPtr
 End If

  For led = from To onto
    msk  = led // 14
    bPtr = led / 14 << 4 | msk >> 3 | IMAGE_BUFFER_START_ADR
    msk  = msk & 7
    msk  = DCD msk
    Select Case mode
      Case 0 : @bPtr = @bPtr &/ msk  ; Clear
      Case 1 : @bPtr = @bPtr |  msk  ; Set
      Else   : @bPtr = @bPtr ^  msk  ; Toggle
    End Select
That has space for other things to be added such as single byte pseudonyms for groups of LEDs to be added.

There's no simple answer on how best to do that because it would depend on how you were going to be doing your groupings.

It also might not be necessary because 25 bytes isn't a lot to send and handle if you choose a fast enough baud rate and fast processing, and the handler doesn't need to handle everything in one go. Some LEDs going on or off 20mS later than others would likely not be noticed.

I wouldn't worry about that unless you have to. See how a simple scheme pans out before going for something more complicated.
that seems sensible... Perhaps my best route for now is to go ahead and order the circuit boards (which I need anyway), as the hardware won't change, only the firmware which we've been discussing (at great length!!) here... I'll get a proper circuit diagram and pcb design rolled out when I get home tonight.

I'll stick with a really simple program for now, just to get it working, then I can tinker with it to my heart's content later :)

One final question, specifically regarding the hardware - if I wanted to have the serial communications transmitted over larger distances, say 10m, would I need an interface chip? I know when I use i2c I'll need something like an NXP PCA9600 interface chip, and transmit everything over CAT5 ethernet patch cable. Is something similar needed for serial, or do I just need to reduce the baud rate?


Senior Member
I have had mixed results using raw 5V/0V serial over distances. It sometimes works and sometimes doesn't, even when lowering the baud rate. There are so many factors at play that it's hard to say if it will or won't.

One option is to use proper RS232 with MAX232 or similar drivers at each end.


New Member
The heating controller described earlier connects to its (Picaxe-based) thermostats using serial at 5V/0V 300 baud over mains cable (with the original wiring as installed for the original (dumb) thermostats). I guess the length of some of the cabling is 10m or so, and I have not seen any problems. Using a checksum or similar helps to ensure you pick up any transmission errors.
Caution - I may have just found another multiplex module... ;-)

Has anyone here used an LED171596A from Texas Instruments before?


Senior Member
Has anyone here used an LED171596A from Texas Instruments before?
I had never heard of that nor heard of anyone using it with a PICAXE but it should be usable.

It being QFN may present some challenges to the average PICAXE user but it does seem a capable chip.

The advantage the 40X2 has over other devices is that it can directly drive a 14x14 matrix, a MAX7219 equivalent would need four of those, a LED171596A equivalent three.

If just looking at alternatives for 196 LED control; APA102 LEDs may also be in the running. All those could even be controlled from an 08M2 with just two signal lines.
Last edited: