PIC C, A Four Digit 7-Seg LED Display


// Program 5832_1.C (CCS PCM - PIC16F84)
//
// Illustrates how to interface with four 7-segment LEDs using an
// Allegro UCN5832A 32 bit shift register.  Note that this is a sink
// device and inverts the data in the shift register.
//
// Note that we sell the UCN5832A, common anode 7-segment LED displays
// and 330 Ohm resistors.  If you want a drawing showing the connection
// of the 5832 to the four 7-seg LEDs, please ask.
//
// Program assumes the least digit first and the pattern is shifted
// out least significant bit first.  Decimal point is on most significant
// bit.
//
// This concept might be used with similar Allegro devices such as the
// 5841 (8-bit, sink) and 5818 (32-bit source).
//
// PIC16F84			UCN5832
//
// RB.3	-----------------------> DAT
// RB.2 -----------------------> CLK
// RB.1 -----------------------> STB
// RB.0 -----------------------> OE
//
// Routine displays the characters '0' - 'F' on least significant digit.
// Other digits are blanked.  The message "HELP is displayed" and flashed
// a number of times.
//
// Illustrates how to display four decimal digits with leading
// zero suppression, sign plus three decimal digits and four hexadecimal
// digits.
//
// Illustrates how to insert a decimal point.
//
// Copyright, Peter H. Anderson, Baltimore, MD, Apr, '99

#case

#include <16F84.h>
#include <defs_f84.h>

#define MINUS 0x01	// display a minus sign
#define BLANK 0x00	// blanks LED

#define OE rb0
#define STB rb1
#define CLK rb2
#define DAT rb3

void convert_to_dec_patt_unsigned(long val, int *patts);
void convert_to_dec_patt_signed(signed long val, int *patts);
void convert_to_hex_patt(long val, int *patts);

void display(int *patts);
long pow_10(int n);
void out_8(int patt);

// delay routines
void delay_ms(long t);
void delay_10us(int t);

// note that the patts are declared globally as they can not be passed
// by reference.  That is, pointers to const arrays are not permitted.

// a table used in deriving these patterns appears at the end of this 
// program.

int const hex_digit_patts[16]=
//    0     1     2     3     4     5     6     7
   {0x7e, 0x30, 0x6d, 0x79, 0x33, 0x5b, 0x5f, 0x70,
//    8     9     A     B     C     D     E     F
    0x7f, 0x7b, 0x77, 0x1f, 0x4e, 0x3d, 0x4f, 0x47};

int const help_patts[4] = {0x37, 0x4f, 0x0f, 0x67};
			  // 	H     E     L     P

#define ARRAY_TECNIQUE // see note at function pow_10
void main(void)
{

   int disp_patts[4], n;
   signed long val;

   TRISB = 0xf0;	// lower nibble are outputs
   STB = 0;  CLK = 0;  OE = 0;
   // set STB to 0, CLK to 0 and Output disabled

   // display 0 - F on least sig 7-seq.  Others are balnked.

   disp_patts[1] = BLANK; //BLANK;
   disp_patts[2] = BLANK;
   disp_patts[3] = BLANK;// most sig digit

   for (n=0; n<0x10; n++)
   {
       disp_patts[0] = hex_digit_patts[n];	// least sig digit
       display(disp_patts);
       OE = 1;		// output enable
       delay_ms(1000);
   }

   // display "HELP"

   for (n=0; n<4; n++)
   {
      disp_patts[n] = help_patts[3-n];	// output least sig digit first
   }
   display(disp_patts);

   for (n=0; n<10; n++)	// flash the message on and off ten times
   {
      OE = 0;	// turn it off
      delay_ms(100);
      OE = 1;	// turn it on
      delay_ms(100);
   }

   // display 207 in decimal format with leading zero suppression
   val = 207;
   convert_to_dec_patt_unsigned(val, disp_patts);
   display(disp_patts);
   delay_ms(5000);

   // display as signed plus 3 digits with leading zero suppression
   val = -7;
   convert_to_dec_patt_signed(val, disp_patts);
   display(disp_patts);
   delay_ms(5000);

   // display a value in hexadecimal format, no leading zero suppression
   val = 0x3f3c;
   convert_to_hex_patt(val, disp_patts);
   display(disp_patts);
   delay_ms(5000);

   // display 98.6
   val = 986;
   convert_to_dec_patt_unsigned(val, disp_patts);
   // ele 3 is blank, ele 2 is 9, ele 1 is 8, ele 0 is 6
   disp_patts[0] = disp_patts[0] | 0x80;  // left hand dec point
   display(disp_patts);
   delay_ms(5000);
}

