LCD Sunfounder 1602 and I2C for PIC24FJ128GA110  - Page 1
Forum section: electronics / beginners

author: juicycrunchwrap
author_rank: Contributor
timestamp: July 26, 2023, 06:31:46 pm
content: Hello everyone,I have bought a LCD Sunfounder 1602 to display data from the PIC24FJ128GA110 to the LCD, I have written an I2C library that seems to work (I can observe SCL and SDA on my scope). So I connect the LCD (4 pins : VDD, GND, SCL and SDA), and when I observe with my scope, I can indeed see the signals SCL and SDA on the pins of the LCD. But else, nothing is being displayed on the LCD and I don't really know why.I have done continuity testing, to check that the software driver matches the port assignments and it seemed to be fine even if the anode and cathode were connected weirdly in my opinion. I'm just trying to display a character, and I'd like to display a string after that. I suspect a problem in the code, (maybe the LCD initialization that is not correct ?), but as I am pretty new with microcontrollers, I don't know what could be wrong to be honest. I am including the I2C and LCD libraries in case someone had already worked with one of those LCD.My LCD library :Code:#include <xc.h>#include "lcd.h"#include "i2c1.h"#include <string.h>#ifndef _XTAL_FREQ#define _XTAL_FREQ 8000000UL#endif#ifndef FCY#define FCY _XTAL_FREQ/2#endif#include <libpic30.h>void lcd_init() {// Initialization sequence for 16x2 LCDlcd_send_command(0x33); // Initialize the LCD in 8-bit mode__delay_ms(5);lcd_send_command(0x32); // Initialize the LCD in 4-bit mode then__delay_ms(5);lcd_send_command(0x28); // 2 lines, 5x7 matrix__delay_ms(5);lcd_send_command(0x0C); // Display ON, Cursor OFF, Blink OFF__delay_ms(5);lcd_clear(); // Clear the LCD}void lcd_clear() {lcd_send_command(0x01); // Clear display command__delay_ms(5); // Delay to allow the LCD to process the command}void write_word_lcd(int data){int temp = data;temp |= 0x08;send_i2c1_byte(LCD_ADDRESS >> 0);send_i2c1_byte(temp);}void lcd_send_command(unsigned int command) {//send_i2c1_byte(LCD_ADDRESS << 1); //RW = 0//send_i2c1_byte(command);int buffer;//Write bit 7 to 4buffer = command & 0xF0;buffer |= 0x04; // RS = 0, RW = 0, EN = 1write_word_lcd(buffer);__delay_ms(50);buffer &= 0xFB;write_word_lcd(buffer);//Write bit 3 to 0buffer = (command & 0x0F) << 4;buffer |= 0x04; // RS = 0, RW = 0, EN = 1write_word_lcd(buffer);__delay_ms(50);buffer &= 0xFB; // EN = 0write_word_lcd(buffer);}void lcd_send_data(unsigned char data) {// Set RS high (0x80) for data, and send data byte to LCD//send_i2c1_byte((LCD_ADDRESS << 1) | 0x01); // Set R/W bit high for Write//send_i2c1_byte(data);int buffer;//Write bit 7 to 4buffer = data & 0xF0;buffer |= 0x05; // RS = 1, RW = 0, EN = 1write_word_lcd(buffer);__delay_ms(50);buffer &= 0xFB; // EN = 0write_word_lcd(buffer);//Write bit 3 to 0buffer = (data & 0x0F) << 4;buffer |= 0x05; // RS = 1, RW = 0, EN = 1write_word_lcd(buffer);__delay_ms(50);buffer &= 0xFB; // EN = 0write_word_lcd(buffer);}void write(int x, int y, char data[]){int addr, i;int temp;if (x < 0) x = 0;if (x > 15) x = 15;if (y < 0) y = 0;if (y > 1) y = 1;// Move cursoraddr = 0x80 + 0x40 * y + x;lcd_send_command(addr);temp = strlen(data);for (i = 0; i < temp; i++){lcd_send_data(data);}}I2C Library for the PIC I'm using :Code:#include <xc.h>#include "i2c1.h"#ifndef _XTAL_FREQ#define _XTAL_FREQ 8000000UL#endif#ifndef FCY#define FCY _XTAL_FREQ/2#endif#include <libpic30.h>void i2c1_init() {I2C1BRG = 18; //8 7 4 = 300kH//I2C1CONbits.I2CEN = 0; // Disable I2C Mode// Clear InterruptIFS3bits.MI2C2IF = 0;//Continue module operation in Idle modeI2C1CONbits.I2CSIDL = 0;//IPMIEN mode disabledI2C1CONbits.IPMIEN = 0;//Slew rate enabledI2C1CONbits.DISSLW = 0;//General call address disabledI2C1CONbits.GCEN = 0;//Disable SMBus input thresholdsI2C1CONbits.SMEN = 0;//Software or receive call stretching disabledI2C1CONbits.STREN = 0;// Enable I2C ModeI2C1CONbits.I2CEN = 1;}void i2c1_wait(void) {//while(!IFS3bits.MI2C2IF);__delay_ms(500);IFS3bits.MI2C2IF = 0; // Reset flag}void i2c1_start(void) {I2C1CONbits.SEN = 1; //Initiate Start conditioni2c1_wait();}void i2c1_restart(void) {I2C1CONbits.RSEN = 1; //Initiate restart conditioni2c1_wait();}void i2c1_stop(void) {I2C1CONbits.PEN = 1; //Initiate Start conditioni2c1_wait();}void reset_i2c1_bus(void) {I2C1CONbits.PEN = 1; //initiate stop bitwhile (I2C1CONbits.PEN);I2C1CONbits.RCEN = 0;IFS3bits.MI2C2IF = 0; // Clear InterruptI2C1STATbits.IWCOL = 0;I2C1STATbits.BCL = 0;}char send_i2c1_byte(unsigned char data) {I2C1TRN = data; // load the outgoing data bytei2c1_wait();return (I2C1STATbits.ACKSTAT == 0); // return 1 if ACK}unsigned char receive_i2c1_byte(char send_ack) {unsigned char data;if (send_ack)I2C1CONbits.ACKDT = 0;elseI2C1CONbits.ACKDT = 1;I2C1CONbits.RCEN = 1; // Start reception//i2c1_wait();data = I2C1RCV; // Read dataI2C1CONbits.ACKEN = 1; // Send ACK/NAK//i2c1_wait();while (I2C1CONbits.ACKEN);return data;}unsigned char I2C1write(unsigned char dev, unsigned char reg, unsigned char value) {dev <<= 1; // RW=0i2c1_start(); // Send startif (!send_i2c1_byte(dev) || !send_i2c1_byte(reg)) { // Send device address and reg addressi2c1_stop(); // Cancel if NACKreturn 0;}send_i2c1_byte(value); // Write datai2c1_stop(); // Send stopreturn 1;}char I2C1read(unsigned char dev, unsigned char reg) {unsigned char data;dev = (dev << 1); // RW=0i2c1_start(); // Send startif (!send_i2c1_byte(dev) || !send_i2c1_byte(reg)) { // Send device address and reg addressi2c1_stop(); // Cancel if NACKreturn 0;}dev |= 1; // RW=1i2c1_restart(); // Send restartif (!send_i2c1_byte(dev)) { // Send device addressi2c1_stop(); // Cancel if NACKreturn 0;}data = receive_i2c1_byte(0);i2c1_stop(); // Send stopreturn data; // Return read data}My mainCode://LCD test 2LED_IGN_TRIS = LED_OUTPUT;LED_IGN_LAT = LED_OFF;LED_ES_TRIS = LED_OUTPUT;LED_ES_LAT = LED_OFF;INV_TRIS = LED_OUTPUT;INV_LAT = LED_OFF;AL_TRIS = LED_OUTPUT;AL_LAT = LED_OFF;LED_IGN_LAT = LED_ON; //Init I2Ci2c1_init();__delay_ms(50);LED_IGN_LAT = LED_OFF;__delay_ms(500);LED_ES_LAT = LED_ON; //Start I2Ci2c1_start();__delay_ms(50);LED_ES_LAT = LED_OFF;__delay_ms(500);AL_LAT = LED_ON;lcd_init();__delay_ms(50);AL_LAT = LED_OFF;__delay_ms(500);while(1){INV_LAT = INV_ON;lcd_send_data('J');AL_LAT = LED_ON;__delay_ms(5000);INV_LAT = LED_OFF;__delay_ms(5000);AL_LAT = LED_OFF;}I have used heaps of LED debug, and each instruction seems to work, but no character is being displayed on the screen :/I've been looking for the past week and a half but it's hard to find example of this LCD with the PIC24FJ128GA110. If anyone had an idea, I'd be really grateful. Thank you for your time!

