Microcontroller - A Beginners Guide - Passing a String to the LCD using Pointers
We had a great way of displaying characters to the display and controlling the display
to do what we want, so why would we need to do anything else? If we needed to send
one character at a time, the programming would be very tidious. We also wouldn't
have a good way to display numbers. So, let's enhance the program to display a lot
of characters using a single command.
How will the program work? We will first introduce the string of characters in a
command, like this:
Send_A_String("NewbieHack.com");. We will need to make the program traverse through
the string of characters. We have a few options on how to do this. We could store
the string into an array of characters, and then create a loop to traverse through
the array, displaying each character. But, we already know a little bit about how
to use arrays. Let's challenge ourselves and use pointers. Pointers may actually
start to creep into the intermediate level of programming, but there is no reason
we can't use the pointer for this operation.
Pointers pretty much describe itself. It points. The pointer will accept memory
addresses, but they return the value of the data in that memory address. That is
to say, they point to memory locations "see the value" instead of containing the
value. In the video, I use the analogy of comparing memory addresses to the addresses
of houses in a city. Each memory location must contain an address (similar to the
address of a house). You can store data in these memory locations. So, let's say,
we store a string of characters like "NewbieHack.com" in memory and we tell the
pointer to point at this string of characters. Initially, this pointer will be pointing
to the "N" in the string. If you increment the pointer by one (1), it will point
to the "e". Doing this for each character, you will eventually get to the end, which
is populated by a 0, or null. The null character, or "0" informs us where we may
be able to stop the loop as we travers through the string.
The pointer looks just like a standard variable, but with one minor difference.
The pointer uses the asterisk (*) character before the variable. For example, using
the char data type, a standard declaration of a variable may be: char aCharacter
= 0x41;. The 0x41 is an ASCII (American Standard Code for Information Interchange)
code and represents the "A" character. The "aCharacter" is the variable that has
the data type of "char". If we put an * just before the variable (can also be located
just after the data type), then the variable becomes a pointer.
So, we know what the command may look like: Send_A_String("NewbieHack.com");. What
might the routine look like to send each character to the display? It will contain
a loop that checks for the end of the string, and the pointer will be incremented
with the unary operator "++" at the end. Remember, the ++ is just a shortcut to
increment a variable. It might look like this: aValue++; Ours will have an asterisk,
so it may look like this: *aPointer++; Don't let the variable name confuse you,
it can be almost anything. So, here is what the routine may look like:
void Send_A_String(char *StringOfCharacters)
{
while(*StringOfCharacters > 0)
{
Send_A_Character(*StringOfCharacters++);
}
}
Wow, that's a very short routine. This is the benefit of using pointers. When you
send the command: Send_A_String("NewbieHack.com");, the string "NewbieHack.com"
automagically gets stored in a memory location of the microcontrollers choosing,
but we HUMANS don't have the slightest clue as to this memory location. That's why
the MAN created pointers!! thank heavens for the pointer, because, now you can just
increment this pointer and it will point to each of the characters and inform the
HUMAN of the contents of each of these memory locations. The really cool thing is
that all of this happens in the Send_A_Character() command. Inside those beautiful
parenthesis. Notice the asterisks riddled throughout the routine.
Here is what the full program may look like:
include <avr/io.h>
#include <util/delay.h>
#define MrLCDsCrib PORTB
#define DataDir_MrLCDsCrib DDRB
#define MrLCDsControl PORTD
#define DataDir_MrLCDsControl DDRD
#define LightSwitch 5
#define ReadWrite 7
#define BiPolarMood 2
void Check_IF_MrLCD_isBusy(void);
void Peek_A_Boo(void);
void Send_A_Command(unsigned char command);
void Send_A_Character(unsigned char character);
void Send_A_String(char *StringOfCharacters);
int main(void)
{
DataDir_MrLCDsControl |= 1<<LightSwitch | 1<<ReadWrite | 1<<BiPolarMood;
_delay_ms(15);
Send_A_Command(0x01); //Clear Screen 0x01 = 00000001
_delay_ms(2);
Send_A_Command(0x38);
_delay_us(50);
Send_A_Command(0b00001110);
_delay_us(50);
Send_A_String("NewbieHack.com");
while(1)
{
}
}
void Check_IF_MrLCD_isBusy()
{
DataDir_MrLCDsCrib = 0;
MrLCDsControl |= 1<<ReadWrite;
MrLCDsControl &= ~1<<BiPolarMood;
while (MrLCDsCrib >= 0x80)
{
Peek_A_Boo();
}
DataDir_MrLCDsCrib = 0xFF; //0xFF means 0b11111111
}
void Peek_A_Boo()
{
MrLCDsControl |= 1<<LightSwitch;
asm volatile ("nop");
asm volatile ("nop");
MrLCDsControl &= ~1<<LightSwitch;
}
void Send_A_Command(unsigned char command)
{
Check_IF_MrLCD_isBusy();
MrLCDsCrib = command;
MrLCDsControl &= ~ ((1<<ReadWrite)|(1<<BiPolarMood));
Peek_A_Boo();
MrLCDsCrib = 0;
}
void Send_A_Character(unsigned char character)
{
Check_IF_MrLCD_isBusy();
MrLCDsCrib = character;
MrLCDsControl &= ~ (1<<ReadWrite);
MrLCDsControl |= 1<<BiPolarMood;
Peek_A_Boo();
MrLCDsCrib = 0;
}
void Send_A_String(char *StringOfCharacters)
{
while(*StringOfCharacters > 0)
{
Send_A_Character(*StringOfCharacters++);
}
}
If all of this code looks confusing to you, you may want to check out the
earlier tutorial.