Thanks, Ben and Eeyore. I got it working.
For some reason (noise, ground bounce, V sag, ...) when I was feeding 5V to
the LED module the display MPU was going haywire and drawing way too much
current (290mA!). Once I ran 9V to the display presto, 40mA (@9V), and
clean (enough) analog signals.
Not that I suppose it matters any more, but you can reduce noise & RFI
by slowing down the multiplexing. The amount of noise is directly
related to the how often the drive signals change state, so the slower
you switch them, the fewer the switching transients. For 7-segment
displays, 50 refreshes/second (20mS each, divided by the number of
digits) is plenty. In your circuit, that works out to 5ms per digit,
or a mux frequency of 200Hz. I haven't looked very closely at your
code, but it seems to be refreshing way more than neccessary, which
will increase your power requirements, generate lots of noise, & waste
lots of CPU cycles you could be using for something else.
Pseudo-code: (All straight out of my head, & completely untested.)
; **************************************************
; **** 7 segment LED display multiplexing stuff. ***
; **************************************************
;Variables:
DISP_MUX = 0
; DISP_0-3 are the segments for each
; 7 segment display, Bits 0-6 = Seg's
; A-F, bit 7 = DP (if used)
; Main prog can update patterns any time.
DISP_0 = 0xFF ; All segments lit for debugging.
DISP_1 = 0xFF : **Assuming that '1' = on, '0' = off*
DISP_2 = 0xFF ; **Invert if neccessary! **
DISP_3 = 0xFF
; Output Ports:
DISP_SEGS = PORTA ; Or whatever.
DISP_DIG = PORTB ; Or whatever.
DISP_BLANK = 0x00 ; All digits disabled.
; Setup:
conf TIMER for 5ms (5ms/digit * 4 digits = 20ms = 50Hz)
set TIMER interrupt to DISPLAY subroutine
enable TIMER interrupt
; Main:
;-----------------------------
; Your main program goes here.
;-----------------------------
; DISPLAY: ; Multiplexing IRQ routine.
OUT DISP_DIG, DISP_BLANK ;Blank display until
;segment data valid.
IF DISP_MUX = 0 ; DIGIT 0 (Left)
OUT DISP_SEGS, DISP_0
OUT DISP_DIG b'00000001' ; Enable Digit 0
ELSE
IF DISP_MUX = 1 ; DIGIT 1
OUT DISP_SEGS, DISP_1
OUT DISP_DIG b'00000010' ; Enable Digit 0
ELSE
IF DISP_MUX = 2 ; DIGIT 2
OUT DISP_SEGS, DISP_2
OUT DISP_DIG b'00000100' ; Enable Digit 0
ELSE
IF DISP_MUX = 3 ; DIGIT 3 (Right)
OUT DISP_SEGS, DISP_3
OUT DISP_DIG b'00001000' ; Enable Digit 0
ELSE
INC DISP_MUX
AND DISP_MUX, 3 ; Roll counter over if needed
RETI ; Return from interrrupt.
; *************************************************************
Oh well, even if you don't need this, hopefully someone else will find
it useful.