MAX7221
Maxim Integrated(tm) MAX7219/MAX7221
The MAX7219/MAX7221 are compact, serial input/output common-cathode display drivers that interface microprocessors (µPs) to 7-segment numeric LED displays of up to 8 digits, bar-graph displays, or 64 individual LEDs. Included on-chip are a BCD code-B decoder, multiplex scan circuitry, segment and digit drivers, and an 8x8 static RAM that stores each digit. Only one external resistor is required to set the segment current for all LEDs. The MAX7221 is compatible with SPI™, QSPI™, and MICROWIRE™, and has slew-rate-limited segment drivers to reduce EMI.
A convenient 4-wire serial interface connects to all common µPs. Individual digits may be addressed and updated without rewriting the entire display. The MAX7219/MAX7221 also allow the user to select code-B decoding or no-decode for each digit.
The devices include a 150µA low-power shutdown mode, analog and digital brightness control, a scan-limit register that allows the user to display from 1 to 8 digits, and a test mode that forces all LEDs on.
Main Features of Swordfish MAX7219/MAX7221 module :
1- Handle one or multiple cascaded MAX72xx modules
2- Use Hardware or Software SPI to manage the MAX72xx module(s). Software SPI is usefull for little PICs that does not have SPI hardware module (like the little 18 pins 18F1320 mcu).
For any comment, or feedback (or bug report), please post to Swordfish Forum.
VERY IMPORTANT NOTICE concerning Swordfish SPI module:
PS. I never got this module to work with official Swordfish hatdware SPI driver. I used instead a modified version of (hardware) SPI driver using some comments published by Steven on the forum (and sent to me by mail - Great thank Steven). The modified version of the SPI module is also published at the END of this article.
IMPORTANT Options
- option MAX7219_DISP_COUNT = nnValue // where nnValue is the number of your cascaded modules. if not specified, defaults to 1 module
- option MAX7219_SPI_SOFTWARE = true // use Software SPI
- option MCPSPI_SOFTWARE = false // Use Hardware SPI (default to false)
- Option MCP_CS = PORTC.1 // Chip Select Pin
connection to hardware SPI is shown in following diagram (for software SPI you can use Swordfish Options to modify pins affectation).
??? diagram (coming soon)
Example Code for single MAX7219 module
This program uses 2 MAX7219 modules, each handling 8 7Seg display module.
{ ************************************************************************* * Name : MAX7219.BAS * * Author : Ahmed Lazreg ([email protected]) * * http://www.pocketmt.com * * http://www.microcodeproject.com * * Notice : Copyright (c) 20015 Pocket MicroTechnics * * : All Rights Reserved * * Date : 01/01/2015 * * Version : 1.0 * * Desc. : Test program for MAX7219 module * * * ************************************************************************* } Program Max7219_test Device = 18F452 Clock = 20 Include "utils.bas" Include "convert.bas" //#option MAX7219_SPI_SOFTWARE = true Include "Max7219.bas" Dim RED_LED As PORTB.1 Dim i As Word Sub Init_MCU() PORTA = %00000000 TRISA = %11111100 PORTB = %00000000 TRISB = $00 SetAllDigital() End Sub Init_MCU() High(RED_LED) // main program loop SetDisplayTestMode(true) DelayMS(300) SetDisplayTestMode(false) WriteDigit(6, SYMB_E) WriteDigit(7, SYMB_P) DelayMS(1000) BlankDisplay() WriteValue("HELP-. ") DelayMS(1000) BlankDisplay() WriteDigit(7, SYMB_H) i = 0 While (true) Toggle(RED_LED) DelayMS(800) WriteValue(DecToStr(i)) ' use FloatToStr(..) for floating point values i = i +1 End While End Program
Example Code two (02) cascaded MAX7219 modules
This program uses 2 cascaded MAX7219 modules, each handling 8 7Seg display module.
{ ************************************************************************* * Name : MAX7219.BAS * * Author : Ahmed Lazreg ([email protected]) * * http://www.pocketmt.com * * http://www.microcodeproject.com * * Notice : Copyright (c) 20015 Pocket MicroTechnics * * : All Rights Reserved * * Date : 01/01/2015 * * Version : 1.0 * * Desc. : Test program for MAX7219 module * * * ************************************************************************* } Program Max7219_test Device = 18F452 Clock = 20 Include "utils.bas" Include "convert.bas" //#option MAX7219_SPI_SOFTWARE = true #option MAX7219_DISP_COUNT = 2 Include "Max7219.bas" Dim RED_LED As PORTB.1 Dim i, j As Word Dim k As Float Sub Init_MCU() PORTA = %00000000 TRISA = %11111100 PORTB = %00000000 TRISB = $00 SetAllDigital() End Sub Init_MCU() High(RED_LED) // main program loop SelectDisplay(0) SetDisplayTestMode(true) DelayMS(300) SetDisplayTestMode(false) SelectDisplay(0) WriteDigit(6, SYMB_E) WriteDigit(7, SYMB_P) DelayMS(1000) SelectDisplay(0) BlankDisplay() WriteValue("HELP-. ") SelectDisplay(1) WriteValue("-41.73") DelayMS(1000) SelectDisplay(0) BlankDisplay() WriteValue("HELP ") SelectDisplay(1) BlankDisplay() WriteDigit(7, SYMB_L) i = 0 j = 0 k = 0.0 While (true) Toggle(RED_LED) DelayMS(800) SelectDisplay(0) WriteValue(DecToStr(i)) SelectDisplay(1) WriteValue(FloatToStr(k)) i = i +1 k = k + 0.2 End While End Program
Module Code
{ ************************************************************************* * Name : MAX7219.BAS * * Author : Ahmed Lazreg ([email protected]) * * http://www.pocketmt.com * * http://www.microcodeproject.com * * Notice : Copyright (c) 20015 Pocket MicroTechnics * * : All Rights Reserved * * Date : 01/01/2015 * * Version : 1.0 * * Modified: * * Notes : This module handles Maxime Integrated(TM) MAX7219/MAX7221 * * Serially Interfaced, 8-Digit LED Display Drivers * * It can be used via Hardware or Software SPI modules * ************************************************************************* } Module MAX7219 Include "convert.bas" Include "string.bas" // Number of cascaded display modules #if IsOption(MAX7219_DISP_COUNT) And Not (MAX7219_DISP_COUNT > 0) #error MAX7219_DISP_COUNT, "Invalid option, must be > 0" #endif #option MAX7219_DISP_COUNT = 1 Const MAX7219_DISPLAY_COUNT = MAX7219_DISP_COUNT // hardware or software SPI... #if IsOption(MAX7219_SPI_SOFTWARE) And Not (MAX7219_SPI_SOFTWARE in (true, false)) #error MAX7219_SPI_SOFTWARE, "Invalid option, must be TRUE or FALSE." #endif #option MAX7219_SPI_SOFTWARE = false #if MAX7219_SPI_SOFTWARE = true // use software SPI... Include "sspi.bas" #else // use hardware SPI... Include "SPI.BAS" #EndIf #Option MAX7219_CS = PORTC.1 // validate SCK pin... #If IsOption(MAX7219_CS) And Not IsValidPortPin(MAX7219_CS) #Error MAX7219_CS, "Invalid option. MCP_CS must be a valid port pin." #EndIf Public Dim CS As MAX7219_CS.MAX7219_CS@ // Chip select ' To be used with WriteDigit(...) subroutine ' for digits (0..9) just send the digit itself ' for non-digits, use codes below as pValue param in WriteDigit(...) Public Const SYMB_DASH As Byte = $A, ' displays "-" symbol SYMB_E As Byte = $B, ' displays "E" symbol SYMB_H As Byte = $C, ' displays "H" symbol SYMB_L As Byte = $D, ' displays "L" symbol SYMB_P As Byte = $E, ' displays "P" symbol SYMB_BLANK As Byte = $F ' blanks the digit ' Internal registers of the MAX7219 Private Const REG_NO_OP As Byte = $00, REG_DIGIT(8) As Byte = ($01, $02, $03, $04, $05, $06, $07, $08), REG_DECODE_MODE As Byte = $09, REG_INTENSITY As Byte = $0A, REG_SCAN_LIMIT As Byte = $0B, REG_SHUTDOWN As Byte = $0C, REG_DISP_TEST As Byte = $0F Private Dim fSelectedDisp As Byte { **************************************************************************** * Name : SelectDisplay * * Purpose : Selects active display * * param : pDisplayIndex = display index. * * - First display module = 0 * * - pDisplayIndex must be < MAX7219_DISP_COUNT * **************************************************************************** } Public Sub SelectDisplay(pDisplayIndex As Byte) If pDisplayIndex >= MAX7219_DISPLAY_COUNT Then pDisplayIndex = MAX7219_DISPLAY_COUNT EndIf fSelectedDisp = pDisplayIndex End Sub { **************************************************************************** * Name : GetSelectedDisplay() * * Purpose : returns actually selected Max7219 module * **************************************************************************** } Public Function GetSelectedDisplay() As Byte result = fSelectedDisp End Function ' MAX7219 command format is ' [D15][D14][D13][D12] [D11 - D8] [D7 - D0] ' [ x ][ x ][ x ][ x ] [ REG_ADDR] [msb DATA lsb] { **************************************************************************** * Name : WriteReg * * Purpose : Writes pData byte to register address given by pRegAddress * **************************************************************************** } Public Sub WriteReg(pRegAddress As Byte, pData As Byte) Dim i, count As Byte CS = 0 ' must be set here mainly because of Max7221 count = MAX7219_DISPLAY_COUNT-fSelectedDisp-1 For i = 1 To count WriteByte(REG_NO_OP) WriteByte($00) Next WriteByte(pRegAddress) WriteByte(pData) count = fSelectedDisp For i = 1 To count WriteByte(REG_NO_OP) WriteByte($00) Next CS = 1 End Sub { **************************************************************************** * Name : SetDecodeMode * * Purpose : Sets de Max7219 decode mode for each digit * * param : pDecodeMask = a 8 bit value. Setting bit to 0/1 * * disables/enables the B-Decoding for concerned digit * * When the code B decode mode is used, the decoder * looks only at the lower nibble of the data in the digit * registers (D3–D0), disregarding bits D4–D6. D7, which * sets the decimal point (SEG DP), is independent of the * decoder and is positive logic (D7 = 1 turns the decimal * point on). * When no-decode is selected, data bits D7–D0 correspond * to the segment lines of the MAX7219/MAX7221. * Table 6 shows the one-to-one pairing of each data bit * to the appropriate segment line. * **************************************************************************** } Public Inline Sub SetDecodeMode(pDecodeMask As Byte) WriteReg(REG_DECODE_MODE, pDecodeMask) End Sub { **************************************************************************** * Name : DisplayOn * * Purpose : Enables the display * **************************************************************************** } Public Inline Sub DisplayOn() WriteReg(REG_SHUTDOWN, $1) End Sub { **************************************************************************** * Name : DisplayOff * * Purpose : Shuts down the display * **************************************************************************** } Public Inline Sub DisplayOff() WriteReg(REG_SHUTDOWN, $0) End Sub { **************************************************************************** * Name : SetNumberOfDigits * * Purpose : Sets the number of digits availble in the display * * param : pNumberOfDigits = value from 1 to 8 * * * * WARNING:::: DANGEOUROUS !!!!! * * * * If the scan-limit register is set for three digits or less, * * individual digit drivers will dissipate excessive amounts * * of power. Consequently, the value of the RSET resistor * * must be adjusted according to the number of digits displayed, * * to limit individual digit driver power dissipation. * * * * This function has been added only for debug purpose or * * - when the Max7219 is used a LED driver (i.e Decode mode disabled) * * - when the Max7219 is cascaded with another Max7219 that is used to * * less than 8 digits * **************************************************************************** } Public Sub SetNumberOfDigits(pNumberOfDigits As Byte) If pNumberOfDigits > 8 Then pNumberOfDigits = 8 ElseIf pNumberOfDigits < 1 Then pNumberOfDigits = 1 EndIf WriteReg(REG_SCAN_LIMIT, pNumberOfDigits-1) End Sub { **************************************************************************** * Name : SetBrightness * * Purpose : Sets de Max7219 brightness * * param : pIntensity = a 8 bit value between 0 to 15 (16 steps) * **************************************************************************** } Public Inline Sub SetBrightness(pIntensity As Byte) WriteReg(REG_INTENSITY, (pIntensity And $F)) End Sub { **************************************************************************** * Name : SetDisplayTestMode * * Purpose : Enables/Disables de Max7219 test mode * * param : pDispTestEnabled = True/False to Enable/Disable test mode * **************************************************************************** } Public Sub SetDisplayTestMode(pDispTestEnabled As Boolean) If pDispTestEnabled Then WriteReg(REG_DISP_TEST, 1) Else WriteReg(REG_DISP_TEST, 0) EndIf End Sub { **************************************************************************** * Name : WriteDigit * * Purpose : writes a value to position pDigitPos * * param : pDigitPos = digit position from 0 to 7 * * pValue = value to write to at pDigitPos digit position * * values from 0 to 9 displays corresponding digit * * $A = displays "-" symbol * * $B = displays "E" symbol * * $C = displays "H" symbol * * $D = displays "L" symbol * * $E = displays "P" symbol * * $F = blanks the digit * Use the constants defined at top of this module * SYMB_DASH As Byte = $A, ' displays "-" symbol * SYMB_E As Byte = $B, ' displays "E" symbol * SYMB_H As Byte = $C, ' displays "H" symbol * SYMB_L As Byte = $D, ' displays "L" symbol * SYMB_P As Byte = $E, ' displays "P" symbol * SYMB_BLANK As Byte = $F ' blanks the digit * * * pDecimalPointOn = True/False Switch On/Off decimal point led * **************************************************************************** } Public Sub WriteDigit(pDigitPos As Byte, pValue As Byte, pDecimalPointOn As Boolean = false) pValue.Booleans(7) = pDecimalPointOn WriteReg(REG_DIGIT(pDigitPos), pValue) End Sub { **************************************************************************** * Name : BlankDisplay * * Purpose : Writes blank to each digit of the display * * Note : this is **NOT** the same as DisplayOff() which puts the disp * * in shutdown mode * **************************************************************************** } Public Sub BlankDisplay() Dim i As Byte For i = 0 To 7 WriteDigit(i, $F, false) Next End Sub { **************************************************************************** * Name : WriteValue * * Purpose : Writes a value to the display. * * Param : pStr = string containing a formate value to display * * Blanks and special symbols ("-", "H", "L", "E", "P", " ", "." * * are handled automatically by this routine * **************************************************************************** } Public Sub WriteValue(pStr As String) Dim i, value, curDigit As Byte Dim NextDigitHasDecPoint As Boolean NextDigitHasDecPoint = false curDigit = 0 'Length(pStr)-1 For i = Length(pStr)-1 To 0 Step -1 value = pStr(i) If value = 45 Then ' "-" value = SYMB_DASH ElseIf value = 32 Then ' " " value = SYMB_BLANK ElseIf value = 72 Then ' "H" value = SYMB_H ElseIf value = 76 Then ' "L" value = SYMB_L ElseIf value = 69 Then ' "E" value = SYMB_E ElseIf value = 80 Then ' "P" value = SYMB_P ElseIf value = 46 Then ' "." NextDigitHasDecPoint = true Continue EndIf WriteDigit(curDigit, value, NextDigitHasDecPoint) NextDigitHasDecPoint = false Inc(curDigit) Next End Sub { **************************************************************************** * Name : InitChip * * Purpose : Initialize the MAX7219 CHIP * **************************************************************************** } Public Sub InitChip() Dim i As Byte For i = 0 To MAX7219_DISPLAY_COUNT-1 SelectDisplay(i) SetNumberOfDigits(8) DisplayOn() SetDecodeMode(%11111111) // enables B-Decoding for all digits SetBrightness($A) // can be up to 15. intensity 0 means display off WriteReg(REG_NO_OP, $FF) BlankDisplay() Next SelectDisplay(0) End Sub { **************************************************************************** * Name : Initialize * * Purpose : Initializes module and included modules * **************************************************************************** } Public Sub Initialize() #If MAX7219_SPI_SOFTWARE SSPI.Initialize SSPI.SetClock(spiIdleHigh) #Else SPI.SetAsMaster(spiOscDiv16) SPI.SetClock(spiIdleHigh, spiRisingEdge) SPI.SetSample(spiSampleMiddle) EnableSPI() #EndIf Output(CS) High(CS) InitChip() End Sub Initialize()
Modified Hardware SPI module
{ **************************************************************************** * Name : SPI.BAS * * Author : John Barrat * * : David John Barker * * Notice : Copyright (c) 2006 Mecanique * * : All Rights Reserved * * Date : 26/05/2006 * * Version : 1.1 Changed WriteByte() param to WREG for silicon workaround * * : 1.0 Release * * : ============================================================= * * Notes : This post was made on the Swordfish forum by Steve B on * * : April 8th 2007... * * : I was having some trouble with the SPI module as well. I am * * : using an 18F2620, Silicon A4, and was getting some * * : inconsistent reads/writes, especially when doing a lot of * * : TX/RX one after another. After looking at the errata, I * * : changed the ReadByte and WriteByte routines as follows... * * : * * : Public Function ReadByte() As SSPBuffer * * : SSPIF = 0 * * : SSPBuffer = 0 * * : Repeat * * : ClrWDT * * : Until SSPIF = 1 * * : End Function * * : * * : Public Sub WriteByte(pData As WREG) * * : SSPIF = 0 * * : SSPBuffer = pData * * : Repeat * * : ClrWDT * * : Until SSPIF = 1 * * : End Sub * * : * * : Now they use the SSP Interrupt flag instead of the Buffer * * : Full flag (SSPSTAT.0). I also added a subroutine to enable * * : the SPI module * * : * * : Public Sub EnableSPI() * * : Dim pDummy As Byte * * : Enabled = true * * : pDummy = SSPBuffer * * : SSPIF = 0 * * : End Sub * **************************************************************************** } Module SPI Include "system.bas" // map registers to SSP(x) #if _mssp = 0 #error _device + " does not support MSSP" // single SSP... #elseif _mssp = 1 Dim SSPControl1 As SSPCON1, SSPStatus As SSPSTAT, SSPBuffer As SSPBUF, SCK As PORTC.3, SDI As PORTC.4, SDO As PORTC.5, _SS As PORTA.5 // has more than one SSP module... #else Dim // -> MSSP2 SSPControl1 As SSP1CON1, // as SSP2CON1 SSPStatus As SSP1STAT, // as SSP2STAT SSPBuffer As SSP1BUF, // as SSP2BUF SCK As PORTC.3, SDI As PORTC.4, SDO As PORTC.5, _SS As PORTA.5 #endif // SSPSTAT bitnames Public Dim BF As SSPStatus.0, // buffer full (receive and transmit) SMP As SSPStatus.7, // read sample mode CKE As SSPStatus.6 // clock edge control // SSPCON1 bitnames, master mode only... Public Dim WCOL As SSPControl1.7, // write collision Detect SSPOV As SSPControl1.6, // receive overflow SSPEN As SSPControl1.5, // synchronous receive enable CKP As SSPControl1.4, // clock polarity // synchronous mode select bits, %00XX for master mode SSPM3 As SSPControl1.3, // always zero SSPM2 As SSPControl1.2, // slave Mode SSPM1 As SSPControl1.1, // clock Mode (MSB) SSPM0 As SSPControl1.0 // clock Mode (LSB) // interrupt flag Public Dim SSPIF As PIR1.3 Public Const spiOscDiv4 = 0, // master mode FOSC/4 spiOscDiv16 = 1, // master mode FOSC/16 spiOscDiv64 = 2, // master mode FOSC/64 spiOscTimer2 = 3, // master mode TMR2 provides clock spiSlaveSSEnabled = 4, // slave mode slave synch enabled spiSlaveSSDisabled = 5, // slave mode slave synch disabled // clock idle and edge settings (SPI Mode compatible) spiRisingEdge = $40, // data transmitted on rising edge of clock spiFallingEdge = $00, // data transmitted on falling edge of clock spiIdleHigh = $10, // idle state for clock is high spiIdleLow = $00, // idle state for clock is low // RX data sampling settings (SSPStatus.6 - SMP) spiSampleEnd = $80, // input data sampled at end of data output time spiSampleMiddle = $00 // input data sampled at middle of data output time // local helper aliases... Dim FBufferIsFull As SSPStatus.Booleans(0) // BF as boolean // public aliases... Public Dim Enabled As SSPControl1.Booleans(5), // SSPEN as boolean Overflow As SSPControl1.Booleans(6), // SSPOV as boolean WriteCollision As SSPControl1.Booleans(7) // WCOL as boolean { **************************************************************************** * Name : SetAsMaster * * Purpose : Set SPI to master mode * * : spiOscDiv4 (DEFAULT), spiOscDiv16, spiOscDiv64, spiOscTmr2 * **************************************************************************** } Public Sub SetAsMaster(pMode As Byte = spiOscDiv4) SSPControl1 = SSPControl1 And $F0 SSPControl1 = SSPControl1 Or pMode Output(SDO) Output(SCK) End Sub { **************************************************************************** * Name : SetAsSlave * * Purpose : Set SPI to slave mode * * : spiSlaveSSEnabled, spiSlaveSSDisabled (DEFAULT) * **************************************************************************** } Public Sub SetAsSlave(pMode As Byte = spiSlaveSSDisabled) SSPControl1 = SSPControl1 And $F0 SSPControl1 = SSPControl1 Or pMode Output(SDO) Input(SCK) Input(_SS) End Sub { **************************************************************************** * Name : SetSample * * Purpose : Sets when the SPI input data in sampled * * : spiSampleEnd, spiSampleMiddle. For slave mode, sample should * * : always be spiSampleMiddle * **************************************************************************** } Public Sub SetSample(pSample As Byte) SSPStatus = SSPStatus And $7F SSPStatus = SSPStatus Or pSample End Sub { **************************************************************************** * Name : SetClock * * Purpose : Set SPI idle state and data transmission clock edge * * : pIdle -> spiIdleHigh, spiIdleLow * * : pEdge -> spiRisingEdge, spiFallingEdge * **************************************************************************** } Public Sub SetClock(pIdle As Byte, pEdge As Byte) SSPControl1 = SSPControl1 And $EF SSPControl1 = SSPControl1 Or pIdle If pIdle = spiIdleHigh Then pEdge = Not pEdge And $BF EndIf SSPStatus = SSPStatus And $BF SSPStatus = SSPStatus Or pEdge End Sub { **************************************************************************** * Name : ReadByte * * Purpose : Read a single byte from the SPI bus * **************************************************************************** } 'Public Function ReadByte() As SSPBuffer ' SSPBuffer = 0 ' Repeat ' ClrWDT ' Until FBufferIsFull 'End Function Public Function ReadByte() As SSPBuffer SSPIF = 0 SSPBuffer = $FF Repeat ClrWDT Until SSPIF = 1 ReadByte = SSPBuffer End Function Public Sub WriteByte(pData As Byte) SSPIF = 0 SSPBuffer = pData Repeat ClrWDT Until SSPIF = 1 End Sub { **************************************************************************** * Name : WriteByte * * Purpose : Write a single byte to the SPI bus * **************************************************************************** } 'Public Sub WriteByte(pData As WREG) ' SSPBuffer = pData ' Repeat ' ClrWDT ' Until FBufferIsFull 'End Sub Public Sub EnableSPI() Dim pDummy As Byte Enabled = true pDummy = SSPBuffer SSPIF = 0 End Sub