Source code for this project.
Header File: LCD_Init_Class.h
Constructor: LCD_Init_Class(..) upto 7 pin definitions
Public Methods: begin( );
char2lcd(byte val); write val to LCD
setX(int x): col number 0->13
setY(int y): row number 0->5
setXY(int x, int y);
clear( );
-----------------------------------
Abstract: The project initialises a 84 x 48 Dot Matrix LCD Display. To generate the required bit patterns on the display the project class will include the LCD_ROM_Class developed in an earlier project.
Possible Audience: Micro-controller enthusiasts who wish to understand using the LCD display and programmers interested in a simple example of Cpp inheritance.
Keywords: Arduino UNO, Dot Matrix LCD Display, Module XC-4616, Arduino Class/Library, SPI Serial Peripheral Interface
Components: The project was tested using an Arduino Uno, Dot Matrix Display (Jaycar XC-4616), 2 8-pin wire wrap sockets/headers.
Inherited Libraries: LCD_ROM_Class
I have a number of projects that use a display. One such project was to use the display for debugging when the serial monitor was not available.
This project is concerned with evaluating the 84x48 dot matrix display. These displays are sold by Jaycar as the XC-4616 and are identical to displays found in old Nokia phones. They feature the PCD8544 chipset.
The objectives of this page may be summarised as:
1. Develop code to initialise the LCD.
2. Develop a method to test writing to the LCD. In this example the method will be write(char).
3. Demonstrate setting up a C++ class. In this example LCD_Init_Class.
4. Demonstrate inheriting a C++ library.
This project builds on the LCD_ROM_Class that developed a public method from readings characters from a ROM to place in the LCD. This project will perform all the tasks necessary to initialise the LCD and physically write characters to the LCD. That is this project ROM_Init_Class will inherit the LCD_ROM_Class. A further project will build on these projects to provide methods that will print the standard print functions on the LCD. This relationship is illustrated below.
This project will require the LCD_ROM_Class library. If this is not available in the users libraries folder the following procedure should be followed.
The safest method is to generate the library rather than attempting to copy files.
1. Create a new project by opening a new file: ie file-->new. Give it the name/label LCD_ROM_Class. The Arduino IDE will give it the extension *.ino.
2. In the Arduino IDE use the inverted triangle near the top right and create two new_tabs. Give these the labels LCD_ROM_Class.cpp and LCD_ROM_Class.h - note the extensions
3. From the folder LCD_ROM_Class_code on this site copy the source code LCD_ROM_Class.ino, LCD_ROM_Class.cpp and LCD_ROM_Class.h
4. Check that the project will compile. If necessary remove any embedded printing characters.
5. Using for example Windows Explorer create a *.zip file of the *.cpp and *.h files. It will have the label LCD_ROM_Class.zip***
6. In the Arduino IDE use sketch-->Include Library-->Add *.zip library and select LCD_ROM_Class.zip. LCD_ROM_Class. is now available as a contributed library.
***The file structure is shown below where in this example "This_Project" is LCD_ROM_Class. Under the sketchbook folder there will be a sub-directory LCD_ROM_Class that contains the three files of the same name with extensions *ino, *.cpp and *.h. This is where the files are found to make a *.zip copy.
Following the include library operation there will be copies of the *.cpp and *.h files under the folder LCD_ROM_Class that is in turn a sub folder of the folder LCD_ROM_Class.
The circuit for the LCD-Arduino Interface for this example is shown below. Note the Arduino and the LCD use mixed conventions for pin numbering. The Arduino starts at pin 0 while the LCD is numbered starting at pin 1.
Notes
The LCD is a 3.3 volt device. Jaycar specifications states the LCD needs 10k resistors for IO pins if running off 5V but their examples seem to be able to be directly connected. The input pins, including Vcc are driven by the microcontroller output pins which might not be a solid 5V giving a little margin.
The LED input is not connected. If used it should be wired to 3.3v.
One practical issue with the wiring is the gap between Arduino I/O pins 7 and 8 which will not permit a direct connection. See photo below were the right most pin has a bend. This solution uses headers (Jaycar part number HM-3207) Only seven pins are required. Pin 8 of the LCD is not connected so hangs over the right hand edge. The right hand pin of the header (shown bent) goes to the first pin of the second UNO header. In the example a 10 pin header was available so the three left hand pins are not used. The first pin of the LCD goes to the third pin (Pin 2) of the first UNO header.
An second arrangement is shown below. In this example a shield with male pins is used. The pins of two 8-bit headers are soldered to give a socket to socket link. As above pin 8 of the LCD is left floating and the left hand connection of the link overhangs the shield socket.
The first step is to use the Arduino IDE to create a new file. ie : File--> New. As noted I have chosen to give it the name LCD_Init_Class where the name attempts to give a brief description of the project. In this case it is a class that initialises the LCD. The Arduiino IDE will give it the extension *.ino.
At this point is is probably worthwhile compiling and running the code to ensure items like the correct board and port are selected.
Since one of the objectives of this project is to demonstrate setting up a class that will be started in this section. The various files will be populated in later sections.
1. In the Arduino IDE use the inverted triangle at the top right of the screen and select New_Tab.
2. Give it the name/label LCD_Init_Class.cpp. Note the extension. This *.cpp will contain the implementation code for the class/library.
3. Repeat for a second file LCD_Init_Class.h. The *.h file is the header file and will be used by programs that wish to use the class LCD_Init_Class.
4. Add the following code to the three LCD_INIT_Class files:
The *.ino code has been enhanced to include the header file.
The *.cpp implementation code includes the header file also.
The actual header file includes a wrapper #ifndef ..........#endif which means the header file will only be included into a project once***.
If LCD_INIT_CLASS_H is not defined it is then defined by this project.
Also the header file Arduino.h is included. This file contains definitions that might be used by the library. Arduino.h is included in standard IDE operaions but not included in library generation.
***In larger projects there is the possibility that several files may use LCD_Init_Class - there is only need to have one copy.
The header file should be upgraded as illustrated to define a class LCD_Init_Class.
The header file includes the constructor LCD_Init_Class( ) and in anticipation two public methods begin( ) and char2lcd( ).
The implementation file will now be:
In the client/test/application code an instance/object LCD of the class LCD_Init_Class will be created. That is
Note in the declaration since at this point the constructor has no parameters the parenthesis are omitted**.
**While the program will compile with parenthesis I have found later in projects there are subtle errors. A search of the internets suggests that with parenthesis the compiler treats it as a function. (?).
Before becoming engrossed in the detail of initialising the LCD the LCD_ROM_Class can be inherited.
1. use sketch-->include library--> and go to contributed libraries and select LCD_ROM_Class. It will be along way down the list of files.
2. Modify the class definition: class LCD_Init_Class : public LCD_ROM_Class {
The method char_pat( ) created as part of the LCD_ROM_Class is now available to this project. This can be verified by modifying the *.ino code:
Compiling this code will not give any errors verifying that the public method char_pat( ) of LCD_ROM_Class is visible to this project.
One of the objectives of this project is to write something to the LCD so the next step will be to make the LCD operational. That is include the initialisation. Often using I/O devices the initialisation steps are the most challenging part of a project.
The constructor could be the null routine as given to this point.
In the circuit as given earlier the LCD was wired to specific pins on the Arduino. However the constructor arguments could be used to re-define the pin wirings.
If the class LCD_Init_Class contained an additional constructor defined as follows:
the wiring could be defined for each instance of LCD_Init_Class. That is in the client code (the *.ino file) the code would become:
The object/instance would have 7 arguments corresponding to each pin in the LCD interface.***
The implementation file will become:
*** Good software engineering would expect the pins to be previously defined
and creating the object/instance:
UNO_LCD_Class LCD(PIN_VCC, PIN_GND, PIN_SCE, PIN_RESET, PIN_DC, PIN_SDIN, PIN_SCLK);
By convention the private pin names will be defined with an underscore. That is in the header file add the following to the class
private : int _vcc,_gnd,_sce,_res,_dc,_sd,_ck;
and in the implementation file modify the constructor as illustrated
LCD_Init_Class::LCD_Init_Class(int pin_vcc, int pin_gnd, int pin_sce, int pin_res, int pin_dc, int pin_sd, int pin_ck ){_vcc=pin_vcc; _gnd=pin_gnd; _sce=pin_sce; _res=pin_res; _dc=pin_dc; _sd=pin_sd; _ck= pin_ck; };These pin definitions will be used in the remainder of the program. They are private so are not known outside the class so nothing, not even the *ino file can change them.
If the wiring is mostly to remain the same the original declaration of the object with no arguments could be retained and the pins defined in the implementation file.** That is
To test the above the original object would be used . ie LCD_Init_Class LCD;
Using multiple definitions is known as overloading. If the definition has a different number of arguments or different types the compiler picks the correct definition to use.
**Note it is not possible to call the 7-argument constructor from the zero argument constructor. I found that the following code
LCD_Init_Class::LCD_Init_Class( ) { LCD_Init_Class(2,3,4,5,6,7 )};will compile but will not work. For example the I/O pins are not set up. To set the pins use the format given above.
All the pins in the LCD interface should be defined as outputs and at this point the _vcc pin can be set high. This will be part of the begin( ) method that will be invoked from the setup( ) code:
In the *.ino code:
and in the implementation code:
The operation can be verified by using a multi-meter to measure the voltage on Arduino pin 2. (It should be a nominal 5volts)
The next step in the initialisation involves writing to the LCD. The LCD is interfaced via a SPI (Serial Peripheral Interface). With the SPI there are two critical connections:
1. The _sd serial data pin that contains the data to write**
2. The _cl clock pin that indicates when the data is valid.
The operation will be to set the first bit to be transferred and then to toggle the clock to tell the LCD, or slave that there is valid data. This would be repeated for all 8 bits.
While writing the code to implement the transfer is not too difficult there is a standard library available.
shiftOut(_sd, _cl, bitOrder, value)
(From the Arduino Reference) Shifts out a byte of data one bit at a time. Starts from either the most (i.e. the leftmost) or least (rightmost) significant bit. Each bit is written in turn to a data pin, after which a clock pin is pulsed (taken high, then low) to indicate that the bit is available.
Note for the bitOrder the Arduino Reference defines MSBFIRST and LSBFIRST. (Most Significant Bit First, or, Least Significant Bit First). The LCD expects the MSB first.
To test the shiftOut( ) add the following code to the begin( ) method and observe the result on an oscilloscope.
The character 'U' is the pattern 01010101 so the display will be a square wave. If an oscilloscope is not available remove the delay( ) and probe the data pin with a voltmeter. For a square wave the reading should be approximately 2.5 volts.
Comments on the waveform:
1. I personally like to know something about the library I am using. For example its speed and memory requirements plus any limitations.
2. The clock pulse is in the centre of the data so it will not matter if the LCD samples the data on the rising or falling edge of the clock.
3. In the trace most operations took approximately 85.2 uSec, but
4. Occasionally one of the transfers was stretched. This is the situation captured between the 3rd and 4th clock pulse above. It must be assumed that the Arduino is doing something in the background. Updating software timers for example. For this project the small jitter should not have any consequences.
** This pin is also known as the MOSI - master out slave in. In this example the Arduino is the master.
Normally there is an addition pin the MISO - master in slave out but in this example the LCD has nothing to write to the master so MISO is not present.
The information going to the LCD is treated as a command when pin _dc is low or data (to be displayed) when pin _dc is high. This suggests code of the form:
The function LcdWrite( ) ** has two parameters
dc: defining if the information will be a command or data. In initialising the LCD it will be a command but to display characters it will be data
data: the actual command or data
Two constants should be added to the program::
The code will be tested as part of the LCD initialisation.
** Commented out in the code are the lines to select the LCD. These will be necessary in a larger system that has more than one SPI Peripheral on the same bus.
The LCD will need to be initialised. This will require
performing a hardware reset (this section)
writing a series of software commands to the LCD (next section)
Resetting the LCD will require the RESET pin being low for at least 100msec. This is implemented with the following code where delay( ) is a defined function giving the delay in milli seconds.
After running this code pin 4 on the LCD should be high.
The LCD commands are defined in the publication PCD8544 48 × 84 pixels matrix LCD controller/driver. The summary page is given below:
Initialising an LCD can be quite confusing. I have chosen to use the code sequence given in the jaycar.com.au/diy-arduino-wifi-example. That is
With the above code there is no concrete evidence that the LCD has been initialised correctly. The best solution is to send something to the LCD. One possibility is to include in the begin( ) an output statement. For example :
_LcdWrite(LCD_DAT,0xAA);
This statement will generate 4 dots on the screen.
If the statement is wrapped in a while(1) on every loop dots will be added to the next 8 pixels so that quickly each row consists of 4 parallel lines. Other patterns could be used. For example 0x55, 0xcc or 0x33.
The next step is to add real characters.
The Character to LCD char2lcd method** will be used to display real characters on the LCD screen.
The method will
(i) take the character as an argument/parameter.
(ii) use the method char_pat inherited from the LCD_ROM_Class to generate the pattern for the character and place in the 6 byte array pattern. _pattern[6] will need to be defined.
(iii) use the method _LcdWrite( ) to write the data in the pattern to the display one column at a time.
(iv) There is a return 1 to match the standard definition of the write method. This project will not use the return value.
To test the the following code was used
This gave the following display:
The LCD is an 84 character display and the test loop sent 96 characters so there was wrap around. The final character, 0x7F which was programmed as 3 dots appears in the second row over writing the characters from space to ','. Note the display is not zero'd so on the next pass the 3 dots will move forward 12 places (96-84).
** This method was originally given the label write( ). In developing a later project I also used the label write and I ran into some problems. One suggestion was to use an alternative name - hence char2lcd. This did not resolve my problem but rather than change and forth I have retained char2lcd( ).
The performance was observed using a MSO (Mixed Signal Oscilloscope).
Writing 96 characters took 52.6 mSec. A 84 character write (full screen) will take 46 mSec which may impact on the display appearance.
Note this operation will be a blocking routine so if other operations cannot be blocked the above routine will need to be re-written to only write a limited number of characters at a time.
The LCD command set contains two values to set the cursor.
(i) 00100YYY to set the Y address of the cursor where 0<= YYY <= 5 and
(ii) 01XXXXXX to set the X address of the cursor where 0<= XXXXXX <= 83.
Note changes in Y correspond to the height of a character where each change in X only changes one column making 6 steps are necessary to move to the next character.
The following methods have been added to the code**:
//Header File
#define COL_SPAN 14#define ROW_SPAN 6//Initialisation File
void LCD_Init_Class::setX(byte x){_LcdWrite(LCD_CMD,0x80 | (x*6)); }void LCD_Init_Class::setY(byte y){ _LcdWrite(LCD_CMD,0x40 | y);}void LCD_Init_Class::setXY(byte x, byte y){setX(x);setY(y);}voidLCD_Init_Class::clear( ){for (int pp= 0; pp < (COL_SPAN * ROW_SPAN); pp++) write(' '); setXY(0,0); }These were tested in the program loop:
Since the cursor is returned to the 0,0 position after each for loop the display is now rock steady with the three dots now always appearing in position 12 on the top row.
** also included is a clear method that, starting from the current cursor position writes a space to every location. I have then chosen to zero the cursor. Note from earlier writing 84 characters takes 46 mSec so the clear( ) can be expected to take this time also.
The clear( ) method uses the #defines COL_SPAN and ROW_SPAN to allow the code to be easily used with LCDs of other sizes.
With the project complete it is sensible to made a library class so the results can be used by other projects.
The LCD_Init_Class can be added to the Arduino as a library.
Currently the *.h and *.cpp files (and *.ino) will be under the user work directory in a sub folder LCD_Init_Class. (Shown as "This Project" in the above diagram)
To generate a library:
1. In the sub folder LCD_Init_Class use a program such as Windows Explorer to create a *.zip file of the *.h and *.cpp files.
2. In the Arduino IDE under sketch --> Include Library --> Add .zip Library
3. Browse your files and select LCD_Init_Class.zip
The operation will, if necessary create another sub folder "libraries". Under the libraries folder will be a folder LCD_Init_Class where there will be the LCD_Init_Class.cpp and LCD_Init_Class.h files.Further libraries will be created under the libraries folder.**
Notes
i. if the two files are copied across the Arduino would need to be restarted for the updates to take affect.
ii. The original client code (*.ino file) included the library using #include "LCD_Init_Class.h" with quotes. To use the files in the libraries folder the #include uses < ..>.
4. For other project to use the library
(i) in the Arduino IDE under sketch-->Include Library and
(ii) in the list of contributed libraries select LCD_Init_Class.
For other users if the *.zip file is available start at step 2.
** This is a different location to libraries included as part of the Arduino IDE. In my case they were at C:\\Program Files(x86)\Arduino\Arduino\libraries. With the "< >" directive the compiler will search in both locations for the given file.
This project is one steppingstone in developing a library/class for the LCD. See LCD_Print_Class. That library will enhance the write( ) method and inherit the standard print library. These methods/routines will simplify the use of the LCD in other projects.
---------------------------------------------------