AN202 L OW P IN -C OUNT LCD I N T E R F A C E 1. Introduction This application note provides an example interface for a C8051F330 device with an example LCD. First, this application note describes how an LCD works and then describes the two types of LCDs: direct drive and multiplexed drive. Next, the software interface and structure are explained. Finally, this note describes how to modify the software example to work with other LCDs. The code accompanying this application note was originally written for C8051F33x devices. The code can also be ported to other devices in the Silicon Labs microcontroller range. 2. Key Points The software provided translates ASCII characters into 7-segment digits, compatible with the printf() standard library function. The LCD used in this example has 19 pins (4 Common and 15 Segment). Seven pins are used on the microcontroller: four for the Common pins and three that serve as a serial interface to a pair of 74HC595 8-bit, latched shift registers which are the segment drivers. The refresh rate of the LCD is chosen to minimize power consumption as well as minimize flickering. C8051F330 LCD COM Pins COM PINS SEGMENT PINS 8 74HC595 7 74HC595 Seg Pin Out 74HC595 Control 2 Figure 1. LCD Interface Block Diagram Rev. 0.2 7/13 Copyright © 2013 by Silicon Laboratories Silicon Laboratories Confidential. Information contained herein is covered under non-disclosure agreement (NDA). AN202 AN202 3. LCD The following sections describe how an LCD works. 3.1. Components of an LCD An LCD consists of a collection of segments that are individually controlled. When there is no voltage across a segment, it is turned OFF and assumes the color of the background of the LCD. Applying an ac voltage across a segment causes it to turn ON and it will look darker than the background of the LCD. The root mean square (rms) value of the voltage across the segment must be greater than a certain threshold for the segment to turn on. This threshold is determined by the LCD manufacturer. Figure 2 shows a diagram of a single segment. Each segment in an LCD has two terminals: a Backplane terminal and a Segment terminal. Electrically the segment looks like a capacitor. Multiple segments can connect to the same backplane. The collection of Segment Drivers is called the Frontplane. Segment Driver Frontplane Connection Capacitor Segment Backplane Backplane Connection Figure 2. Single Segment in an LCD 2 Rev. 0.2 AN202 3.2. Direct Drive LCD In a direct drive LCD, each segment on the LCD is mapped to its own Segment pin. Another pin called the Common pin (COM) provides the voltage to the backplane. A direct drive LCD with N segments requires a total of N + 1 pins. Figure 3 below shows a diagram of an direct-drive LCD with seven segments. S0 S1 S5 S6 S4 S2 S3 COM Figure 3. Connections for a 7-Digit Segment Figure 4 shows a sample timing diagram that illustrates how a single segment is turned on and off. The microcontroller drives all the inputs of a direct drive LCD to either VDD or GND. To turn a segment on, a voltage difference is applied between the backplane and the segment pin for that specific segment. Rev. 0.2 3 AN202 COM First Half Second Half VDD 0 t S1 VDD 0 t S = magnitude (COM – S1) S VDD 0 t ON OFF Figure 4. Sample Timing Diagram for a Single Segment To achieve the best contrast and lowest power consumption, the COM pin should be driven with a 50% duty-cycle square wave. In the first half of the timing diagram, the segment pin S1 is driven to a value opposite the value driven on the COM pin. This leads to a voltage difference across the segment, the magnitude of which is shown by S, and the segment is turned ON for this length of time. In the second half of the timing diagram, the segment pin S1 is driven to the same value as the output on the COM pin. This leads to no potential difference across the segment and thus the segment is turned OFF for this period of time. In summary, to turn a segment ON in a direct drive LCD, drive the corresponding segment pin to the value opposite the value of the COM pin. To turn a segment OFF, drive the segment pin to the same value as the COM pin. Note: An ac excitation waveform is required to turn the segment ON, and the rms value of the voltage across the segment must be above a certain threshold for the segment to change color. 4 Rev. 0.2 AN202 3.3. Multiplexed LCD A multiplexed LCD has more than one backplane, and a corresponding COM pin for each of those backplanes. In a M-way multiplexed LCD, there are M separate backplanes and M COM pins. The segments share Segment pins as well as COM pins. The segments are divided equally between the Segment pins, with each segment possessing a unique combination of Segment and COM pins. If an LCD has M COM pins and N segment pins, it can support up to M x N segments. For example, and LCD with 4 COM pins and 15 Segment pins can have up to 60 segments. S1 COM1 COM2 S2 COM3 COM4 Figure 5. Connections for 8 Segments in a 4-way Multiplexed LCD The LCD discussed in this application note can support up to 60 segments and is 4-way multiplexed. This means it has 15 groups of 4 pins each. Each group shares a single segment pin. This means that 19 pins are needed to interface with this LCD (15 for each segment group + 4 COM pins). If the same LCD was designed as a direct drive LCD, it would require 61 pins (60 pins for each segment and 1 COM pin). The diagram in Figure 5 shows the connections for a sample 4-way multiplexed LCD. The COM pins in a multiplexed LCD are driven to one of three voltage levels: VDD, VDD/2, or GND. At any one moment, only one of the COM pins is driven to either VDD or GND. All other COM pins are driven to VDD/2. The segment pins are still only driven only to VDD or to GND. In a 4-way multiplexed LCD, each refresh cycle or period is separated into 8 phases. During the first four phases of the period, each COM pin is alternately driven to VDD, while the other COM pins are held at VDD/2. In the last 4 phases, each COM pin is alternately driven to GND, while the other COM pins are held at VDD/2. The value of the COM pins during the last 4 phases is an inverse of the values in the first four phases. This is known as “1/4 duty cycle.” Determining the segment pin value to turn a segment ON is similar to the method used for a direct drive LCD. Each segment is connected to one Segment pin and one of the COM pins. When the respective COM driver is high (in one of the first four phases of the period), the segment pin must be driven low to turn ON the segment and driven high to turn OFF the segment. The value of the segment pin is not relevant to a segment if its COM pin is not driven to VDD or GND. Rev. 0.2 5 AN202 Driving the pin low when the respective COM pin is high creates a voltage difference across the segment whose rms value is greater than the threshold necessary to turn on the segment. Whenever a COM pin is set to VDD/2, and the segment pin is set to VDD or GND, the rms voltage is below the threshold to turn on the segment. As long as the rms value of the voltage across the segment over the four phases is above a certain threshold, the segment will remain ON for those four phases. If the rms value is below the threshold, the segment will remain OFF for those four phases. Seg4 – OFF Seg3 – On Seg2 – OFF Seg1 – OFF Seg4 – OFF Seg3 – On Seg2 – OFF Seg1 – OFF The Segment pin value over the last four phases of the period must be the inverse of the value over the first four phases. This will cause the segments to remain in the same state (ON/OFF) that they were in the first four phases. VDD V_RMS VDD/2 0 -VDD/2 V_RMS -VDD Figure 6. RMS thresholds for LCD segments Figure 6 is a graph of the voltage difference over time between a specific Segment pin and four COM pins. Whenever the magnitude of the voltage difference is less than V_RMS, the segment that is between the Segment pin and the active COM pin is OFF. Whenever the magnitude of the voltage difference is greater than V_RMS, the segment that is between the Segment pin and the active COM pin is ON. In Figure 6, the voltage difference is greater than V_RMS only during phase 3 (and the corresponding phase 7) and thus only segment 3 is ON. The other segments will remain off during this refresh cycle. To create a rms value greater than the threshold for a certain segment, set the segment pin low whenever the corresponding COM pin is high, and set the segment pin high whenever the corresponding COM pin is low. Figure 7 shows the waveforms for the COM signals, a single Segment pin, and the delta values for the four segments that the Segment pin is connected to. The delta values are the voltages across the segments. Figure 7 also shows which segments are ON and OFF during each period. 6 Rev. 0.2 AN202 Period 2 Seg1 – Not Active Seg2 – Active Seg3 – Not Active Seg4 – Active Seg1 – Not Active Seg2 – Active Seg3 – Not Active Seg4 – Active Seg1 – Active Seg2 – Not Active Seg3 – Active Seg4 – Not Active Seg1 – Active Seg2 – Not Active Seg3 – Active Seg4 – Not Active Period 1 Segment Connections VDD COM 1 t 0 COM 1 seg 1 COM 2 seg 2 COM 3 seg 3 COM 4 seg 4 VDD S During Period 1 COM 2 t 0 COM 1 seg 1 COM 2 VDD COM 3 t 0 COM 3 seg 3 S COM 4 VDD COM 4 During Period 2 t 0 COM 1 COM 2 VDD S t 0 S1 VDD ON t 0 VDD OFF COM 3 COM 4 S seg 4 OFF -VDD S2 seg 2 ON S1 = COM 1 – S S2 = COM 2 – S S3 = COM 3 – S S4 = COM 4 – S t 0 -VDD S3 VDD ON OFF t 0 -VDD S4 VDD OFF ON t 0 -VDD Figure 7. Timing Diagram for a Sample 4-Way Multiplexed LCD Rev. 0.2 7 AN202 4. How to Generate Segment Values for a Particular Digit Figure 8 shows a single, 7-segment digit from a 4-way multiplexed LCD. Part A of Figure 8 shows the connections between the segment and the Segment pins S1 and S2. Part B of Figure 8 shows the connections between the segment and backplane COM pins COM1, COM2, COM3, and COM4. This example shows how to generate the digit “5”. From Figure 8, segments 0, 2, 3, 5, and 6 need to be turned ON. Segments 1 and 4 need to be turned OFF. Figure 9 shows the timing diagram for one refresh cycle necessary to generate the digit “5”. The Sn show whether a segment is ON or OFF. The delta values for segments 0, 2, 3, 5, and 6 all have an rms value greater than VDD/2. This means that the segments are on for as long as this S pattern is presented to the LCD. S2 0 0 1 5 6 4 5 S1 COM2 6 4 2 COM4 3 Part A COM1 1 COM3 2 3 Part B Figure 8. Sample Connections for a 7-Segment, 4-way Multiplexed LCD 8 Rev. 0.2 AN202 VDD VDD COM1 0 S0 0 ON -VDD VDD VDD COM2 0 S1 0 OFF -VDD VDD COM3 0 VDD S2 0 ON -VDD VDD COM4 0 VDD S3 0 ON -VDD VDD VDD S1 S4 0 0 OFF -VDD S2 VDD VDD 0 S5 0 ON -VDD S0 = COM1 – S1 = COM2 – S2 = COM3 – S3 = COM4 – S4 = COM4 – S5 = COM2 – S6 = COM3 – S2 S1 S1 S1 S2 S2 S2 VDD S6 0 ON -VDD Figure 9. Timing Diagram Showing How to Display the Digit “5” Rev. 0.2 9 AN202 5. Software Example This section describes how the user can interface to the LCD using the putchar() function. 5.1. Software Interface The software provided in "10. Software Example Source Code" on page 16 provides an interface for a C8051F330 to the LCD. This LCD has six 7-segment digits. The four backplanes and 15 segment pins allow for 60 segments, but this LCD only makes 42 segments visible. The example LCD library overloads the standard library putchar() function. The flow chart for the putchar() function is shown below in Figure 10. START Yes Is the input == ‘/n’? No Clear all LCD digits Does the input need to be translated? No Yes Do translation table lookup Disable Interrupts Shift all digits to the left Insert the input to the rightmost digit Enable Interrupts Return printed character to calling function Figure 10. Flowchart for putchar() 10 Rev. 0.2 AN202 The software overloads the standard putchar() function to print its output to the LCD. The function putchar() will insert the character in the rightmost digit on the LCD. Multiple calls to putchar() will shift the text to the left and insert the newest character in the rightmost digit's place. Interrupts are disabled when the display is updated to avoid flickering. The most significant bit (MSB) of the 8-bit character passed to putchar() determines if putchar() uses a translation table or directly displays the value. If an 8-bit character whose ASCII value is between 0 and 127 (MSB is 0) is passed to putchar(), a translation will be made using the translation table. If a value from 128 to 255 (MSB is 1) is passed to putchar(), the digit will be directly displayed on the LCD. If the bit is directly displayed, the 7 lower bits are translated as follows: if the bit is “0”, the corresponding segment is ON; if the bit is “1”, the corresponding segment is OFF. Figure 11 shows which segments are mapped to which bits. This allows the user to easily create every combination possible with seven segments. The function putchar() also clears the six LCD digits when it is passed the newline character, “\n”, whose ASCII value is 10. The translation table is described in detail in "7. LCD driver" on page 13. Figure 11. Bit Mapping Between Segments in a Digit and the putchar() Input Value Rev. 0.2 11 AN202 Table 1. Pin Map for the Example LCD Pin# 1 2 3 4 5 6 7 8 A2 9 10 11 A3 12 A4 13 14 15 A5 16 COM1 A1 COM2 F1 B1 F2 B2 COM3 G1 C1 G2 C2 G3 C3 G4 C4 G5 C5 G6 C6 COM4 COM4 E1 D1 E2 D2 E3 D3 E4 D4 E5 D5 E6 F2 B3 F4 B4 17 18 A6 F5 B5 F6 19 COM1 B6 COM2 COM3 D6 6. Interpreting the LCD Data Sheet The LCD data sheet provides the mapping between the segment pins and Segment pins and the COM pins. Table 1 shows the mapping for the example LCD. Pins 1, 17, 18, and 19 are the COM pins. Pins 2–16 are the Segment pins. The intersection between the segment pin number and the COM pin is the segment connected between those pins. The blank spaces in columns 2-16 indicate that there is no segment between that Segment pin and COM pin. There are blank spaces in columns 1, 17, 18, and 19 because are the COM pins. Table 1 indicates which bits should be shifted out to the shift register in each state of the LCDrefresh_ISR(). For example, when COM2 is active, the segment status bits for segments F1 through B6 (row 1) need to be shifted to the shift register. Any value can be shifted to 2, 3, and 4 on the LCD while COM2 is high because there is no segment that can be activated on those pins during the COM2 phase. Table 2. Example LCD Specifications Electrical / Optical Characteristics Value Units Operating Temperature Range –10°–60° °C Operational Voltage, RMS 2.5–3.5 V-RMS Drive Frequency 60–300 Hz Current Consumption 15 nA/mm2 Turn On Time 80 ms Turn Off Time 80 ms The specification relevant to the firmware design is the drive frequency. The drive frequency determines how many times the LCDrefresh_ISR() should be triggered. 12 Rev. 0.2 AN202 7. LCD driver There are two main components to the LCD driver: the LCD refresh state machine and the ASCII translation table. The LCD refresh state machine is located in LCDrefresh_ISR(). This function is responsible for driving the 4 COM signals and the 15 segment pins. This function is executed every time Timer2 overflows which is scheduled to happen 2000 times a second. This leads to a refresh rate of 250 Hz, which is in the ideal range for the LCD. Each state in the 8-state state machine represents which COM driver is active at the time. Because there are eight phases in each period, this function is called eight times each refresh cycle. Each of the 42 segments has a bit in memory that holds its state. These bits are stored in the variable LCD_digits. During each run of the function, the ISR shifts the 15 bits that hold the state for the segments that are active this phase to the shift registers. These values are then passed in parallel to the LCD Segment pins. After four such phases, four more phases follow with all the output values inverted to match the associated polarity reversal of the COM signals. The flowchart for the ISR is shown in Figure 12. START Select the next active COM signal and tri-state all other COM signals Shift next segment pin value to shift register No Shifted all segment pins? Yes Latch shift registers Set current COM pin to push-pull and drive Figure 12. Flowchart for the LCDrefresh_ISR The ASCII translation table is used whenever putchar() is called with an ASCII character as its parameter. The ASCII table is used to translate the ASCII character to a 7-segment digit. The table indicates which segments should be OFF and ON to best represent that character. If the character cannot be translated properly, the translation table displays a space, which is represented by 0xFF in the table. Figure 13 shows the digit mapping for each ASCII character. The first 32 characters generate spaces, so they are not part of the table. The top left number in each box is the value stored in the translation table. The bottom left number is the ASCII value. Both of these numbers are shown in hexadecimal notation. The bottom right character is the character being translated. If the box is empty, there is no translation available for that ASCII character and the LCD will display a space instead. Rev. 0.2 13 AN202 Figure 13. Mapping for the Basic Set of ASCII Characters (0–127) 14 Rev. 0.2 AN202 8. Implementation Notes To generate the VDD/2 necessary for the backplane, a voltage divider is created using two equal sized resistors. Whenever the COM output pin on the microcontroller is set to “analog in” (high impedance), the voltage divider will provide the necessary VDD/2 voltage to the LCD. Whenever the COM output pin is set to digital output, 1 (VDD) or 0 (GND) will be sent to the LCD. See "11. Schematic" on page 28 for further details. It is also important to note that increasing the refresh rate of the LCD to remove flickering also increases the power consumption. The refresh rate should be set to the minimum amount necessary to prevent flickering. This ideal refresh rate will vary for each manufacturer's LCD. The shift registers (74HC595) are used to reduce the number of pins required on the microcontroller. It is important to choose shift registers that also provide a latching capability. In each phase, all the segment pin values should be shifted to the shift registers before latching those values to the LCD. This will prevent flickering on the LCD. 9. How to Customize the Software Example for a Different Multiplexing LCD There are two parts of the code that need to change to accommodate different LCDs. The Port I/O configuration has to change if the number of backplanes changes. For each backplane, a COM port pin must be allocated. If the number of Segment pins changes, more pins need not be allocated because the bits are shifted out serially. However, the number of shift registers depends directly on how many Segments pins are on the LCD. The LCDrefresh_ISR() must be changed to accommodate the number of backplanes as well as the number of segment pins. This involves changing the number of states if the number of backplanes is different. The structures that store the segment state information must be modified to match the segments on the new LCD. The mapping between which segments are connected to which backplanes will determine which bits are sent to the LCD during each phase. Rev. 0.2 15 AN202 10. Software Example Source Code This section contains the source code for the software example. 16 Rev. 0.2 AN202 //----------------------------------------------------------------------------// LCDInterface.c //----------------------------------------------------------------------------// Copyright 2004 Silicon Laboratories, Inc. // // AUTH: GP // DATE: 19 NOV 04 // // This program interfaces a C8051F330 device with an example LCD. // // Target: C8051F33x // Tool Chain : Keil // //----------------------------------------------------------------------------// Includes //----------------------------------------------------------------------------#include <c8051F330.h> #include <stdio.h> //----------------------------------------------------------------------------// 16-bit SFR Definitions for 'F3xx //----------------------------------------------------------------------------sfr16 TMR2RL sfr16 TMR2 = 0xca; = 0xcc; // Timer2 reload value // Timer2 counter sfr16 TMR3RL sfr16 TMR3 = 0x92; = 0x94; // Timer3 reload value // Timer3 counter //----------------------------------------------------------------------------// Structures, Unions, Enumerations, and Type Definitions //----------------------------------------------------------------------------// The translation table provides the mapping between ASCII characters // and the segment pin values // // // // The first 32 characters (except 10) just produce a space; Character 10 (newline) clears the LCD digits Characters that can't be translated produce a space The MSB in the byte is meaningless because there are only 7 segments // If the bit is low, the corresponding bar in the digit is active // The 'diagram' below shows which bit corresponds to which bar in the LCD digit // // // // // // // 0 _ 5 | | 1 4 | | 2 3 the middle inner bar is bit 6 Rev. 0.2 17 AN202 unsigned char code translation_table[128] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // // // // 0 - 7 8 - 15 16 - 23 24 - 31 0xFF, 0xC6, 0x40, 0x00, 0xCF, 0xF0, 0xF9, 0x10, 0xDD, 0xFF, 0xA4, 0xF9, 0xFF, 0xFF, 0xB0, 0xF9, 0x92, 0xDF, 0x99, 0x9E, 0x9B, 0xBF, 0x92, 0xB7, 0xFF, 0xEF, 0x02, 0xBC, 0xDF, 0xFF, 0xF8, 0x18, // // // // 32 40 48 56 - 39 47 55 63 0xFF, 0x09, 0x0C, 0x09, 0x08, 0xF9, 0x18, 0x11, 0x00, 0xE1, 0xAF, 0xA4, 0xC6, 0x07, 0x92, 0x06, 0x21, 0xC7, 0xF8, 0xFF, 0x06, 0xFF, 0x41, 0xF0, 0x0E, 0x48, 0xE3, 0xDC, 0x02, 0x40, 0xFF, 0xF7, // // // // 64 72 80 88 - 71 79 87 95 0xDF, 0x0B, 0x0C, 0x09, }; 0x20, 0xFB, 0x18, 0x11, 0x03, 0x61, 0xAF, 0xA4, 0xA7, 0x07, 0x92, 0xC6, 0x21, 0xF9, 0x07, 0xF9, 0x04, 0xFF, 0xE3, 0xF0, 0x0E, 0xAB, 0xE3, 0xBF, 0x10, 0x23, 0xFF, 0xFF, // // // // 96 - 103 104 - 111 112 - 119 120 - 127 //----------------------------------------------------------------------------// Global Constants //----------------------------------------------------------------------------#define #define #define #define SYSCLK TIMER2_RATE TIMER3_RATE PULSE_LENGTH 24500000 1000 2000 25 // SYSCLK frequency in Hz // Timer 2 overflow rate in Hz // Timer 3 overflow rate in Hz //----------------------------------------------------------------------------// Port names //----------------------------------------------------------------------------sbit SRCLK = P1^1; sbit RCLK = P1^2; sbit SER = P1^3; // shift register clock // shift register latch // shift register serial in sbit sbit sbit sbit // // // // COM1 COM2 COM3 COM4 = = = = P1^4; P1^5; P1^6; P1^7; COM1 COM2 COM3 COM4 pin pin pin pin on on on on LCD LCD LCD LCD //----------------------------------------------------------------------------// Global LCD Variables //----------------------------------------------------------------------------unsigned char com_cycle = 1; unsigned char com_invert = 0; 18 // start at COM 1 // start with positive cycle Rev. 0.2 AN202 // Below are the bit maps for each of the bars on the LCD; If the bit is low // then the bar is opaque (ON). If the bit is high, the bar is transparent (OFF). // one char per digit on the LCD; initialized to OFF unsigned char bdata LCD_digits[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; // The naming scheme: D1A means the A segment of digit 1 // Digit sbit D1A sbit D1B sbit D1C sbit D1D sbit D1E sbit D1F sbit D1G 1 = = = = = = = (D1) is the leftmost digit on the LCD LCD_digits[0] ^ 0; LCD_digits[0] ^ 1; // D1 is controlled by S1 and S2 LCD_digits[0] ^ 2; LCD_digits[0] ^ 3; LCD_digits[0] ^ 4; LCD_digits[0] ^ 5; LCD_digits[0] ^ 6; sbit sbit sbit sbit sbit sbit sbit D2A D2B D2C D2D D2E D2F D2G = = = = = = = LCD_digits[1] LCD_digits[1] LCD_digits[1] LCD_digits[1] LCD_digits[1] LCD_digits[1] LCD_digits[1] ^ ^ ^ ^ ^ ^ ^ 0; 1; 2; 3; 4; 5; 6; // D2 is controlled by S3 and S4 sbit sbit sbit sbit sbit sbit sbit D3A D3B D3C D3D D3E D3F D3G = = = = = = = LCD_digits[2] LCD_digits[2] LCD_digits[2] LCD_digits[2] LCD_digits[2] LCD_digits[2] LCD_digits[2] ^ ^ ^ ^ ^ ^ ^ 0; 1; 2; 3; 4; 5; 6; // D3 is controlled by S5 and S6 sbit sbit sbit sbit sbit sbit sbit D4A D4B D4C D4D D4E D4F D4G = = = = = = = LCD_digits[3] LCD_digits[3] LCD_digits[3] LCD_digits[3] LCD_digits[3] LCD_digits[3] LCD_digits[3] ^ ^ ^ ^ ^ ^ ^ 0; 1; 2; 3; 4; 5; 6; // D4 is controlled by S7 and S8 sbit sbit sbit sbit sbit sbit sbit D5A D5B D5C D5D D5E D5F D5G = = = = = = = LCD_digits[4] LCD_digits[4] LCD_digits[4] LCD_digits[4] LCD_digits[4] LCD_digits[4] LCD_digits[4] ^ ^ ^ ^ ^ ^ ^ 0; 1; 2; 3; 4; 5; 6; // D5 is controlled by S9 and S10 sbit sbit sbit sbit sbit D6A D6B D6C D6D D6E = = = = = LCD_digits[5] LCD_digits[5] LCD_digits[5] LCD_digits[5] LCD_digits[5] ^ ^ ^ ^ ^ 0; 1; 2; 3; 4; // D6 is controlled by S11 and S12 Rev. 0.2 19 AN202 sbit D6F = LCD_digits[5] ^ 5; sbit D6G = LCD_digits[5] ^ 6; //----------------------------------------------------------------------------// Function Prototypes //----------------------------------------------------------------------------void void void void char void void SYSCLK_Init (void); Port_IO_Init(); Timer2_Init (int); Timer3_Init (int); putchar(char); Strobe(); wait_one_ms(unsigned int); //----------------------------------------------------------------------------// MAIN Routine //----------------------------------------------------------------------------void main(void) { PCA0MD &= ~0x40; SYSCLK_Init(); Port_IO_Init(); Timer2_Init (SYSCLK / TIMER2_RATE); Timer3_Init (SYSCLK / TIMER3_RATE); // // // // // // // WDTE = 0 (clear watchdog timer enable) // // // // initialize the oscillator initialize the ports enable timer to interrupt at some Hz enable timer to overflow at some Hz We first configure the COM ports to analog inputs. This allows us to set them to high impedance if we write a 1 to the COM port. Along with some external resistors, we can then create a VDD/2 voltage. When it is time for the corresponding COM cycle, we can set the pin to push-pull and drive the output to VDD or GND. These 3 levels (VDD, VDD/2, GND) are necessary only for the backplane (Common) pins on the LCD COM1 COM2 COM3 COM4 RCLK SRCLK = = = = = = 1; 1; 1; 1; 0; 0; EA = 1; // // // // // // high impedance high impedance high impedance high impedance don't output anything to LCD don't shift anything to registers yet // enable global interrupts while (1) { printf ("Hello"); wait_one_ms (1000); printf ("\n"); wait_one_ms (1000); } 20 Rev. 0.2 AN202 } //----------------------------------------------------------------------------// Init Functions //----------------------------------------------------------------------------//----------------------------------------------------------------------------// SYSCLK_Init //----------------------------------------------------------------------------// // This routine initializes the system clock to use the internal 24.5MHz // oscillator as its clock source. Also enables missing clock detector reset. // void SYSCLK_Init (void) { OSCICN |= 0x03; // Configure internal osc to max freq RSTSRC = 0x04; // Enable missing clock detector } //----------------------------------------------------------------------------// Port_IO_init //----------------------------------------------------------------------------// // This routine initializes the ports and enables the crossbar // void Port_IO_Init(void) { // P0.0 - Unassigned, Open-Drain, Digital // P0.1 - Unassigned, Open-Drain, Digital // P0.2 - Unassigned, Open-Drain, Digital // P0.3 - Unassigned, Open-Drain, Digital // P0.4 - Unassigned, Open-Drain, Digital // P0.5 - Unassigned, Open-Drain, Digital // P0.6 - Unassigned, Open-Drain, Digital // P0.7 - Unassigned, Open-Drain, Digital // // // // // // // // P1.0 P1.1 P1.2 P1.3 P1.4 P1.5 P1.6 P1.7 P0MDOUT P1MDOUT P1MDIN P1SKIP XBR1 - Unassigned, Skipped, Skipped, Skipped, Skipped, Skipped, Skipped, Skipped, = = = = = 0x80; 0x0E; 0x0F; 0xFE; 0x40; Open-Drain, Push-Pull, Push-Pull, Push-Pull, Open-Drain, Open-Drain, Open-Drain, Open-Drain, Digital Digital Digital Digital Digital Digital Digital Digital // // // // SRCLK for 74HC595 RCLK for 74HC595 SER for 74HC595 COM1 for LCD COM2 for LCD COM3 for LCD COM4 for LCD configure above pins to Push-Pull configure Pins 1.4 - 1.7 to analog in skip pins 1.1 to 1.7 enable crossbar } Rev. 0.2 21 AN202 //----------------------------------------------------------------------------// Timer2_Init //----------------------------------------------------------------------------// // The timer overflows at a rate of TIMER2_RATE times a second // The interrupt generated in handled by the LCD_refresh ISR // void Timer2_Init (int counts) { TMR2CN = 0x00; // STOP Timer2; Clear TF2H and TF2L; // disable low-byte interrupt; disable // split mode; select internal timebase CKCON |= 0x10; // Timer2 uses SYSCLK as its timebase TMR2RL = -counts; TMR2 = TMR2RL; ET2 = 1; TR2 = 1; // // // // Init reload values Init Timer2 with reload value enable Timer2 interrupts start Timer2 } //-----------------------------------------------------------------------------------// Timer3_Init //-----------------------------------------------------------------------------------// // Configure the Timer to overflow without interrupts // The overflow will be used in the wait function // void Timer3_Init (int count) { TMR3CN = 0x00; // STOP Timer3; Clear TF3H and TF3L; // disable low-byte interrupt; disable // split mode; select internal timebase CKCON |= 0x40; // Timer3 uses SYSCLK as its timebase TMR3RL TMR3 EIE1 = -count; = TMR3RL; &= 0x7F; // Init reload values // Init Timer3 with reload value // disable Timer3 interrupts TMR3CN |= 0x01; // start Timer3 } //----------------------------------------------------------------------------// Interrupt Service Routines //----------------------------------------------------------------------------// LCDrefresh is triggered on a Timer2 Overflow // // // // 22 Takes what is in the LCD bar bits and shift them into the two 74HC595 shift registers depending on the COM cycle; The most signficant LCD pin (pin 16) gets shifted out first; Only 15 bits get shifted each COM cycle; Rev. 0.2 AN202 void LCDrefresh_ISR (void) interrupt 5 { int i = 0; if (com_cycle { SER = 1 SER = D6A SER = 1 SER = D5A SER = 1 SER = D4A SER = 1 SER = D3A SER = 1 SER = D2A SER = 1 SER = D1A SER = 1 SER = 1 SER = 1 == 1) ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ com_invert; com_invert; com_invert; com_invert; com_invert; com_invert; com_invert; com_invert; com_invert; com_invert; com_invert; com_invert; com_invert; com_invert; com_invert; Strobe(); Strobe(); Strobe(); Strobe(); Strobe(); Strobe(); Strobe(); Strobe(); Strobe(); Strobe(); Strobe(); Strobe(); Strobe(); Strobe(); Strobe(); // non-existent segment // non-existent segment // non-existent segment // non-existent segment // non-existent segment // non-existent segment // non-existent segment // non-existent segment // non-existent segment RCLK = 1; for (i=0; i<PULSE_LENGTH; i++); RCLK = 0; // put shifted data to LCD - rising edge // keep clock high for a while // turn off clock P1MDIN P1MDIN // configure COM4 to ANALOG_IN; // and COM1 to digital &= ~0x80; |= 0x10; P1MDOUT &= ~0x80; P1MDOUT |= 0x10; COM4 = 1; COM1 = 1 ^ com_invert; com_cycle = 2; // // // // // make COM4 an open-drain make COM1 a push-pull set COM4 to high impedance start the COM1 cycle next state } else if (com_cycle == 2) { SER = D6B ^ com_invert; SER = D6F ^ com_invert; SER = D5B ^ com_invert; SER = D5F ^ com_invert; SER = D4B ^ com_invert; SER = D4F ^ com_invert; SER = D3B ^ com_invert; SER = D3F ^ com_invert; SER = D2B ^ com_invert; SER = D2F ^ com_invert; SER = D1B ^ com_invert; SER = D1F ^ com_invert; SER = 1 ^ com_invert; SER = 1 ^ com_invert; SER = 1 ^ com_invert; Strobe(); Strobe(); Strobe(); Strobe(); Strobe(); Strobe(); Strobe(); Strobe(); Strobe(); Strobe(); Strobe(); Strobe(); Strobe(); Strobe(); Strobe(); Rev. 0.2 // non-existent segment // non-existent segment // non-existent segment 23 AN202 RCLK = 1; for (i=0; i<PULSE_LENGTH; i++); RCLK = 0; // put shifted data to LCD - rising edge // keep clock high for a while // turn off clock P1MDIN P1MDIN // configure COM1 to ANALOG_IN; // and COM2 to digital &= ~0x10; |= 0x20; P1MDOUT &= ~0x10; P1MDOUT |= 0x20; COM1 = 1; COM2 = 1 ^ com_invert; com_cycle = 3; // // // // // make COM1 an open-drain make COM2 a push-pull set COM1 to high impedance start the COM2 cycle next state } else if (com_cycle == 3) { SER = D6C ^ com_invert; SER = D6G ^ com_invert; SER = D5C ^ com_invert; SER = D5G ^ com_invert; SER = D4C ^ com_invert; SER = D4G ^ com_invert; SER = D3C ^ com_invert; SER = D3G ^ com_invert; SER = D2C ^ com_invert; SER = D2G ^ com_invert; SER = D1C ^ com_invert; SER = D1G ^ com_invert; SER = 1 ^ com_invert; SER = 1 ^ com_invert; SER = 1 ^ com_invert; Strobe(); Strobe(); Strobe(); Strobe(); Strobe(); Strobe(); Strobe(); Strobe(); Strobe(); Strobe(); Strobe(); Strobe(); Strobe(); Strobe(); Strobe(); // non-existent segment // non-existent segment // non-existent segment RCLK = 1; for (i=0; i<PULSE_LENGTH; i++); RCLK = 0; // put shifted data to LCD - rising edge // keep clock high for a while // turn off clock P1MDIN P1MDIN // configure COM2 to ANALOG_IN; // and COM3 to digital &= ~0x20; |= 0x40; P1MDOUT &= ~0x20; P1MDOUT |= 0x40; COM2 = 1; COM3 = 1 ^ com_invert; com_cycle = 4; // // // // // make COM2 an open-drain make COM3 a push-pull set COM2 to high impedance start the COM3 cycle next state } else if (com_cycle == 4) { SER = D6D ^ com_invert; SER = D6E ^ com_invert; SER = D5D ^ com_invert; SER = D5E ^ com_invert; SER = D4D ^ com_invert; SER = D4E ^ com_invert; 24 Strobe(); Strobe(); Strobe(); Strobe(); Strobe(); Strobe(); Rev. 0.2 AN202 SER SER SER SER SER SER SER SER SER = = = = = = = = = D3D D3E D2D D2E D1D D1E 1 1 1 ^ ^ ^ ^ ^ ^ ^ ^ ^ com_invert; com_invert; com_invert; com_invert; com_invert; com_invert; com_invert; com_invert; com_invert; Strobe(); Strobe(); Strobe(); Strobe(); Strobe(); Strobe(); Strobe(); Strobe(); Strobe(); // non-existent segment // non-existent segment // non-existent segment RCLK = 1; for (i=0; i<PULSE_LENGTH; i++); RCLK = 0; // put shifted data to LCD - rising edge // keep clock high for a while // turn off clock P1MDIN P1MDIN // configure COM3 to ANALOG_IN; // and COM4 to digital &= ~0x40; |= 0x80; P1MDOUT &= ~0x40; P1MDOUT |= 0x80; COM3 = 1; COM4 = 1 ^ com_invert; com_cycle = 1; // // // // // make COM3 an open-drain make COM4 a push-pull set COM3 to high impedance start the COM4 cycle next state com_invert = com_invert ^ 1; // toggle com_invert } TF2H = 0; // clear TF2 } // end LCDrefresh_ISR //----------------------------------------------------------------------------// Strobe //----------------------------------------------------------------------------// // Strobe is used to clock the data into the 74HC595 shift registers // void Strobe() { int i = 0; SRCLK = 1; for (i = 0; i < PULSE_LENGTH; i++); SRCLK = 0; for (i = 0; i < PULSE_LENGTH; i++); // wait a few cycles // wait a few cycles } //----------------------------------------------------------------------------// wait_one_msec //----------------------------------------------------------------------------// // Assumes Timer3 overflows once every 500 usec // void wait_one_ms(unsigned int count) Rev. 0.2 25 AN202 { count = count * 2; // overflows once every 500 usec // so double that is 1 ms TMR3CN &= ~0x80; TMR3 = TMR3RL; TMR3CN = 0x04; // Clear Timer3 overflow flag while (count--) { while (!(TMR3CN & 0x80)) {} TMR3CN &= ~0x80; } TMR3CN &= ~0x04; // Start Timer3 // wait for overflow // clear overflow indicator // Stop Timer3 } //----------------------------------------------------------------------------// LCD functions //----------------------------------------------------------------------------//----------------------------------------------------------------------------// putchar //----------------------------------------------------------------------------// // putchar only handles the digit components on the LCD screen. // This functions shifts the digit values to the left, shifting out the // left-most digit. This function has 3 potential actions based on the input: // // 1. Any input whose ASCII code is between 0 and 127 gets translated // according to the translation table above // // 2. Any input whose ASCII code is between 128 and 255 is directly sent to // the LCD. The lower 7 bits indicate which of the seven segments are lit. // // 3. Passing a newline char '\n' to this function clears all 6 digits // // This function, unlike standard putchar, does not have any error return msgs. // // This function will not cause an interrupt to force output. The input char // will be displayed on the screen on the next refresh cycle char putchar(char charIN) { unsigned char iter = 0; if (charIN != '\n') { if ((charIN & 0x80) == 0) { charIN = translation_table [charIN]; } 26 // not a new line // translation necesssary // quick lookup EA = 0; // prevent partial display for (iter = 0; iter < 5; iter++) { // shift the digits left Rev. 0.2 AN202 LCD_digits[iter] = LCD_digits[iter+1]; } LCD_digits[5] = charIN; // new digit is rightmost EA = 1; // enable interrupts again } else { EA = 0; for (iter = 0; iter < 6; iter++) { LCD_digits[iter] = 0xFF; } EA = 1; } // input is a newline if (charIN == 0xFF) { charIN = ' '; } // couldn't interpret OR space // return space return charIN; // just like putchar // disable interrupts // clear all digits // enable interrupts } Rev. 0.2 27 C8051F330 28 GND VDD 17 1 P1.7 Rev. 0.2 3 4 5 6 7 8 9 SRCLRb 74HC595 74HC595 RCLK OEb RCLK P1.2 SER SRCLK QH’ 11 12 13 14 15 16 OEb SRCLRb QH’ QA QB QC QD QE QF QG QH 10 SRCLK SER QA QB QC QD QE QF QG QH 2 P1.1 P1.3 18 P1.6 19 KTD522 LCD P1.5 P1.4 All resistors are 500K VDD VDD AN202 11. Schematic AN202 CONTACT INFORMATION Silicon Laboratories Inc. 400 West Cesar Chavez Austin, TX 78701 Tel: 1+(512) 416-8500 Fax: 1+(512) 416-9669 Toll Free: 1+(877) 444-3032 Please visit the Silicon Labs Technical Support web page: https://www.silabs.com/support/pages/contacttechnicalsupport.aspx and register to submit a technical support request. Patent Notice Silicon Labs invests in research and development to help our customers differentiate in the market with innovative low-power, small size, analogintensive mixed-signal solutions. Silicon Labs' extensive patent portfolio is a testament to our unique approach and world-class engineering team. The information in this document is believed to be accurate in all respects at the time of publication but is subject to change without notice. Silicon Laboratories assumes no responsibility for errors and omissions, and disclaims responsibility for any consequences resulting from the use of information included herein. Additionally, Silicon Laboratories assumes no responsibility for the functioning of undescribed features or parameters. Silicon Laboratories reserves the right to make changes without further notice. Silicon Laboratories makes no warranty, representation or guarantee regarding the suitability of its products for any particular purpose, nor does Silicon Laboratories assume any liability arising out of the application or use of any product or circuit, and specifically disclaims any and all liability, including without limitation consequential or incidental damages. Silicon Laboratories products are not designed, intended, or authorized for use in applications intended to support or sustain life, or for any other application in which the failure of the Silicon Laboratories product could create a situation where personal injury or death may occur. Should Buyer purchase or use Silicon Laboratories products for any such unintended or unauthorized application, Buyer shall indemnify and hold Silicon Laboratories harmless against all claims and damages. Silicon Laboratories and Silicon Labs are trademarks of Silicon Laboratories Inc. Other products or brandnames mentioned herein are trademarks or registered trademarks of their respective holders. Rev. 0.2 29