Reputation: 13
My partner and I are currently building out an interface code set for a PIC16F877A microcontroller and a LCD1602 I2C screen
We can onboard everything perfectly fine however nothing shows on the screen. We have double checked the PIN mapping and connected up the proper ports on the I2C module of the screen. The code compiles with no issues and runs into the microcontroller but there is no display.
/* File: config.h */
// [ PIC16F877A ] Configuration Bit Settings
// #pragma config statements should precede project file includes.
// Use project enums instead of #define for ON and OFF.
// CONFIG
#pragma config FOSC = HS
#pragma config WDTE = OFF
#pragma config PWRTE = OFF
#pragma config BOREN = OFF
#pragma config LVP = ON
#pragma config CPD = OFF
#pragma config WRT = OFF
#pragma config CP = OFF
#include <xc.h>
/* File: I2C_LCD.h */
#define _XTAL_FREQ 16000000
#define I2C_BaudRate 100000
#define SCL_D TRISC3
#define SDA_D TRISC4
#define LCD_BACKLIGHT 0x08
#define LCD_NOBACKLIGHT 0x00
#define LCD_FIRST_ROW 0x80
#define LCD_SECOND_ROW 0xC0
#define LCD_THIRD_ROW 0x94
#define LCD_FOURTH_ROW 0xD4
#define LCD_CLEAR 0x01
#define LCD_RETURN_HOME 0x02
#define LCD_ENTRY_MODE_SET 0x06
#define LCD_CURSOR_OFF 0x0C
#define LCD_UNDERLINE_ON 0x0E
#define LCD_BLINK_CURSOR_ON 0x0F
#define LCD_MOVE_CURSOR_LEFT 0x10
#define LCD_MOVE_CURSOR_RIGHT 0x14
#define LCD_TURN_ON 0x0C
#define LCD_TURN_OFF 0x08
#define LCD_SHIFT_LEFT 0x18
#define LCD_SHIFT_RIGHT 0x1C
#define LCD_TYPE 2 // 0 -> 5x7 | 1 -> 5x10 | 2 -> 2 lines
//-----------[ Functions' Prototypes ]--------------
//---[ I2C Routines ]---
void I2C_Master_Init();
void I2C_Master_Wait();
void I2C_Master_Start();
void I2C_Master_RepeatedStart();
void I2C_Master_Stop();
void I2C_ACK();
void I2C_NACK();
unsigned char I2C_Master_Write(unsigned char data);
unsigned char I2C_Read_Byte(void);
//---[ LCD Routines ]---
void LCD_Init(unsigned char I2C_Add);
void IO_Expander_Write(unsigned char Data);
void LCD_Write_4Bit(unsigned char Nibble);
void LCD_CMD(unsigned char CMD);
void LCD_Set_Cursor(unsigned char ROW, unsigned char COL);
void LCD_Write_Char(char);
void LCD_Write_String(char*);
void Backlight();
void noBacklight();
void LCD_SR();
void LCD_SL();
void LCD_Clear();
/* File: I2C_LCD.c */
#include <xc.h>
#include "I2C_LCD.h"
unsigned char RS, i2c_add, BackLight_State = LCD_BACKLIGHT;
//---------------[ I2C Routines ]-------------------
//--------------------------------------------------
void I2C_Master_Init()
{
SSPCON = 0x28;
SSPCON2 = 0x00;
SSPSTAT = 0x00;
SSPADD = ((_XTAL_FREQ/4)/I2C_BaudRate) - 1;
SCL_D = 1;
SDA_D = 1;
}
void I2C_Master_Wait()
{
while ((SSPSTAT & 0x04) || (SSPCON2 & 0x1F));
}
void I2C_Master_Start()
{
I2C_Master_Wait();
SEN = 1;
}
void I2C_Master_RepeatedStart()
{
I2C_Master_Wait();
RSEN = 1;
}
void I2C_Master_Stop()
{
I2C_Master_Wait();
PEN = 1;
}
void I2C_ACK(void)
{
ACKDT = 0; // 0 -> ACK
I2C_Master_Wait();
ACKEN = 1; // Send ACK
}
void I2C_NACK(void)
{
ACKDT = 1; // 1 -> NACK
I2C_Master_Wait();
ACKEN = 1; // Send NACK
}
unsigned char I2C_Master_Write(unsigned char data)
{
I2C_Master_Wait();
SSPBUF = data;
while(!SSPIF); // Wait Until Completion
SSPIF = 0;
return ACKSTAT;
}
unsigned char I2C_Read_Byte(void)
{
//---[ Receive & Return A Byte ]---
I2C_Master_Wait();
RCEN = 1; // Enable & Start Reception
while(!SSPIF); // Wait Until Completion
SSPIF = 0; // Clear The Interrupt Flag Bit
I2C_Master_Wait();
return SSPBUF; // Return The Received Byte
}
//======================================================
//---------------[ LCD Routines ]----------------
//-----------------------------------------------
void LCD_Init(unsigned char I2C_Add)
{
i2c_add = I2C_Add;
IO_Expander_Write(0x00);
__delay_ms(30);
LCD_CMD(0x03);
__delay_ms(5);
LCD_CMD(0x03);
__delay_ms(5);
LCD_CMD(0x03);
__delay_ms(5);
LCD_CMD(LCD_RETURN_HOME);
__delay_ms(5);
LCD_CMD(0x20 | (LCD_TYPE << 2));
__delay_ms(50);
LCD_CMD(LCD_TURN_ON);
__delay_ms(50);
LCD_CMD(LCD_CLEAR);
__delay_ms(50);
LCD_CMD(LCD_ENTRY_MODE_SET | LCD_RETURN_HOME);
__delay_ms(50);
}
void IO_Expander_Write(unsigned char Data)
{
I2C_Master_Start();
I2C_Master_Write(i2c_add);
I2C_Master_Write(Data | BackLight_State);
I2C_Master_Stop();
}
void LCD_Write_4Bit(unsigned char Nibble)
{
// Get The RS Value To LSB OF Data
Nibble |= RS;
IO_Expander_Write(Nibble | 0x04);
IO_Expander_Write(Nibble & 0xFB);
__delay_us(50);
}
void LCD_CMD(unsigned char CMD)
{
RS = 0; // Command Register Select
LCD_Write_4Bit(CMD & 0xF0);
LCD_Write_4Bit((CMD << 4) & 0xF0);
}
void LCD_Write_Char(char Data)
{
RS = 1; // Data Register Select
LCD_Write_4Bit(Data & 0xF0);
LCD_Write_4Bit((Data << 4) & 0xF0);
}
void LCD_Write_String(char* Str)
{
for(int i=0; Str[i]!='\0'; i++)
LCD_Write_Char(Str[i]);
}
void LCD_Set_Cursor(unsigned char ROW, unsigned char COL)
{
switch(ROW)
{
case 2:
LCD_CMD(0xC0 + COL-1);
break;
case 3:
LCD_CMD(0x94 + COL-1);
break;
case 4:
LCD_CMD(0xD4 + COL-1);
break;
// Case 1
default:
LCD_CMD(0x80 + COL-1);
}
}
void Backlight()
{
BackLight_State = LCD_BACKLIGHT;
IO_Expander_Write(0);
}
void noBacklight()
{
BackLight_State = LCD_NOBACKLIGHT;
IO_Expander_Write(0);
}
void LCD_SL()
{
LCD_CMD(0x18);
__delay_us(40);
}
void LCD_SR()
{
LCD_CMD(0x1C);
__delay_us(40);
}
void LCD_Clear()
{
LCD_CMD(0x01);
__delay_us(40);
}
/* File: main.c */
#include <xc.h>
#include "config.h"
#include "I2C_LCD.h"
void main(void) {
I2C_Master_Init();
LCD_Init(0x27); // Initialize LCD module with I2C address = 0x4E
LCD_Set_Cursor(1, 1);
LCD_Write_String(" Khaled Magdy");
LCD_Set_Cursor(2, 1);
LCD_Write_String(" DeepBlue");
while(1)
{
LCD_SR();
__delay_ms(350);
LCD_SR();
__delay_ms(350);
LCD_SL();
__delay_ms(350);
LCD_SL();
__delay_ms(350);
}
return;
}
Would appreciate getting some eyes on this from a different perspective as we have worked tirelessly and are unsure why there is no communication.
Upvotes: 0
Views: 525
Reputation: 2520
Character LCD modules are very slow devices compared to the PIC microcontrollers. So the required time for them to processing the commands and data may vary depending on the specific product.
These modules should be set up properly by respecting their processing time in order to get them functioning. That's why the init
procedure is very important for these devices and should be handled carefully.
For a reference, here is an example init function code snippet taken from a working project.
// Init the LCD module for 4-bit mode
void lcd4bitInit(void)
{
__delay_ms(100); // Character LCD modules are relatively slow, the required time may vary depending on the product
// So give it sufficient time especially when setting up
lcdPort = 0x30; // Send function set command 3 times with 10ms intervals
lcdRW_low(); // Set LCD RW low for write mode
lcdRS_low(); // Set LCD RS low for command mode
lcdEN_high(); // Set EN low so that it latch the data in, should keep high at least 140ns
lcdEN_low(); // Set EN low again
__delay_ms(10); // Let it process the data
lcdEN_high(); // Latch the function set command for 2nd time (still 8-bit mode).
lcdEN_low();
__delay_ms(10); // Let it process the data
lcdEN_high(); // Latch the function set command for 3d time (still 8-bit mode).
lcdEN_low();
__delay_ms(10); // Let it process the data
lcdPort = 0x28; // Set DL 0: 4-bits; N 1: 2 lines; F 0: 5x7 dots
lcdEN_high();
lcdEN_low();
__delay_ms(10);
/* From now on, the LCD module should function in 4-bit mode. So we can write it in 4-bit mode as of now */
lcdCmd_write(0x08); // Here goes the rest of the setup which can be sent in 4-bit mode
lcd_clear();
lcdCmd_write(0x06);
lcdCmd_write(0x0C);
}
The pin setting functions in the snippet are function-like macros. For example the definition of the lcdEN_high()
and the `lcdEN_low would be as following respectively:
#define lcdEN_high() do {lcdENPort |= 1 << lcdEN; } while(0)
#define lcdEN_low() do {lcdENPort &= ~(1 << lcdEN); } while(0)
where lcdENPort
is mapped to the port of the pin where I control the EN
pin, and the lcdEN
is mapped to the bit number through which the EN
of the LCD is connected.
Another important factor for a proper setup is respecting the module's processing time. As I said before, this time may vary slightly among different products or manufacturers. But I see no problem in keeping these times a bit more than required since the init
function is generally executed once on every system init.
Upvotes: 1