16 x 2 LCD Quickstart Guide

INTERFACE TO 16x2 LCD SCREEN (simplified)




My ATtiny24 Eval Board w/ LCD
This is a tutorial on interfacing a 16 x 2 LCD screen to an ATtiny24, read the voltage from a rheostat, AND display the voltage value down to the single millivolt range (with reasonable accuracy). It is also helpful to have an actual voltmeter on hand to test the accuracy of the value displayed (to adjust a conversion factor later on).

This is a list of hardware used and where it came from:

  1. ATtiny24 Atmel Microcontroller:                                                               Mouser.com
  2. Atmel’s MKII AVRISP Programmer                                                           not sure
  3. GDM1602K 16 x 2 LCD screen (HD44780 compatible)                     http://www.sparkfun.com/commerce/product_info.php?products_id=709
  4. 150 ohm resistor, (2) 20 Kohm potentiometers                                  Mouser.com
  5. Jumper wires, bread board, power supply                                           where ever you choose

A Quick Schematic:

This was my first EAGLE schematic, the free version of EAGLE doesn't include an AVR library that contains the ATtiny24. But I was able to go to their site and download a avr-6.lbr file. This includes more of Atmel's chips.

Software:

used to write, compile, and load is AVRStudio4 and winAVR. Both are downloadable from the internet for free.  If you don’t know how to use AVRstudio4 or the AVRISP MKII programmer, then go to “Jesus’ Omnipotence” . . . (google.com) and look it up.
Download AVRstudio4 here: (you will have to register) . . . fah'get ah'bout it.
Download winAVR, for windows here:
For an excellent tutorial on getting started with AVRstudio4 and the MKII AVRISP Programmer, go to this site:
I highly recommend going to this site. This was crucial in getting me started in programming AVRs  9 months ago.
Before I get started on the LCD, I should tell you the tutorial that I used for getting my LCD to work. Go to www.eXtremeElectronics.co.in and look up the LCD tutorial there. The author is Avinash Gupta == F’ING GENIUS. The tutorial is excellent (all of them are), great explanations, and best of all, the author has written macros from easy interfacing with HD44780 compatible 16 x 2 LCD controllers.  The micro-controller he uses is an ATmega128, I believe. I will simply demonstrate using a different uC (microcontroller).
http://extremeelectronics.co.in/avr-tutorials/using-lcd-module-with-avrs/

So onward! (but read that tutorial first!)

IMPORTANT: At the bottom of the LCD tutorial, click on the button to download the files for the tutorial which include c and hex files needed to go further in this tutorial ! )
Place the downloaded files:  lcd.c lcd.h & myutils.h into this file: C:\WinAVR-20100110\avr\include
(My main hard drive is C, yours might be different, and assuming that you have downloaded winAVR)

Place these files, from whichever file they were initially downloaded to, and copy/paste them into the library of winAVR
Since including files in your code is done by putting something like this, #include , at the top of your code. I would assume “the magic genie inside my computer” will look in libraries of winAVR to find them. So I put them directly into my C:\WinAVR-20100110\avr\include   folder to make things simple.
*Note: If I didn’t have the lcd.c file in the winAVR folder, I got errors post build in AVRstudio4 when I “added existing source file(s)” lcd.c from somewhere else than winAVR. (I also had lcd.c saved in other folders on my comp. outside of winAVR)
*Note: When the code compiles in AVRstudio4, down in the “build” section, where it reports errors and such, it says that the device is an “atmega128”. Despite having selected attiny24 at start of project and in the connection dialog box, it still says this, but fear not, everything still worked.

This is how I include the necessary files
AGAIN!!: I believe that if you want to include lcd.h and myutils.h by putting them directly into your code with the #include function, then those files have to be put into winAVR libraries.

How to convert a 10-bit (0 – 1023) ADC value reading into Volts and millivolts values:


Jumpin' JiggaWatts!! (meant to raise morale). As you can see, my voltmeter reads 2.810 volts and I am reading 10 millivolts off.
Volts:

