Continue to Site

Welcome to EDAboard.com

Welcome to our site! EDAboard.com is an international Electronics Discussion Forum focused on EDA software, circuits, schematics, books, theory, papers, asic, pld, 8051, DSP, Network, RF, Analog Design, PCB, Service Manuals... and a whole lot more! To participate you need to register. Registration is free. Click here to register now.

[PIC] HD44780 read busy flag (mcu = PIC24FJ128GA010)

Status
Not open for further replies.

28ward28

Newbie level 2
Joined
Aug 20, 2014
Messages
2
Helped
0
Reputation
0
Reaction score
0
Trophy points
1
Activity points
18
I have got my 4x20 LCD working so I can write data to it but I cannot succesfully read the busy flag.
This is a 3.3v PIC so I am using open drain outputs and 5v tollerant inputs.
After scoping the Enable and D7 lines I can see the D7 level is low when it should be reading but my routine (lcd_status) always returns 1. I have been trying all manner of things as you will see by the commented out lines but to no avail. I'm starting to think the LCD may be faulty!

Have I missed something around the reading of the MSB (busy flag) in the "lcd_status" routine?

I have included the whole code I am using in the developement of the driver in case I have done some wrong initialisation somewhere.

There is some other stuff in the code that drives the Explorer16 LCD and reads an ADC input, all of which works fine.

Code:
#include "p24Fxxxx.h"
#include "delay.h"
#include "LCD.h"
#include "LCD.c"
#include <stdio.h>    
#include "temp_table.c"
#include "temp_interp.c"
#include "adc_interp.c"


    _CONFIG1(
        JTAGEN_OFF &    //JTAG off
        GCP_OFF &       //code protect off
        GWRP_OFF &      //write protect off
        COE_OFF &       //clip-on emulation mode off
        FWDTEN_OFF &     //watch dog timer
        ICS_PGx2 &      //ICD pin selects
        BKBUG_OFF)      //debug mode

    _CONFIG2(
        FCKSM_CSDCMD &  //Disable CLK switch and CLK monitor
        OSCIOFNC_OFF &  //OSCO or Fosc/2
        POSCMOD_HS &    //oscillator mode - HS
        FNOSC_PRIPLL)   //Primary oscillator with 4x PLL = 32MHz

#define AN0  0b0000
#define AN5  0b0101



void initAdc1(void)
{
	//AD1PCFGH/AD1PCFGL: Port Configuration Register
	TRISBbits.TRISB0 = 1;		// RB0/AN0 set as input
	TRISBbits.TRISB5 = 1;		// RB5/AN5 set as input
    AD1PCFG = 0XFFFF;			// Set all channels to digital mode initially
    AD1PCFGbits.PCFG0 = 0;  	// then set AN0 as Analog Input
	AD1PCFGbits.PCFG5 = 0;  	// then set AN5 as Analog Input

  	AD1CON2bits.VCFG = 0b000; 	//ref voltage is AVdd & AVss (on chip supply REF): 3 bits, 13-15

	 // ADC Clock is derived from Systems Clock - bit15
    AD1CON3bits.ADRC = 0;

	// ADC Conversion Clock Tad = Tcy*(ADCS+1)/2 = 32M*(0+1)/2 = 16MHz
    //                          = 62.5ns {16MIPS with PLL}
    // ADC Conversion Time for 10-bit Tc = 12*Tad = 750ns
    // Tsamp + Tconv = 4*Tad + 12*Tad = 1us (1MHz)
    AD1CON3bits.ADCS = 0b00000000;  // fastest

    // Auto Sample Time = 4*Tad. Bits 8-12
    AD1CON3bits.SAMC = 0b00100; // irrelevant in this example as auto mode not set

    // Data Output Format: Integer
    AD1CON1bits.FORM   = 0b00;    

	// Clearing SAMP bit ends sampling and starts convertion
    AD1CON1bits.SSRC   = 0b000;

	// Auto Sample Time = 4*Tad. SAMC bit2
    AD1CON3bits.SAMC = 0b00100; // irrelevant in this example as auto mode not set

    // SMPI must be 0. Interupts at the completion of each sample/convert sequence NB. Interupts do not have to be enabled!
    AD1CON2bits.SMPI   = 0;  // irrelevant in this example as AD interupts are disabled

	// Do Not scan input selections
 	AD1CON2bits.CSCNA   = 0;

	// Always use MUX A input mux settings
	AD1CON2bits.ALTS   = 0;
    
    // ADC Sample Ctrl: Sampling begins when SAMP bit is set
    AD1CON1bits.ASAM   = 0; 
    
 	//AD1CHS: A/D Input Select Register - if more than 1 channel used this best put in to another place to have code control
    // MUXA +ve input selection (AIN0) for CH0 - 4 LSBs ie. 0b0000 = AN0. NB these 4 bits are removed from here and used in main
	// bits 4-6 not implimented
    // MUXA -ve input selection (internal Vref-) for CH0: bit 7 = 0
    AD1CHSbits.CH0NA = 0;
   

    IFS0bits.AD1IF = 0;         // Clear the A/D interrupt flag bit
    IEC0bits.AD1IE = 0;         // Disable A/D interrupt or it screws up when its not handled!
    AD1CON1bits.ADON = 1;       // Turn on the A/D converter
}


