• Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

GLCD PIC16F877 lack of character output Issue (SOLVED)

GettinBetter

New Member
H/W: EastPIC V7 Dev Board, ICD3, WDG0151-TMI GLCD, PIC16DF877, Salea16 Analyser.
S/W: MPLABX, XC8,

I have the following example code slightly modified to suit my application, but it's not quite there yet, hopefully it's something simple that you guys might spot.
The EasyPIC board has a pot directly connected that allows contrast to be adjusted, works fine in that I can brighten and darken the pixels, but alas not characters will appear.
Ant pointers would be appreciated

C:
/*
* File:   main.c
* Author: Example Code from exploreembedded.com
* Created on 09 January 2021, 23:37
* Project name: Testing GLCD with PIC16F877
*/

// CONFIG
#pragma config FOSC = XT        // Oscillator Selection bits (XT oscillator)
#pragma config WDTE = OFF       // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = OFF      // Power-up Timer Enable bit (PWRT disabled)
#pragma config CP = OFF         // FLASH Program Memory Code Protection bits (Code protection off)
#pragma config BOREN = ON       // Brown-out Reset Enable bit (BOR enabled)
#pragma config LVP = OFF        // Low Voltage In-Circuit Serial Programming Enable bit (RB3 is digital I/O, HV on MCLR must be used for programming)
#pragma config CPD = OFF        // Data EE Memory Code Protection (Code Protection off)
#pragma config WRT = ON         // FLASH Program Memory Write Enable (Unprotected program memory may be written to by EECON control)

#include <xc.h>

#define  _XTAL_FREQ 4000000L  

/**
*  Control bus PB0:PB4
*  Data bus PD0:PD7
*/

#define GlcdDataBus      PORTD
#define GlcdControlBus   PORTB

#define GlcdDataBusDirnReg   TRISD
#define GlcdCtrlBusDirnReg   TRISB

#define CS1     0x00       // Select segments 1-64
#define CS2     0x01       // Select segments 65-128
#define RS      0x02       // H: Data - L: Instruction
#define RW      0x03       // H: Read Data - L: Write Data
#define EN      0x04       // Enable

/* 5x7 Font including 1 space to display HELLO WORLD */
char H[]={0x7F, 0x08, 0x08, 0x08, 0x7F, 0x00};
char E[]={0x7F, 0x49, 0x49, 0x49, 0x41, 0x00};
char L[]={0x7F, 0x40, 0x40, 0x40, 0x40, 0x00};
char O[]={0x3E, 0x41, 0x41, 0x41, 0x3E, 0x00};

char W[]={0x3F, 0x40, 0x38, 0x40, 0x3F, 0x00};
char R[]={0x7F, 0x09, 0x19, 0x29, 0x46, 0x00};
char D[]={0x7F, 0x41, 0x41, 0x22, 0x1C, 0x00};


void Glcd_SelectPage0() // CS1=1, CS2=0
{
    GlcdControlBus |= (1<<CS1);
    GlcdControlBus &= ~(1<<CS2);
}

void Glcd_SelectPage1() // CS1=0, CS1=1
{
    GlcdControlBus &= ~(1<<CS1);
    GlcdControlBus |= (1<<CS2);
}

/* Function to send the command to LCD */
void Glcd_CmdWrite(char cmd)
{
   
    GlcdControlBus |= (1<<EN);   // Generate a High-to-low pulse on EN pin
    __delay_us(500);
    GlcdDataBus = cmd;           //Send the Command byte
    GlcdControlBus &= ~(1<<EN);  // Bring the EN pin low again
   
    GlcdControlBus &= ~(1<<RS);  // Send LOW pulse on RS pin for selecting Command register
    GlcdControlBus &= ~(1<<RW);  // Send LOW pulse on RW pin for Write operation
   

    __delay_us(1000);
}

/* Function to send the Data to LCD */
void Glcd_DataWrite(char dat)
{
   

    GlcdControlBus |= (1<<EN);   // Generate a High-to-low pulse on EN pin
    __delay_us(500);
    GlcdDataBus = dat;           //Send the data on DataBus byte
    GlcdControlBus &= ~(1<<EN);  // Bring the EN pin low again
    GlcdControlBus |= (1<<RS);   // Send HIGH pulse on RS pin for selecting data register
    GlcdControlBus &= ~(1<<RW);  // Send LOW pulse on RW pin for Write operation
   

    __delay_us(1000);
}

void Glcd_DisplayChar(char *ptr_array)
{
    int i;
    for(i=0;i<6;i++) // 5x7 font, 5 chars + 1 blank space
        Glcd_DataWrite(ptr_array[i]);
}
                                                                 