Understanding is best imagined by dividing the ADC value by the max 10-bit reading possible (1023). This will turn the ADC value into a PERCENTAGE of the total 1024 values ( 0 - 1.0 ) . Then simply multiply this percentage by the value of Vcc (supply voltage for me is 5 volts) and you have values which swing from 0 up to Vcc.
Volts = (ADC_reading * 5) /1023;
But there is a flaw with this way of thinking. You must multiply the ADC reading by 5 FIRST!!! This is why the 5 and ADC reading are in parenthesis. If you made the equation like this:
Volts = 5* (ADC_reading/1023);
Then it would not work so well. Maybe if you made the variable a float type it might (or maybe it works fine and I screwed up something). But this ensures that the number doesn’t get turned into a percentage ( a number less than 1) in the Arithmetic Logic Unit. I think the arithmetic hardware in the chip will lose these numbers when they become less than 1? I am not really sure how that works. Someone feel free to criticize me for being ignorant.

Millivolts:

So then you might think “then how the hell do you get a millivolt value if you lose numbers right of the decimal place”. Well if you understand the formula for it, you’ll see that the millivolt value is actually an int with 0 - 999 values.

Enter modulus  “%”  in C programming.

This is actually my first time to use this in my code. Here is a quick definition:

This was done on my ATtiny24 Eval Board
x%y  == the remainder of x/y
f’ing nifty eh?
Some examples :
9%5 == 4
10%5 == 0
11%5 == 1
300%204 == 96
Man%Women == a baby  . . . . . hehe, . . ok, that was lame
Even if x is smaller than y, it still works:
10%204 == 10
150%204 == 150
200%204 == 200
I know this works because I had the LCD screen, at one point, display ONLY the modulus value of the ADC reading divided by 204. So starting at what would have been 0 volts, turning the potentiometer, the display starts at 0 and counts up to 203 and starts over at 0 where the potentiometer should be at 1 volt. And then between 1 volt and 2 volts, it counts again from 0 to 203, etc. three more times, up to 5 volts.
I should also mention why I am using 204 as the modulus divisor (Right term ?). If I am trying to turn the values 0 - 1023 into values 0 -  5, then how many values compromise a single volt? Simple, 1024 values divided by 5 (volts) gives me 204.8, so use either 204 or 205 whenever you see it in the equations.
SO, if you know you can very easily turn the values in between the single volt readings into values that range from 0-204, then following the Volts example. Turn that 0-204 range into a percentage and multiply it by 1000 (or 999) and now you have a range of 0 – 999. So the value of the variable displayed is not actually a decimal (less than 1), you are really displaying 0 to 999. It is perceived as a decimal value because we wrote a string “.” right before the area that the millivolts are displayed. Fooling you into thinking you stored a decimal in an int variable!!! Mwahahaha . . . . .
You might also notice that my equation used in the actual code is
milliVolts = (100/18)*(reading%204);
instead of milliVolts = (1000/204)*(reading%204);
I did this because 100/18 (or 1000/180) gives me an output reading which is closer to the actual voltage value (according to The White Wizard in my voltmeter). I suppose this is due to my 5 volt regulator putting out a little bit above 5 volts.  Play around with this, 100/18, conversion factor and see what gets you close.

Altering the downloaded files to suite your needs:

Follow the directions in the LCD tutorial for changing the lcd.h files.


This is what I did to make it work

A quick synopsis on the LCD and connections made to it:

The basic 16X2 LCD (HD44780 controller compatible) has 16 connections, they are:


Refer to your datasheet for which pins are which on the physical LCD module
*More notes: Some LCD schematics have you connect the Contrast pin to ground. This did not work for me, thus I had to use a rheostat. This LCD also had a back light (connections A and K, or 15-16). Treat it as a simple LED. I used a 150 ohm resistor. I think the data sheet called for quite a bit of current for the back light, so I tried a few smaller resistor values to allow more current, but didn’t see much of a difference, so I stayed with the 150 ohm resistor.