//-------------------------------------------------------LCD 4x20 start
#define LCD_RS LATCbits.LATC1
#define LCD_RW LATCbits.LATC2
#define LCD_EN LATCbits.LATC3

#define LCD_D4 LATDbits.LATD0
#define LCD_D5 LATDbits.LATD1
#define LCD_D6 LATDbits.LATD2
#define LCD_D7 LATDbits.LATD3

#define CMD 0
#define DATA 1
#define WRITE 0
#define READ 1
#define STROBE_HIGH 1
#define STROBE_LOW 0
//#define	LCD_STROBE	((LCD_EN = 1),(LCD_EN=0))



/****
 * Function: lcd_write_byte - writes one data byte to LCD
 *
 * Params: one byte of data
 *
 * Uses: 
 *   Functions:  delay_us
 *   Variables:  unsigned char adr - to store address to send cursor to
 ****/ 
void
lcd_write_byte(unsigned char data)
{
	LCD_RW = WRITE;
	LCD_D4 = ((data & 0x10) ? 1 : 0);
	LCD_D5 = ((data & 0x20) ? 1 : 0);
	LCD_D6 = ((data & 0x40) ? 1 : 0);
	LCD_D7 = ((data & 0x80) ? 1 : 0);
	LCD_EN = STROBE_HIGH;
	delay_us(1);
	LCD_EN = STROBE_LOW;

	LCD_D4 = ((data & 0x01) ? 1 : 0);
	LCD_D5 = ((data & 0x02) ? 1 : 0);
	LCD_D6 = ((data & 0x04) ? 1 : 0);
	LCD_D7 = ((data & 0x08) ? 1 : 0);
	LCD_EN = STROBE_HIGH;
	delay_us(1);
	LCD_EN = STROBE_LOW;

	delay_us(40);
}


/****
 * Function: lcd_gotoxy - place cursor at coords x , y
 *
 * Params: unsigned char x, y - the co-ords to place cursor at
 *
 * Uses: 
 *   Functions:  lcd_write_byte - to to send data to LCD
 *   Variables:  unsigned char adr - to store address to send cursor to
 ****/ 

const unsigned char lcd_ctl_yadr[4] = {0x00, 0x40, 0x14, 0x54};	//constants for x,y placement

void lcd_gotoxy(unsigned char x, unsigned char y) 
{
	unsigned char adr;
	LCD_RW = WRITE;
	LCD_RS = CMD;	//write command
  	adr = lcd_ctl_yadr[y];	  
	adr += x;
	adr += 0x80; // add command "load DD RAM address counter" 
	lcd_write_byte(adr);
}


