Continue to Site

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.

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

Interfacing LCD with PIC18F2580

Status
Not open for further replies.

Cantafford

Member
Hello,

I'm trying to make a DC motor driver in which I read the motor speed with an encoder, turn it into an digital value with the PIC's internal ADC and then display it on an LCD.

I'm trying to interface the LCD with the PIC for starters I just want the LCD to display "DC Motor driver" before any command is given to the motor. Then once I manage to do this I will continue with the code.

This is the schematic:
jzwie9.png


As you can see I have connected E to logic 1 so I can have access to the LCD, R/W to logic 0 so I can write data on the LCD and RS to logic 0 so bits D4-D7 are interpreted as commands. D4-D7 are connected to the PIC's RC2-RC5.

Here is the code:
Code:
/*
* File:   driver.c
* Author: Paul
*
* Created on October 23, 2015, 11:06 AM
*/

#include <stdio.h>
#include <stdlib.h>
#include "project.h"
#include <plib/adc.h>
#include <plib/xlcd.h>
#include <plib/delays.h>

void init_XLCD(void);              //Initialize LCD display
void DelayFor18TCY( void );        //18 cycles delay
void DelayPORXLCD (void);          // Delay of 15ms
void DelayXLCD (void);             // Delay of 5ms


void main()
{
OSCCON = 0x76; // set internal oscillator to 8Mhz
ADCON1 = 0x0f;
TRISA = 0b11111111;
TRISB = 0b11111111; // portB as input(the switches)
TRISC = 0b11111100; // set portC as output(RC0 = in1, RC1 = in2)

LATCbits.LATC0 = 0; // motor is not running
LATCbits.LATC1 = 0;

init_XLCD();                    //Call the Initialize LCD display function
putrsXLCD("DC Motor");          //Display text
SetDDRamAddr(0x40);            //shift cursor to beginning of second line
putrsXLCD("Driver");      //Display text
while(PORTBbits.RB0 == 1 && PORTBbits.RB0 == 1)

  while(1)
  {

     if(PORTBbits.RB0 == 0 && PORTBbits.RB1 == 1 && PORTBbits.RB2 == 1) // clockwise
     {LATCbits.LATC0 = 1; LATCbits.LATC1 = 0;}


     if(PORTBbits.RB0 == 1 && PORTBbits.RB1 == 0 && PORTBbits.RB2 == 1) // counter clockwise
     {LATCbits.LATC0 = 0; LATCbits.LATC1 = 1;}

     if(PORTBbits.RB2 == 0) // motor stops
     {LATCbits.LATC0 = 1; LATCbits.LATC1 = 1;}
  }

}

void init_XLCD(void)                //Initialize LCD display
{
    OpenXLCD(FOUR_BIT&LINES_5X7);  //configure LCD in 4-bit Data Interface mode
                                     //and 5x7 characters, multiple line display
    while(BusyXLCD());             //Check if the LCD controller is not busy
                                     //before writing some commands?
    WriteCmdXLCD(0x06);            // move cursor right, don?t shift display
    WriteCmdXLCD(0x0C);            //turn display on without cursor

}
void DelayFor18TCY( void )         //18 cycles delay
{
//Delay10TCYx(20);
    Nop( ); Nop( ); Nop( ); Nop( ); // 18 cycle delay
Nop( ); Nop( ); Nop( ); Nop( );
Nop( ); Nop( ); Nop( ); Nop( );
Nop( ); Nop( );
return;
}

void DelayPORXLCD (void)          // Delay of 15ms
{
Delay1KTCYx(30);
}

void DelayXLCD (void)            // Delay of 5ms
{
Delay1KTCYx(10);
}

As you can see I have created and prototyped the functions needed for the LCD(configured it in 4 bit configuration).
Then with these lines of code I'm trying to get some text on the LCD before any command is given to the motor:
Code:
init_XLCD();                    //Call the Initialize LCD display function
putrsXLCD("DC Motor");          //Display text
SetDDRamAddr(0x40);            //shift cursor to beginning of second line
putrsXLCD("Driver");      //Display text
while(PORTBbits.RB0 == 1 && PORTBbits.RB0 == 1)

