ChuckR
- 8th October 2021, 20:26
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.
'************************************************* ***************
'* 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
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.
'************************************************* ***************
'* 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