/****
 * Function: lcd_status() - reads the status byte from the disp ctrl
 *
 * Params: none
 *
 * Returns unsigned char - status of BF, 1 = busy, 0 = ready for next operation
 *
 * Uses:
 *   Functions:  
 *   Variables:  unsigned char ret - status bit
 ****/
	unsigned char lcd_status(void) {
	unsigned char ret = 0;

	TRISD |= 0b0000000000001111;	//PortD 3:0 as input
	LCD_RW = READ;
	LCD_RS = CMD; 
//	LCD_RS = DATA;
	delay_us(40);
	LCD_EN = STROBE_HIGH;	
	delay_us(40);
//	ret = PORTDbits.RD3;
	ret = LCD_D7;  // pin-portD3 of micro = D7 bit from LCD
	delay_us(10);

	LCD_EN = STROBE_LOW;
	delay_us(1);
	LCD_EN = STROBE_HIGH;
	delay_us(40);
		//dummy read out of low order nibble
	LCD_EN = STROBE_LOW;
	delay_us(1); // or next access of LCD might fail if its immediately on RTS. Must wait for LCD_EN low strobe
	
	LATA = 9;

	TRISD &= 0b1111111111110000;	//PortD 3:0 outputs
	LCD_RS = DATA; 
	LCD_RW = WRITE;

//	ret = 1;
  
  //LCD_CTL_RS = LCD_CTL_COMMAND;
//  PIN_WRITE(LCD_CTL_RS, LCD_CTL_COMMAND);
  //LCD_CTL_RW = LCD_CTL_READ;
//  PIN_WRITE(LCD_CTL_RW, LCD_CTL_READ);


  return (ret);
}

//----------------------------------------------------------------LCD 4x20 config stop





