Sunday, 18 November 2012

PIC programming - Interfacing Seven Segment Display to Microcontroller

In this tutorial, PIC microcontroller will be interfaced with 8 digits 7-segment display using IC named MAX7221.
MAX7221 is an compact, serial input/output common-cathode display driver that interfaces microcontrollers to 7 segment numeric LED displays of up to 8 digits.

This IC works with only common cathode 7 segment LED display.
The reason of using MAX7221 is that, we require only 3 wires of microcontroller to display upto 8 digits.


Proteus snapshot of PIC with 7 segment display using MAX7221
Snapshot of Proteus simulation
    

First lets understand the hardware connections:

  1. Connect the clock pins (SCK) of PIC and MAX7221 i.e. RB1 of PIC to pin no. 13 of MAX7221.
  2. Connect the SDO of PIC to DIN of MAX7221 i.e. RC7 of PIC to pin no. 1 of MAX7221. The serial data from PIC to MAX7221 will pass through this connection.
  3. Connect the CS (Chip Select) pin of MAX7221 to any output enabled PIC pin. Here we use RB0 pin (SDI - Serial Data Input is not used in this program, so this pin is configured as output and we are using it as any other output pin). 
  4. Connect DOUT of MAX7221 to ground, as we are using a single MAX7221 IC (This pin is used to daisy-chain several MAX7221’s).
  5. MAX7221 allows display brightness to be controlled with an external resistor (RSET) connected between V+ and ISET. The peak current sourced from the segment drivers is nominally 100 times the current entering ISET. This resistor can either be fixed or variable to allow brightness adjustment from the front panel. Its minimum value should be 9.53kΩ, which typically sets the segment current at 40mA. We select the register of value 43K ohms and connect its one end to 5 Volts and other to ISET pin.
  6. Now connect the segment driver pins A to DP to the respective pins of the common-cathode display. If you are using two 4-digit displays as shown in diagram, then there will be 2 connections from each pin of MAX7221 from A to DP; one set of connections for each display.
  7. Connect the Digit Drive lines DIG0 to DIG7 to the respective display. Here there will be a single connection from each pin of display driver MAX7221. 
  8. The supply connections are not visible in Proteus. The PIC, MAX7221 & 7-segment displays, all require 5V and Ground.

The Program for PIC18F4550 is given below. 

You can use it for any other PIC controller with few changes in configuration bits.

#include<p18f4550.h>
#pragma config FOSC = HS          //High Speed Crystal Oscillator
#pragma config WDT = OFF         //Watch Dog Timer disabled
#pragma config LVP = OFF           //Single-Supply ICSP disabled
#pragma config MCLRE = OFF     //MCLR pin disabled. Now there is no need to give High Logic on this pin to keep the controller functioning
#define  cs   PORTBbits.RB0

void display(int , int);
void delay(unsigned int);

void main(void)
{
TRISBbits.TRISB0 = 0;         //used for Chip Select function; configured as output
TRISBbits.TRISB1 = 0;         //used for SCK; configured as output
TRISCbits.TRISC7 = 0;         //used for transferring serial data; configured as output

//SPI
 SSPSTAT = 0xC0;                 //SSPSTAT=11000000  i.e. configure SPI settings
 SSPCON1 = 0x20;                //SSPCON1=00100000 Enables serial port and configures SCK, SDO and SDI as serial port pins & clock=FOSC/4
 display(0x0B,0X07);              //scanlimit(digits 0-7)
 display(0x09,0X00);               //decode mode off
 display(0x0A,0X0F);              //intensity highest
 display(0x0C,0X01);              //shutdown mode off
 display(0x0F,0X00);               //test mode off

 display(1,'9');
 display(2,'2');
 display(3,'3');
 display(4,'4');
 display(5,'5');
 display(6,'P');
 display(7,'d');
 display(8,'b');
 delay(1500);
 display(1,'6');
 display(2,'1');
 display(3,'0');
 display(4,'t');
 while(1);
}

