CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to support@ccsinfo.com

Flex_LCD driver

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
Nevillestone



Joined: 05 Aug 2014
Posts: 21

View user's profile Send private message

Flex_LCD driver
PostPosted: Thu May 04, 2017 12:02 am     Reply with quote

Hi Guys
I'm using the standard library Lcd Driver modified to 20X4line display mapped to port C and my LCD is working.
However I want to move pins so I tried using Flex_LCD and with the standard port setup and although the LCD is being initialised the strings displayed is garbage.
Is anybody aware of issues or mods required for the driver ?
If I compare the code it all looks similar.
My Standard driver:
Code:

// define the pinout.
// only required if port access is being used.
typedef struct 
{                            // This structure is overlayed
   BOOLEAN enable;           // on to an I/O port to gain
   BOOLEAN rs;               // access to the LCD pins.
   BOOLEAN rw;               // The bits are allocated from
   BOOLEAN unused;           // low order up.  ENABLE will
   int     data : 4;         // be LSB pin of that port.
  #if defined(__PCD__)       // The port used will be LCD_DATA_PORT.
   int    reserved: 8;
  #endif
} LCD_PIN_MAP;

// this is to improve compatability with previous LCD drivers that accepted
// a define labeled 'use_portb_lcd' that configured the LCD onto port B.
#if ((defined(use_portb_lcd)) && (use_portb_lcd==TRUE))
 #define LCD_DATA_PORT getenv("SFR:PORTB")
#endif

#if defined(__PCB__)
   // these definitions only need to be modified for baseline PICs.
   // all other PICs use LCD_PIN_MAP or individual LCD_xxx pin definitions.
/*                                    EN, RS,   RW,   UNUSED,  DATA  */
 const LCD_PIN_MAP LCD_OUTPUT_MAP =  {0,  0,    0,    0,       0};
 const LCD_PIN_MAP LCD_INPUT_MAP =   {0,  0,    0,    0,       0xF};
#endif

////////////////////// END CONFIGURATION ///////////////////////////////////

#ifndef LCD_ENABLE_PIN
   #define lcd_output_enable(x) lcdlat.enable=x
   #define lcd_enable_tris()   lcdtris.enable=0
#else
   #define lcd_output_enable(x) output_bit(LCD_ENABLE_PIN, x)
   #define lcd_enable_tris()  output_drive(LCD_ENABLE_PIN)
#endif

#ifndef LCD_RS_PIN
   #define lcd_output_rs(x) lcdlat.rs=x
   #define lcd_rs_tris()   lcdtris.rs=0
#else
   #define lcd_output_rs(x) output_bit(LCD_RS_PIN, x)
   #define lcd_rs_tris()  output_drive(LCD_RS_PIN)
#endif

#ifndef LCD_RW_PIN
   #define lcd_output_rw(x) lcdlat.rw=x
   #define lcd_rw_tris()   lcdtris.rw=0
#else
   #define lcd_output_rw(x) output_bit(LCD_RW_PIN, x)
   #define lcd_rw_tris()  output_drive(LCD_RW_PIN)
#endif

// original version of this library incorrectly labeled LCD_DATA0 as LCD_DATA4,
// LCD_DATA1 as LCD_DATA5, and so on.  this block of code makes the driver
// compatible with any code written for the original library
#if (defined(LCD_DATA0) && defined(LCD_DATA1) && defined(LCD_DATA2) && defined(LCD_DATA3) && !defined(LCD_DATA4) && !defined(LCD_DATA5) && !defined(LCD_DATA6) && !defined(LCD_DATA7))
   #define  LCD_DATA4    LCD_DATA0
   #define  LCD_DATA5    LCD_DATA1
   #define  LCD_DATA6    LCD_DATA2
   #define  LCD_DATA7    LCD_DATA3
#endif

#ifndef LCD_DATA4
#ifndef LCD_DATA_PORT
   #if defined(__PCB__)
      #define LCD_DATA_PORT      0x06     //portb
      #define set_tris_lcd(x)   set_tris_b(x)
   #else
     #if defined(PIN_D0)
      #define LCD_DATA_PORT      getenv("SFR:PORTD")     //portd
     #else
      #define LCD_DATA_PORT      getenv("SFR:PORTB")     //portb
     #endif
   #endif   
#endif

#if defined(__PCB__)
   LCD_PIN_MAP lcd, lcdlat;
   #byte lcd = LCD_DATA_PORT
   #byte lcdlat = LCD_DATA_PORT
#elif defined(__PCM__)
   LCD_PIN_MAP lcd, lcdlat, lcdtris;
   #byte lcd = LCD_DATA_PORT
   #byte lcdlat = LCD_DATA_PORT
   #byte lcdtris = LCD_DATA_PORT+0x80