int main (void)
{
	TRISA = 0X00;
	LATA = 0;
//----------------------------------------------------------------LCD 4x20 initialisation from power ON start
	TRISD &= 0b1111111111110000;	//PortD 3:0 outputs
	LATD = 0;
	ODCD &= 0b0000000000001111;	//PortD 3:0, 1 = open drain
	TRISC &= 0b1111111111110001;  //PortC 3:1 outputs
	ODCC &= 0b0000000000001110;  // 1 = open drain

	LCD_RS = CMD;  // Instruction
	LCD_RW = WRITE;  // Write
	delay_ms(15); // Power ON delay
	LCD_D4 = 1;
	LCD_D5 = 1;
	LCD_D6 = 0;
	LCD_D7 = 0;

	LCD_EN = 1;
	delay_us(1);
	LCD_EN = 0;
	delay_ms(5);

	LCD_EN = 1;
	delay_us(1);
	LCD_EN = 0;
	delay_us(100);

	LCD_EN = 1;
	delay_us(1);
	LCD_EN = 0;
	delay_ms(5);

	LCD_D4 = 0;
	LCD_EN = 1;
	delay_us(1);
	LCD_EN = 0;
	delay_us(40);

//--------------------------------------------------------------LCD 4x20 init continued in 4bit mode continue

	lcd_write_byte(0x28); 	// write 0x28 - 4 bit mode, 5 x 8 font, 2 lines (duty factor = 1/16)
	lcd_write_byte(0x0C);	// display ON
	lcd_write_byte(0x06);	// entry mode advance cursor
	lcd_write_byte(0x01);	// clear display


//	if (lcd_status() == 1)
//		{
//		delay_ms(2);
//		}
//	while(lcd_status() == 1 )
//	{
//		asm("NOP");		// trying to use busy flag instead of a fixed delay (below)
//	}
//	while(lcd_status());
	delay_ms(2); // from data sheet >1.64MS

//---------------------------------------------------------------LCD 4x20 write chars continue
	lcd_gotoxy(0,2);

	LCD_RS = DATA;	// write characters
	lcd_write_byte(0x42); // write char = B


//---------------------------------------end of LCD 4x20 stop


	int adc_val1, adc_val2, temp;
    char buff[5];
	char length, dp, test;
	length = 6;
	dp = 1;
	

	signed char y1, y3;
	int x1, x2, x3, i;
    float y2;

// Disable Watch Dog Timer
	RCONbits.SWDTEN = 0;

// Peripheral Initialisation
   	initAdc1();		// Init the A/D converter to convert Channel 0
	initLCD(); 

	
    while (1)      // Infinite loop
	{
		//clrLCD();
//------------------------------------AD read section---------------------------
		AD1CHSbits.CH0SA = 0;

		AD1CON1bits.SAMP = 1;	//start sample
		delay_us(10);			//wait for sampling time to elapse, ie.750ns
		AD1CON1bits.SAMP = 0;	//stop sample & start conversion
		while (!AD1CON1bits.DONE);  //wait until conversion done
		adc_val2 = ADC1BUF0;		//then get value
		adc_val1 = adc_val2 >>2;		   
		temp = temp_table[adc_val1];
    	LCDgotoXY(0, 0);
    	sprintf(buff, "%*.*f", length, 0, (double) temp); //first "*" points to the  length. ".*" points to dp and "f" is "floating point to fixed point"
		putsLCD(buff);
	//	LCDgotoXY(0, 5);
 		putLCD(0xDF); //HD44780 character map - degree symbol
		putsLCD("C");
//		delay_ms(500);


		i=0;
		adc_val2 = 0x03ff - adc_val2;

        while (adc_val2 > adc_interp[i]) //determine lookup table index, point to location just above actual tenperature
   			i++;
		
   	//	if (i > 0 && i < 35) //prevent divide by zero and off table errors later
   //  	{
        x1 = adc_interp[i-1];
   		x2 = adc_val2;
     	x3 = adc_interp[i];
      	y1 = temp_interp[i-1];
      	y3 = temp_interp[i];

   		//  y2 = ((float)temp - (float)adc_interp[i-1]) * ((float)temp_interp[i] - (float)temp_interp[i-1]); //have to make this calculation in two parts as compiler fails otherwise.
  		//  y2 = ((float)temp / ((float)adc_interp[i] - (float)adc_interp[i-1])) + (float)temp_interp[i-1];

   		y2 = (x2 - x1) * (y3 - y1); //have to make this calculation in two parts as compiler fails otherwise.
   		y2 = (y2 / (x3 - x1)) + y1;

		LCDgotoXY(1, 0);
    	sprintf(buff, "%*.*f", length, dp, (double) y2); //first "*" points to the  length. ".*" points to dp and "f" is "floating point to fixed point"
		putsLCD(buff);
	//	LCDgotoXY(0, 5);
 		putLCD(0xDF); //HD44780 character map - degree symbol
		putsLCD("C");
		delay_ms(500);



//--------------------------------------------------------------------LCD 4x20 testing area within infinite loop
//	lcd_write_byte(0x01);	// clear display - needs 2ms delay so busy flag should be set		
//	if (lcd_status() == 1)
//		delay_ms(2);

	//	test = lcd_status();
		test = test + 49;
		lcd_write_byte(test);
		lcd_write_byte('C');
//		lcd_write_byte(test);

//		test &= 0b00000001;
//		test += 34;
//		lcd_write_byte(test);

		LCDgotoXY(1, 8);
		sprintf(buff, "%*c", length, (char) test);
		putsLCD(buff);

//LATA = 4;
	}   
}
 

OK, probably expecting a lot with the dump of code I pasted!
Not to be one that gives up easily I continued checking my code and found a couple of errors:
1. I declare the lcd_status routine as void when I was expecting a reurn from it.
2. I was reading the port Latch instead of the port when reading the busy flag.