int main()
{
    GlcdDataBusDirnReg = 0x00;  // Configure all the Data (PORTD) pins as output
    GlcdCtrlBusDirnReg = 0x00;  // Configure the Ctrl (PORTB) pins as output

    /* Select the Page0/Page1 and Turn on the GLCD */
    Glcd_SelectPage0();     // CS1=1, CS2=0
    Glcd_CmdWrite(0x3F);    // Display ON
    Glcd_SelectPage1();     // CS1=0, CS1=1
    Glcd_CmdWrite(0x3F);    // Display ON
    __delay_us(500);
   
    /* Select the Page0/Page1 and Enable the GLCD */
    Glcd_SelectPage0();     // CS1=1, CS2=0
    Glcd_CmdWrite(0xC0);    //
    Glcd_SelectPage1();     // CS1=0, CS1=1
    Glcd_CmdWrite(0xC0);    //
    __delay_us(500);

    Glcd_SelectPage0();     // Display HELLO on Page0, Line1
    Glcd_CmdWrite(0xB8);    // 0xB8 = line 0 (the top line)
    Glcd_DisplayChar(H);
    Glcd_DisplayChar(E);
    Glcd_DisplayChar(L);
    Glcd_DisplayChar(L);
    Glcd_DisplayChar(O);

    Glcd_SelectPage1();     // Display WORLD on Page1, Last line
    Glcd_CmdWrite(0xBF);    // 0xB8 = line 7 (the bottom line)
    Glcd_DisplayChar(W);
    Glcd_DisplayChar(O);
    Glcd_DisplayChar(R);
    Glcd_DisplayChar(L);
    Glcd_DisplayChar(D);

    while(1);
}
GLCD_nalyser-01.png
 
Last edited:

Pommie

Well-Known Member
Most Helpful Member
Can I suggest blinking an LED to ensure that your code is actually running? There could be lots of reasons it isn't working, for example, I don't see where you turn off analogue pins.

Mike.
 

GettinBetter

New Member
Hi Mike,
Does the attached logic image not prove the output form the PIC? It was obtained after programming by releasing the reset button on the dev board, after starting the analyser, the resultant waveform was created by the PIC.

As far as I know the datasheet only shows Port A as analogue capable, which I'm not using, and so haven't included them in the setup. Would they still need to be turned off?

Les.
 

rjenkinsgb

Well-Known Member
Most Helpful Member
You do not appear to be checking the Busy/Ready state between operations?
If there is not adequate time for the LCD controller to finish an operation, it will ignore anything written until it does.

It's advisable to check the state by reading the control register and looking at bit 7; if that is high, wait a short time and check again until it is low.

You do not have to do it for every operation as long as you leave adequate time between them, but some may take a long time (compared to MCU cycles) so it's either that or use program delays longer than the maximum execution time of each operation..

Typically with LCD controllers, any instruction that affects more than a single memory location will need an extended wait.


With that specific LCD panel, it also appears you have to treat it as two separate displays.
So you need to separately configure, and check ready for the two controllers, completely independently, depending which CS input you are using.

eg. It functions as two logically separate half screens in a single device.
 

Ian Rogers

User Extraordinaire
Forum Supporter
Most Helpful Member
To be fair to Pommie it does state pic16f887 in the header.

I can not, however! See any font defined.. The KS0108 doesn't have a built in font..

