Programming PICDEM LCD’s LCD Driver

 

I would like to acknowledge Dogus Cubuk, class of 2010, who wrote the initial version of this document.

  back

 

The PICDEM LCD is provided by Microchip as a way of exploring their microcontrollers with LCD drive capability.  However, the documentation on the boards is sparse and often incorrect.  There is also very little documentation in C.  This document provides some working code (for the CCS C compiler, but easily modifiable), as well as a brief description of how the LCD can be understood

 

 

Image from Microchip web site.

 

 

Background

 

The PICDEM LCD uses characters in a “starburst” format (so called because the segments form a starburst at the center of each character).

On the display on the board there are 8 of these characters.  Letters are displayed by driving the individual segments with a time varying function.  However, the PIC makes this easier for us – we just have to specify which segments we want to be lit up, and the PIC’s hardware takes care of generating the appropriate signal.

 

Let’s first examine the case of a 7 segment display (plus decimal point) with two digits.

 

 

Digit 1

Digit 2

 

It appears, at first glance, that this would require 16 lines coming from the microcontroller to control the LCD.  However, it can be done with much fewer.   An LCD typically has two sets of inputs: segment inputs and COM (or common) inputs.  Typically a display like the one above might have 10 lines, 8 segment lines, and 2 COM lines.  A simple arrangement would be the following (“S” denotes the segment number, “C” denotes the COM number):

With this layout, if we wanted to write the number “1.3” we would first assert S1, S2, and S7 (segments 1, 2 and 7) while asserting COM0 (but not COM1); this puts a “1.” on the left digit.  We then assert S0, S1, S2, S3, and S6 while also asserting COM1 (but not COM0); this puts a “3” on the right digit.  We alternate between these two patterns quickly enough that the eye cannot see it.  Note: we don’t actually have to do this, the PIC will do it for us. 

 

Unfortunately, LCD displays are not usually so simply laid out.  A more complicated layout is shown below in which each COM0 affects both digits (as does COM1).

 

 

However, the task of displaying information isn’t changed.  To display “23” we would light up S0, S1, S3, S4, S5 and S7 with COM0, and then  S2, S3, S4, and S7 with COM1.

 

 

How the PIC controls the LCD

 

This section briefly describes how the PIC interfaces with the LCD.  You don’t need to understand the details to use C to control the display.

 

There are several PIC microcontrollers that can drive LCD’s directly.  We’ll look at the PIC16F913 , but the process is similar on all.  The pinout for the device is shown below:

Note that there are 16 Segment outputs (SEG0-SEG15) and 4 COM outputs (COM0-COM3).  However, SEG15 and COM3 are on the same line, and thus cannot both be active.  With 16 segments and 3 COM outputs, we could control 16x3=48 segments.   With 15 segments and 4 COM outputs we could control 15x4=50 segments.  We will consider how we would use the chip to drive the device above.

 

There are 12 registers on the PIC that are used to control the LCD driver hardware.  Two of the registers (LCDCON and LCDPS) determine how many COMs are used, how fast the COM lines are sequenced, and other important parameters; we won’t go into those here.  Of the 10 other registers, 2 (LCDSE0 and LCDSE1) are used to select which of the SEG lines are used for the LCD controller, and which are available for general purpose use.  Two registers yields 16 bits, one per segment.  To enable a SEG output, write a “1” into the corresponding bit of the LCDSEx register.  The other 8 registers are used to control the individual segments – the mapping from register to segment is given below:

REGISTER

SEGMENTS

LCDDATA0

SEG<7:0>COM0

LCDDATA1

SEG<15:8>COM0

LCDDATA2

SEG<23:16>COM0

LCDDATA3

SEG<7:0>COM1

LCDDATA4

SEG<15:8>COM1

LCDDATA5

SEG<23:16>COM1

LCDDATA6

SEG<7:0>COM2

LCDDATA7

SEG<15:8>COM2

LCDDATA8

SEG<23:16>COM2

LCDDATA9

SEG<7:0>COM3

LCDDATA10

SEG<15:8>COM3

LCDDATA11

SEG<23:16>COM3

