My earlier post provided sample code for a single HW-179 8-digit 7-segment LED display. The MAX7219 provides for a cascade configuration such that multiples of 8-digit displays can be connected to achieve displays having a greater number of digits, and is the topic of this post.

The MAX7219 spec sheet vaguely describes this capability in a couple short paragraphs and in a wiring diagram, but is absent of timing diagrams or command listings. The most revealing clue that conveys exactly how to communicate with cascaded MAX7219 devices is the spec sheet's functional diagram on page 5. Here it shows that address and data is shifted into a 16-bit register over 16 cycles of the SPI clock signal, and that this address and data is subsequently shifted out of this register on the DOUT pin over the next 16 clock cycles into the cascaded MAX7219 whose DIN pin is connected to the first MAX7219's DOUT pin. Particularly important to understanding how this functions is that the 16-bit address and data is strobed into the MAX7219 on the rising edge of the SPI's CS signal, and that a "No-Op" (a 16-bit address and data sequence that is 0xXX0X) inhibits any change in the MAX7219 in whose register it appears.

Consequently, when the SHIFTOUT command sends two 16-bit quantities within a single CS strobe cycle, each MAX7219 responds to the command that appears in its register when the CS signal rises. For example, with 2 MAX7219s cascaded, the following command sequence could be sent:

CS = 0
SHIFTOUT Data, Clock, Mode, [$0105, $0206]
CS = 1

Shifting begins with the most significant bit of the first argument in the command, and so after execution, $0105 appears in the second MAX7219 and $0206 appears in the first MAX7219. Referencing the data sheet, these commands result in the number "5" appearing in the second MAX7219's first digit and the number "6" appearing in the first MAX7219's second digit. If either of these arguments is replaced with a No-Op ($0000), then the respective display remains unchanged.

In the attached code sample, this register 16-bit shifting is exploited in the first section where the MAX7219s are initialized. The first command is the test command $0F01 which causes all segments in all digits to illuminate for 1 second, followed by return to normal operation. When executed from reset, the first MAX7219 goes into this test mode for 1 second while the second MAX7219 remains unlit. Then the first MAX7219 returns to normal and the second MAX7219 enters test mode. With this sample code structured this way, each of the 16-bit initialization commands ripples from the first device to the second, with the final No-Op command ensuring that the last command $0C01 makes it way to the second MAX7219. Thus, all 6 initialization commands are entered into both MAX7219s.

Alternately, each of these initialization commands could contain the argument twice -- for example [$0F01, $0F01], which would cause both display modules to simultaneously light up in test mode. Using this technique, the final No-Op command can be eliminated.

The sample code provided here extends the demo of my earlier post by creating the same progressive display counting, starting with the right-hand most digit on the second display and proceeding through the the left-hand most digit of the first display. Notice that the difference between the two code loops that apply to the two display modules is the exchange of the position of the No-Op argument in the SHIFTOUT command. This technique allows a number to be placed in any digit position on either display module.

Code:
'****************************************************************
'*  Name    : MAX7219_Cascade_8-Digit_7-Segment_Demo.BAS        *
'*  Author  : Roberts                                           *
'*  Notice  : Copyright (c) 2021                                *
'*          : All Rights Reserved                               *
'*  Date    : 10/6/2021                                         *
'*  Version : 1.0                                               *
'*  Notes   : Developed on Microchip Curiosity Board with       *
'*          : PIC16F183456 uC and the HW-179 8-digit/7-segment  *
'*          : display driven by the MAX7219 driver via 3-wire   *
'*          : SPI interface, set to operate at 5 VDC.           *
'****************************************************************
'
' This brief demo program writes the digits 0 thru 9 and "-" to two HW-179 8-digit 7-segment LED displays based on the
' MAX7219 interface driver.  The code includes the initialization sequence that (a) sets the decode mode to BCD,
' (b) sets the LED intensity to half-scale (8/16), (c) sets all 8 digits active, and (d) activates the displays.
' During initialization, the code sets the test mode active for 1 second (e.g., all digits and all segments lit). This
' initialization is structured such that each command ripples from the first display to the second.
' Then all digits are cleared, and counting begins with the right-hand most digit on the second display.  Each digit
' counts from 0 to 9 and then "-", and the counting progresses to the left one digit at a time.  Once all 8 digits are
' showing "-", counting progresses to the first display and repeats the same sequence.  After all digit positions have
' counted, the program cycles back to clearing the display and repeats.
' Data is sent to the displays using PBP's SHIFTOUT command.  The code demonstrates how commands are structured to 
' send data to one display while holding the other display unchanged. 
'
'****************************************** PIC16F18346 Initialization *************************************************                                                 
'************************************** Configuration File for the PIC 16F18346 ****************************************
#CONFIG
    __config _CONFIG1, _FEXTOSC_OFF & _RSTOSC_HFINT32 & _CLKOUTEN_OFF & _CSWEN_ON & _FCMEN_ON
    __config _CONFIG2, _MCLRE_ON & _PWRTE_OFF & _WDTE_ON & _LPBOREN_OFF & _BOREN_ON & _BORV_LOW & _PPS1WAY_OFF & _STVREN_ON & _DEBUG_OFF
    __config _CONFIG3, _WRT_OFF & _LVP_OFF
    __config _CONFIG4, _CP_OFF & _CPD_OFF