#elif defined(__PCH__)
   LCD_PIN_MAP lcd, lcdlat, lcdtris;
   #byte lcd = LCD_DATA_PORT
   #byte lcdlat = LCD_DATA_PORT+9
   #byte lcdtris = LCD_DATA_PORT+0x12
#elif defined(__PCD__)
   LCD_PIN_MAP lcd, lcdlat, lcdtris;
   #word lcd = LCD_DATA_PORT
   #word lcdlat = LCD_DATA_PORT+2
   #word lcdtris = LCD_DATA_PORT-0x02
#endif
#endif   //LCD_DATA4 not defined

#ifndef LCD_TYPE
   #define LCD_TYPE 2           // 0=5x7, 1=5x10, 2=2 lines
#endif

#ifndef LCD_LINE_TWO
   #define LCD_LINE_TWO 0x40    // LCD RAM address for the second line
#endif

#ifndef LCD_LINE_LENGTH
   #define LCD_LINE_LENGTH 20
#endif

BYTE const LCD_INIT_STRING[4] = {0x40 | (LCD_TYPE << 2), 0xc, 1, 6};
                             // These bytes need to be sent to the LCD
                             // to start it up.

BYTE lcd_read_nibble(void);

BYTE lcd_read_byte(void)
{
   BYTE low,high;

 #if defined(__PCB__)
   set_tris_lcd(LCD_INPUT_MAP);
 #else
  #if (defined(LCD_DATA4) && defined(LCD_DATA5) && defined(LCD_DATA6) && defined(LCD_DATA7))
   output_float(LCD_DATA4);
   output_float(LCD_DATA5);
   output_float(LCD_DATA6);
   output_float(LCD_DATA7);
  #else
   lcdtris.data = 0xF;
  #endif
 #endif
       
   lcd_output_rw(1);
   delay_cycles(1);
   lcd_output_enable(1);
   delay_cycles(1);
   high = lcd_read_nibble();
     
   lcd_output_enable(0);
   delay_cycles(1);
   lcd_output_enable(1);
   delay_us(1);
   low = lcd_read_nibble();
     
   lcd_output_enable(0);

 #if defined(__PCB__)
   set_tris_lcd(LCD_OUTPUT_MAP);
 #else
  #if (defined(LCD_DATA4) && defined(LCD_DATA5) && defined(LCD_DATA6) && defined(LCD_DATA7))
   output_drive(LCD_DATA4);
   output_drive(LCD_DATA5);
   output_drive(LCD_DATA6);
   output_drive(LCD_DATA7);
  #else
   lcdtris.data = 0x0;
  #endif
 #endif

   return( (high<<4) | low);
}

BYTE lcd_read_nibble(void)
{
  #if (defined(LCD_DATA4) && defined(LCD_DATA5) && defined(LCD_DATA6) && defined(LCD_DATA7))
   BYTE n = 0x00;

   /* Read the data port */
   n |= input(LCD_DATA4);
   n |= input(LCD_DATA5) << 1;
   n |= input(LCD_DATA6) << 2;
   n |= input(LCD_DATA7) << 3;
   
   return(n);
  #else
   return(lcd.data);
  #endif
}

void lcd_send_nibble(BYTE n)
{
  #if (defined(LCD_DATA4) && defined(LCD_DATA5) && defined(LCD_DATA6) && defined(LCD_DATA7))
   /* Write to the data port */
   output_bit(LCD_DATA4, bit_test(n, 0));
   output_bit(LCD_DATA5, bit_test(n, 1));
   output_bit(LCD_DATA6, bit_test(n, 2));
   output_bit(LCD_DATA7, bit_test(n, 3));
  #else     
   lcdlat.data = n;
  #endif
     
   delay_cycles(1);
   lcd_output_enable(1);
   delay_us(2);
   lcd_output_enable(0);
}

void lcd_send_byte(BYTE address, BYTE n)
{
  #if defined(__PCB__)
   set_tris_lcd(LCD_OUTPUT_MAP);
  #else
   lcd_enable_tris();
   lcd_rs_tris();
   lcd_rw_tris();
  #endif

   lcd_output_rs(0);
   while ( bit_test(lcd_read_byte(),7) ) ;
   lcd_output_rs(address);
   delay_cycles(1);


   lcd_output_rw(0);
   delay_cycles(1);
   lcd_output_enable(0);
   lcd_send_nibble(n >> 4);
   lcd_send_nibble(n & 0xf);
}