void display(int addr, int value)
{
  cs = 0;                                         //enable MAX7221 to receive new value
  SSPBUF = addr;                         //sending the address of digit serially
  while(!SSPSTATbits.BF);            //wait until the address is sent
  switch(value)
   {
    case '0' : value = 126; break;
    case '1' : value = 48;   break;
    case '2' : value = 109; break;
    case '3' : value = 121; break;
    case '4' : value = 51;   break;
    case '5' : value = 91;   break;
    case '6' : value = 95;   break;
    case '7' : value = 112; break;
    case '8' : value = 127; break;
    case '9' : value = 115; break;
    case 't' : value = 15;    break;
    case 'P' : value = 103; break;
    case 'b' : value = 31;   break;
    case 'd' : value = 61;   break;
   }
 
  SSPBUF = value;                    //sending the value which is to be displayed
  while(!SSPSTATbits.BF);        //wait until the value is sent
  cs = 1;                                     //disable MAX7221 to receive any new value
}


void delay(unsigned int time)      //delay function
{
 unsigned int i,j;
 for(i=0;i<time;i++)
   for(j=0;j<120;j++); 

}

Now lets understand how the program works:


#include<p18f4550.h>
#pragma config FOSC = HS          //High Speed Crystal Oscillator
#pragma config WDT = OFF         //Watch Dog Timer disabled
#pragma config LVP = OFF           //Single-Supply ICSP disabled
#pragma config MCLRE = OFF     //MCLR pin disabled. Now there is no need to give High Logic on this pin to keep the controller functioning
#define  cs   PORTBbits.RB0

void display(int , int);
void delay(unsigned int);

void main(void)
{
TRISBbits.TRISB0 = 0;          //used for Chip Select function; configured as output
TRISBbits.TRISB1 = 0;          //used for SCK; configured as output
TRISCbits.TRISC7 = 0;          //used for transferring serial data; configured as output

//SPI
 SSPSTAT = 0xC0;                //SSPSTAT=11000000  i.e. configure SPI settings
 SSPCON1 = 0x20;               //SSPCON1=00100000 Enables serial port and configures SCK, SDO and SDI as serial port pins & clock=FOSC/4


First set all the general configurations and set RB0, RB1 & RC7 as output ports for serial communication.
Then enable the SPI mode of PIC and set the SPI clock as Fosc/4. Do this by setting the following values:
   Status Register - SSPSTAT = 11000000 i.e. SSPSTAT = 0xC0;  &

   MSSP CONTROL - REGISTER 1 SSPCON1 = 00100000 i.e. SSPCON1=0x20;

 display(0x0B,0X07);              //scanlimit(digits 0-7)
 display(0x09,0X00);               //decode mode off
 display(0x0A,0X0F);              //intensity highest
 display(0x0C,0X01);              //shutdown mode off
 display(0x0F,0X00);               //test mode off


display is the user-defined function which sends the value to MAX7221 serially. It has 2 arguments; first argument is the address at which the data is to be sent and second argument is the data which is to be sent to MAX7221.
Before sending any data to display, we need to configure some control registers of MAX7221. They are addressed directly so that individual digits can be updated and retain data as long as V+ typically exceeds 2V. The control registers consist of scan limit, decode mode, display intensity, shutdown, and display test.

  • The scan-limit register sets the total number of digits that are displayed, from 1 to 8. Here we are using 8 digits and hence we will send a value 0x08 to MAX7221. The address of scan-limit register is 0x0B. Hence we send a value of 0x08 to the address 0x0B. If you use less than 8 digits, then refer to the datasheet of MAX7221 to know which value needs to be sent.
  • The decode-mode register sets BCD code B (0-9, E, H, L, P, and -) or no-decode operation for each digit. It means that we can configure MAX7221 IC to display numeric numbers and some alphabets without us having to define each segment of 7 LED of segment display individually. But we won't use this function and we will ourselves define each and every number/alphabet, so that we can display any alphabet we want. Hence we will send 0x00 for no decode mode to the decode mode register which has an address 0x09.
  • The intensity register controls the brightness of the 7-segment display. The address of this register is 0x0A and we send a value of 0x0F for brightest intensity.
  • The shutdown register shuts the display off to save power. For normal mode operation, we need to send 0x01 to the shutdown register at an address 0x0C. This turns off the shutdown mode.
  • The display-test register turns all LEDs ON. For normal mode operation, we need to turn off this mode by sending the value 0x00 to the adress 0x0F. 
Now we can send the actual data which is to be displayed by calling the 'display'  function. It has 2 arguments: first argument takes the position at which the data is to be displayed and the 2nd argument takes the value which is to be displayed.
 display(2,'2');
 display(3,'3');
 display(4,'4');
 display(5,'5');
 display(6,'P');
 display(7,'d');
 display(8,'b');
 delay(1500);
 display(1,'6');
 display(2,'1');
 display(3,'0');
 display(4,'t');
 while(1);
}