author: DavidAlfa
author_rank: Super Contributor
timestamp: July 26, 2023, 06:55:07 pm
content: Start bitI2c addressSend as many data as you wantStop bitYou aren't sending the i2c address anywhere.So try:Code:LED_ES_LAT = LED_ON; //Start I2Ci2c1_start();send_i2c1_byte(LCD_ADDRESS << 1); //RW = 0Of course this wouldn't work , it should be IFS1bits.MI2C1IF!Code:void i2c1_wait(void) {//while(!IFS3bits.MI2C2IF);__delay_ms(500);IFS3bits.MI2C2IF = 0; // Reset flag}Code:void i2c1_wait(void) {while(!IFS1bits.MI2C1IF);IFS1bits.MI2C1IF = 0; // Reset flag}Same error on i2c_init and reset_i2c1_busAlso have a look here:https://github.com/Matiasus/HD44780_PCF8574/blob/master/lib/hd44780pcf8574.c

author: juicycrunchwrap
author_rank: Contributor
timestamp: July 26, 2023, 07:22:43 pm
content: Hello ! Thank you so much for your reply !Now this is my what I'm doing before the while (1) :Code:i2c1_init();__delay_ms(50);LED_IGN_LAT = LED_OFF;__delay_ms(500);LED_ES_LAT = LED_ON; //Start I2Ci2c1_start();send_i2c1_byte(LCD_ADDRESS << 1);__delay_ms(50);LED_ES_LAT = LED_OFF;__delay_ms(500);AL_LAT = LED_ON;lcd_init();as well as i corrected IFS1bits.MI2C1IF that i didn't seeNow the LCD blinks during the lcd_init function, which i don't know if it's good or not :/Still no characters are being displayed though. But thank you heaps, I think I would have never known I had made this mistake on the flag bit!

author: DavidAlfa
author_rank: Super Contributor
timestamp: July 26, 2023, 07:42:20 pm
content: lcd_send_command and lcd_send_data look weird to me.Try this.Addvoid lcd_send_E_pulse(unsigned char data) ;at the beginning of lcd.c or in lcd.h.Code:void lcd_send_E_pulse(unsigned char data) {write_word_lcd(data|0x04); // EN = 1__delay_us(1);write_word_lcd(data); // EN = 0__delay_us(1);}void lcd_send_command(unsigned char command) {write_word_lcd(command&0xF0); // Bits 7-4, RS = 0, RW = 0, EN = 0lcd_send_E_pulse(command&0xF0);__delay_us(100);write_word_lcd(command<<4); // Bits 3-0, RS = 0, RW = 0, EN = 0lcd_send_E_pulse(command<<4);__delay_ms(1);}void lcd_send_data(unsigned char data) {write_word_lcd((data&0xF0) | 0x01); // Bits 7-4, RS = 1, RW = 0, EN = 0lcd_send_E_pulse((data&0xF0) | 0x01);__delay_us(100);write_word_lcd((data<<4) | 0x01); // Bits 3-0, RS = 1, RW = 0, EN = 0lcd_send_E_pulse((data<<4) | 0x01);__delay_ms(1);}

author: juicycrunchwrap
author_rank: Contributor
timestamp: July 26, 2023, 08:05:45 pm
content: Thanks again heaps for your help !I have changed lcd_send_command and lcd_send_data the way you told me too, now the screen is blinking when I try to send the 'J' character, but for soe reason, nothing is displayed on the screen still. I have consulted several articles about the initialization of the LCD, and I think it's the correct one ?But thank you a lot for helping me !It's hard to find other projects that use this screen and a PIC microcontroller

author: DavidAlfa
author_rank: Super Contributor
timestamp: July 26, 2023, 08:20:32 pm
content: It's not important if it is for a PIC or what, what you really need is the commands and the procedures.In the link I posted earlier the init sequence wasCode:// +---------------------------+// | Power on |// | Wait for more than 15 ms | // 15 ms wait// | after VCC rises to 4.5 V |// +---------------------------+// |// +---------------------------+// | RS R/W DB7 DB6 DB5 DB4 |// | 0 0 0 0 1 1 | // Initial sequence 0x30// | Wait for more than 4.1 ms | // 4.1 ms us writing DATA into DDRAM or CGRAM// +---------------------------+// |// +---------------------------+// | RS R/W DB7 DB6 DB5 DB4 |// | 0 0 0 0 1 1 | // Initial sequence 0x30// | Wait for more than 0.1 ms | // 100 us writing DATA into DDRAM or CGRAM// +---------------------------+// |// +---------------------------+// | RS R/W DB7 DB6 DB5 DB4 | // Initial sequence 0x30// | 0 0 0 0 1 1 | // 37 us writing DATA into DDRAM or CGRAM 4 us tadd - time after busy flag disapeared// | Wait for more than 45 us | // 37 us + 4 us = 41 us * (270/250) = 45us// +---------------------------+// |// +---------------------------+ // 4bit mode 0x20 !!! MUST BE SET TIME, BF CHECK DOESN'T WORK CORRECTLY !!!// | RS R/W DB7 DB6 DB5 DB4 | //// | 0 0 0 0 1 0 | // 37 us writing DATA into DDRAM or CGRAM 4 us tadd - time after busy flag disapeared// | Wait for more than 45 us | // !!! MUST BE SET DELAY TIME, BUSY FLAG CHECK DOESN'T WORK CORRECTLY !!!// +---------------------------+// |// +---------------------------+// | RS R/W DB7 DB6 DB5 DB4 | // Display off 0x28// | 0 0 0 0 1 0 | //// | 0 0 1 0 0 0 | //// | Wait for BF Cleared | // Wait for 50us// +---------------------------+// |// +---------------------------+// | RS R/W DB7 DB6 DB5 DB4 | // Display clear 0x01// | 0 0 0 0 0 0 | //// | 0 0 0 0 0 1 | //// | Wait for BF Cleared | // Wait for 50us// +---------------------------+// |// +---------------------------+// | RS R/W DB7 DB6 DB5 DB4 | // Entry mode set 0x06// | 0 0 0 0 0 0 | //// | 0 0 0 1 1 0 | // shift cursor to the left, without text shifting// | Wait for BF Cleared | // Wait for 50us// +---------------------------+So try:Code:void lcd_init() {__delay_ms(5);lcd_send_command(0x30);__delay_ms(5);lcd_send_command(0x30);__delay_us(200);lcd_send_command(0x30);__delay_us(100);lcd_send_command(0x20);__delay_us(100);__delay_ms(5);lcd_send_command(0x28); // 2 lines, 5x7 matrix__delay_us(100);lcd_send_command(0x0C); // Display ON, Cursor OFF, Blink OFF__delay_ms(5);lcd_clear(); // Clear the LCD}

author: DavidAlfa
author_rank: Super Contributor
timestamp: July 26, 2023, 08:30:30 pm
content: Oh! Also make sure to play with the potentiometer in the display, that's for the contrast!Too high and everything will be black, too low and you'll see nothing!

author: juicycrunchwrap
author_rank: Contributor
timestamp: July 26, 2023, 09:10:43 pm
content: Thank you for your reply ! I tried changing my lcd_init function, to what you sent me and with code on the github repo as well but it's still blinking when the PIC is supposed to send the letter to the LCD, and nothing is displayed. But I know data is sent because i can see the signals on my scope !I also tried to adjust the potentiometer behind the screen but it didn't really helpI'm a bit out of ideas on what to try to be honest

author: DavidAlfa
author_rank: Super Contributor
timestamp: July 26, 2023, 09:54:37 pm
content: It's for a different PIC, but all you need to do is changing the i2c calls.https://microdigisoft.com/pic16f877a-interfacing-i2c-lcd-16x2-with-pic-microcontrollers/

author: juicycrunchwrap
author_rank: Contributor
timestamp: July 27, 2023, 05:45:34 pm
content: Thanks heaps ! I will try that and let you know if it worked !

author: juicycrunchwrap
author_rank: Contributor
timestamp: July 31, 2023, 03:17:35 pm
content: Edit :I still isn't working... I have spent my weekend trying different things to make it work.I have used another libraryCode:#define SLAVE_ADDRESS 0x27#define LCD_RS 0x01#define LCD_RW 0x02#define LCD_E 0x04#define LCD_BACKLITE 0x08//-------------------------------------// LCD functions//-------------------------------------void LCD_cmd(char ch){char d;d = (ch & 0xF0) + LCD_BACKLITE; // send upper nybblesend_i2c1_byte(d);send_i2c1_byte(d + LCD_E); // pulse E high-lowsend_i2c1_byte(d);send_i2c1_byte(LCD_BACKLITE);d = ((ch << 4) & 0xF0) + LCD_BACKLITE; // send lower nybblesend_i2c1_byte(d);send_i2c1_byte(d + LCD_E); // pulse E high-lowsend_i2c1_byte(d);send_i2c1_byte(LCD_BACKLITE);__delay_ms(1);}void LCD_ch(char ch){char d;d = (ch & 0xF0) + LCD_RS + LCD_BACKLITE; // send upper nybblesend_i2c1_byte(d);send_i2c1_byte(d + LCD_E); // pulse E high-lowsend_i2c1_byte(d);send_i2c1_byte(LCD_RS + LCD_BACKLITE);d = ((ch << 4) & 0xF0) + LCD_RS + LCD_BACKLITE; // send lower nybblesend_i2c1_byte(d);send_i2c1_byte(d + LCD_E); // pulse E high-lowsend_i2c1_byte(d);send_i2c1_byte(LCD_RS + LCD_BACKLITE);}void LCD_init(void){//i2c1_start();LCD_cmd(0x02); // homeLCD_cmd(0x28); // dual line, 4 bitsLCD_cmd(0x06); // increment modeLCD_cmd(0x0C); // cursor turned offLCD_cmd(0x10); // cursor rightLCD_cmd(0x01); // clear display}But still no success. I am starting to be a bit desperate. Even by changing the i2c calls of the lib from the link you sent, it didn't work. But I don't think the problem comes from the i2c, because I do get a clock and data signal on my scope. A problem with the i2c and something wrong with the initialization are the only two things i can think about, but if someone else had an idea of what could be wrong i would gladly take it

author: DavidAlfa
author_rank: Super Contributor
timestamp: July 31, 2023, 04:35:58 pm
content: I never said it was the i2c, the problem was clearly in the lcd code!Can you send me the MplabX project? I'll simulate it in proteus.

author: juicycrunchwrap
author_rank: Contributor
timestamp: July 31, 2023, 05:08:40 pm
content: Hi thank you loads for your reply. yes sure, here is a zip of my project :Thank you a lot for your help i really appreciate it.

author: DavidAlfa
author_rank: Super Contributor
timestamp: July 31, 2023, 05:25:17 pm
content: You forgot the file!

author: juicycrunchwrap
author_rank: Contributor
timestamp: July 31, 2023, 05:43:39 pm
content: Sorry I thought it was attached ! My bad !

author: DavidAlfa
author_rank: Super Contributor
timestamp: July 31, 2023, 06:29:28 pm
content: Maybe you forgot to send the i2c slave address somewhere?

author: juicycrunchwrap
author_rank: Contributor
timestamp: July 31, 2023, 06:40:08 pm
content: I changed my main to thisCode:LED_IGN_TRIS = LED_OUTPUT;LED_IGN_LAT = LED_OFF;LED_ES_TRIS = LED_OUTPUT;LED_ES_LAT = LED_OFF;INV_TRIS = LED_OUTPUT;INV_LAT = LED_OFF;AL_TRIS = LED_OUTPUT;AL_LAT = LED_OFF;configureTimer();LED_IGN_LAT = LED_ON;i2c1_init();delay();delay();i2c1_start();delay();delay();send_i2c1_byte(LCD_ADDRESS);LED_IGN_LAT = LED_OFF;delay();delay();LED_ES_LAT = LED_ON;LCD_init();delay();delay();LED_ES_LAT = LED_OFF;delay();delay();delay();delay();while (1) {INV_LAT = LED_ON;LCD_ch('A');delay();delay();INV_LAT = LED_OFF;delay();delay();delay();}return 0;Where i send 0x27. But i still see those 16 small white rectangles and that's it , no characters are being displayedThis is my SCL signal that i get on my scope when i probe it on the SCL pin of the LCD. I think this is what i am supposed to get...Thank you loads for your help with this, i really appreciate it

author: DavidAlfa
author_rank: Super Contributor
timestamp: July 31, 2023, 08:07:33 pm
content: Try this!Make sure to turn some led on in lcd_error() function to let you know the i2c didn't ack.Check the A0/A1/A2 jumpers in the lcd, make sure they're all open!Leaving the i2c bus busy is a bad practice, better to run a start/stop sequence between each command or data!This is fixed in the project among other things, including the missing 4-bit init sequence.The Proteus project is included, you can use the demo version or the "proteus 8.15 sp1" available everywhere.Some literature:https://web.alfredstate.edu/faculty/weimandn/lcd/lcd_initialization/lcd_initialization_index.htmlhttp://www.dinceraydin.com/lcd/commands.htm

author: juicycrunchwrap
author_rank: Contributor
timestamp: July 31, 2023, 08:26:52 pm
content: Thank you SO much DavidAlfa.I will use the demo version because I think I can learn a lot from it. Thanks loads for your help. I will update my github repo so people can have access to this.Again thank you so much. Your help is huge for beginners like me and I'm very grateful for it !

author: DavidAlfa
author_rank: Super Contributor
timestamp: August 01, 2023, 07:06:17 pm
content: So how did it went?

author: juicycrunchwrap
author_rank: Contributor
timestamp: August 01, 2023, 07:10:15 pm
content: It works really fine ! I can display string and characters, thank you so much !! I didn't know about leaving the i2c bus busy before but now i will pay attention to that ! Thank you again loads !