USB to Serial Converter
18.432 MHz Crystal Oscillator 18pf 30ppm
22 pF Multilayer Ceramic Capacitor
16 MHz Crystal Oscillator 20 pF Through Hole
4x4 Keypad with Adhesive Backing
4 Channel Logic Analyzer
Quad Buffer Line Driver (Through Hole)
USB AVR Programmer
SPDT Slide Switch 3 pin 30V
We've produced a bit of reusable code to talk to the LCD, sending commands and characters.
We enabled as many functions for the LCD as we need at the moment (we will later
implement the 4-wire - 4-bit - mode to use fewer pins on the microcontroller), so
this is a good time to take the code and make a library out of it. There are a couple
of ways we can make these libraries. The library can reside in a ".h" file and a
".c" file, or it can all reside in the ".h" file. The latter is a bit easier, but
is not recommended if there is a huge amount of code.
First, a very important condition must be included in both methods. Sometimes, these
libraries may be included in multiples files in the overall project. But, libraries
cannot be included more than once. You will see that the #include <avr/io.h>
will be included in the main file, and in the library files because both of these
files need the information in the io.h file, but this file cannot be loaded twice,
so a condition must be added so that the code within the ".h" file is not loaded
twice. The #ifndef conditional statement is used in this case.
At the beginning of the ".h" file, we use a #define statement to essentially label
the file. Let's say, the io.h file has a #define statement at the beginning like
this: #define _IO_H_. The #define statement in this case is just telling the compiler
that the label _IO_H_ is defined. That's it! now, if after this statement has been
processed and _IO_H_ has been defined, you could ask the compiler: "has _IO_H_ been
defined?". The compiler would say yes. We actually do the opposite. We ask if the
_IO_H_ has not been defined, and then we tell the compiler to go ahead and process
the code. This is where the #ifndef comes in. #ifndef is short for "if not defined".
So, in the case of the io.h file, the following would exist:
The #endif would be at the very end of the file. If this file was processed once,
you know that the code would be processed because _IO_H_ would not have been defined
yet (hence the if not defined). If the code had been processed previously (from
another file) then the #define _IO_H_ would have already been processed and the
compiler would say to the #ifndef and say, yes it has been defined and I'm not going
to process the following code. In fact, I'm going all the way to the end of the
file, to the #end if statement and getting out of this file and continuing where
I left off!!
We need to do this to our ".h" file. Since I am calling the .h and .c file MrLCD.h
and MrLCD.c, we will call the label in the #define statement simply MrLCD, like
So, now that we got that out of the way, let's talk about the first method, even
though I will be using the second. As I said, in the first method, two files will
be created, a ".h" file and a ".c" file. The ".h" file will contain all of the macros
(#define) statements, and all of the prototypes. The ".c" file will contain all
of the actual routines. The main reason the ".h" file is created with only the definitions
and prototypes is to give you a convenient place to change configurations. In the
case of the LCD library, the #define statements contain information that will most
likely change, such as the port that is connected to the data lines of the LCD,
and the port and pins that will receive the control lines of R/W (Read/Write), RS
(Register Select) and E (enable).
In the tutorial, I name the ".c" and ".h" files MrLCD.c and MrLCD.h. We will create
these file by clicking on File -> New -> C / C++. When you do this, you will see
a new tab appear called "new". You will do this two times, one for MrLCD.c and MrLCD.h.
To name the tabs, you will need to use the "Save As..." menu selection and you will
be prompted to name the file. Optionally, you can name the files when you close
the files since the program will ask you for the name of the file.
Now we need to copy the information from the main file into the ".h" file and ".c"
file. For the .h file, you will need to copy the #include statements, #define statements
and prototype information:
Notice the #ifndef and the #endif. This code would only happen one time in the entire
project, even if I included this file in many other files. This will come in handy
in larger projects, but we are using include statements for other files more than
The ".c" file will receive the all of the routines (except the main routine) and
the ".c" file will also receive any global variables that the routines use. We have
one called: firstColumnPositionsForMrLCD which is declared at the top of the file,
but this could have been defined just before the routine that uses it. The ".c"
file also has the #include statements that the routines use.
The ".c" file may look like this:
You might notice a few differences. First, there is no need to use the #ifndef since
the ".h" file already has that. Second, there is a need to add an include to the
"Mr.LCD.h" to get the prototypes. And finally, there is a new routine called InitializeMrLCD.
This routine just packages up the first few lines of code pertaining to the LCD.
We don't want that code taking up any room in our main file. Using it this way,
all we need to do it add a line in the main routine:
The first method also requires you to inform the makefile of the ".c" file that
will be included. In this file, you will have the remark: "# List C source files
here." Just under that statement, you will see "SRC = $(TARGET).c ". The new ".c"
file that you just created will be included here. This is what that are might look
That is all you need to do in the makefile.
The Second Method:
The second method is the easiest, but as the program gets very long, it may pose
an issue on readability, so for very large files, the first method is the best one
to use. There is a big benefit to using this method, and I used the word easiest,
because it is easy. You are not going to mess with the makefile and add the MrLCD.c
reference. You will only copy and paste the code pertaining to the LCD into a ".h"
file. We will call this file "MrLCD.h", just like before. After that, all you need
to do is have an #include "MrLCD.h" at the top of your main file and add the InitializeMrLCD()
in the main routine. Below is the main file and the MrLCD.h file and how they may
The Main File (main.c):
The Header file (MrLCD.h):