#if defined(LCD_EXTENDED_NEWLINE)
unsigned int8 g_LcdX, g_LcdY;
#endif

void lcd_init(void)
{
   BYTE i;

 #if defined(__PCB__)
   set_tris_lcd(LCD_OUTPUT_MAP);
 #else
  #if (defined(LCD_DATA4) && defined(LCD_DATA5) && defined(LCD_DATA6) && defined(LCD_DATA7))
   output_drive(LCD_DATA4);
   output_drive(LCD_DATA5);
   output_drive(LCD_DATA6);
   output_drive(LCD_DATA7);
  #else
   lcdtris.data = 0x0;
  #endif
   lcd_enable_tris();
   lcd_rs_tris();
   lcd_rw_tris();
 #endif

   lcd_output_rs(0);
   lcd_output_rw(0);
   lcd_output_enable(0);
   
   delay_ms(15);
   for(i=1;i<=3;++i)
   {
       lcd_send_nibble(3);
       lcd_send_nibble(0);
       delay_ms(5);
   }
   
   lcd_send_nibble(2);
   lcd_send_nibble(0);
   delay_ms(5);
   for(i=0;i<=3;++i)
      lcd_send_byte(0,LCD_INIT_STRING[i]);

  #if defined(LCD_EXTENDED_NEWLINE)
   g_LcdX = 0;
   g_LcdY = 0;
  #endif
}

void lcd_gotoxy(BYTE x, BYTE y)
{
   BYTE address;
   
   switch(y) {
     case 1 : address=0x80;break;
     case 2 : address=0xc0;break;
     case 3 : address=0x94;break;
     case 4 : address=0xd4;break;
     }
   //if(y!=1)
   //   address=LCD_LINE_TWO;
   //else
   //   address=0;
     
   address+=x-1;
   lcd_send_byte(0,0x80|address);
   

  #if defined(LCD_EXTENDED_NEWLINE)
   g_LcdX = x - 1;
   g_LcdY = y - 1;
  #endif
}

void lcd_putc(char c)
{
   switch (c)
   {
      case '\a'   :  lcd_gotoxy(1,1);     break;

      case '\f'   :  lcd_send_byte(0,1);
                     delay_ms(2);
                    #if defined(LCD_EXTENDED_NEWLINE)
                     g_LcdX = 0;
                     g_LcdY = 0;
                    #endif
                     break;

     #if defined(LCD_EXTENDED_NEWLINE)
      case '\r'   :  lcd_gotoxy(1, g_LcdY+1);   break;
      case '\n'   :
         while (g_LcdX++ < LCD_LINE_LENGTH)
         {
            lcd_send_byte(1, ' ');
         }
         lcd_gotoxy(1, g_LcdY+2);
         break;
     #else
      case '\n'   : lcd_gotoxy(1,2);        break;
     #endif
     
      case '\b'   : lcd_send_byte(0,0x10);  break;
     
     #if defined(LCD_EXTENDED_NEWLINE)
      default     :
         if (g_LcdX < LCD_LINE_LENGTH)
         {
            lcd_send_byte(1, c);
            g_LcdX++;
         }
         break;
     #else
      default     : lcd_send_byte(1,c);     break;
     #endif
   }
}
 
char lcd_getc(BYTE x, BYTE y)
{
   char value;

   lcd_gotoxy(x,y);
   while ( bit_test(lcd_read_byte(),7) ); // wait until busy flag is low
   lcd_output_rs(1);
   value = lcd_read_byte();
   lcd_output_rs(0);
   
   return(value);
}



The Flex_ LCD Driver
Code:

// Flex_LCD420.c

// These pins are for my Microchip PicDem2-Plus board,
// which I used to test this driver.
// An external 20x4 LCD is connected to these pins.
// Change these pins to match your own board's connections.

#define LCD_DB4   PIN_B4
#define LCD_DB5   PIN_B5
#define LCD_DB6   PIN_B6
#define LCD_DB7   PIN_B7

#define LCD_RS    PIN_B1
#define LCD_RW    PIN_B2
#define LCD_E     PIN_B0

/*
// To prove that the driver can be used with random
// pins, I also tested it with these pins:
#define LCD_DB4   PIN_D4
#define LCD_DB5   PIN_B1
#define LCD_DB6   PIN_C5
#define LCD_DB7   PIN_B5

#define LCD_RS    PIN_E2
#define LCD_RW    PIN_B2
#define LCD_E     PIN_D6
*/

