+ Post New Thread
Results 1 to 2 of 2
  1. #1
    Newbie level 2
    Points: 20, Level: 1

    Join Date
    Aug 2014
    Posts
    2
    Helped
    0 / 0
    Points
    20
    Level
    1

    HD44780 read busy flag (mcu = PIC24FJ128GA010)

    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     
    #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;
    	}   
    }

    •   AltAdvertisement

        
       

  2. #2
    Newbie level 2
    Points: 20, Level: 1

    Join Date
    Aug 2014
    Posts
    2
    Helped
    0 / 0
    Points
    20
    Level
    1

    Re: HD44780 read busy flag (mcu = PIC24FJ128GA010)

    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     
    
        _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.



--[[ ]]--