#ENDCONFIG
@ ERRORLEVEL -306   ; turn off crossing page boundary message

OSCFRQ = 000100    ' Set internal osc to 16MHz
DEFINE OSC 16        ' Tell PBP that the device will clock at 16MHz (necessary for uSec pauses used in bit-banging)

ANSELA = 000000    ' PortA all digital / available pins are A0 through A5 (6 I/O's)
TRISA = 000000   ' PortA all outputs
'
'************************** Initialization and Variable Declarations *****************************************
'
' SPI signals use LATx.x assignments to prevent read/modify/write conflicts on a single I/O port
CS  Var LATA.2  ' uC chip select output pin
SCK Var LATA.4  ' uC clock out pin
SI  Var LATA.5  ' uC data out / LCD Data in pin
'
'
i VAR WORD       ' loop counter variable
j VAR byte       ' loop counter variable
CS = 1           ' Disable SPI device select CS for OLED 
'
'************************* Initialize the display parameters by sending 16-bit constants **********************
'                (See MAX7219 spec sheet for tables listing full range of initialization values)

    CS = 0
    SHIFTOUT SI, SCK, 1, [$0F01\16]         'Test display - illuminate all segments & all digits     
    CS = 1    
    PAUSE 1000                              'hold for 1 second
    
    CS = 0
    SHIFTOUT SI, SCK, 1, [$0F00\16]         'Return to normal display mode
    CS = 1    
    PAUSE 1000                              'Pause 1 second

    CS = 0
    SHIFTOUT SI, SCK, 1, [$09FF\16]         'Set decode mode for BCD
    CS = 1                                  '($0900 for no decoding - see MAX7219 spec sheet)
    PAUSE 10
    
    CS = 0
    SHIFTOUT SI, SCK, 1, [$0A01\16]         'Set display intensity to 2/16 brightness
    CS = 1                                  '($0A00 for minimum brightness / $0A0F for maximum brightness)
    PAUSE 10
    
    CS = 0
    SHIFTOUT SI, SCK, 1, [$0B07\16]         'All 8 digits active
    CS = 1                                  '($0B01 for only digit 1 active - see MAX7219 spec sheet)
    PAUSE 10
    
    CS = 0
    SHIFTOUT SI, SCK, 1, [$0C01\16]         'Activate display
    CS = 1                                  '($0C00 for powering down the display - see MAX7219 spec sheet)
    PAUSE 10
    
    CS = 0
    SHIFTOUT SI, SCK, 1, [$0000\16]         'Send no-op to complete initialization of 2nd display
    CS = 1 
    
'********************************************* Main Program ********************************************************** 
main:                                       
  For i = 1 to 8                            'Clear all 8 digit registers to blank
    CS = 0
    Shiftout SI, SCK, 1, [i,$0F]            'Clear each digit "i" by sending the blank character constant 0x0F
    CS = 1
    pause 10
    
    CS = 0
    SHIFTOUT SI, SCK, 1, [$0000\16]         'Send no-op to complete blanking on 2nd display's 8th digit
    CS = 1 
    pause 10
    
  Next i                                    'hold the display blank for 1 second
  pause 1000

 
'This first nested loop sends digits through the 1st display to the 2nd display:
        
 For j = 1 to 8                             'the variable j is the 8-bit digit address register
  For i = 0 to 10                           'the variable i is the 8-bit data register
    CS = 0
    SHIFTOUT SI, SCK, 1, [j,i,$0000\16]     'Display 0 through 9 and "-" right to left ($0000 is a No-Op)
    CS = 1
    PAUSE 500                               'pause 1/2 second for counting
  next i 
 Next j
     
'This second nested loop sends digits to the 1st display and inhibits change to the 2nd display:
         
 For j = 1 to 8                             'the variable j is the 8-bit digit address register
  For i = 0 to 10                           'the variable i is the 8-bit data register
    CS = 0
    SHIFTOUT SI, SCK, 1, [$0000\16,j,i]     'Display 0 through 9 and "-" right to left ($0000 is a No-Op)
    CS = 1
    PAUSE 500                               'pause 1/2 second for counting
  Next i 
 Next j        
                                            
 Pause 2000                                 'hold the display for 2 seconds       
                                           
Goto main