// If you want only a 6-pin interface to your LCD, then
// connect the R/W pin on the LCD to ground, and comment
// out the following line.  Doing so will save one PIC
// pin, but at the cost of losing the ability to read from
// the LCD.  It also makes the write time a little longer
// because a static delay must be used, instead of polling
// the LCD's busy bit.  Normally a 6-pin interface is only
// used if you are running out of PIC pins, and you need
// to use as few as possible for the LCD.
#define USE_RW_PIN   1     


// These are the line addresses for most 4x20 LCDs.
#define LCD_LINE_1_ADDRESS 0x80
#define LCD_LINE_2_ADDRESS 0xc0
#define LCD_LINE_3_ADDRESS 0x94
#define LCD_LINE_4_ADDRESS 0xd4

// These are the line addresses for LCD's which use
// the Hitachi HD66712U controller chip.
/*
#define LCD_LINE_1_ADDRESS 0x00
#define LCD_LINE_2_ADDRESS 0x20
#define LCD_LINE_3_ADDRESS 0x40
#define LCD_LINE_4_ADDRESS 0x60
*/


//========================================

#define lcd_type 2  // 0=5x7, 1=5x10, 2=2 lines(or more)

int8 lcd_line;

int8 const LCD_INIT_STRING[4] =
{
 0x40 | (lcd_type << 2),  // Set mode: 4-bit, 2+ lines, 5x8 dots
 0xc,                     // Display on
 1,                       // Clear display
 6                        // Increment cursor
 };
                             

//-------------------------------------
void lcd_send_nibble(int8 nibble)
{
// Note:  !! converts an integer expression
// to a boolean (1 or 0).
 output_bit(LCD_DB4, !!(nibble &1 ));
 output_bit(LCD_DB5, !!(nibble &2 )); 
 output_bit(LCD_DB6, !!(nibble &4 ));   
 output_bit(LCD_DB7, !!(nibble &8 ));   

 delay_cycles(1);
 output_high(LCD_E);
 delay_us(2);
 output_low(LCD_E);
}

//-----------------------------------
// This sub-routine is only called by lcd_read_byte().
// It's not a stand-alone routine.  For example, the
// R/W signal is set high by lcd_read_byte() before
// this routine is called.     

#ifdef USE_RW_PIN
int8 lcd_read_nibble(void)
{
int8 retval;
// Create bit variables so that we can easily set
// individual bits in the retval variable.
#bit retval_0 = retval.0
#bit retval_1 = retval.1
#bit retval_2 = retval.2
#bit retval_3 = retval.3

retval = 0;
   
output_high(LCD_E);
delay_us(1);

retval_0 = input(LCD_DB4);
retval_1 = input(LCD_DB5);
retval_2 = input(LCD_DB6);
retval_3 = input(LCD_DB7);
 
output_low(LCD_E);
delay_us(1);
   
return(retval);   
}   
#endif

//---------------------------------------
// Read a byte from the LCD and return it.

#ifdef USE_RW_PIN
int8 lcd_read_byte(void)
{
int8 low;
int8 high;

output_high(LCD_RW);
delay_cycles(1);

high = lcd_read_nibble();

low = lcd_read_nibble();

return( (high<<4) | low);
}
#endif

//----------------------------------------
// Send a byte to the LCD.
void lcd_send_byte(int8 address, int8 n)
{
output_low(LCD_RS);

#ifdef USE_RW_PIN
while(bit_test(lcd_read_byte(),7)) ;
#else
delay_us(60); 
#endif

if(address)
   output_high(LCD_RS);
else
   output_low(LCD_RS);
     
 delay_cycles(1);

#ifdef USE_RW_PIN
output_low(LCD_RW);
delay_cycles(1);
#endif

output_low(LCD_E);

lcd_send_nibble(n >> 4);
lcd_send_nibble(n & 0xf);
}
//----------------------------

void lcd_init(void)
{
int8 i;

lcd_line = 1;

output_low(LCD_RS);

#ifdef USE_RW_PIN
output_low(LCD_RW);
#endif

output_low(LCD_E);

// Some LCDs require 15 ms minimum delay after
// power-up.  Others require 30 ms.  I'm going
// to set it to 35 ms, so it should work with
// all of them.
delay_ms(35);         

for(i=0 ;i < 3; i++)
   {
    lcd_send_nibble(0x03);
    delay_ms(5);
   }

lcd_send_nibble(0x02);

for(i=0; i < sizeof(LCD_INIT_STRING); i++)
   {
    lcd_send_byte(0, LCD_INIT_STRING[i]);
   
    // If the R/W signal is not used, then
    // the busy bit can't be polled.  One of
    // the init commands takes longer than
    // the hard-coded delay of 50 us, so in
    // that case, lets just do a 5 ms delay
    // after all four of them.
    #ifndef USE_RW_PIN
    delay_ms(5);
    #endif
   }

}