(Note that this table includes registers not available on the PIC16F913.  Since we only have 16 segment outputs, LCDDATA2, LCDDATA5, LCDDATA8, and LCDDATA11 are unused.  Other PIC devices, with more SEG outputs use these).

 

After the PIC is properly configured (using LCDCON, LCDPS and LCDSEx) the individual segments are lit by choosing the appropriate bits.

 

Using our previous example (2 COM lines, 8 SEG outputs, display  23” on the 2 digit display), we want to light up SEG1, SEG4, SEG5 and SEG7 with COM0, and then SEG0, SEG1, SEG4, and SEG7 with COM1.  So we would set

LCDDATA0 = 1011 10112 = BB16       (Note: bits 7, 5, 4, 3, 1 and 0 are set, corresponding to the segments for COM0).

LCDDATA3 = 1001 11002 = 9C16       (Note: bits 7, 4, 3 and 2 are set, corresponding to the segments for COM1).

 

The complete mapping is given in the table below.

 

COM0

COM1

 

LCDDATA0 Address

LCD Segment

LCDDATA1 Address

LCD Segment

SEG0

LCDDATA0,0

1A

LCDDATA1,0

1DP

SEG1

LCDDATA0,1

1B

LCDDATA1,1

1C

SEG2

LCDDATA0,2

1F

LCDDATA1,2

1E

SEG3

LCDDATA0,3

1G

LCDDATA1,3

1D

SEG4

LCDDATA0,4

2A

LCDDATA1,4

2C

SEG5

LCDDATA0,5

2B

LCDDATA1,5

2DP

SEG6

LCDDATA0,6

1F

LCDDATA1,6

2E

SEG7

LCDDATA0,7

2G

LCDDATA1,7

2D

 

 

 

 

How C controls the PIC

 

Controlling the LCD using the methods above is laborious.  The CCS C compiler makes this much easier.  There are two C functions that are particularly useful, lcd_setup(), which takes care of configuring the LCD by setting the LCDCON, LCDPS and LCDSEx registers.   The other pertinent function is lcd_symbol().

 