Here is mine..
C:
const char font3x8[] = {
0x00, 0x00, 0x00,// (space)
0x00, 0x5F, 0x00,// !
0x07, 0x00, 0x07,// "
0x7F, 0x14, 0x7F,// #
0x66, 0xFF, 0x7E,// $
0x72, 0x18, 0x4E,// %
0x76, 0x5A, 0x76,// &
0x00, 0x06, 0x00,// '
0x7E, 0x81, 0x00,// (
0x00, 0x81, 0x7E,// )
0x6C, 0x18, 0x6C,// *
0x10, 0x7C, 0x10,// +
0x00, 0xC0, 0x00,// ,
0x10, 0x10, 0x10,// -
0x00, 0x40, 0x00,// .
0xC0, 0x38, 0x07,// /
0x3C, 0x42, 0x3C,// 0
0x44, 0x7E, 0x40,// 1
0x64, 0x52, 0x4C,// 2
0x24, 0x52, 0x2C,// 3
0x3E, 0x20, 0x70,// 4
0x2E, 0x4A, 0x3A,// 5
0x3C, 0x4A, 0x32,// 6
0x02, 0x72, 0x0E,// 7
0x34, 0x4A, 0x34,// 8
0x24, 0x4A, 0x3C,// 9
0x00, 0x50, 0x00,// :
0x00, 0xD0, 0x00,// ;
0x08, 0x14, 0x22,// <
0x28, 0x28, 0x28,// =
0x22, 0x14, 0x08,// >
0x04, 0xB2, 0x0C,// ?
0x3C, 0x5A, 0x3C,// @
0x7C, 0x0A, 0x7C,// A
0x7E, 0x4A, 0x34,// B
0x3C, 0x42, 0x24,// C
0x7E, 0x42, 0x3C,// D
0x7E, 0x4A, 0x42,// E
0x7E, 0x0A, 0x02,// F
0x3C, 0x42, 0x74,// G
0x7E, 0x08, 0x7E,// H
0x42, 0x7E, 0x42,// I
0x42, 0x7E, 0x02,// J
0x7E, 0x08, 0x76,// K
0x7E, 0x40, 0x40,// L
0x7E, 0x0C, 0x7E,// M
0x7E, 0x38, 0x7E,// N
0x3C, 0x42, 0x3C,// O
0x7E, 0x08, 0x06,// P
0x3C, 0x62, 0x7C,// Q
0x7E, 0x0A, 0x74,// R
0x2C, 0x52, 0x64,// S
0x02, 0x7E, 0x02,// T
0x3E, 0x40, 0x3E,// U
0x1E, 0x60, 0x1E,// V
0x7E, 0x30, 0x7E,// W
0x6E, 0x18, 0x6E,// X
0x0E, 0x78, 0x0E,// Y
0x62, 0x5A, 0x46,// Z
0xFF, 0x81, 0x81,// [
0x07, 0x38, 0xC0,// "\"
0x81, 0x81, 0xFF,// ]
0x02, 0x01, 0x02,// ^
0x80, 0x80, 0x80,// _
0x01, 0x02, 0x00,// `
0x7A, 0x4A, 0x7C,// a
0x7E, 0x48, 0x78,// b
0x78, 0x48, 0x48,// c
0x78, 0x48, 0x7E,// d
0x78, 0x58, 0x58,// e
0x88, 0xFE, 0x0A,// f
0xB8, 0xA8, 0xF8,// g
0x7E, 0x08, 0x78,// h
0x00, 0x7A, 0x00,// i
0x80, 0xFA, 0x00,// j
0x7E, 0x10, 0x68,// k
0x00, 0x7E, 0x00,// l
0x78, 0x10, 0x78,// m
0x78, 0x08, 0x78,// n
0x78, 0x48, 0x78,// o
0xF8, 0x48, 0x78,// p
0x78, 0x48, 0xF8,// q
0x78, 0x08, 0x08,// r
0x58, 0x68, 0x68,// s
0x08, 0x7E, 0x48,// t
0x78, 0x40, 0x78,// u
0x38, 0x60, 0x38,// v
0x78, 0x20, 0x78,// w
0x48, 0x30, 0x48,// x
0x38, 0xE0, 0x38,// y
0x68, 0x58, 0x48,// z
0x10, 0x6C, 0x82,// {
0x00, 0xFE, 0x00,// |
0x82, 0x6C, 0x10,// }
};
the bit you supplied is for a toshiba....

you just need a external define.

extern const char Font[]; and a function to write it..

I use screen buffering, so my code will be OTT.. If you want it you can have it..
 

Ian Rogers

User Extraordinaire
Forum Supporter
Most Helpful Member
No forget my whole post.... The code does work... BUT!! are you sure you are getting a compile?? The WRT isn't for this chip

#pragma config WRT = ON // FLASH Program Memory Write Enable (Unprotected program memory may be written to by EECON control)

it throws an error.... I commented it out
 

GettinBetter

New Member
It's been compiling fine, and the waveform is from the PIC after a reset, I've attached a screenshot of a later version and I have the analyser conected to all pins used on the GLCD.

I commented it out but no joy.

GLCD_nalyser-02.png
and here's a zoomed view of same.
GLCD_nalyser-02_ex.png
 

rjenkinsgb

Well-Known Member
Most Helpful Member
Timing, timing? Timing!! ;)

The write sequence looks to be out of order.

All other signals must be stable for at least 140nS before E (EN) goes high, and must be held for a short time after that goes low; don't change any other signals in the same instruction.

Your timing images - and program - have other signals changing whilst E is high. That's going to give unpredictable results.

See the device write timing requirements below, grabbed from the datasheet:

WDG0151-TMI_Write_Timing.png
 

Ian Rogers

User Extraordinaire
Forum Supporter
Most Helpful Member
Yep... RJW has it right