//----------------------------

void lcd_gotoxy(BYTE x, BYTE y)
{
BYTE address;


switch(y)
  {
  case 1:
     address = LCD_LINE_1_ADDRESS;
     break;

   case 2:
     address = LCD_LINE_2_ADDRESS;
     break;

   case 3:
     address = LCD_LINE_3_ADDRESS;
     break;

   case 4:
     address = LCD_LINE_4_ADDRESS;
     break;

   default:
     address = LCD_LINE_1_ADDRESS;
     break;
     
  }

address += x-1;
lcd_send_byte(0, 0x80 | address);
}

//-----------------------------
void lcd_putc(char c)
{
 switch(c)
   {
    case '\f':
      lcd_send_byte(0,1);
      lcd_line = 1;
      delay_ms(2);
      break;
   
    case '\n':
       lcd_gotoxy(1, ++lcd_line);
       break;
   
    case '\b':
       lcd_send_byte(0,0x10);
       break;
   
    default:
       lcd_send_byte(1,c);
       break;
   }
}

//------------------------------
#ifdef USE_RW_PIN
char lcd_getc(int8 x, int8 y)
{
char value;

lcd_gotoxy(x,y);

// Wait until busy flag is low.
while(bit_test(lcd_read_byte(),7)); 

output_high(LCD_RS);
value = lcd_read_byte();
output_low(LCD_RS);

return(value);
}
#endif

_________________
Neville
temtronic



Joined: 01 Jul 2010
Posts: 9117
Location: Greensville,Ontario

View user's profile Send private message

PostPosted: Thu May 04, 2017 5:12 am     Reply with quote

OK... I'm confused...par t of the 'fun' of getting old...

Are you saying the unmodified version of flex_lcd is giving you garbage ? That may be because the driver has defined the LCD to be on PORT B pins. If your test program defined others, it will give garbage as the driver is using PORT B pins.

I actually need to use the flex_lcd driver on a new project (OK, update a 26 year old one..) later this week, so I am interested in this!

Jay
Nevillestone



Joined: 05 Aug 2014
Posts: 21

View user's profile Send private message

LCD Driver
PostPosted: Thu May 04, 2017 5:49 am     Reply with quote

The Flex driver mapped to port C same pins as the old LCD driver gives garbage.
But still initializes the LCD (verifying pin connections).
Does that make sense now?
_________________
Neville
Ttelmah



Joined: 11 Mar 2010
Posts: 19231

View user's profile Send private message

PostPosted: Thu May 04, 2017 7:00 am     Reply with quote

Honestly means something in the mapping of the pins if not right, or a specific difference about your PIC.

The Flex driver is one of the most reliable bits of code around, but there are chip oddities that can cause problems. For instance Port A on some PIC's has A4, not actually able to pull up, or some PIC's have individual peripherals that must be turned off to use specific pins. Given you don't show us the processor, fuses, or give us details of the LCD involved, we haven't really go much hope of being able to diagnose anything....
Nevillestone



Joined: 05 Aug 2014
Posts: 21

View user's profile Send private message

PostPosted: Thu May 04, 2017 1:26 pm     Reply with quote

Hi
Thanks for the reply.
As you say it is strange in both tests the hardware stays the same
Pic 24EP128HG204 LCD on port C.
The code is exactly the same for both tests except the driver being called is ether
#include <C:\Users\Neville\Documents\Stone age\mplabpj\Nitro\LCDflexi.C>
Garbage on screen and only writes to line 1.
or
#include <C:\Users\Neville\Documents\Stone age\mplabpj\Nitro\LCD.C>
All 4 lines populated with correct characters.
Any ideas will be appreciated, otherwise 3 hours of debugging me thinks.
_________________
Neville
Ttelmah



Joined: 11 Mar 2010
Posts: 19231

View user's profile Send private message

PostPosted: Thu May 04, 2017 2:03 pm     Reply with quote

Problem is you are not showing us what settings you are using for the standard driver, so we can't tell how it is actually mapping things.
Nevillestone



Joined: 05 Aug 2014
Posts: 21

View user's profile Send private message

PostPosted: Thu May 04, 2017 11:59 pm     Reply with quote

Hi
Its on port B Enable= B0, RS=B1, w/r= B2, B3 not used, D4=B4, D5=B5, D6=B6, D7=b7.
Code:

// define the pinout.
// only required if port access is being used.
typedef struct 
{                            // This structure is overlayed
   BOOLEAN enable;           // on to an I/O port to gain
   BOOLEAN rs;               // access to the LCD pins.
   BOOLEAN rw;               // The bits are allocated from
   BOOLEAN unused;           // low order up.  ENABLE will
   int     data : 4;         // be LSB pin of that port.
  #if defined(__PCD__)       // The port used will be LCD_DATA_PORT.
   int    reserved: 8;
  #endif
} LCD_PIN_MAP;

_________________
Neville
Ttelmah



Joined: 11 Mar 2010
Posts: 19231

View user's profile Send private message

PostPosted: Fri May 05, 2017 1:01 am     Reply with quote

You need to show use the lines _before_ you include the lcd file, and the code before it is used in the main.
If your code is too complex, simplify. Do a minimum file we can compile, so we can see how you are actually using this. What chip it is etc..
Though you show us the lcd data port definition, this won't be used, if you have LCD pin definitions loaded before the driver is loaded..... If so the mapping may be nothing like what is in the driver file.
There are lots of things in the standard include file, that depend on how it is included, what defines are set before it is included etc..
jeremiah



Joined: 20 Jul 2010
Posts: 1318

View user's profile Send private message

PostPosted: Fri May 05, 2017 6:05 am     Reply with quote

Agreed.

The two drivers act very differently depending on the defines. For example, the flex driver has a define to decide whether or not to use the R/W pin while the other driver assumes you always use it. If the pin isn't defined, the two drivers will operate completely different.

Another difference is the flex driver doesn't manually affect tris while the other driver does, which may or may not lead to differences (I didn't study it in depth to see).

As Ttelmah suggested, we need more context. A small compilable program that replicates the problem. We don't need much, just:
Chip Include
Fuses
clock statement
any pin selects if applicable
#use statements for any peripherals
#defines for your LCD Driver prior to inclusion
#include for the lcd driver
small main with init code and something that tries to print to the LCD

The code should fail for the flex driver and pass for the other driver and it should be compilable by simply copy/paste into our IDE/editors + compile.
Ttelmah



Joined: 11 Mar 2010
Posts: 19231

View user's profile Send private message

PostPosted: Fri May 05, 2017 8:47 am     Reply with quote

This one is very important:

Quote:

Another difference is the flex driver doesn't manually affect tris while the other driver does, which may or may not lead to differences (I didn't study it in depth to see).


The flex_lcd driver will not work correctly if fast_io is specified on the port.
Nevillestone



Joined: 05 Aug 2014
Posts: 21

View user's profile Send private message

FlexiLCD driver
PostPosted: Sat May 06, 2017 6:02 am     Reply with quote

Problem solved
Involved some real electronics
Control of the tris register was the issue
The lcd_read_nibble Subroutine enables the LCD E and then reads the pins which was to fast to allow the LCD drive to settle.
Doing a read before enabling the LCD E line alow's the data lines to be in hi impedance mode earlier allowing data to settle before a read .
hence reading the bus correctly.. solving the problem.

the Driver with the mods are attached
Use it dont use it...

// Flex_LCD420.c

// These pins are for my Microchip PicDem2-Plus board,
// which I used to test this driver.
// An external 20x4 LCD is connected to these pins.
// Change these pins to match your own board's connections.

#define LCD_DB4 PIN_B4
#define LCD_DB5 PIN_B5
#define LCD_DB6 PIN_B6
#define LCD_DB7 PIN_B7

#define LCD_RS PIN_B1
#define LCD_RW PIN_B2
#define LCD_E PIN_B0

/*
// To prove that the driver can be used with random
// pins, I also tested it with these pins:
#define LCD_DB4 PIN_D4
#define LCD_DB5 PIN_B1
#define LCD_DB6 PIN_C5
#define LCD_DB7 PIN_B5

#define LCD_RS PIN_E2
#define LCD_RW PIN_B2
#define LCD_E PIN_D6
*/

// If you want only a 6-pin interface to your LCD, then
// connect the R/W pin on the LCD to ground, and comment
// out the following line. Doing so will save one PIC
// pin, but at the cost of losing the ability to read from
// the LCD. It also makes the write time a little longer
// because a static delay must be used, instead of polling
// the LCD's busy bit. Normally a 6-pin interface is only
// used if you are running out of PIC pins, and you need
// to use as few as possible for the LCD.
#define USE_RW_PIN 1


// These are the line addresses for most 4x20 LCDs.
#define LCD_LINE_1_ADDRESS 0x80
#define LCD_LINE_2_ADDRESS 0xc0
#define LCD_LINE_3_ADDRESS 0x94
#define LCD_LINE_4_ADDRESS 0xd4

