Thursday 29 May 2014

MAX7912 and PIC18F


  MAX7219 Display Controller with the PIC18F4620
by Joe DiMeglio



So i ordered a MAX7912 from ebay - it arrived and then i went looking for some quick code so that i can start playing with it. How wrong was I.  I found bits and peace's here and there but ultimately i ended up writing my own code and thought i'd post it so maybe that it can help some other poor sole.

The 8x8 matrix i received from ebay actually comes on a mounted board with a MAX7219.  I fired up Proteus and wired up my PIC to a MAX7219 and then to my 8x8 LED matrix. This was the hardest part due to the lack of pin out descriptions for the 8x8 matrix within Proteus. 

Probably would have been quicker if i'd just played with it on my bread board. :-)

Here's my MAX7912 8x8 driver. The code was written with CCS C Compiler - i'm sure it could be pretty easily to modify for other C Compilers.

This is my basic driver file. It contains the routines that have been tested with a PICF4620 using its built in hardware SPI. It never made any sense to me to write a software SPI routine when i have a fully working SPI port.  It includes font, that i borrowed from someone on the internet, saved me some trouble putting it together. If find his code again i'll provide a link to his code.



max7212.c 

#use delay (clock=40M)

/*
******************************************************************************* Module     : MAX7219.C
* Author     : Joe DiMeglio
* Description: MAX7219 LED Display Driver Routines
*
*  The host communicates with the MAX7219 using three signals: DATA, CLK, and LOAD using SPI.
*
******************************************************************************/
// CONSTANTS //
#define Chip_Select             PIN_D0       //this is the pin on the PIC that connects to LOAD
#define Chip_Select_Direction   TRIS_D
#define scanLimitAmount         0x07

// - Mode Selection
#define nop                      0x00
#define decode                   0x09
#define brightness               0x0A
#define scanLimit                0x0B
#define shutDown                 0x0C
#define dispTest                 0x0F