Refer to the schematic towards the beginning of this tutorial for the connections required for the macro files written by Avinash. The files call for only 4 data lines used to send data to the LCD instead of the full 8 wires for to form a byte.
Remember: The macros require the lower four pins of a uC port (e.g. PORTC 0-3). I use Port A pins 0-3, and my connections from LCD to uC are:
R/S --> Port B, pin 1
R/W --> Port B, pin 0
E --> Port B, pin 2

Concerning the LCD Backlight: Treat the + & -  connections as you would the anode and cathode of a regular LED. (on this LCD, connection A == positive (+) lead & K == negative (-) lead). The current limiting resistor can connect to either side.

The white LED backlight on my LCD has a forward voltage drop of 4.2 Volts. White LEDs generally have a forward voltage drop of 3.2 - 3.7 volts, this one just happened to be a little more.



CODE:

/****
Quick code to read the ADC value on PORTA pin 4 on an ATtiny24.
And display the value in Volts, with close to +/- 10 millivolts of accuracyGraham A. Monahan
March 23, 2010
****/
#define F_CPU 8000000UL

#include
#include
#include //make sure these files were put into the winAVR
#include //"include" folder

/***Function Declarations***/

int ADC_read(void);

/***Variable Declarations***/

unsigned int Volts = 0;
unsigned long milliVolts = 0;
unsigned long reading = 0;
unsigned char i = 0; void main(void)
{
DDRA &= ~(1 << 4);                                      //make PORTA Pin 4 an input (for ADC channel)
ADMUX |= (1 << MUX2);                           //select ADC Channel 4
//Initialize LCD module
InitLCD(LS_BLINK|LS_ULINE);
//Clear the screen
LCDClear();
LCDWriteStringXY(0,0,"OMFG! There are");
LCDWriteStringXY(6,1,"JiggaVolts");
LCDWriteStringXY(1,1,".");                        //display a "." in position 11 on first row
while(1)
{
reading = ADC_read();                                 //take ADC reading
Volts = (5*reading)/1023;                            //take 10-bit ADC reading and convert into 0 - 5 values
milliVolts = (100/18)*(reading%204);     //use modulus and conversion factor to create millivolt values
if(milliVolts >= 1000)    { Volts++; }        //fixes a bug , take it out and swing pot. close to single volt readings
_delay_ms(100);                                           //simple delay
LCDWriteIntXY(0,1,Volts,1);                    //display Volts (0 - 5) variable in position 10 on first row
LCDWriteIntXY(2,1,milliVolts,3);           //display milliVolt value in position 12 on first row that takes up 3 spaces (12,13,14)
}
}
int ADC_read(void)                        /***select ADC channel prior to calling this function***/
{
int ADC_value = 0;
int ADCsample;
ADCSRA |= (1<//enable ADC
ADCSRA |= (1<//Do dummy conversion
while ((ADCSRA & ADSC));               //Wait for conversion to complete, and forget about it

for (i=0; i<16; i++)                              //do this 64 times, any longer and ADC1_value will need to be larger than an unsigned int
{
ADCSRA |= (1<//start a conversion
while ((ADCSRA & ADSC));          //wait for conversion to finish
ADCsample = ADCL;                       //change back to ADCL for 10 bit precision, and remove left-shift bit setting
ADCsample += (ADCH<<8);        //Left shift the top two bits 8 places
ADC_value += ADCsample;          //add ADCsample to ADC_sensor
}
ADC_value = (ADC_value >> 4);     //average sample by right shifting 5 places, same as dividing by 32

return ADC_value;
ADCSRA &= ~(1<//disable ADC
}

Well, that is all I am going to add to this tutorial for now. Please make comments, and ask questions. I can not always reply quickly (as I might be out to sea for a while). But will try to answer your questions, and thus update the tutorial to make it more accurate and informative.


Matey!


Thanks for checking in!