在 LCD 中显示十六进制值
displaying Hexadecimal value in LCD
我为Atmega328写了LCD接口程序(虽然有可用的库,但我想从头开始写)。但是有两个问题。
1. 有时LCD显示不正确。只看到几条。我最终重置了一两次。
2. 我无法使用 displayOneByteHexValue() 显示十六进制值。但是 ASCII 转换是正确的,我可以在 Atmel Simulator 中看到它。下面是代码。我正在使用 Atmel Studio 6.2
/*
* EmbeddedProgram1.c
*
* Created: 16-05-2015 08:19:38
* Author: Mahesha
*/
#ifndef F_CPU
#define F_CPU 8000000UL
#endif
#include <avr/io.h>
#include <util/delay.h>
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Only Change following when changing pin numbers.
// All Data bits have to be assigned sequentially in same the port . RS and EN must be allocated in the same port
#define LCDPORT PORTD
#define LCDDDR DDRD
// used pins on port appropriate ports
#define LCD_DB4 2 // PORTD.2
#define LCD_DB5 3 // PORTD.3
#define LCD_DB6 4 // PORTD.4
#define LCD_DB7 5 // PORTD.5
#define LCD_ENABLE_BIT 6 // PORTD.6 Enable
#define LCD_RS 7 // PORTD.7 Register Select
//#define LCD_RW // R/W is connected to GND permanently
#define LCD_DATA_BITS_MASK 0x3C
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#define SET_EIGHT_BIT_MODE() LCDPORT|=((1<<LCD_DB5)|(1<<LCD_DB4)) // Set DB4 and DB5 as 1 for setting eight bit mode.
#define SET_FOUR_BIT_MODE() LCDPORT|=(1<<LCD_DB5)
#define SET_INSTRUCTION_MODE() LCDPORT&=~(1<<LCD_RS) //Function to select command port on LCD RS pin bit 2
#define SET_DATA_MODE() LCDPORT|=(1<<LCD_RS) //Function to select data port on LCD
#define DISABLE_LCD() LCDPORT&=~(1<<LCD_ENABLE_BIT) //Function to disable LCD P0.18
#define ENABLE_LCD() LCDPORT|=(1<<LCD_ENABLE_BIT) //Function to Enable LCD P0.18
//#define EIGHT_BIT_MODE 0x30 // 0 0 1 1 x x x x DB7 to DB0 0f LCD
#define BUSY_FLAG_WAIT_TIME 20
#define FOUR_BIT_5_BY_10_2LINE 0x28
#define LCD_INIT_DELAY 100 // Give a delay of 200 msec after reset. // Datasheet says 10 msec delay.
//Commands Finalized
#define CLEAR_DISPLAY 0x01
#define CURSOR_HOME 0x02 // return home
#define ENTRY_MODE_LEFT_TO_RIGHT 0x06 // Cursor direction from Left to right , Bit 1 of entry mode in LCD
#define DISPLAY_OFF 0x08 // Blink ON, Cursor ON etc are don't care
#define CURSOR_OFF_BLINK_OFF 0x0C
#define CURSOR_OFF_BLINK_ON 0x0D // blink on Even 0x0D also works. So cursor need not be ON
#define CURSOR_ON_BLINK_OFF 0x0E // blink off
#define CURSOR_ON_BLINK_ON 0x0F
#define SHIFT_ENTIRE_LEFT 0x18
#define SHIFT_CURSOR_LEFT 0x10
#define SHIFT_ENTIRE_RIGHT 0x1C
#define SHIFT_CURSOR_RIGHT 0x14
// Function prototypes
void unpackAndSend(char data);
void waitForBusyFlagToClear(void);
void sendLCDPulse(void);
void displayInRow1(char* data);
void displayInRow2(char* data);
void displayInRow1WithPosition(char* data, uint8_t position);
void displayInRow2WithPosition(char* data, uint8_t position);
void sendTextToLCD(char *data);
void displayOneByteHexValue(unsigned char,unsigned char,char);
void initializeLCD(void);
void CL_delayMS(unsigned int delayMS)
{
while(delayMS--)
{
_delay_ms(1);
}
}
void CL_delayuS(unsigned int delayus)
{
while(delayus--)
{
_delay_us(1);
}
}
// writes a char to the LCD
void writeCharToLCD(unsigned char data)
{
SET_DATA_MODE(); // RS bit has to be 1 for data mode
unpackAndSend(data);
}
// sendLCD pulse will just enable and disable the EN bit of LCD display.
void sendLCDPulse(void)
{
DISABLE_LCD();
CL_delayuS(50);
ENABLE_LCD();
CL_delayMS(1);
DISABLE_LCD();
CL_delayMS(1);
}
// writes an instruction to the LCD
void sendLCDCommand(unsigned char inst)
{
SET_INSTRUCTION_MODE();
unpackAndSend(inst);
waitForBusyFlagToClear();
}
// Unpack and send data will separate two nibbles and send twice.
void unpackAndSend(char inst)
{
char temp=inst;
DISABLE_LCD();
// SET_WRITE_MODE(); // If write is permanently disabled, do not use this.
LCDPORT &= (~LCD_DATA_BITS_MASK); // Clear the data bits
//sendLCDPulse();
inst&=0xF0;
inst=inst>>4; // Get the upper nibble
LCDPORT|=inst<<LCD_DB4; //Replace the bits starting from position of bit LCD_DB4 with this new data
sendLCDPulse();
LCDPORT &= (~LCD_DATA_BITS_MASK); // Clear the data bits again
//sendLCDPulse();
temp &=0x0f; //send low nibble
LCDPORT|=temp<<LCD_DB4;
sendLCDPulse();
}
// waitForBusyFlagToClear functio can wait for the busy bit, But since we are permanently connected R/W pin to ground, we cannot read
// the flag from LCD. In case busy bit has to be read, implementation has to be changed.
void waitForBusyFlagToClear(void)
{
CL_delayMS(BUSY_FLAG_WAIT_TIME);
}
// clear display
void clearDisplay(void)
{
sendLCDCommand (CLEAR_DISPLAY);
}
// return home
void returnCursorHome(void)
{
sendLCDCommand (CURSOR_HOME);
}
// LCD off
void displayOFF(void)
{
sendLCDCommand (DISPLAY_OFF);
}
// LCD on
void displayONCursorOFF(void)
{
sendLCDCommand (CURSOR_OFF_BLINK_OFF);
}
// cursor on
void displayONCursorON(void)
{
sendLCDCommand (CURSOR_ON_BLINK_OFF);
}
// blink on
void cursorOffBlinkOn(void)
{
sendLCDCommand (CURSOR_OFF_BLINK_ON);
}
// blink OFF, but display and cursors are ON
void cursorOnBlinkOff(void)
{
sendLCDCommand (CURSOR_ON_BLINK_OFF);
}
// All are ON
void cursorOnBlinkOn(void)
{
sendLCDCommand (CURSOR_ON_BLINK_ON);
}
//go to first line
void LCDline1 (void)
{
sendLCDCommand (0b10000000);
}
//go to second line
void LCDline2 (void)
{
sendLCDCommand (0b11000000);
}
// goto position x,y
// row1 or row2 are the parameters
// So parameters can be 1 or 2
void setRowAndColumnPositionOnDisplay (unsigned char rowNumber, unsigned char position)
{
unsigned char pos;
if (rowNumber == 1)
{
pos = 0x00 + position;
pos|=0x80; // Command to set 1st Row.
}
else //if (rowNumber == 1) // Either row 1 or two. We cannot have else option.
{
pos = 0x40 + position;
pos|=0xC0; // Command to set second row.
}
sendLCDCommand (pos);
}
void displayInRow1(char* data)
{
sendLCDCommand(0x80); // Set DDRAM Address as 0
sendTextToLCD(data);
}
void displayInRow1WithPosition(char* data, unsigned char position)
{
// The position cannot be more than 15. Display is 16 characters (0-15).
if(position>15)
{
position = 15;
}
sendLCDCommand(0x80|position); // Change the DDRAM address to first line by
// keeping D7 high and setting address to 0 onwards
sendTextToLCD(data);
}
////////////////////////////// diaplayInRow2 /////////////////////////////////////////////
void displayInRow2(char* data)
{
sendLCDCommand(0xC0); // Change the DDRAM address to next line 0x40 to 4F
sendTextToLCD(data);
}
////////////////////// diaplayInRow2WithPosition //////////////////////
void displayInRow2WithPosition(char* data, unsigned char position)
{
// The position cannot be more than 15. Display is 16 characters (0-15).
if(position>15)
{
position = 15;
}
sendLCDCommand(0xC0|position); // Change the DDRAM address to second line by
//keeping Bit D7 high and setting address at 0x40 onwards
sendTextToLCD(data);
}
void scrollLcd(char *row1Data,char *row2Data)
{
while(1)
{
sendLCDCommand(SHIFT_CURSOR_LEFT);
sendLCDCommand(SHIFT_ENTIRE_LEFT);
CL_delayMS(200);
}
}
//write text to the LCD
void sendTextToLCD(char *data)
{
while (*data)
{
writeCharToLCD(*data);
data++;
}
}
// Function to convert lower nibble to ASCII Value
//Only lower nibble of the input is considered and higher nibble is lost
char convertLowerNibbleToASCIIValue(char data)
{
data&=0x0F;
if(data<=9)
{
return(data+0x30);
}
else // There is no chance for getting more than 0x0F in the lowerNibble Parameter)
{
return(data+0x37);
}
}
// Function to convert Higher nibble to ASCII Value
//Only higher nibble of the input is considered and lower nibble is lost
char convertHigherNibbleToASCIIValue(char data)
{
data>>=4;
if(data<=9)
{
return(data+0x30);
}
else // There is no chance for getting more than 0x0F in the lowerNibble Parameter)
{
return(data+0x37);
}
}
void displayOneByteHexValue(unsigned char rowNum, unsigned char pos, char data)
{
char temp;
setRowAndColumnPositionOnDisplay(rowNum,pos);
temp = convertHigherNibbleToASCIIValue(data);
sendTextToLCD(&temp);
temp = convertLowerNibbleToASCIIValue(data);
sendTextToLCD(&temp);
}
// init LCD
void initializeLCD(void)
{
// Set the direction of port pins connected to LCD display as output ports.
// We are permanently connecting R/W pin to ground. So there is no read instruction in this case..
LCDDDR |= (1<<LCD_DB4)|(1<<LCD_DB5)|(1<<LCD_DB6)|(1<<LCD_DB7)|(1<<LCD_RS)|(1<<LCD_ENABLE_BIT);
//After reset, data sheet suggests some delay.
CL_delayMS(LCD_INIT_DELAY);
// Note some sites says three times 8 bit mode setting commands need to be sent.
// But it is observed that even without this, LCD works fine. So 1st Command, 2nd Command and 3rd Commands can be deleted below.
//ENABLE_LCD();
// 1st Command
SET_EIGHT_BIT_MODE();
//sendLCDPulse(); // Do not delete this. Need to further analyse. If pulse if sent it is not working
//CL_delayMS(5); // Do not use this delay since delay is not acurate, Either use _delay_ms or use timers
_delay_ms(5);
// Second Command
SET_EIGHT_BIT_MODE();
//sendLCDPulse();
_delay_us(100);
//CL_delayuS(100); // Do not use this delay since delay is not acurate, Either use _delay_ms or use timers
// third Command
SET_EIGHT_BIT_MODE();
//sendLCDPulse();
//CL_delayuS(100); // Do not use this delay since delay is not acurate, Either use _delay_ms or use timers
_delay_us(37);
// Finally Set four bit mode
SET_FOUR_BIT_MODE();
//sendLCDPulse();
CL_delayuS(100);
// First time when 4 bit mode command is sent, only one higher nibble was sent since
// only 4 bits are connected from MPU to LCD. Since D0 to D3 of LCD are not connected,
// their values depend on how the pins are connected in LCD module (May be grounded, may kept open etc)
//So again send function set command to set 2 line display mode mode and 5x7 character mode. But now two write operations to LCD is made
// inside the function sendLCDCommand.
sendLCDCommand (FOUR_BIT_5_BY_10_2LINE);
//turn on display and cursor OFF, Blink OFF (sent two times using below command)
sendLCDCommand (CURSOR_OFF_BLINK_OFF);
//clr display
sendLCDCommand (CLEAR_DISPLAY);
// Set Entry mode left to right
sendLCDCommand (ENTRY_MODE_LEFT_TO_RIGHT);
}
void LCDProgramCallFromMain(char *row1Data, char *row2Data)
{
initializeLCD();
setRowAndColumnPositionOnDisplay (1,0);
sendTextToLCD (row1Data);
displayOneByteHexValue(2,0,0xF4);
//setRowAndColumnPositionOnDisplay (2,5);
//displayInRow1(row1Data);
}
int main(void)
{
LCDProgramCallFromMain("Hello", "Welcome to cloude");
while(1)
{
}
}
下面是我得到的显示器图像。无法弄清楚问题出在哪里。
是的,我如下更改功能后它起作用了。谢谢您的帮助。
void displayOneByteHexValue(unsigned char rowNum, unsigned char pos, char data)
{
char temp;
setRowAndColumnPositionOnDisplay(rowNum,pos);
temp = convertHigherNibbleToASCIIValue(data);
writeCharToLCD(temp);
temp = convertLowerNibbleToASCIIValue(data);
writeCharToLCD(temp);
}
我进一步优化了代码,下面是完整的工作代码,其中包含从 main 调用的小演示函数。
#ifndef F_CPU
#define F_CPU 16000000UL
#endif
#include <avr/io.h>
#include <util/delay.h>
#include <stdlib.h>
#include <avr/sfr_defs.h>
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Only Change following when changing pin numbers.
// All Data bits have to be assigned sequentially in same the port . RS and EN must be allocated in the same port
#define LCDPORT PORTD
#define LCDDDR DDRD
// used pins on port appropriate ports
#define LCD_DB4 2 // PORTD.2
#define LCD_DB5 3 // PORTD.3
#define LCD_DB6 4 // PORTD.4
#define LCD_DB7 5 // PORTD.5
#define LCD_ENABLE_BIT 6 // PORTD.6 Enable
#define LCD_RS 7 // PORTD.7 Register Select
//#define LCD_RW // R/W is connected to GND permanently
#define LCD_DATA_BITS_MASK ((1<<LCD_DB4)|(1<<LCD_DB5)|(1<<LCD_DB6)|(1<<LCD_DB7))
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/*
#define SET_EIGHT_BIT_MODE() LCDPORT|=((1<<LCD_DB5)|(1<<LCD_DB4)) // Set DB4 and DB5 as 1 for setting eight bit mode.
#define SET_FOUR_BIT_MODE() LCDPORT|=(1<<LCD_DB5)
*/
#define SET_INSTRUCTION_MODE() LCDPORT&=~(1<<LCD_RS) //Function to select command port on LCD RS pin bit 2
#define SET_DATA_MODE() LCDPORT|=(1<<LCD_RS) //Function to select data port on LCD
#define DISABLE_LCD() LCDPORT&=~(1<<LCD_ENABLE_BIT) //Function to disable LCD P0.18
#define ENABLE_LCD() LCDPORT|=(1<<LCD_ENABLE_BIT) //Function to Enable LCD P0.18
#define BUSY_FLAG_WAIT_TIME 20
#define FOUR_BIT_5_BY_10_2LINE 0x28
#define LCD_INIT_DELAY 100 // Give a delay of 100 msec after reset. // Datasheet says 10 msec delay.
// Function prototypes for external use (to be kept in Header file)
void sendLCDCommand(unsigned char cmd);
void displayInRow1(unsigned char* data);
void displayInRow2(unsigned char* data);
void displayInRow1WithPosition(unsigned char* data, uint8_t position);
void displayInRow2WithPosition(unsigned char* data, uint8_t position);
void sendTextToLCD(unsigned char *data);
void initializeLCD(void);
// Function prototypes for file use (only internal)
void unpackAndSend(char data);
void waitForBusyFlagToClear(void);
void sendLCDPulse(void);
void displayOneByteHexValue(unsigned char,unsigned char,char); // This function is no longer needed. Only for demo purpose.
// #define commands are replaced by enums to demonstrate the feature of enumerated data types
enum DISPLAY_PROPERTY
{
CLEAR_DISPLAY=0x01,
CURSOR_HOME = 0x02, // return home
ENTRY_MODE_LEFT_TO_RIGHT= 0x06, // Cursor direction from Left to right , Bit 1 of entry mode in LCD
DISPLAY_OFF= 0x08, // Blink ON, Cursor ON etc are don't care
CURSOR_OFF_BLINK_OFF= 0x0C,
CURSOR_OFF_BLINK_ON= 0x0D, // blink on Even 0x0D also works. So cursor need not be ON
CURSOR_ON_BLINK_OFF=0x0E, // blink off
CURSOR_ON_BLINK_ON=0x0F,
SHIFT_ENTIRE_LEFT= 0x18,
SHIFT_CURSOR_LEFT= 0x10,
SHIFT_ENTIRE_RIGHT= 0x1C,
SHIFT_CURSOR_RIGHT= 0x14,
};
void SET_EIGHT_BIT_MODE(void)
{
LCDPORT=(LCDPORT&(~LCD_DATA_BITS_MASK))|((0<<LCD_DB7)|(0<<LCD_DB6)|(1<<LCD_DB5)|(1<<LCD_DB4)); // Set DB4 and DB5 as 1 for setting eight bit mode.
}
void SET_FOUR_BIT_MODE(void)
{
LCDPORT= (LCDPORT&(~LCD_DATA_BITS_MASK)) | ((0<<LCD_DB7)|(0<<LCD_DB6)|(1<<LCD_DB5)|(0<<LCD_DB4));
}
void CL_delayMS(unsigned int delayMS)
{
while(delayMS--)
{
_delay_ms(1);
}
}
void CL_delayuS(unsigned int delayus)
{
while(delayus--)
{
_delay_us(1);
}
}
// writes a char to the LCD
void writeCharToLCD(unsigned char data)
{
SET_DATA_MODE(); // RS bit has to be 1 for data mode
unpackAndSend(data);
}
// sendLCD pulse will just enable and disable the EN bit of LCD display.
void sendLCDPulse(void)
{
//DISABLE_LCD();
//CL_delayuS(50);
ENABLE_LCD();
CL_delayMS(1);
DISABLE_LCD();
CL_delayMS(1);
}
// writes an instruction to the LCD
void sendLCDCommand(unsigned char cmd)
{
SET_INSTRUCTION_MODE();
unpackAndSend(cmd);
waitForBusyFlagToClear();
}
// Unpack and send data will separate two nibbles and send twice.
void unpackAndSend(char inst)
{
char temp=inst;
DISABLE_LCD();
// SET_WRITE_MODE(); // If write is permanently disabled, do not use this.
LCDPORT &= (~LCD_DATA_BITS_MASK); // Clear the data bits
//sendLCDPulse();
inst&=0xF0;
inst=inst>>4; // Get the upper nibble
LCDPORT|=inst<<LCD_DB4; //Replace the bits starting from position of bit LCD_DB4 with this new data
sendLCDPulse();
LCDPORT &= (~LCD_DATA_BITS_MASK); // Clear the data bits again
//sendLCDPulse();
temp &=0x0f; //send low nibble
LCDPORT|=temp<<LCD_DB4;
sendLCDPulse();
}
// waitForBusyFlagToClear function can wait for the busy bit, But since we are permanently connected R/W pin to ground, we cannot read
// the flag from LCD. In case busy bit has to be read, implementation has to be changed.
void waitForBusyFlagToClear(void)
{
CL_delayMS(BUSY_FLAG_WAIT_TIME);
}
// goto position x,y
// row1 or row2 are the parameters
// So parameters can be 0 or 1
void setRowAndColumnPositionOnDisplay (unsigned char rowNumber, unsigned char position)
{
unsigned char pos;
if (rowNumber==0) // If Row is 0, display in 1st row
{
pos = 0x00 + position;
pos|=0x80; // Command to set 1st Row.
}
else //if (rowNumber == 1) // Either row 1 or two etc
{
pos = 0x40 + position;
pos|=0xC0; // Command to set second row.
}
sendLCDCommand (pos);
}
void displayInRow1(unsigned char* data)
{
sendLCDCommand(0x80); // Set DDRAM Address as 0
sendTextToLCD(data);
}
void displayInRow1WithPosition(unsigned char* data, unsigned char position)
{
sendLCDCommand(0x80|position); // Change the DDRAM address to first line by
// keeping D7 high and setting address to 0 onwards
sendTextToLCD(data);
}
////////////////////////////// diaplayInRow2 /////////////////////////////////////////////
void displayInRow2(unsigned char* data)
{
sendLCDCommand(0xC0); // Change the DDRAM address to next line 0x40 to 4F
sendTextToLCD(data);
}
////////////////////// diaplayInRow2WithPosition //////////////////////
void displayInRow2WithPosition(unsigned char* data, unsigned char position)
{
sendLCDCommand(0xC0|position); // Change the DDRAM address to second line by
//keeping Bit D7 high and setting address at 0x40 onwards
sendTextToLCD(data);
}
//write text to the LCD
void sendTextToLCD(unsigned char *data)
{
while (*data)
{
writeCharToLCD(*data);
data++;
}
}
// initialize LCD
void initializeLCD(void)
{
// Set the direction of port pins connected to LCD display as output ports
// We are permanently connecting R/W pin to ground. So there is no read instruction in this case
LCDDDR |= (1<<LCD_DB4)|(1<<LCD_DB5)|(1<<LCD_DB6)|(1<<LCD_DB7)|(1<<LCD_RS)|(1<<LCD_ENABLE_BIT);
//After reset, data sheet suggests some delay.
CL_delayMS(LCD_INIT_DELAY);
// Note some sites says three times 8 bit mode setting commands need to be sent.
// But it is observed that even without this, LCD works fine. So 1st Command, 2nd Command and 3rd Commands can be deleted below.
//ENABLE_LCD();
// 1st Command
SET_EIGHT_BIT_MODE();
sendLCDPulse(); // Do not delete this. Need to further analyze. If pulse if sent it is not working
//CL_delayMS(5); // Do not use this delay since delay is not acurate, Either use _delay_ms or use timers
_delay_ms(5);
// Second Command
SET_EIGHT_BIT_MODE();
sendLCDPulse();
_delay_us(100);
//CL_delayuS(100); // Do not use this delay since delay is not acurate, Either use _delay_ms or use timers
// third Command
SET_EIGHT_BIT_MODE();
sendLCDPulse();
//CL_delayuS(100); // Do not use this delay since delay is not acurate, Either use _delay_ms or use timers
_delay_us(37);
// Finally Set four bit mode
SET_FOUR_BIT_MODE();
sendLCDPulse();
CL_delayuS(100);
// First time when 4 bit mode command is sent, only one higher nibble was sent since
// only 4 bits are connected from MPU to LCD. Since D0 to D3 of LCD are not connected,
// their values depend on how the pins are connected in LCD module (May be grounded, may kept open etc)
//So again send function set command to set 2 line display mode mode and 5x7 character mode. But now two write operations to LCD is made
// inside the function sendLCDCommand.
sendLCDCommand (FOUR_BIT_5_BY_10_2LINE);
//turn on display and cursor OFF, Blink OFF (sent two times using below command)
sendLCDCommand (CURSOR_OFF_BLINK_OFF);
//clr display
sendLCDCommand (CLEAR_DISPLAY);
// Set Entry mode left to right
sendLCDCommand (ENTRY_MODE_LEFT_TO_RIGHT);
}
void displayNumberTest(int range)
{
float fNum = -0.332;
unsigned char lcdBuffer[50];
sendLCDCommand(CLEAR_DISPLAY);
//setRowAndColumnPositionOnDisplay (0,0);
//sendTextToLCD("Decimal = ");
for (int count=0;count<range;count++)
{
itoa(count,lcdBuffer,2);
setRowAndColumnPositionOnDisplay (1,0);
sendTextToLCD(lcdBuffer);
itoa(count,lcdBuffer,16);
setRowAndColumnPositionOnDisplay (1,13);
sendTextToLCD(lcdBuffer);
itoa(count,lcdBuffer,10);
setRowAndColumnPositionOnDisplay (0,0); // 1st Row, 0th position
sendTextToLCD(lcdBuffer);
// Now display some floating value number.
// use the function dtostrf(). It take 4 parameters
// Parameter1: The value to be converted
// 2: Width
//3. Precision
// 4. String to hold ASCII
// It is also possible to use sprintf function. But it takes more memory.
fNum=(fNum*(-1)*(count+1)/101.12)+0.012;
dtostrf(fNum,5,3,lcdBuffer);
setRowAndColumnPositionOnDisplay (0,7);
sendTextToLCD(lcdBuffer);
CL_delayMS(300);
}
}
// Scroll the display
// This program need to be modified further to display only whatever characters are there in DDRAM.
// Right now 4 columns will be shifted just to demonstrate.
void scrollingDisplayTest(unsigned char *row1Text, unsigned char* row2Text, unsigned int scrollDelay)
{
unsigned char count=40;
sendLCDCommand(CLEAR_DISPLAY);
displayInRow1(row1Text);
displayInRow2(row2Text);
CL_delayMS(2000);
while(count--)
{
sendLCDCommand(SHIFT_ENTIRE_LEFT);
CL_delayMS(scrollDelay);
}
}
void LCDDemoProgram(unsigned char *row1Data, unsigned char *row2Data)
{
initializeLCD();
sendTextToLCD (row1Data);
setRowAndColumnPositionOnDisplay (1,0);
sendTextToLCD (row2Data);
CL_delayMS(3000);
sendLCDCommand(CLEAR_DISPLAY);
// Just to demostrate how Hex values can be displayed without using itoa library function
displayOneByteHexValue(0,0,CLKPR);
setRowAndColumnPositionOnDisplay (1,0);
displayOneByteHexValue(1,12,0xE2);
CL_delayMS(3000);
sendLCDCommand(CLEAR_DISPLAY);
//Wait for 3 seconds
scrollingDisplayTest("Display will be cleared for 3 sec after displaying DONE ", "Please Wait.... Please Wait.....",1000);
CL_delayMS(2000);
sendLCDCommand(CLEAR_DISPLAY);
displayInRow1("DONE");
CL_delayMS(3000);
displayInRow1("Cur OFF Bl OFF");
sendLCDCommand(CURSOR_OFF_BLINK_OFF);
CL_delayMS(3000);
sendLCDCommand(CLEAR_DISPLAY);
displayInRow1("Cur OFF Bl ON");
sendLCDCommand(CURSOR_OFF_BLINK_ON);
CL_delayMS(3000);
sendLCDCommand(CLEAR_DISPLAY);
displayInRow1("Cur ON Bl OFF");
sendLCDCommand(CURSOR_ON_BLINK_OFF);
CL_delayMS(3000);
sendLCDCommand(CLEAR_DISPLAY);
displayInRow1("Cur ON Bl ON");
sendLCDCommand(CURSOR_ON_BLINK_ON);
CL_delayMS(3000);
displayNumberTest(100);
}
//////////////////////////////////////////////// Do No Use Below code. This is Only for demo purpose//////////////////////////////////////////
// Function to convert lower nibble to ASCII Value
//Only lower nibble of the input is considered and higher nibble is lost
// This function is written for understanding purpose only.
// itoa () library function can be used instead of this with base of 16.
unsigned char convertLowerNibbleToASCIIValue(unsigned char data)
{
data&=0x0F;
if(data<=9)
{
return(data+0x30);
}
else // There is no chance for getting more than 0x0F in the lowerNibble Parameter)
{
return(data+0x37);
}
}
// Function to convert Higher nibble to ASCII Value
//Only higher nibble of the input is considered and lower nibble is lost
unsigned char convertHigherNibbleToASCIIValue(unsigned char data)
{
data>>=4;
if(data<=9)
{
return(data+0x30);
}
else // There is no chance for getting more than 0x0F in the lowerNibble Parameter)
{
return(data+0x37);
}
}
void displayOneByteHexValue(unsigned char rowNum, unsigned char pos, char data)
{
char temp;
setRowAndColumnPositionOnDisplay(rowNum,pos);
temp = convertHigherNibbleToASCIIValue(data);
writeCharToLCD(temp);
temp = convertLowerNibbleToASCIIValue(data);
writeCharToLCD(temp);
}
// Displays 8 bit Register values in LCD display unit.
// name is the name of the register to be displayed. You can give any name.
// port is the register name.
void displayRegisterValues(unsigned char *displayName, volatile uint8_t *registerName)
{
char temp2;
displayInRow1(displayName);
//temp2=registerName;
temp2 = (convertLowerNibbleToASCIIValue(*registerName));
displayInRow1(&temp2);
// Get the higher Nibble
temp2 = (convertLowerNibbleToASCIIValue((*registerName)>>4));
displayInRow2(&temp2);
}
int main(void)
{
LCDDemoProgram("Hello", "World is Great");
while(1)
{
//TODO:: Please write your application code
}
return(0);
}
我为Atmega328写了LCD接口程序(虽然有可用的库,但我想从头开始写)。但是有两个问题。 1. 有时LCD显示不正确。只看到几条。我最终重置了一两次。 2. 我无法使用 displayOneByteHexValue() 显示十六进制值。但是 ASCII 转换是正确的,我可以在 Atmel Simulator 中看到它。下面是代码。我正在使用 Atmel Studio 6.2
/*
* EmbeddedProgram1.c
*
* Created: 16-05-2015 08:19:38
* Author: Mahesha
*/
#ifndef F_CPU
#define F_CPU 8000000UL
#endif
#include <avr/io.h>
#include <util/delay.h>
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Only Change following when changing pin numbers.
// All Data bits have to be assigned sequentially in same the port . RS and EN must be allocated in the same port
#define LCDPORT PORTD
#define LCDDDR DDRD
// used pins on port appropriate ports
#define LCD_DB4 2 // PORTD.2
#define LCD_DB5 3 // PORTD.3
#define LCD_DB6 4 // PORTD.4
#define LCD_DB7 5 // PORTD.5
#define LCD_ENABLE_BIT 6 // PORTD.6 Enable
#define LCD_RS 7 // PORTD.7 Register Select
//#define LCD_RW // R/W is connected to GND permanently
#define LCD_DATA_BITS_MASK 0x3C
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#define SET_EIGHT_BIT_MODE() LCDPORT|=((1<<LCD_DB5)|(1<<LCD_DB4)) // Set DB4 and DB5 as 1 for setting eight bit mode.
#define SET_FOUR_BIT_MODE() LCDPORT|=(1<<LCD_DB5)
#define SET_INSTRUCTION_MODE() LCDPORT&=~(1<<LCD_RS) //Function to select command port on LCD RS pin bit 2
#define SET_DATA_MODE() LCDPORT|=(1<<LCD_RS) //Function to select data port on LCD
#define DISABLE_LCD() LCDPORT&=~(1<<LCD_ENABLE_BIT) //Function to disable LCD P0.18
#define ENABLE_LCD() LCDPORT|=(1<<LCD_ENABLE_BIT) //Function to Enable LCD P0.18
//#define EIGHT_BIT_MODE 0x30 // 0 0 1 1 x x x x DB7 to DB0 0f LCD
#define BUSY_FLAG_WAIT_TIME 20
#define FOUR_BIT_5_BY_10_2LINE 0x28
#define LCD_INIT_DELAY 100 // Give a delay of 200 msec after reset. // Datasheet says 10 msec delay.
//Commands Finalized
#define CLEAR_DISPLAY 0x01
#define CURSOR_HOME 0x02 // return home
#define ENTRY_MODE_LEFT_TO_RIGHT 0x06 // Cursor direction from Left to right , Bit 1 of entry mode in LCD
#define DISPLAY_OFF 0x08 // Blink ON, Cursor ON etc are don't care
#define CURSOR_OFF_BLINK_OFF 0x0C
#define CURSOR_OFF_BLINK_ON 0x0D // blink on Even 0x0D also works. So cursor need not be ON
#define CURSOR_ON_BLINK_OFF 0x0E // blink off
#define CURSOR_ON_BLINK_ON 0x0F
#define SHIFT_ENTIRE_LEFT 0x18
#define SHIFT_CURSOR_LEFT 0x10
#define SHIFT_ENTIRE_RIGHT 0x1C
#define SHIFT_CURSOR_RIGHT 0x14
// Function prototypes
void unpackAndSend(char data);
void waitForBusyFlagToClear(void);
void sendLCDPulse(void);
void displayInRow1(char* data);
void displayInRow2(char* data);
void displayInRow1WithPosition(char* data, uint8_t position);
void displayInRow2WithPosition(char* data, uint8_t position);
void sendTextToLCD(char *data);
void displayOneByteHexValue(unsigned char,unsigned char,char);
void initializeLCD(void);
void CL_delayMS(unsigned int delayMS)
{
while(delayMS--)
{
_delay_ms(1);
}
}
void CL_delayuS(unsigned int delayus)
{
while(delayus--)
{
_delay_us(1);
}
}
// writes a char to the LCD
void writeCharToLCD(unsigned char data)
{
SET_DATA_MODE(); // RS bit has to be 1 for data mode
unpackAndSend(data);
}
// sendLCD pulse will just enable and disable the EN bit of LCD display.
void sendLCDPulse(void)
{
DISABLE_LCD();
CL_delayuS(50);
ENABLE_LCD();
CL_delayMS(1);
DISABLE_LCD();
CL_delayMS(1);
}
// writes an instruction to the LCD
void sendLCDCommand(unsigned char inst)
{
SET_INSTRUCTION_MODE();
unpackAndSend(inst);
waitForBusyFlagToClear();
}
// Unpack and send data will separate two nibbles and send twice.
void unpackAndSend(char inst)
{
char temp=inst;
DISABLE_LCD();
// SET_WRITE_MODE(); // If write is permanently disabled, do not use this.
LCDPORT &= (~LCD_DATA_BITS_MASK); // Clear the data bits
//sendLCDPulse();
inst&=0xF0;
inst=inst>>4; // Get the upper nibble
LCDPORT|=inst<<LCD_DB4; //Replace the bits starting from position of bit LCD_DB4 with this new data
sendLCDPulse();
LCDPORT &= (~LCD_DATA_BITS_MASK); // Clear the data bits again
//sendLCDPulse();
temp &=0x0f; //send low nibble
LCDPORT|=temp<<LCD_DB4;
sendLCDPulse();
}
// waitForBusyFlagToClear functio can wait for the busy bit, But since we are permanently connected R/W pin to ground, we cannot read
// the flag from LCD. In case busy bit has to be read, implementation has to be changed.
void waitForBusyFlagToClear(void)
{
CL_delayMS(BUSY_FLAG_WAIT_TIME);
}
// clear display
void clearDisplay(void)
{
sendLCDCommand (CLEAR_DISPLAY);
}
// return home
void returnCursorHome(void)
{
sendLCDCommand (CURSOR_HOME);
}
// LCD off
void displayOFF(void)
{
sendLCDCommand (DISPLAY_OFF);
}
// LCD on
void displayONCursorOFF(void)
{
sendLCDCommand (CURSOR_OFF_BLINK_OFF);
}
// cursor on
void displayONCursorON(void)
{
sendLCDCommand (CURSOR_ON_BLINK_OFF);
}
// blink on
void cursorOffBlinkOn(void)
{
sendLCDCommand (CURSOR_OFF_BLINK_ON);
}
// blink OFF, but display and cursors are ON
void cursorOnBlinkOff(void)
{
sendLCDCommand (CURSOR_ON_BLINK_OFF);
}
// All are ON
void cursorOnBlinkOn(void)
{
sendLCDCommand (CURSOR_ON_BLINK_ON);
}
//go to first line
void LCDline1 (void)
{
sendLCDCommand (0b10000000);
}
//go to second line
void LCDline2 (void)
{
sendLCDCommand (0b11000000);
}
// goto position x,y
// row1 or row2 are the parameters
// So parameters can be 1 or 2
void setRowAndColumnPositionOnDisplay (unsigned char rowNumber, unsigned char position)
{
unsigned char pos;
if (rowNumber == 1)
{
pos = 0x00 + position;
pos|=0x80; // Command to set 1st Row.
}
else //if (rowNumber == 1) // Either row 1 or two. We cannot have else option.
{
pos = 0x40 + position;
pos|=0xC0; // Command to set second row.
}
sendLCDCommand (pos);
}
void displayInRow1(char* data)
{
sendLCDCommand(0x80); // Set DDRAM Address as 0
sendTextToLCD(data);
}
void displayInRow1WithPosition(char* data, unsigned char position)
{
// The position cannot be more than 15. Display is 16 characters (0-15).
if(position>15)
{
position = 15;
}
sendLCDCommand(0x80|position); // Change the DDRAM address to first line by
// keeping D7 high and setting address to 0 onwards
sendTextToLCD(data);
}
////////////////////////////// diaplayInRow2 /////////////////////////////////////////////
void displayInRow2(char* data)
{
sendLCDCommand(0xC0); // Change the DDRAM address to next line 0x40 to 4F
sendTextToLCD(data);
}
////////////////////// diaplayInRow2WithPosition //////////////////////
void displayInRow2WithPosition(char* data, unsigned char position)
{
// The position cannot be more than 15. Display is 16 characters (0-15).
if(position>15)
{
position = 15;
}
sendLCDCommand(0xC0|position); // Change the DDRAM address to second line by
//keeping Bit D7 high and setting address at 0x40 onwards
sendTextToLCD(data);
}
void scrollLcd(char *row1Data,char *row2Data)
{
while(1)
{
sendLCDCommand(SHIFT_CURSOR_LEFT);
sendLCDCommand(SHIFT_ENTIRE_LEFT);
CL_delayMS(200);
}
}
//write text to the LCD
void sendTextToLCD(char *data)
{
while (*data)
{
writeCharToLCD(*data);
data++;
}
}
// Function to convert lower nibble to ASCII Value
//Only lower nibble of the input is considered and higher nibble is lost
char convertLowerNibbleToASCIIValue(char data)
{
data&=0x0F;
if(data<=9)
{
return(data+0x30);
}
else // There is no chance for getting more than 0x0F in the lowerNibble Parameter)
{
return(data+0x37);
}
}
// Function to convert Higher nibble to ASCII Value
//Only higher nibble of the input is considered and lower nibble is lost
char convertHigherNibbleToASCIIValue(char data)
{
data>>=4;
if(data<=9)
{
return(data+0x30);
}
else // There is no chance for getting more than 0x0F in the lowerNibble Parameter)
{
return(data+0x37);
}
}
void displayOneByteHexValue(unsigned char rowNum, unsigned char pos, char data)
{
char temp;
setRowAndColumnPositionOnDisplay(rowNum,pos);
temp = convertHigherNibbleToASCIIValue(data);
sendTextToLCD(&temp);
temp = convertLowerNibbleToASCIIValue(data);
sendTextToLCD(&temp);
}
// init LCD
void initializeLCD(void)
{
// Set the direction of port pins connected to LCD display as output ports.
// We are permanently connecting R/W pin to ground. So there is no read instruction in this case..
LCDDDR |= (1<<LCD_DB4)|(1<<LCD_DB5)|(1<<LCD_DB6)|(1<<LCD_DB7)|(1<<LCD_RS)|(1<<LCD_ENABLE_BIT);
//After reset, data sheet suggests some delay.
CL_delayMS(LCD_INIT_DELAY);
// Note some sites says three times 8 bit mode setting commands need to be sent.
// But it is observed that even without this, LCD works fine. So 1st Command, 2nd Command and 3rd Commands can be deleted below.
//ENABLE_LCD();
// 1st Command
SET_EIGHT_BIT_MODE();
//sendLCDPulse(); // Do not delete this. Need to further analyse. If pulse if sent it is not working
//CL_delayMS(5); // Do not use this delay since delay is not acurate, Either use _delay_ms or use timers
_delay_ms(5);
// Second Command
SET_EIGHT_BIT_MODE();
//sendLCDPulse();
_delay_us(100);
//CL_delayuS(100); // Do not use this delay since delay is not acurate, Either use _delay_ms or use timers
// third Command
SET_EIGHT_BIT_MODE();
//sendLCDPulse();
//CL_delayuS(100); // Do not use this delay since delay is not acurate, Either use _delay_ms or use timers
_delay_us(37);
// Finally Set four bit mode
SET_FOUR_BIT_MODE();
//sendLCDPulse();
CL_delayuS(100);
// First time when 4 bit mode command is sent, only one higher nibble was sent since
// only 4 bits are connected from MPU to LCD. Since D0 to D3 of LCD are not connected,
// their values depend on how the pins are connected in LCD module (May be grounded, may kept open etc)
//So again send function set command to set 2 line display mode mode and 5x7 character mode. But now two write operations to LCD is made
// inside the function sendLCDCommand.
sendLCDCommand (FOUR_BIT_5_BY_10_2LINE);
//turn on display and cursor OFF, Blink OFF (sent two times using below command)
sendLCDCommand (CURSOR_OFF_BLINK_OFF);
//clr display
sendLCDCommand (CLEAR_DISPLAY);
// Set Entry mode left to right
sendLCDCommand (ENTRY_MODE_LEFT_TO_RIGHT);
}
void LCDProgramCallFromMain(char *row1Data, char *row2Data)
{
initializeLCD();
setRowAndColumnPositionOnDisplay (1,0);
sendTextToLCD (row1Data);
displayOneByteHexValue(2,0,0xF4);
//setRowAndColumnPositionOnDisplay (2,5);
//displayInRow1(row1Data);
}
int main(void)
{
LCDProgramCallFromMain("Hello", "Welcome to cloude");
while(1)
{
}
}
下面是我得到的显示器图像。无法弄清楚问题出在哪里。
是的,我如下更改功能后它起作用了。谢谢您的帮助。
void displayOneByteHexValue(unsigned char rowNum, unsigned char pos, char data)
{
char temp;
setRowAndColumnPositionOnDisplay(rowNum,pos);
temp = convertHigherNibbleToASCIIValue(data);
writeCharToLCD(temp);
temp = convertLowerNibbleToASCIIValue(data);
writeCharToLCD(temp);
}
我进一步优化了代码,下面是完整的工作代码,其中包含从 main 调用的小演示函数。
#ifndef F_CPU
#define F_CPU 16000000UL
#endif
#include <avr/io.h>
#include <util/delay.h>
#include <stdlib.h>
#include <avr/sfr_defs.h>
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Only Change following when changing pin numbers.
// All Data bits have to be assigned sequentially in same the port . RS and EN must be allocated in the same port
#define LCDPORT PORTD
#define LCDDDR DDRD
// used pins on port appropriate ports
#define LCD_DB4 2 // PORTD.2
#define LCD_DB5 3 // PORTD.3
#define LCD_DB6 4 // PORTD.4
#define LCD_DB7 5 // PORTD.5
#define LCD_ENABLE_BIT 6 // PORTD.6 Enable
#define LCD_RS 7 // PORTD.7 Register Select
//#define LCD_RW // R/W is connected to GND permanently
#define LCD_DATA_BITS_MASK ((1<<LCD_DB4)|(1<<LCD_DB5)|(1<<LCD_DB6)|(1<<LCD_DB7))
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/*
#define SET_EIGHT_BIT_MODE() LCDPORT|=((1<<LCD_DB5)|(1<<LCD_DB4)) // Set DB4 and DB5 as 1 for setting eight bit mode.
#define SET_FOUR_BIT_MODE() LCDPORT|=(1<<LCD_DB5)
*/
#define SET_INSTRUCTION_MODE() LCDPORT&=~(1<<LCD_RS) //Function to select command port on LCD RS pin bit 2
#define SET_DATA_MODE() LCDPORT|=(1<<LCD_RS) //Function to select data port on LCD
#define DISABLE_LCD() LCDPORT&=~(1<<LCD_ENABLE_BIT) //Function to disable LCD P0.18
#define ENABLE_LCD() LCDPORT|=(1<<LCD_ENABLE_BIT) //Function to Enable LCD P0.18
#define BUSY_FLAG_WAIT_TIME 20
#define FOUR_BIT_5_BY_10_2LINE 0x28
#define LCD_INIT_DELAY 100 // Give a delay of 100 msec after reset. // Datasheet says 10 msec delay.
// Function prototypes for external use (to be kept in Header file)
void sendLCDCommand(unsigned char cmd);
void displayInRow1(unsigned char* data);
void displayInRow2(unsigned char* data);
void displayInRow1WithPosition(unsigned char* data, uint8_t position);
void displayInRow2WithPosition(unsigned char* data, uint8_t position);
void sendTextToLCD(unsigned char *data);
void initializeLCD(void);
// Function prototypes for file use (only internal)
void unpackAndSend(char data);
void waitForBusyFlagToClear(void);
void sendLCDPulse(void);
void displayOneByteHexValue(unsigned char,unsigned char,char); // This function is no longer needed. Only for demo purpose.
// #define commands are replaced by enums to demonstrate the feature of enumerated data types
enum DISPLAY_PROPERTY
{
CLEAR_DISPLAY=0x01,
CURSOR_HOME = 0x02, // return home
ENTRY_MODE_LEFT_TO_RIGHT= 0x06, // Cursor direction from Left to right , Bit 1 of entry mode in LCD
DISPLAY_OFF= 0x08, // Blink ON, Cursor ON etc are don't care
CURSOR_OFF_BLINK_OFF= 0x0C,
CURSOR_OFF_BLINK_ON= 0x0D, // blink on Even 0x0D also works. So cursor need not be ON
CURSOR_ON_BLINK_OFF=0x0E, // blink off
CURSOR_ON_BLINK_ON=0x0F,
SHIFT_ENTIRE_LEFT= 0x18,
SHIFT_CURSOR_LEFT= 0x10,
SHIFT_ENTIRE_RIGHT= 0x1C,
SHIFT_CURSOR_RIGHT= 0x14,
};
void SET_EIGHT_BIT_MODE(void)
{
LCDPORT=(LCDPORT&(~LCD_DATA_BITS_MASK))|((0<<LCD_DB7)|(0<<LCD_DB6)|(1<<LCD_DB5)|(1<<LCD_DB4)); // Set DB4 and DB5 as 1 for setting eight bit mode.
}
void SET_FOUR_BIT_MODE(void)
{
LCDPORT= (LCDPORT&(~LCD_DATA_BITS_MASK)) | ((0<<LCD_DB7)|(0<<LCD_DB6)|(1<<LCD_DB5)|(0<<LCD_DB4));
}
void CL_delayMS(unsigned int delayMS)
{
while(delayMS--)
{
_delay_ms(1);
}
}
void CL_delayuS(unsigned int delayus)
{
while(delayus--)
{
_delay_us(1);
}
}
// writes a char to the LCD
void writeCharToLCD(unsigned char data)
{
SET_DATA_MODE(); // RS bit has to be 1 for data mode
unpackAndSend(data);
}
// sendLCD pulse will just enable and disable the EN bit of LCD display.
void sendLCDPulse(void)
{
//DISABLE_LCD();
//CL_delayuS(50);
ENABLE_LCD();
CL_delayMS(1);
DISABLE_LCD();
CL_delayMS(1);
}
// writes an instruction to the LCD
void sendLCDCommand(unsigned char cmd)
{
SET_INSTRUCTION_MODE();
unpackAndSend(cmd);
waitForBusyFlagToClear();
}
// Unpack and send data will separate two nibbles and send twice.
void unpackAndSend(char inst)
{
char temp=inst;
DISABLE_LCD();
// SET_WRITE_MODE(); // If write is permanently disabled, do not use this.
LCDPORT &= (~LCD_DATA_BITS_MASK); // Clear the data bits
//sendLCDPulse();
inst&=0xF0;
inst=inst>>4; // Get the upper nibble
LCDPORT|=inst<<LCD_DB4; //Replace the bits starting from position of bit LCD_DB4 with this new data
sendLCDPulse();
LCDPORT &= (~LCD_DATA_BITS_MASK); // Clear the data bits again
//sendLCDPulse();
temp &=0x0f; //send low nibble
LCDPORT|=temp<<LCD_DB4;
sendLCDPulse();
}
// waitForBusyFlagToClear function can wait for the busy bit, But since we are permanently connected R/W pin to ground, we cannot read
// the flag from LCD. In case busy bit has to be read, implementation has to be changed.
void waitForBusyFlagToClear(void)
{
CL_delayMS(BUSY_FLAG_WAIT_TIME);
}
// goto position x,y
// row1 or row2 are the parameters
// So parameters can be 0 or 1
void setRowAndColumnPositionOnDisplay (unsigned char rowNumber, unsigned char position)
{
unsigned char pos;
if (rowNumber==0) // If Row is 0, display in 1st row
{
pos = 0x00 + position;
pos|=0x80; // Command to set 1st Row.
}
else //if (rowNumber == 1) // Either row 1 or two etc
{
pos = 0x40 + position;
pos|=0xC0; // Command to set second row.
}
sendLCDCommand (pos);
}
void displayInRow1(unsigned char* data)
{
sendLCDCommand(0x80); // Set DDRAM Address as 0
sendTextToLCD(data);
}
void displayInRow1WithPosition(unsigned char* data, unsigned char position)
{
sendLCDCommand(0x80|position); // Change the DDRAM address to first line by
// keeping D7 high and setting address to 0 onwards
sendTextToLCD(data);
}
////////////////////////////// diaplayInRow2 /////////////////////////////////////////////
void displayInRow2(unsigned char* data)
{
sendLCDCommand(0xC0); // Change the DDRAM address to next line 0x40 to 4F
sendTextToLCD(data);
}
////////////////////// diaplayInRow2WithPosition //////////////////////
void displayInRow2WithPosition(unsigned char* data, unsigned char position)
{
sendLCDCommand(0xC0|position); // Change the DDRAM address to second line by
//keeping Bit D7 high and setting address at 0x40 onwards
sendTextToLCD(data);
}
//write text to the LCD
void sendTextToLCD(unsigned char *data)
{
while (*data)
{
writeCharToLCD(*data);
data++;
}
}
// initialize LCD
void initializeLCD(void)
{
// Set the direction of port pins connected to LCD display as output ports
// We are permanently connecting R/W pin to ground. So there is no read instruction in this case
LCDDDR |= (1<<LCD_DB4)|(1<<LCD_DB5)|(1<<LCD_DB6)|(1<<LCD_DB7)|(1<<LCD_RS)|(1<<LCD_ENABLE_BIT);
//After reset, data sheet suggests some delay.
CL_delayMS(LCD_INIT_DELAY);
// Note some sites says three times 8 bit mode setting commands need to be sent.
// But it is observed that even without this, LCD works fine. So 1st Command, 2nd Command and 3rd Commands can be deleted below.
//ENABLE_LCD();
// 1st Command
SET_EIGHT_BIT_MODE();
sendLCDPulse(); // Do not delete this. Need to further analyze. If pulse if sent it is not working
//CL_delayMS(5); // Do not use this delay since delay is not acurate, Either use _delay_ms or use timers
_delay_ms(5);
// Second Command
SET_EIGHT_BIT_MODE();
sendLCDPulse();
_delay_us(100);
//CL_delayuS(100); // Do not use this delay since delay is not acurate, Either use _delay_ms or use timers
// third Command
SET_EIGHT_BIT_MODE();
sendLCDPulse();
//CL_delayuS(100); // Do not use this delay since delay is not acurate, Either use _delay_ms or use timers
_delay_us(37);
// Finally Set four bit mode
SET_FOUR_BIT_MODE();
sendLCDPulse();
CL_delayuS(100);
// First time when 4 bit mode command is sent, only one higher nibble was sent since
// only 4 bits are connected from MPU to LCD. Since D0 to D3 of LCD are not connected,
// their values depend on how the pins are connected in LCD module (May be grounded, may kept open etc)
//So again send function set command to set 2 line display mode mode and 5x7 character mode. But now two write operations to LCD is made
// inside the function sendLCDCommand.
sendLCDCommand (FOUR_BIT_5_BY_10_2LINE);
//turn on display and cursor OFF, Blink OFF (sent two times using below command)
sendLCDCommand (CURSOR_OFF_BLINK_OFF);
//clr display
sendLCDCommand (CLEAR_DISPLAY);
// Set Entry mode left to right
sendLCDCommand (ENTRY_MODE_LEFT_TO_RIGHT);
}
void displayNumberTest(int range)
{
float fNum = -0.332;
unsigned char lcdBuffer[50];
sendLCDCommand(CLEAR_DISPLAY);
//setRowAndColumnPositionOnDisplay (0,0);
//sendTextToLCD("Decimal = ");
for (int count=0;count<range;count++)
{
itoa(count,lcdBuffer,2);
setRowAndColumnPositionOnDisplay (1,0);
sendTextToLCD(lcdBuffer);
itoa(count,lcdBuffer,16);
setRowAndColumnPositionOnDisplay (1,13);
sendTextToLCD(lcdBuffer);
itoa(count,lcdBuffer,10);
setRowAndColumnPositionOnDisplay (0,0); // 1st Row, 0th position
sendTextToLCD(lcdBuffer);
// Now display some floating value number.
// use the function dtostrf(). It take 4 parameters
// Parameter1: The value to be converted
// 2: Width
//3. Precision
// 4. String to hold ASCII
// It is also possible to use sprintf function. But it takes more memory.
fNum=(fNum*(-1)*(count+1)/101.12)+0.012;
dtostrf(fNum,5,3,lcdBuffer);
setRowAndColumnPositionOnDisplay (0,7);
sendTextToLCD(lcdBuffer);
CL_delayMS(300);
}
}
// Scroll the display
// This program need to be modified further to display only whatever characters are there in DDRAM.
// Right now 4 columns will be shifted just to demonstrate.
void scrollingDisplayTest(unsigned char *row1Text, unsigned char* row2Text, unsigned int scrollDelay)
{
unsigned char count=40;
sendLCDCommand(CLEAR_DISPLAY);
displayInRow1(row1Text);
displayInRow2(row2Text);
CL_delayMS(2000);
while(count--)
{
sendLCDCommand(SHIFT_ENTIRE_LEFT);
CL_delayMS(scrollDelay);
}
}
void LCDDemoProgram(unsigned char *row1Data, unsigned char *row2Data)
{
initializeLCD();
sendTextToLCD (row1Data);
setRowAndColumnPositionOnDisplay (1,0);
sendTextToLCD (row2Data);
CL_delayMS(3000);
sendLCDCommand(CLEAR_DISPLAY);
// Just to demostrate how Hex values can be displayed without using itoa library function
displayOneByteHexValue(0,0,CLKPR);
setRowAndColumnPositionOnDisplay (1,0);
displayOneByteHexValue(1,12,0xE2);
CL_delayMS(3000);
sendLCDCommand(CLEAR_DISPLAY);
//Wait for 3 seconds
scrollingDisplayTest("Display will be cleared for 3 sec after displaying DONE ", "Please Wait.... Please Wait.....",1000);
CL_delayMS(2000);
sendLCDCommand(CLEAR_DISPLAY);
displayInRow1("DONE");
CL_delayMS(3000);
displayInRow1("Cur OFF Bl OFF");
sendLCDCommand(CURSOR_OFF_BLINK_OFF);
CL_delayMS(3000);
sendLCDCommand(CLEAR_DISPLAY);
displayInRow1("Cur OFF Bl ON");
sendLCDCommand(CURSOR_OFF_BLINK_ON);
CL_delayMS(3000);
sendLCDCommand(CLEAR_DISPLAY);
displayInRow1("Cur ON Bl OFF");
sendLCDCommand(CURSOR_ON_BLINK_OFF);
CL_delayMS(3000);
sendLCDCommand(CLEAR_DISPLAY);
displayInRow1("Cur ON Bl ON");
sendLCDCommand(CURSOR_ON_BLINK_ON);
CL_delayMS(3000);
displayNumberTest(100);
}
//////////////////////////////////////////////// Do No Use Below code. This is Only for demo purpose//////////////////////////////////////////
// Function to convert lower nibble to ASCII Value
//Only lower nibble of the input is considered and higher nibble is lost
// This function is written for understanding purpose only.
// itoa () library function can be used instead of this with base of 16.
unsigned char convertLowerNibbleToASCIIValue(unsigned char data)
{
data&=0x0F;
if(data<=9)
{
return(data+0x30);
}
else // There is no chance for getting more than 0x0F in the lowerNibble Parameter)
{
return(data+0x37);
}
}
// Function to convert Higher nibble to ASCII Value
//Only higher nibble of the input is considered and lower nibble is lost
unsigned char convertHigherNibbleToASCIIValue(unsigned char data)
{
data>>=4;
if(data<=9)
{
return(data+0x30);
}
else // There is no chance for getting more than 0x0F in the lowerNibble Parameter)
{
return(data+0x37);
}
}
void displayOneByteHexValue(unsigned char rowNum, unsigned char pos, char data)
{
char temp;
setRowAndColumnPositionOnDisplay(rowNum,pos);
temp = convertHigherNibbleToASCIIValue(data);
writeCharToLCD(temp);
temp = convertLowerNibbleToASCIIValue(data);
writeCharToLCD(temp);
}
// Displays 8 bit Register values in LCD display unit.
// name is the name of the register to be displayed. You can give any name.
// port is the register name.
void displayRegisterValues(unsigned char *displayName, volatile uint8_t *registerName)
{
char temp2;
displayInRow1(displayName);
//temp2=registerName;
temp2 = (convertLowerNibbleToASCIIValue(*registerName));
displayInRow1(&temp2);
// Get the higher Nibble
temp2 = (convertLowerNibbleToASCIIValue((*registerName)>>4));
displayInRow2(&temp2);
}
int main(void)
{
LCDDemoProgram("Hello", "World is Great");
while(1)
{
//TODO:: Please write your application code
}
return(0);
}