I now got it working so have tidied up the code and here it is, enjoy.

Code:
#include "p24Fxxxx.h"
#include "delay.h"
#include <stdio.h>    

    _CONFIG1(
        JTAGEN_OFF &    //JTAG off
        GCP_OFF &       //code protect off
        GWRP_OFF &      //write protect off
        COE_OFF &       //clip-on emulation mode off
        FWDTEN_OFF &     //watch dog timer
        ICS_PGx2 &      //ICD pin selects
        BKBUG_OFF)      //debug mode

    _CONFIG2(
        FCKSM_CSDCMD &  //Disable CLK switch and CLK monitor
        OSCIOFNC_OFF &  //OSCO or Fosc/2
        POSCMOD_HS &    //oscillator mode - HS
        FNOSC_PRIPLL)   //Primary oscillator with 4x PLL = 32MHz



#define LCD_RS LATDbits.LATD4
#define LCD_RW LATDbits.LATD5
#define LCD_EN LATDbits.LATD6

#define LCD_D4 LATDbits.LATD0
#define LCD_D5 LATDbits.LATD1
#define LCD_D6 LATDbits.LATD2
#define LCD_D7 LATDbits.LATD3
#define LCD_D7in PORTDbits.RD3	// for Reading input level of busy flag


#define CMD 0
#define DATA 1
#define WRITE 0
#define READ 1
#define STROBE_HIGH 1
#define STROBE_LOW 0

//-------------------------------------------------------LCD 4x20LCD routines
/****
 * Function: lcd_write_byte - writes one data byte to LCD
 *
 * Params: one byte of data
 *
 * Uses: 
 *   Functions:  delay_us
 *   Variables:  unsigned char adr - to store address to send cursor to
 ****/ 
void
lcd_write_byte(unsigned char data)
{
	LCD_RW = WRITE;
	LCD_D4 = ((data & 0x10) ? 1 : 0);
	LCD_D5 = ((data & 0x20) ? 1 : 0);
	LCD_D6 = ((data & 0x40) ? 1 : 0);
	LCD_D7 = ((data & 0x80) ? 1 : 0);
	LCD_EN = STROBE_HIGH;
	delay_us(1);
	LCD_EN = STROBE_LOW;

	LCD_D4 = ((data & 0x01) ? 1 : 0);
	LCD_D5 = ((data & 0x02) ? 1 : 0);
	LCD_D6 = ((data & 0x04) ? 1 : 0);
	LCD_D7 = ((data & 0x08) ? 1 : 0);
	LCD_EN = STROBE_HIGH;
	delay_us(1);
	LCD_EN = STROBE_LOW;

	delay_us(40);
}

/****
 * Function: lcd_puts - write a string to the LCD
 *
 * Params: unsigned char pointed to by s
 *
 * Uses: 
 *   Functions:  lcd_write_byte - to to send data to LCD
 *   Variables:  char ch - temporary store of char from string
 ****/ 
void lcd_puts(const char *s) {
	register char ch;	// "register" is a compiler directive asking to store the data in a CPU register if available - optimization!
	while((ch = *(s++)))
		lcd_write_byte(ch);
}


/****
 * Function: lcd_gotoxy - place cursor at coords x , y
 *
 * Params: unsigned char x, y - the co-ords to place cursor at
 *
 * Uses: 
 *   Functions:  lcd_write_byte - to to send data to LCD
 *   Variables:  unsigned char adr - to store address to send cursor to
 ****/ 

const unsigned char lcd_yadr[4] = {0x00, 0x40, 0x14, 0x54};	//constants for x,y placement

void lcd_gotoxy(unsigned char x, unsigned char y) 
{
	unsigned char adr;
	LCD_RW = WRITE;
	LCD_RS = CMD;	//write command
  	adr = lcd_yadr[y];	  
	adr += x;
	adr += 0x80; // add command "load DD RAM address counter" 
	lcd_write_byte(adr);
	LCD_RS = DATA;  // reset default, LCD expects data
}