// FOR more space, we don't need byte 0 and byte 7.
// We have stripped them out, IF/when we have a need and a bigger PIC,
// then we can put them back IF we want.
// Here we define row values FOR each of the six columns corresponding to the
// Alphabet, from  A through Z.
byte Alphabet[156]=
{
   0x7f, 0x88, 0x88, 0x88, 0x88, 0x7f, // A
   0xff, 0x91, 0x91, 0x91, 0x91, 0x6e, // B
   0x7e, 0x81, 0x81, 0x81, 0x81, 0x42, // C
   0xff, 0x81, 0x81, 0x81, 0x81, 0x7e, // D
   0x81, 0xff, 0x91, 0x91, 0x91, 0x91, // E
   0x81, 0xff, 0x91, 0x90, 0x90, 0x80, // F
   0x7e, 0x81, 0x81, 0x89, 0x89, 0x4e, // G
   0xff, 0x10, 0x10, 0x10, 0x10, 0xff, // H
   0x00, 0x81, 0xff, 0xff, 0x81, 0x00, // I
   0x06, 0x01, 0x81, 0xfe, 0x80, 0x00, // J
   0x81, 0xff, 0x99, 0x24, 0xc3, 0x81, // K
   0x81, 0xff, 0x81, 0x01, 0x01, 0x03, // L
   0xff, 0x60, 0x18, 0x18, 0x60, 0xff, // M
   0xff, 0x60, 0x10, 0x08, 0x06, 0xff, // N
   0x7e, 0x81, 0x81, 0x81, 0x81, 0x7e, // O
   0x81, 0xff, 0x89, 0x88, 0x88, 0x70, // P
   0x7e, 0x81, 0x85, 0x89, 0x87, 0x7e, // Q
   0xff, 0x98, 0x98, 0x94, 0x93, 0x61, // R
   0x62, 0x91, 0x91, 0x91, 0x91, 0x4e, // S
   0xc0, 0x81, 0xff, 0xff, 0x81, 0xc0, // T
   0xfe, 0x01, 0x01, 0x01, 0x01, 0xfe, // U
   0xfc, 0x02, 0x01, 0x01, 0x02, 0xfc, // V
   0xff, 0x02, 0x04, 0x04, 0x02, 0xff, // W
   0xc3, 0x24, 0x18, 0x18, 0x24, 0xc3, // X
   0xc0, 0x20, 0x1f, 0x1f, 0x20, 0xc0, // Y
   0xc3, 0x85, 0x89, 0x91, 0xa1, 0xc3, // Z
};

 byte Symbols[114] =
 {
    0x00, 0x3c, 0x42, 0x81, 0x00, 0x00, // (
    0x00, 0x00, 0x81, 0x42, 0x3c, 0x00, // )
    0x00, 0x00, 0xff, 0x81, 0x00, 0x00, // [
    0x00, 0x00, 0x81, 0xff, 0x00, 0x00, // ]
    0x00, 0x18, 0xe7, 0x81, 0x00, 0x00, //
    0x00, 0x00, 0x81, 0xe7, 0x18, 0x00, //
    0x00, 0x18, 0x24, 0x42, 0x81, 0x00, // <
    0x00, 0x81, 0x42, 0x24, 0x18, 0x00, // >
    0x00, 0x03, 0x0c, 0x30, 0xc0, 0x00, // /
    0x00, 0xc0, 0x30, 0x0c, 0x03, 0x00, // \
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //
    0x00, 0x00, 0xfd, 0xfd, 0x00, 0x00, // !
    0x60, 0x80, 0x8d, 0x8d, 0x90, 0x60, // ?
    0x42, 0x24, 0xff, 0xff, 0x24, 0x42, // *
    0x24, 0xff, 0x24, 0x24, 0xff, 0x24, // #
    0x62, 0x91, 0xff, 0xff, 0x91, 0x4e, // $
    0x66, 0x99, 0x99, 0x66, 0x09, 0x00, // &
    0x42, 0xa4, 0x48, 0x12, 0x25, 0x42, // %
    0x20, 0x3f, 0x20, 0x20, 0x3e, 0x21, // pi
 };




 // latch the bits into the MAX
 VOID pulseCS(void)
 {
    output_bit (Chip_Select, 1);
    delay_us (2) ;
    output_bit (Chip_Select, 0);
 }

 //send data to MAX
 VOID send7219 (byte address, byte data)
 {
    //make a 16 bit variable address byte high, data byte low
    Int16 SerialData;
    SerialData = make16 (address, data);   
    spi_xfer (SerialData); //transfer using spi software
    pulseCS ();
 }

 //Init MAX
 VOID InitMax7219 (void)
 {

  
    output_bit (Chip_Select, 0);                      // Set RD0 pin as hight
    set_tris_d(0x00) ;   
   
    //configure properly
    send7219 (decode, 0x00);                         //no decoding (MSB)
    send7219 (brightness, 0x09);                     //Segment luminosity intensity
    send7219 (scanLimit, scanLimitAmount);       //Scan all 7 digits
    send7219 (shutDown, 0x01);                      //No Shutdown reg.
    send7219 (dispTest, 0x00);                        // No test
 }

 // This is clear matrix function.
 VOID Clear7219 (void)
 {
    byte Addr;
    FOR (Addr = 1; Addr < scanLimitAmount+1; Addr++)
    {
       send7219 (Addr, 0x00) ;
    }
 }

 //Write CHAR to the screen
 VOID WriteChar7219 (byte myChar)
 {
    byte Column, Start_Byte;
   
    Start_Byte = (myChar - 65) * 6; // 65 represents the letter "A" in ASCII code.
   
    // We are using only columns from 2 through 7 FOR displaying the character.
    FOR (Column = 7; Column > 1; Column--)
    {
       send7219 (Column, Alphabet[Start_Byte++]);
    }
 }

So all you would then do is this:

#include <18F4620.h>
#fuses HS,NOLVP,NOWDT
#use delay (clock=40M)
#use fast_io(D)
#USE SPI (MASTER, SPI1, BAUD=1000000, MODE=0, BITS=16, LOAD_ACTIVE=1, STREAM=SPI_1, MSB_FIRST,IDLE=1)

#include <max7912.c>                             //my driver file :-)


#define Chip_Select PIN_D0      //this is the pin on the PIC the LOAD must be connected to.
#define Chip_Select_Direction tris_d(0)     

// Main function.
VOID main()
{
   InitMax7219 ();

   DO // infinite loop.
  {
     // You can write the characters this way, one at a time.

       WriteChar7219("J");
       delay_ms(50);
       WriteChar7219("O");
       delay_ms(50);
      WriteChar7219("E");
      delay_ms(50);
   }WHILE (1); // do forever.
}


So it then occurred to me to have some fun and write some graphic routines. Still in early stages.
Demoone(); scrolls the characters across the screen. While demotwo(); has two bouncing balls bouncing off the edges.