C:
void Glcd_CmdWrite(char cmd)
{
  
    GlcdControlBus |= (1<<EN);   // Generate a High-to-low pulse on EN pin
    __delay_us(500);
    GlcdDataBus = cmd;           //Send the Command byte
    GlcdControlBus &= ~(1<<EN);  // Bring the EN pin low again
  
    GlcdControlBus &= ~(1<<RS);  // Send LOW pulse on RS pin for selecting Command register
    GlcdControlBus &= ~(1<<RW);  // Send LOW pulse on RW pin for Write operation  

    __delay_us(1000);
}
I changed to this
C:
void Glcd_CmdWrite(char cmd)
{
    GlcdControlBus &= ~(1<<RS);  // Send LOW pulse on RS pin for selecting Command register
    GlcdControlBus &= ~(1<<RW);  // Send LOW pulse on RW pin for Write operation
   
    GlcdControlBus |= (1<<EN);   // Generate a High-to-low pulse on EN pin
    __delay_us(500);
    GlcdDataBus = cmd;           //Send the Command byte
    GlcdControlBus &= ~(1<<EN);  // Bring the EN pin low again
   
      __delay_us(1000);
}
works tons better.. Also you will need a clrscreen function..
 

rjenkinsgb

Well-Known Member
Most Helpful Member
You still have it sending the command after E is high?

And the comment on the line that sets E high, from the original code, says high-to-low pulse - which makes me think it may be code intended for a different LCD controller that has been only partly adapted?

C:
void Glcd_CmdWrite(char cmd)
{
    GlcdDataBus = cmd;           //Set the Command byte

    GlcdControlBus &= ~(1<<RS);  // Set LOW level on RS pin for selecting Command register
    GlcdControlBus &= ~(1<<RW);  // Set LOW level on RW pin for Write operation

    GlcdControlBus |= (1<<EN);   // Set E High
    __delay_us(500);             // Delay - 500nS is sufficient after debugging, eg. a few NOPs
    GlcdControlBus &= ~(1<<EN);  // Bring E low again

    __delay_us(1000);            // And check busy rather than the fixed delay, once its working
}
 

Ian Rogers

User Extraordinaire
Forum Supporter
Most Helpful Member
Yes, but my sim allows me to see these errors, and the time on a 4Mhz chip for that command allows it..

But you are right... I checked my library and that is how I do it.. I'm sure clocking last would be better...
 

GettinBetter

New Member
... Also you will need a clrscreen function..
Lol, yep, I think it needs a ton of other stuff.... won't stop it working would it.

That looks like the original example... but



Timing, timing? Timing!! ;)

The write sequence looks to be out of order.

All other signals must be stable for at least 140nS before E (EN) goes high, and must be held for a short time after that goes low; don't change any other signals in the same instruction.

Your timing images - and program - have other signals changing whilst E is high. That's going to give unpredictable results.

See the device write timing requirements below, grabbed from the datasheet:
Ah, I looked at those timings and at first glance could see they weren't quite right, but the detail


went right over my head, guess I'll have to revisit.....

You still have it sending the command after E is high?

And the comment on the line that sets E high, from the original code, says high-to-low pulse - which makes me think it may be code intended for a different LCD controller that has been only partly adapted?

C:
void Glcd_CmdWrite(char cmd)
{
    GlcdDataBus = cmd;           //Set the Command byte

    GlcdControlBus &= ~(1<<RS);  // Set LOW level on RS pin for selecting Command register
    GlcdControlBus &= ~(1<<RW);  // Set LOW level on RW pin for Write operation

    GlcdControlBus |= (1<<EN);   // Set E High
    __delay_us(500);             // Delay - 500nS is sufficient after debugging, eg. a few NOPs
    GlcdControlBus &= ~(1<<EN);  // Bring E low again

    __delay_us(1000);            // And check busy rather than the fixed delay, once its working
}

You are correct, just looked back over the page I got them from, and I have managed to get them mixed up but they do use the same KS0108 type controllers,


Really appreciate you guys taking the time to have a look at it.

Regards
Les
 

GettinBetter

New Member
Ok, It's working.
After fixing the timing, as per "rjenkinsgb" suggestion, I still had nothing. So I decided over dinner, that it must be the the onboard connections.

I trawled the internet for any insight to the inner workings/connections of the EasyPIC V7 board and the GLCD and any issues. Someone had posted that they had to connect the reset pin to make it work. So I did, and it worked. Makes you wonder why the example code didn't have it connected?

Now I can tweak it to my hearts content.

Really appreciate your feedback guys.
 

Latest threads

EE World Online Articles

Loading
Top