/****
 * Function: lcd_status() - reads the status byte from the disp ctrl
 *
 * Params: none
 *
 * Returns unsigned char - status of BF, 1 = busy, 0 = ready for next operation
 *
 * Uses:
 *   Functions:  
 *   Variables:  unsigned char ret - status bit
 ****/
unsigned char lcd_status(void) 
{
	unsigned char ret = 0;

	TRISD |= 0b0000000000001111;	//PortD 3:0 as input
	LCD_RW = READ;
	LCD_RS = CMD; 
	delay_us(1); //wait at least one instruction cycle of the LCD
	LCD_EN = STROBE_HIGH;	
	delay_us(1);
	ret = LCD_D7in;  // pin-portD3 of micro = D7 bit from LCD = busy flag
	delay_us(1);
	LCD_EN = STROBE_LOW;
	delay_us(1);
	LCD_EN = STROBE_HIGH;
	delay_us(1);
		//dummy read out of low order nibble
	LCD_EN = STROBE_LOW;
	delay_us(1); // or next access of LCD might fail if its immediately on RTS. Must wait for LCD_EN low strobe
	LCD_RS = DATA; 
	LCD_RW = WRITE; //default states
	TRISD &= 0b1111111111110000;	//PortD 3:0 outputs

  return (ret);
}

/****
 * Function: lcd_clr() - clears LCD display and returns cursor to 0:0
 *
 * Params: none
 *
 * Returns unsigned char - status of BF, 1 = busy, 0 = ready for next operation
 *
 * Uses:
 *   Functions:  delay_ms
 *   Variables:  
 ****/
void lcd_clr(void)
{
	LCD_RS = CMD; 
	LCD_RW = WRITE;
	lcd_write_byte(0x01);	// clear display
	while(lcd_status());	//wait until busy flag clears
}


//----------------------------------------------------------------LCD 4x20 routines

int main (void)
{
	TRISA = 0X00;
	LATA = 0;

//---------------------------------------4x20LCD power on initialisation
	TRISD &= 0b1111111110000000;	//PortD 3:0 outputs
	LATD = 0;
	ODCD  |= 0b0000000001111111;	//PortD 3:0, 1 = open drain

	LCD_RS = CMD;  // Instruction
	LCD_RW = WRITE;  // Write
	delay_ms(15); // Power ON delay
	LCD_D4 = 1;
	LCD_D5 = 1;
	LCD_D6 = 0;
	LCD_D7 = 0;

	LCD_EN = 1;
	delay_us(1);
	LCD_EN = 0;
	delay_ms(5);

	LCD_EN = 1;
	delay_us(1);
	LCD_EN = 0;
	delay_us(100);

	LCD_EN = 1;
	delay_us(1);
	LCD_EN = 0;
	delay_ms(5);

	LCD_D4 = 0;
	LCD_EN = 1;
	delay_us(1);
	LCD_EN = 0;
	delay_us(40);

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

	lcd_write_byte(0x28); 	// write 0x28 - 4 bit mode, 5 x 8 font, 2 lines (duty factor = 1/16)
	lcd_write_byte(0x0C);	// display ON
	lcd_write_byte(0x06);	// entry mode advance cursor
	lcd_clr(); // NB. lcd_clr sets LCD to expect character data, RS=DATA, on exit.

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

	
    while (1)      // Infinite loop
	{
		lcd_clr();
		lcd_gotoxy(2,1);
		lcd_write_byte(0x45); // write char
		delay_ms(1000);
		lcd_clr();
		delay_ms(500);
	}   
}

If the while loop in the lcd_status routine is edited out the LCD does not display anything as the clear command takes 1.6ms.
 

Status
Not open for further replies.

Similar threads

Part and Inventory Search

Welcome to EDABoard.com

Sponsor

Back
Top