#include <18F4620.h>
#fuses HS,NOLVP,NOWDT
#use delay (clock=40M)
#use fast_io(D)
#USE SPI (MASTER, SPI1, BAUD=1000000, MODE=0, BITS=16, LOAD_ACTIVE=1, STREAM=SPI_1, MSB_FIRST,IDLE=1)
#include <max7912.c>

#define Chip_Select PIN_D0
#define Chip_Select_Direction tris_d(0)

byte VR[8];                           //Video RAM used to draw the screen before sending to the 8x8 matrix

int x=5 ,y=0;                         // starting position of ball one
int xdir=1, ydir=1;                 //the director ball one will go

int x1=0 ,y1=3;                    //starting poistion of ball two
int x1dir=1, y1dir=-1;          //its starting position


//clear the "screen" - its the video ram thats getting cleared
void grx_cls(void)
{
   byte x;
   for (x=0;x<8; x++)
  {
     VR[x] = 0;
   }
}


//send it to the hardware
void grx_display(void)
{
   byte Addr, Row=0;
   FOR (Addr = 1; Addr < 9; Addr++)
   {
     send7219 (Addr, VR[row++]);
   }
}

//light up that LED at x,y position
void grx_setxy(byte x,y)
{
   byte temp;
   temp = 1;
   temp <<=x;
   VR[y]|=temp;        // read row X at column Y
}

//turn off that LED at x,y position
void grx_unsetxy(byte x,y)
{
    byte temp;
    temp = 1;
    temp <<=x;
    temp = 255 - temp;
    VR[y]&=temp;     // read row X at column Y
}



//Write CHAR to the screen
VOID grx_WriteChar(byte myChar)
{
     byte Column, Start_Byte;
    Start_Byte = (myChar - 65) * 6; // 65 represents the letter "A" in ASCII code.

    // We are using only columns from 2 through 7 FOR displaying the character.
    FOR (Column = 7; Column > 1; Column--)
   {
      VR[Column]=Alphabet[Start_Byte++];
   }
}

//scroll left the contents in the video ram
void grx_scroll(byte x,clip,dir)
{
    byte scroll,edge;
    edge=VR[7];                 //save last row

   for (scroll=7;scroll>0;scroll--)
   {
      VR[scroll]=VR[scroll-1];
   }
   VR[0]=edge;                 //put in the first row
}




//anitmate the bouncing balls.
void bouncing(void)
{
     grx_setxy(x,y);      //draw the ball one
     grx_setxy(x1,y1);  //draw ball two
     grx_display();        //display it
     Delay_ms (5) ;
     grx_unsetxy(x,y);   //turn them off
     grx_unsetxy(x1,y1);
     x= x + xdir;           //new position
     y =y + ydir;
     if (x>=7||x<=0)     //check if they hit the edges if so change direction.
    {
       xdir=-xdir;
    }

    if (y>=7||y<=0)
   {
     ydir=-ydir;
    }

    x1= x1 + x1dir;     //same for ball two
    y1 =y1 + y1dir;
    if (x1>=7||x1<=0)
   {
      x1dir=-x1dir;
   }
   if (y1>=7||y1<=0)
  {
     y1dir=-y1dir;
  }
}

//demo scrolling left
VOID demoone(VOID)
{

    grx_scroll(1,0,0);
    delay_ms(5);
    grx_display(); //display it

}

 VOID demotwo(VOID)
{
   bouncing();
}


// Here we have the main function.
VOID main()
{

    Delay_ms(100);
    InitMax7219 ();

    grx_cls(); // clear RAM
    grx_display(); //display it
    grx_WriteChar('X');
    grx_display(); //display it

   DO // infinite loop.
   {
   // You can write the characters this way, one at a time.

    demoone();
    //demotwo();

  
   }WHILE (1); // do forever.
}

 One more video of it scrolling - see it works. :-)



My circuit diagram too.
enjoy.



References:
http://www.bristolwatch.com/PIC18F2550/PIC18F2550_MAX7219.htm
http://www.maximintegrated.com/en/products/power/display-power-control/MAX7219.html

3 comments:

  1. Can I use this microchip picf4620 for bigger displys like lcd tvs

    ReplyDelete
  2. Thanks but I'm not sure I understand your question . The picf4620 is the processor which drives the max chip which in turn drives the 8x8 matrix.

    For bigger displays you'd need to change the max chip wih the equivalent chip/video chip.

    This is an old circuit and there are so many other easier options available - that other than fun factor probably wouldn't use this chip. Eg raspberry Pi

    :-)

    ReplyDelete