// These are the line addresses for LCD's which use
// the Hitachi HD66712U controller chip.
/*
#define LCD_LINE_1_ADDRESS 0x00
#define LCD_LINE_2_ADDRESS 0x20
#define LCD_LINE_3_ADDRESS 0x40
#define LCD_LINE_4_ADDRESS 0x60
*/


//========================================

#define lcd_type 2 // 0=5x7, 1=5x10, 2=2 lines(or more)

int8 lcd_line;

int8 const LCD_INIT_STRING[4] =
{
0x40 | (lcd_type << 2), // Set mode: 4-bit, 2+ lines, 5x8 dots
0xc, // Display on
1, // Clear display
6 // Increment cursor
};


//-------------------------------------
void lcd_send_nibble(int8 nibble)
{
// Note: !! converts an integer expression
// to a boolean (1 or 0).
#if (defined(LCD_DB4) && defined(LCD_DB5) && defined(LCD_DB6) && defined(LCD_DB7))
/* Write to the data port */

output_bit(LCD_DB4, bit_test(nibble, 0));
output_bit(LCD_DB5, bit_test(nibble, 1));
output_bit(LCD_DB6, bit_test(nibble, 2));
output_bit(LCD_DB7, bit_test(nibble, 3));

#else
lcdlat.data = n;
#endif

delay_cycles(1);
output_high(LCD_E);
delay_us(2);
output_low(LCD_E);
}

//-----------------------------------
// This sub-routine is only called by lcd_read_byte().
// It's not a stand-alone routine. For example, the
// R/W signal is set high by lcd_read_byte() before
// this routine is called.

#ifdef USE_RW_PIN
int8 lcd_read_nibble(void)
{
int8 retval;
// Create bit variables so that we can easily set
// individual bits in the retval variable.
#bit retval_0 = retval.0
#bit retval_1 = retval.1
#bit retval_2 = retval.2
#bit retval_3 = retval.3

retval = 0;

retval_0 = input(LCD_DB4);
retval_1 = input(LCD_DB5);
retval_2 = input(LCD_DB6);
retval_3 = input(LCD_DB7);

output_high(LCD_E);


delay_cycles(1);
retval_0 = input(LCD_DB4);
retval_1 = input(LCD_DB5);
retval_2 = input(LCD_DB6);
retval_3 = input(LCD_DB7);

output_low(LCD_E);
delay_us(1);

return(retval);
}
#endif

//---------------------------------------
// Read a byte from the LCD and return it.

#ifdef USE_RW_PIN
int8 lcd_read_byte(void)
{
int8 low;
int8 high;

output_high(LCD_RW);
delay_cycles(1);

high = lcd_read_nibble();

low = lcd_read_nibble();

return( (high<<4) | low);
}
#endif

//----------------------------------------
// Send a byte to the LCD.
void lcd_send_byte(int8 address, int8 n)
{
output_low(LCD_RS);
delay_cycles(2);

#ifdef USE_RW_PIN
while(bit_test(lcd_read_byte(),7)) ;
#else
delay_us(100);
#endif


if(address)
output_high(LCD_RS);
else
output_low(LCD_RS);

delay_cycles(2);

#ifdef USE_RW_PIN
output_low(LCD_RW);
#endif
delay_cycles(2);
output_low(LCD_E);

lcd_send_nibble(n >> 4);
delay_cycles(2);
lcd_send_nibble(n & 0xf);
delay_cycles(2);
}
//----------------------------

void lcd_init(void)
{
int8 i;

lcd_line = 1;

output_low(LCD_RS);

#ifdef USE_RW_PIN
output_low(LCD_RW);
#endif

output_low(LCD_E);

// Some LCDs require 15 ms minimum delay after
// power-up. Others require 30 ms. I'm going
// to set it to 35 ms, so it should work with
// all of them.
delay_ms(15);

for(i=0 ;i < 3; i++)
{
lcd_send_nibble(0x03);
delay_ms(5);
}

lcd_send_nibble(0x02);

for(i=0; i < sizeof(LCD_INIT_STRING); i++)
{
lcd_send_byte(0, LCD_INIT_STRING[i]);

// If the R/W signal is not used, then
// the busy bit can't be polled. One of
// the init commands takes longer than
// the hard-coded delay of 50 us, so in
// that case, lets just do a 5 ms delay
// after all four of them.
#ifndef USE_RW_PIN
delay_ms(5);
#endif
}

}

//----------------------------

void lcd_gotoxy(BYTE x, BYTE y)
{
BYTE address;


switch(y)
{
case 1:
address = LCD_LINE_1_ADDRESS;
break;

case 2:
address = LCD_LINE_2_ADDRESS;
break;

case 3:
address = LCD_LINE_3_ADDRESS;
break;

case 4:
address = LCD_LINE_4_ADDRESS;
break;

default:
address = LCD_LINE_1_ADDRESS;
break;

}

address += x-1;
lcd_send_byte(0, 0x80 | address);
}