When I try to simulate the program nothing pops up on the LCD, there is no text on it :(.
I'm guessing that there is a problem with those 4 pins, D4-D7 I just connected them to the ADC and assigned them as inputs in the code. I suppose I have to somehow specify in the program that those pins are connected to the LCD but I don't know how. When I simulate in proteus they are shown as beiing in high-impedance.

Please help me correct this problem if possible so I can display the text on the LCD. Thank you!
 
Hi Paul,

You need to control the LCD 'E' and 'RS' lines. Is that described in the PLIB XLCD documentation?

BTW, where would I find PLIB for XC8 and its documentation (I'm coming up blank)?

Regards, Mike

--< added >-----

Found this file which includes LCD connections on the Microchip website; Library for External LCD module (for C Language)
 
Last edited:
First off...you need to be able to send both instructions and characters to the display, which requires control of the RS (Register Select) pin. There is an initialization process of instructions you'll need to send to the LCD to even make it work. One of the first instructions you'll send to it is 0x28, as this instruction places the LCD in 4-bit interface/2-line display mode. Default is 8-bit interface so the 4-bit interface will not work without the display receiving this instruction.

Also, the data pins on the LCD are sampled on the falling edge of the E pin, not on a high level like you have it tied here. This requires control of the E pin so that you can bring it high for 1uS or longer, then low again. The high to low transition (i.e. the falling edge) is when the LCD's data pins are sampled. R/W can be left as is.

Furthermore...splitting the LCD data pins onto non-nibble aligned port pins makes coding for them loads more difficult. I would place them on either pins 0-3 or 4-7 of the selected port.

For your chip, I would have RA4-RA7 drive the LCD while using RA2 for RS and RA3 for E.

LCD RS - RA2
LCD E - RA3
LCD D4 - RA4
LCD D5 - RA5
LCD D6 - RA6
LCD D7 - RA7


Another trick I do is use a CCP pin in PWM mode to drive the Vee contrast pin. Set it for 25% duty cycle and this should give you the perfect contrast setting. The benefit to doing this is that with minimal code modifications, you can make the contrast adjustment a programmable setting either via a potentiometer on an ADC pin or a push button interface if you wish.

In my code below I will include routines to set up PWM for LCD contrast control so go ahead and connect LCD Vee directly to RC2.

Once all of this is sorted out, you can use this code to drive the LCD -

Code:
#define DATAPORT PORTA
#define RS PORTAbits.RA2
#define E PORTAbits.RA3

/*
* Default _XTAL_FREQ to 4000000 if not defined elsewhere
*/

#ifndef _XTAL_FREQ
#define _XTAL_FREQ 4000000 //or whatever your oscillator frequency is
#endif

#define _PR2_VALUE ((_XTAL_FREQ / 62500) / 4 - 1
#define _PWM_DUTY (_PR2_VALUE + 1) / 4

void enablePulse(void){
    __delay_us(1);               //1uS wait
    E = 1;                            //enable high
    __delay_us(1);               //1uS wait
    E = 0;                            //enable low
    __delay_us(1);               //1uS wait
}

void lcdWrite(unsigned char a){
    for(char i = 0; i < 2; i++){                   //do 2 times
        DATAPORT = (a & 0xF0) + (DATAPORT & 0x0F);            //character or instruction high nibble to display data pins
        enablePulse();             //send enable pulse
        a = a * 16;                  //swap nibbles
    }
    __delay_us(125);           //125uS wait (required)
}

void lcdCommand(unsigned char a){
    RS = 0;                        //select instruction register
    lcdWrite(a);                 //write instruction to LCD
}

void lcdData(unsigned char a){
    RS = 1;                        //select character register
    lcdWrite();                  //write character to LCD
}

void lcdInit(void){
    TRISA = TRISA & 0b00000011;          //RA2-RA7 output
    ADCON1 = ADCON1 | 0b00001110;           //RA0 analog, rest digital I/O
    PR2 = _PR2_VALUE;                         //set PWM frequency - #define sets to 62.5kHz
    T2CON = 0b00000100;                     //enable timer 2
    CCPR1L = _PWM_DUTY;                   //set duty cycle - #define sets to 25%
    CCP1CON = 0b00001100;                 //enable CCP1 in PWM mode
    TRISC = TRISC & 0b11111011;         //RC2 PWM output for LCD contrast
    DATAPORT = (DATAPORT & 0x0F) + 0x30         //wake up message 1
    enablePulse();                   //send enable pulse
    __delay_us(125);               //wait 125uS
    lcdCommand(0x20);           //wake up message 2
    lcdCommand(0x28);           //4-bit interface, 2-line display
    lcdCommand(0x0C);           //display on, no visible cursor
    lcdCommand(0x06);           //cursor shifts to the right
    lcdCommand(0x01);           //clear display
    __delay_ms(2);                  //2mS wait
}

void putch(unsigned char a){
    switch(a){          //if a =
        case '\n':      //new line
            lcdCommand(0xC0);       //cursor to bottom line
            break;
        case '\f':      //form feed
            lcdCommand(0x01);       //clear display
            __delay_ms(2);          //2mS wait
            break;
        default:
            lcdData(a);     //send character if a != '\n' or '\f'
            break;
    }
}

I have also included PWM initialization in the lcdInit(); function. In your main function, call lcdInit(); after the rest of your initialization routines to initialize the LCD display.

Out of all of the functions above, there are only 3 that you will ever use in your main code -

lcdInit(); - call this function to initialize the display
lcdCommand(<INST>); - call this function to send instructions to the display
lcdData(<CHAR>); - call this function to send single characters to the display

The rest of the functions above are dependencies of the 3 main functions. You will never call any of the other functions in main.

Using the functions above, you can use lcdCommand(<INST>); to send instructions to the LCD while using lcdData(<CHAR>); to send single characters to the display. For sending single characters, you can use the ASCII character itself by appending single quotes to the character as such -

lcdData('A');

As shown, this will send the ASCII character A, or "capital A".

This function by itself will only send a single character to the display. If you want to send a string of characters, include the stdio.h file at the top of your code to make the printf function available. I have included a putch function in the code above to allow it to work with the display.

Once you include stdio, the printf function can be used to send a complete string of characters to the display as such -

Code:
    printf("DC Motor Driver");

Be sure to include stdio.h at the top of your code as shown -

Code:
#include <xc.h>
#include <stdio.h>

Hope this helps.
 
Last edited:
Thanks Ian, but all the header files are missing...

Never used the libraries before. Always used my own low level code.

What did they replace the libraries with? One of the peripheral configuration applications?
 
Just goes in your include folder
What did they replace the libraries with? One of the peripheral configuration applications?
No Idea... I, like you, write all my own libraries...

Funny... I bought MikroC pic32 because the libraries were all done.... I have ended up porting all my own libraries to MikcroC....

Still cheaper in the long run..
 

Attachments

  • plib.zip
    112.4 KB · Views: 215
Was it my driver code and suggestions that worked for you?
 
Was it my driver code and suggestions that worked for you?
Yes indeed I have connected the E(nable) pin to one of the PIC's pins and that solved the LCD issue. I have also re-arranged the schematic because as you said it was difficult to have the LCD pins on more than one port as I had it before. But basically that changing of enable pin did the trick. Thanks!
 
I'm trying to make a DC motor driver in which I read the motor speed with an encoder, turn it into an digital value with the PIC's internal ADC and then display it on an LCD.

If you just want RPM and not position, you do not need an encoder, you could use a marker pulse if the encoder has it or just a simple 1/rev opto or hall sensor int the CCP capture pin.
Also saves on proc overhead.
Max.
 
Status
Not open for further replies.

Latest threads

New Articles From Microcontroller Tips

Back
Top