The LCD_symbol() function has two arguments and is called using the syntax “LCD_symbol(Character,Configuration).”  It is useful to look at the example code (EX_92LCD.c in Examples directory of the PICC compiler.

 

Using, again, the example above, we define the mapping between the segments using the C code:

 

//Digit segments: A,      B,       C,      D,      E,     F,      G,     DP
#define DIGIT1  COM0+0, COM0+1, COM1+1, COM1+3, COM1+2, COM0+2, COM0+3, COM1+0
#define DIGIT2  COM0+4, COM0+5, COM1+4, COM1+7, COM1+6, COM0+6, COM0+7, COM1+5

 

Now, to write the number "23”, we would use the code:

 

LCD_symbol(0xDA, DIGIT1);  //Light segments A, B, D, E, and G.
LCD_symbol(0xF2, DIGIT2);  //Light segments A, B, C, D, and G.

 

One more level of abstraction makes this task even easier.  We can define the mappings for each character in an array.

 

// character                  0    1    2    3    4   5    6    7    8    9
byte const = Digit_Map[10] {0xFC,0x60,0xDA,0xF2,0x66,0xB6,0xBE,0xE0,0xFE,0xE6};

 

Now we can use the more readable:

 

LCD_symbol(Digit_Map[2], DIGIT1);  //Display “2” in digit 1
LCD_symbol(Digit_Map[3], DIGIT2);  //Display “3” in digit 2

  

 

 

Controlling the LCD on the PICDEM LCD board

 

The concepts above are directly applicable to the PICDEM LCD board but it uses a starburst display with 16 segments per character (14 lines, a decimal point, and an apostrophe), and it has 8 digits.  The decimal point and apostrophe aren’t shown in the diagram below.

 

 

We need to have the mapping in our code, that matches each segment with its corresponding register. The documentation in the PICDEM LCD User’s Kit is inaccurate (reproduced below - from PICDEM LCD documentation):

picdem

 

We see that next to each segment, we have four numbers. The first number is the segment associated with COM0, second number is COM1, third number is COM2, and fourth number is COM3. So for example 5A, which would stand for segment A of the 5th digit on the LCD,  is COM0+16. Or another example, 6K, which would stand for segment K of 6th digit of LCD is COM2+21.

 

This diagram, however is incorrect, in particular the segments A B C D E F G H are switched with I J K L M N DP CA. So the first 8 segments are actually second 8 segments. Also digit 1 above actually is digit 8. Below is the corrected table in picture format and the corrected code as a table (to format better and be readable in a web page).

 

 

as

 

Digit segments

A

B

C

D

E

F

G

H

I

J

K

L

M

N

DP

CA

#define DIGIT1

COM0+29

COM1+29

COM2+29

COM2+30

COM3+30

COM3+29

COM3+28

COM0+31

COM0+28

COM1+28

COM2+28

COM3+31

COM2+31

COM1+31

COM1+30

COM0+30

#define DIGIT2

COM0+25

COM1+25

COM2+25

COM2+26

COM3+26

COM3+25

COM3+24

COM0+27

COM0+24

COM1+24

COM2+24

COM3+27

COM2+27

COM1+27

COM1+26

COM0+26

#define DIGIT3

COM0+21

COM1+21

COM2+21

COM2+22

COM3+22

COM3+21

COM3+20

COM0+23

COM0+20

COM1+20

COM2+20

COM3+23

COM2+23

COM1+23

COM1+22

COM0+22

#define DIGIT4

COM0+17

COM1+17

COM2+17

COM2+18

COM3+18

COM3+17

COM3+16

COM0+19

COM0+16

COM1+16

COM2+16

COM3+19

COM2+19

COM1+19

COM1+18

COM0+18

#define DIGIT5

COM0+13

COM1+13

COM2+13

COM2+14

COM3+14

COM3+13

COM3+12

COM0+15

COM0+12

COM1+12

COM2+12

COM3+15

COM2+15

COM1+15

COM1+14

COM0+14

#define DIGIT6

COM0+9

COM1+9

COM2+9

COM2+10

COM3+10

COM3+9

COM3+8

COM0+11

COM0+8

COM1+8

COM2+8

COM3+11

COM2+11

COM1+11

COM1+10

COM0+10

#define DIGIT7

COM0+5

COM1+5

COM2+5

COM2+6

COM3+6

COM3+5

COM3+4

COM0+7

COM0+4

COM1+4

COM2+4

COM3+7

COM2+7

COM1+7

COM1+6

COM0+6

#define DIGIT8

COM0+1

COM1+1

COM2+1

COM2+2

COM3+2

COM3+1

COM3

COM0+3

COM0

COM1

COM2

COM3+3

COM2+3

COM1+3

COM1+2

COM0+2

 

Now that we have defined segments, all we have to do is use the lcd_symbol() function.

LCD_SYMBOL(0xFC00,DIGIT8);

 First parameter is the character. As discussed above, 0xFC00 stands for the character ‘0’ for 14 segment display. Second parameter specifies the position of the character. Digit8 would be the 8th digit. Likewise,  

LCD_SYMBOL(0xda20,DIGIT6);

Would write the character ‘2’ on the 6th digit.

 

C Code is in the file lcdDrv.c (and below):
Note: if  you want to print this legibly, print in “landscape” mode.

 


#include "lcdDrv"
//This code demostrates how to write characters on the LCD of PICDEM LCD board. 

//Below is the mapping between segments on the LCD and registers in the microcontroller
/////////////////////////////////////////////////////////////////////////////////////////
//                                 LCD Configuration                                   //
/////////////////////////////////////////////////////////////////////////////////////////
// Digit segments   A           B         C         D         E         F         G         H        I          J            K        L          M          N        DP            CA
#define DIGIT1  COM0+29,  COM1+29,  COM2+29,  COM2+30,  COM3+30,  COM3+29,  COM3+28,  COM0+31, COM0+28,   COM1+28,   COM2+28,   COM3+31,    COM2+31,  COM1+31,   COM1+30,     COM0+30
#define DIGIT2  COM0+25,  COM1+25,  COM2+25,  COM2+26,  COM3+26,  COM3+25,  COM3+24,  COM0+27, COM0+24,   COM1+24,   COM2+24,   COM3+27,    COM2+27,  COM1+27,   COM1+26,     COM0+26
#define DIGIT3  COM0+21,  COM1+21,  COM2+21,  COM2+22,  COM3+22,  COM3+21,  COM3+20,  COM0+23, COM0+20,   COM1+20,   COM2+20,   COM3+23,    COM2+23,  COM1+23,   COM1+22,     COM0+22
#define DIGIT4  COM0+17,  COM1+17,  COM2+17,  COM2+18,  COM3+18,  COM3+17,  COM3+16,  COM0+19, COM0+16,   COM1+16,   COM2+16,   COM3+19,    COM2+19,  COM1+19,   COM1+18,     COM0+18
#define DIGIT5  COM0+13,  COM1+13,  COM2+13,  COM2+14,  COM3+14,  COM3+13,  COM3+12,  COM0+15, COM0+12,   COM1+12,   COM2+12,   COM3+15,    COM2+15,  COM1+15,   COM1+14,     COM0+14
#define DIGIT6  COM0+9,   COM1+9,   COM2+9,   COM2+10,  COM3+10,  COM3+9,   COM3+8,   COM0+11, COM0+8,    COM1+8,    COM2+8,    COM3+11,    COM2+11,  COM1+11,   COM1+10,     COM0+10
#define DIGIT7  COM0+5,   COM1+5,   COM2+5,   COM2+6,   COM3+6,   COM3+5,   COM3+4,   COM0+7,  COM0+4,    COM1+4,    COM2+4,    COM3+7,     COM2+7,   COM1+7,    COM1+6,      COM0+6
#define DIGIT8  COM0+1,   COM1+1,   COM2+1,   COM2+2,   COM3+2,   COM3+1,   COM3,     COM0+3,  COM0,      COM1,      COM2,      COM3+3,     COM2+3,   COM1+3,    COM1+2,      COM0+2
/////////////////////////////////////////////////////////////////////////////////////////
 
//counter for the FOR loop
int16 x;
 
//         character             0    1      2      3     4      5       6     7       8     9
long const Digit_Map[10] = {0xFC00,0x6040,0xDA20,0xF220,0x6620,0xB620,0xBE20,0xE000,0xFE20,0xE620};

void main() {
   //Initialization code from CCS compiler.   
   setup_adc_ports(NO_ANALOGS|VSS_VDD);
   setup_adc(ADC_OFF|ADC_TAD_MUL_0);
   setup_spi(SPI_SS_DISABLED);
   setup_lcd(LCD_DISABLED);
   setup_wdt(WDT_OFF);
   setup_timer_0(RTCC_INTERNAL);
   setup_timer_1(T1_DISABLED);
   setup_timer_2(T2_DISABLED,0,1);
   setup_comparator(NC_NC_NC_NC);
   setup_vref(FALSE);

   //initialize the lcd. First parameter indicates that we will use all 4 COMS.
   setup_lcd(LCD_MUX14,2);

   lcd_symbol(Digit_Map[0],DIGIT1);      //write '0' on digit 1
   lcd_symbol(Digit_Map[1],DIGIT2);      //write '1' on digit 2
   lcd_symbol(Digit_Map[2],DIGIT3);      //write '2' on digit 3
   lcd_symbol(0x154,DIGIT4);             //write 'X' on digit 4

   delay_ms(2000);

   //On every digit, cycle each segment from A to I one by one
   for(x=0x8000;x>=1;x/=2){
      lcd_symbol(x,DIGIT8);
      lcd_symbol(x,DIGIT7);
      lcd_symbol(x,DIGIT6);
      lcd_symbol(x,DIGIT5);
      lcd_symbol(x,DIGIT5);
      lcd_symbol(x,DIGIT4);
      lcd_symbol(x,DIGIT3);
      lcd_symbol(x,DIGIT2);
      lcd_symbol(x,DIGIT1);
      delay_ms(500);
   }
}

  back