//-----------------------------
void lcd_putc(char c)
{
switch(c)
{
case '\f':
lcd_send_byte(0,1);
lcd_line = 1;
delay_ms(2);
break;

case '\n':
lcd_gotoxy(1, ++lcd_line);
break;

case '\b':
lcd_send_byte(0,0x10);
break;

default:
lcd_send_byte(1,c);
break;
}
}

//------------------------------
#ifdef USE_RW_PIN
char lcd_getc(int8 x, int8 y)
{
char value;

lcd_gotoxy(x,y);

// Wait until busy flag is low.
while(bit_test(lcd_read_byte(),7));

output_high(LCD_RS);
value = lcd_read_byte();
output_low(LCD_RS);

return(value);
}
#endif
_________________
Neville
temtronic



Joined: 01 Jul 2010
Posts: 9117
Location: Greensville,Ontario

View user's profile Send private message

PostPosted: Sat May 06, 2017 7:44 am     Reply with quote

I didn't see any commented lines in your posted ,modified driver....
also it's only used during reading of the LCD not sending data to it, from what I glen from the original driver.
probably 99.999% of LCD users never read data from the module

I've used both 7 and 6 pin modes and never had an issue.Currently using a 46k22 at 32 MHz and both 1602 and 2004 LCDs are fine.

Jay
Ttelmah



Joined: 11 Mar 2010
Posts: 19231

View user's profile Send private message

PostPosted: Sat May 06, 2017 8:25 am     Reply with quote

It's perfectly correct for the driver to set the enable and immediately read. On the PIC an output instruction occurs the penultimate clock cycle in an instruction, while a read occurs at the end of the first clock cycle in an instruction. Even at 48MHz, this gives 416nSec. The quoted response time for the Hitachi 44780 chip from enable to data valid, is 360nSec. If the poster is having problems either there is a problem with the connections (significant capacitance), or the chip involved is a clone that is not actually emulating the Hitachi chip properly, or he is clocking at 64MHz, which only a few chips support, and wasn't considered when the driver was written.
If he had posted code this we could have seen.
If he had told us the LCD, we could have looked at it's data sheet.
Both were asked.
The posters absolute determination to 'not give us data', has been a real annoyance in this thread.
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Sat May 06, 2017 4:33 pm     Reply with quote

Agreed. He has also changed things in the flex_lcd420.c driver with no
explanation given.

1. He changed the line addresses by OR'ing them with 0x80:
Quote:
// These are the line addresses for most 4x20 LCDs.
#define LCD_LINE_1_ADDRESS 0x80
#define LCD_LINE_2_ADDRESS 0xc0
#define LCD_LINE_3_ADDRESS 0x94
#define LCD_LINE_4_ADDRESS 0xd4

These constants are used in the lcd_gotoxy() function. But, that function
already sets bit 7 as shown in the line below:
Code:
lcd_send_byte(0, 0x80 | address);

His changes are unnecessary.

2. He has altered one of the init bytes. He has changed the command in
the first byte to 0x40. If his LCD was the standard Hitachi interface, it
would be the original value of 0x20. Of course, he refused to ever tell
us what LCD he is using, so we can't check if this change is accurate:
Quote:

int8 const LCD_INIT_STRING[4] =
{
0x40 | (lcd_type << 2), // Set mode: 4-bit, 2+ lines, 5x8 dots
0xc, // Display on
1, // Clear display
6 // Increment cursor
};


3. He informs us that his PIC is:
Quote:
Pic 24EP128HG204

I can't find any PIC in that series with "HG" in the part number.
I'm not even sure how it could be a typo.



Then in the CCS lcd.c driver, he has modified the lcd_init() function
by sending a 2nd nibble of 0. All these changes are undocumented,
no reason is given:
Quote:
delay_ms(15);
for(i=1;i<=3;++i)
{
lcd_send_nibble(3);
lcd_send_nibble(0);
delay_ms(5);
}

lcd_send_nibble(2);
lcd_send_nibble(0);
delay_ms(5);


That's why I didn't participate in this thread. It was another thread where
getting any information out of the guy was like pulling hen's teeth. We
just finished one of those and I didn't want to do it again.
Ttelmah



Joined: 11 Mar 2010
Posts: 19231

View user's profile Send private message

PostPosted: Sun May 07, 2017 12:50 am     Reply with quote

I suspect with genetic engineering, "hen's teeth" might actually be easier... Smile
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group