For example, suppose we call display(1,'9'); This will display digit 9 at position no. 1 of the 7-segment display.


Now lets understand the working of 'display' function:

In order to understand the working of display function, first we need to understand how MAX7221 accepts the data.
MAX7221 accepts data in 16 bits format as shown below
Serial Data format for display driver MAX7221
So we need to send 16 bits in which 8 bits are the data(value that is to be displayed), 4 bits are the address at which the data is to be displayed and remaining 4 bits are don't care(we keep them as 0000).

As PIC is capable of sending only 8 bits of data at a time, we break 16 bits, that is to be sent, into 2 parts:
First we send the 4 bit address appended with 0000. This will be the digit number of the 7 segment display i.e. on which of the 8 digits we want the value to be displayed. The address of each digit number is given below:
Register Address Map of seven segment display driver MAX7221




For example if you want to display on digit no. 4, then the address is 0x05 or just 5(in decimal).
Remember that counting of the digit starts from 0 and ends at 7; while address starts from 1 and ends at 8.
 

Here the addresses of the control registers of MAX7221 are also given. 




















After sending the address, we send the value of the data which is to be displayed.
As we have selected 'no-decode' mode, we ourselves need to define each digit and alphabet.
Here's how we define each digit and alphabet.

No decode mode of seven segment display driver MAX7221



Now suppose you want to display the number '1'. For that we need to turn on segment B and segment C of the 7 segment LED.
For doing so, we set D5 & D4 to 1 and all others to 0. Therefore we set the register data to value '00110000' i.e 48(in decimal).
Similarly, if you want to display an alphabet 'P', then you need to turn on LEDs E, F, A, B & G. For doing so, set D6, D5, D2, D1 & D0 to High Logic and all others to Low Logic. Therefore set the register to the value 103.
Similarly, we find the values for all digits and alphabets.









void display(int addr, int value)
{
  cs = 0;                                     //enable MAX7221 to receive new value
  SSPBUF = addr;                     //sending the address of digit serially
  while(!SSPSTATbits.BF);         //wait until the address is sent

First enable MAX7221 IC to receive new data by setting 'cs' to '1' and then send the address serially to MAX7221. The address which is to be sent, is stored in 'addr' variable. Placing the data in SSPBUF buffer, sends the data serially through SDO pin of PIC. Then wait until the data is sent.


Having sent the address, now we need to send the value to display.
  switch(value)
   {
    case '0' : value = 126; break;
    case '1' : value = 48;   break;
    case '2' : value = 109; break;
    case '3' : value = 121; break;
    case '4' : value = 51;   break;
    case '5' : value = 91;   break;
    case '6' : value = 95;   break;
    case '7' : value = 112; break;
    case '8' : value = 127; break;
    case '9' : value = 115; break;
    case 't' : value = 15;    break;
    case 'P' : value = 103; break;
    case 'b' : value = 31;   break;
    case 'd' : value = 61;   break;
   }

  SSPBUF = value;                  //sending the value which is to be displayed
  while(!SSPSTATbits.BF);      //wait until the value is sent

Corresponding to the digit or alphabet that we received in the parameters of display function, we need to set the appropriate value in the register data. For this we use switch case. It changes the data of the 'value' so as to turn on the appropriate LEDs of 7-segment display. Place this value in SSPBUF buffer which will automatically send it serially.


  cs = 1;                                   //disable MAX7221 to receive any new value
}

Having sent the address and the value, disable MAX7221 to receive any new value by setting 'cs' to '1'. MAX7221 will keep on displaying this value until any new value is sent.

void delay(unsigned int time)   //delay function
{
 unsigned int i,j;
 for(i=0;i<time;i++)
   for(j=0;j<120;j++); 

}
This delay function used to insert pauses between the program.

This photograph is from my project.
PIC with 7 segment display driver MAX7221

Download the Proteus, Mplab & Source code files from here