convert_to_dec_patt_unsigned(long val, int *patts)
// converts val to four digits and place corresponding hex digit patterns
// in patt[]
{
   int n, suppress_zero = TRUE;
   long x;
   for (n=0; n<4; n++)
   {
       x = pow_10(3-n);	// calculate 1000, 100, 10, 1
       if ((val/x == 0) && (suppress_zero) && (n!=3))
       {
          patts[3-n] = BLANK;     // leading zero suppression
       }
       else
       {
          suppress_zero = FALSE;
          patts[3-n] = hex_digit_patts[val/x];
       }
       val = val%x;
    }
}

convert_to_dec_patt_signed(signed long val, int *patts)
// patt[3] is for the sign, either MINUS or BLANK
// hex patterns for the three digits are paced in elements 2, 1 and 0
{
   int n, suppress_zero=TRUE; // could have used a short for suppress_zero
   long x;

   if (val < 0)
   {
      patts[3] = MINUS;
   }
   else
   {
      patts[3] = BLANK;
   }
   val = -val;		// convert to a positive number

   for (n=0; n<3; n--)
   {
       x = pow_10(2-n);	// compute 100, 10, 1
       if ((val/x == 0) && (suppress_zero) && (n!=2))
       {
          patts[2-n] = BLANK;
       }
       else
       {
          suppress_zero = FALSE;
          patts[2-n] = hex_digit_patts[val/x];
       }
       val = val%x;
    }
}

void convert_to_hex_patt(int val, int *patts)
// converts to four nibbles and places corresponding hex patterns
// in patts[]
{
   int n;
   for(n=0; n<4; n++)
   {
      patts[n] = hex_digit_patts[val & 0x0f];
                           // patt[0] is least sig digit
      val = val >> 4;	// next nibble
   }
}

long pow_10(int n)

// Note the multiply technique was done simply to see if it could be done.
// The array technique which uses an array is far more efficient.
#ifdef ARRAY_TECHNIQUE  
{
   long const v[4] = {1, 10, 100, 1000};
   return v[n]
}
#else
{
   long v=1;
   while(n)
   {
      v = v * 10;
      --n;
   }
   return(v);
}
#endif

void display(int *patts)
// outputs the 4 values in patts[], beginning with patts[0].
{
    int n;
    STB = 0;	// be sure latch is isolated from shift register
    CLK = 0;
    for (n=0; n<4; n++)
    {
       out_8(patts[n]);
    }
    STB = 1;	// transfer shift register to latch
    STB = 0;
}

void out_8(int patt)
// shifts out an 8-bit pattern, least sign bit first
{
   int n;
   for (n=0; n<8; n++)
   {
      DAT = (patt>>7)&0x01;
      CLK = 1;
      CLK = 0;
      patt = patt << 1;
   }
}

// delay routines

void delay_10us(int t)
{
#asm
      BCF STATUS, RP0
DELAY_10US_1:
      CLRWDT
      NOP
      NOP
      NOP
      NOP
      NOP
      NOP
      DECFSZ t, F
      GOTO DELAY_10US_1
#endasm
}

void delay_ms(long t)	// delays t millisecs
{
   do
   {
     delay_10us(100);
   } while(--t);
}

// The following was used in developing the values for hex_digit_patts[]
// and help_patts[]

#ifdef XXX
		  P7 P6 P5 P4 P3 P2 P1 P0
	Char	      a  b  c  d  e  f  g   	HEX

	0	      1  1  1  1  1  1  0	$7E
	1	      0  1  1  0  0  0  0       $30
	2	      1  1  0  1  1  0  1 	$6D
	3	      1  1  1  1  0  0  1	$7F
	4	      0	 1  1  0  0  1  1       $33
	5	      1  0  1  1  0  1  1	$5B
	6	      1  0  1  1  1  1  1	$5f
	7	      1  1  1  0  0  0  0	$70
	8	      1  1  1  1  1  1  1	$7F
	9	      1  1  1  1  0  1  1	$7B
	A	      1  1  1  0  1  1  1	$77
	B	      0  0  1  1  1  1  1	$1F
	C	      1  0  0  1  1  1  0	$4E
	D	      0  1  1  1  1  0  1	$3D
	E 	      1  0  0  1  1  1  1	$4F
	F	      1  0  0  0  1  1  1	$47

	H	      0  1  1  0  1  1  1	$37
	E	      1  0  0  1  1  1  1	$4F
	L	      0  0  0  1  1  1  1	$0F
	P             1  1  0  0  1  1  1       $67
	-	      0	 0  0  0  0  0  1	$01
	blank	      0  0  0  0  0  0  0	$00

#endif