Controlling an LED matrix with a MAX7221

While building a project with a 4×7-segment display, I was irritated by how many pins needed to be used just for the display. I barely had enough to be able to complete the project.

So I looked into methods for controlling an LED matrix with fewer pins. I learned about Charlieplexing, which seems interesting but non-trivial. Then I found the MAX7221 IC, and an Arduino library to interface with it. Perfect. I bought some to try out and they’re very easy to use. Though the display I was using would not run on USB power — I had to connect it to 7.5V from my bench supply.

Useful things:

Progress on the PIC-powered die

Having realised that I was indulging in some horrible premature optimisation, I stopped trying to make this circuit work using a three-bit output and cracked on with a 4-bit version. The circuit just took a few minutes to build on top of the flashing LED circuit:

I added extra LEDs on RA0-4, and added a push-to-close switch on RB0/INT. I also added a capacitor across the input to even out any voltage fluctuations, and included a diode to save me from my own stupidity – I’ve already zapped more than one IC by getting the polarity wrong.

In the code, I stripped out the delay stuff, and started by just turning all the LEDs on, which was pretty simple:

       movlw	0x0F
       movwf   PORTA

I also had to add some more setup stuff:

	bsf	STATUS,5       	  ; Switch to Bank 1
        bsf     OPTION_REG, INTEDG  ; Set RB0 to generate an interrupt on a rising edge
        bcf     INTCON, INTF      ; clear interrupt flag
        bsf     INTCON, INTE      ; mask for external interrupts
        bsf     INTCON, GIE       ; enable interrupts
 
        movlw   0x01		  ; Port B to input
        movwf   TRISB

Then I turned the bulk of the old program into a sleep routine:

; Turn the LED off 
start
       	movlw	0x00              ; Turn the LED on by first putting
        movwf   PORTA             ; it into the w register and then on the port
	sleep
 
	END                       ; directive 'end of program'

And added some code to the interrupt handling routine:

; isr 
        btfss   PORTB, 0          ; Only run if RB0 is high
        goto    end_isr           ; If it's not high, branch to the end
 
        btfss   PORTA, 0          ; Invert Port A - if bit 0 of Port A is set, 
        movlw	0x0F              ; skip the instruction that makes it high, 
        btfsc   PORTA, 0          ; and vice-versa
        movlw	0x00
 
        movwf   PORTA             ; Put the selected value into PORTA
        bcf     PORTB, 0          ; Clear PORTB now that we're done with it.
                                  ; I don't know if this works or if it's necessary

The full source code is here.

This nearly works. As in, when I press the button, the LEDs do all light up. And then pressing it again turns them off. However, they also come on by themselves and flash very quickly, and then turn off. I assume this is because of some other interrupt being called, and that my starting btfss doesn’t sufficiently differentiate between interrupts caused by RB0 going high, and other sorts of interrupts.

Or, it might be because the pin is not tied to ground when the pin is open, and floating voltage is enough to trigger the interrupt.

More fiddling needed.

Useful things found while doing this:

Beginning attempts at a PIC-powered LED die

I thought a good step up from the blinking LED would be to make a die with an LED display, having seen someone do something similar.

I wasn’t sure from that diagram how the PIC was driving the LEDs, but I am sure that a naive implementation using 7 outputs would be a bit wasteful. And dull. I wondered if  it might be possible to output a 3-bit binary number between 1 and 6 and use the external electronics to determine which LEDs should be lit, based on the outputs. This might not be the best way to do it but it seemed fun.

I started by writing out a table of inputs and outputs, assuming a die consisting of 7 LEDs  laid out like this:

Each LED has a number, making a table with 3 inputs and 7 outputs:

I1 I2 I3   O1 O2 O3 O4 O5 O6 O7
-------------------------------
0  0  0    ?  ?  ?  ?  ?  ?  ?  INVALID
0  0  1    0  0  0  0  0  0  1
0  1  0    1  0  0  0  0  1  0
0  1  1    1  0  0  0  0  1  1
1  0  0    1  1  0  0  1  1  0
1  0  1    1  1  0  0  1  1  1
1  1  0    1  1  1  1  1  1  0
1  1  1    ?  ?  ?  ?  ?  ?  ?  INVALID

It’s pretty clear from this table that the problem is actually simpler, as many of the outputs mirror each other, and O7 mirrors I3:

I0 I1 I2   O1,6 O2,5 O3,4 O7
---------------------------
0  0  0    ?    ?    ?    ?  INVALID
0  0  1    0    0    0    1
0  1  0    1    0    0    0
0  1  1    1    0    0    1
1  0  0    1    1    0    0
1  0  1    1    1    0    1
1  1  0    1    1    1    0
1  1  1    ?    ?    ?    ?  INVALID

I was going to make a Karnaugh Map to simplify this further, but then realised that I’d have to make one for each output, which would be tedious, and that actually the simplifications are easy to spot anyway — all the more so because we can ignore outputs for 000 and 111 which the PIC will not generate.

O7 can be connected directly to I3, which is to be expected, since the middle LED only lights up for odd numbers. O2,5 is similarly simple, and can be connected directly to I0. O1,6 should be on when either I0 or I1 is high, and O3,4 should be on when both I0 and I1 are high. So: a couple of easy connections, an OR and an AND to implement! Neat.

I started sketching out a schematic for the circuit, but I don’t think I’ve got it right yet — either that, or my breadboard implementation is wrong.

I’m using 2 NPN transistors to form an AND gate for O3,4. I0 and I1 are both connected directly to O1,6, and I2 directly to O7. O2,5 is connected to I0 behind a diode, to prevent a high signal on I1 illuminating O2,5 via their shared connection on O1,6.

However: this doesn’t work properly. The transistor pair doesn’t stop the LED lighting up when I0 is low and I1 is high — the LED remains on, albeit dimmer. Also, the diode arrangement for O1,6 and O2,5 doesn’t work with the schematic above. When I replace the connection from I0 to O2 with another diode, it does work correctly. I also added D2 as an experiment — it doesn’t work without it — and I don’t really understand why that’s necessary either. I suspect voltage drop(s) are at fault, somehow.

I ended up going round in circles trying to get this working properly, and ran out of things to try. Am also not sure if this is the right basic approach or if I’m going down a blind alley. Might have another go at it another day, or perhaps start with a sucky 7-output wasteful (but functional) design, and iterate from there!

Edited to add: Obviously, now that I’ve simplified it down to 4 outputs, I wouldn’t need to make something with 7 outputs. Duh. And this seems like an awful lot of work just to have one less output. So I think this approach is a bit silly, and I’m just making work for myself. Next, I will try making something with 4 outputs, and get on with writing the PIC program. That said, it would be nice to understand why this circuit doesn’t work. Perhaps I’ll meet someone I can ask.