I tried to interface PIC16f722A with LM016L LCD in proteus. I had wrote the C-code for initializing (LCD) and displaying characters on LCD.
The problems are as follows:
1)When I transfer X number of characters, it just shows (X-1) number of characters, ignoring the last character.
2)LCD shows Numbers and Special characters BUT NOT ALPHABETS.
Here is the screenshot of sending four characters i.e. (1,2,3,4) and it only shows 1 2 3. Here is the screenshot.
#include <stdio.h>
#include <stdbool.h>
#define LCD_DATA PORTB
#define LCD_CTRL PORTA
sbit LCD_EN at PORTA.B0; // LCD Latch pin (Enable)
sbit LCD_RS at PORTA.B1; // Command/Data select PIN | Command(RS=0) | DATA(RS=1)
sbit LCD_RW at PORTA.B2; // READ/WRTIE pin | Read (1) | Write (0)
#define LCD_DATA_DIRECTION TRISB
sbit LCD_EN_Direction at TRISA0_bit;
sbit LCD_RS_Direction at TRISA1_bit;
sbit LCD_RW_Direction at TRISA2_bit;
#define CHECK_High_BIT(var,pos) (((var)>>(pos)) & 1)
// ========================= LCD FUNCTIONS =========================
void lcd_ready(){
LCD_DATA_DIRECTION=0xff; // Data ports input
LCD_RS=0; // Command register
LCD_RW=1; // Read from LCD
Delay_ms(1);LCD_EN=0;Delay_ms(1);LCD_EN=1;Delay_ms(1); // Read requires Low-to-High sig
while(CHECK_High_BIT(LCD_DATA,7));
LCD_DATA_DIRECTION=0x00; // Data ports output
}
void lcd_Command_write(char value){
LCD_RS=0;LCD_RW=0;
LCD_DATA=value;
Delay_ms(1);LCD_EN=1;delay_ms(1);LCD_EN=0;Delay_ms(1);
}
void lcd_Data_write(char value){
LCD_DATA_DIRECTION=0x00;
LCD_RS=1;LCD_RW=0;
LCD_DATA=value;
Delay_ms(1);LCD_EN=1;delay_ms(1);LCD_EN=0;Delay_ms(1);
}
void LCD_POWER(bool flag){
if(flag){
Delay_ms(250); // Power up delay
lcd_ready(); // Is LCD BUSY ? wait here
lcd_Command_write(0x38); // LCD 2 Lines, 5x7 characters
lcd_ready();
lcd_Command_write(0x0E); // Display ON, Cursor ON
lcd_ready();
lcd_Command_write(0x01); // Clear LCD
lcd_ready();
lcd_Command_write(0x06); // Shift Cursor Right
lcd_ready();
lcd_Command_write(0x80); // Cursor at line 1 position 1
}
else{
lcd_ready(); // Is LCD BUSY ? wait here
LCD_RS=0; // Select Command Register
LCD_RW=0; // Write operation
LCD_DATA=0x10; // Cursor and Display off
Delay_us(1);LCD_EN=1;Delay_us(1);LCD_EN=0; // Latch
}
}
void main() {
LCD_DATA_DIRECTION=LCD_EN_Direction=LCD_RS_Direction=LCD_RW_Direction=0x00;
LCD_POWER(1);
LCD_ready();
LCD_DATA_write('1');
LCD_ready();
LCD_DATA_write('2');
LCD_ready();
LCD_DATA_write('3');
LCD_ready();
LCD_DATA_write('4');
}
Here is the C Code
NOTE: I am not using delay b/w transfering characters, instead i use D7 pin of LCD to monitor whether the LCD is busy or not.
Best Answer
You have a fundamental problem with how you have coded the LCD controller busy flag check routine.
Referencing the HD44780 character mode LCD data sheet found at Sparkfun there is this timing sequence chart:
You will note that it is necessary to have the E pin high to be able to see the DB7 busy bit. In your code you pulse the E pin but the wait 1msec before even looking at the DB7. By the time you look any valid data is gone. You want to sample DB7 while the E is still high.
The typical way I code an LCD driver is to make low low level routines to support WriteCmd, ReadSts, WriteDat and ReadDat. Then the LCD busy check is a loop that calls the ReadSts inside a loop and checks the readback D7 value each read.
Another thing to be aware of is that the best time to do the LCD busy check is just BEFORE you intend to do a transaction to the LCD. This way you can get a small performance boost because your code can go off to do other things after the last write to LCD controller.