Interfacing a PIC and a 16×2 LCD

So in a recent project I had to implement a 16×2 LCD  on a PIC16F1827, but the system will work on most PIC microcontrollers, with slight changes to the code. For this project I am using MPLAB X v3.40, a free development environment, and a PICKIT 3, which can be bought at a number of stores online.

Setting up the MPLAB project

  1. Start off by loading up MPLAB X, if you don’t have it already, install it from the Microchip website microchip.com/mplab/mplab-x-ide.
  2. Start a new project, by going to File->New Project… or by pressing Ctrl+Shift+Nnew project
  3. The project we want is a standalone project, it should be chosen by default. Next choose the device we want, write in the box PIC16F1827 there should only be one. We want to use the PicKit 3 for the programmer. I am using XC8 as the compiler, this is available from the Microchip to9 stawebsite. Finally choose the name of the project, and where you want to keep it. Then click finish.
  4. It wont look like mush at the moment, but under Projects on the right, there should now be your newly created project, with a drop down, and a set of folders. Something like this: 
  5. To start the project, we need a main.c. So right click on Source Files, go to New->C Main File… then in the Dialog, change the name to main. After pressing okay, it should look like this: 

Connecting the LCD

LCDs have what is known as a parallel connection. This means that we send data 8 bits at a time, rather than serial where it is one at a time. The datasheet for the PIC is found on the microchip website here. The pinout is found on page 4.

PIC pinout

Register Select (RS) pin, this decides which of the two registers that is getting written to. Either the instruction register (what the screen does) or the data register (what is shown on the screen). This is connected to A4 on the PIC.

Read/Write (RW) pin, this decides whether you are writing to or reading from the LCD. This is connected to pin A3.

Enable (E) pin, is to tell the LCD when data needs to be transferred. Pulsing this pin will write or read to the registers the data on the data pins. This is connected to pin  A2.

These pins are defined at the top of the code, to make life easier for us later on.

#define LCD_RS LATAbits.LATA4   //LCD Command/Data Control
#define LCD_RW LATAbits.LATA3   //LCD Read/Write Select
#define LCD_E LATAbits.LATA2    //LCD Enable Line

Data Pins (D0-D7), are the pins that transfer the information between the LCD and the PIC. These are connected to pins B0-B7. So B0 is connected to D0, and so on until B7 is connected to D7.

Vdd and Vss are connected to 5v and GND respectively.

Contrast (Vo) is connected to a 10k pot between the 5v  and  GND.

If the LCD you are using has connections for a backlight, follow the datasheet for instructions, on mine I connect it t0 5v and GND.

The Code

Below is the function I create to send data to the LCD.

#define LCD_CMD 0
#define LCD_TXT 1 

void LCD_DATA (unsigned char data, int type)
{
    __delay_ms(100);   // short delay

    LATB = 0x00;       // reset the register to 0
    LATB = data;       // set B output to the data we want to send
    __delay_ms(1);     // short delay for data to set

    if (type == LCD_CMD)
    {
        LCD_RS = 0;    // command mode
    }
    else
    {
        LCD_RS = 1;    // character/data mode
    }

    LCD_RW = 0;        // start the pulse
    __delay_ms(1);     // small delay
    LCD_E = 1;         // enable LCD data line
    __delay_ms(1);     // small delay
    LCD_E = 0;         // disable LCD data line
    __delay_ms(5);
}

Calling this function in the main, like follows will send data to the LCD. Notice the #define at the top, these are declaring LCD_CMD and LCD_TXT. Basically, when the type is LCD_CMD the LCD is sent into command mode, by setting the RS pin. Equally, sending LCD_TXT will clear the RS pin, putting the LCD in character mode.

The information on the data pins will then get written to the LCD, by clearing RW. To actually tell the LCD that it needs to be sent new instructions the enable pin needs to be pulsed. Once this happens, the screen should be updated with the new information.

#include <stdio.h>
#include <stdlib.h>
#include <xc.h>

#define _XTAL_FREQ 500000

#define LCD_RS LATAbits.LATA4   //LCD Command/Data Control
#define LCD_RW LATAbits.LATA3   //LCD Read/Write Select
#define LCD_E LATAbits.LATA2    //LCD Enable Line

#define LCD_CMD 0
#define LCD_TXT 1 

void LCD_DATA (unsigned char data, int type)
{
    __delay_ms(100);   // short delay

    LATB = 0x00;       // reset the register to 0
    LATB = data;       // set B output to the data we want to send
    __delay_ms(1);     // short delay for data to set

    if (type == LCD_CMD)
    {
        LCD_RS = 0;    // command mode
    }
    else
    {
        LCD_RS = 1;    // character/data mode
    }

    LCD_RW = 0;        // start the pulse
    __delay_ms(1);     // small delay
    LCD_E = 1;         // enable LCD data line
    __delay_ms(1);     // small delay
    LCD_E = 0;         // disable LCD data line
    __delay_ms(5);
}


int main(int argc, char** argv) {
 
    // write "hello world!" on the first line
    LCD_DATA('h', LCD_TXT);
    LCD_DATA('e', LCD_TXT);
    LCD_DATA('l', LCD_TXT);
    LCD_DATA('l', LCD_TXT);
    LCD_DATA('o', LCD_TXT);
    LCD_DATA(' ', LCD_TXT);
    LCD_DATA('w', LCD_TXT);
    LCD_DATA('o', LCD_TXT);
    LCD_DATA('r', LCD_TXT);
    LCD_DATA('l', LCD_TXT);
    LCD_DATA('d', LCD_TXT);
    LCD_DATA('!', LCD_TXT);
 
    while (1)
    {
 
    }
 
 return (EXIT_SUCCESS);
}

Current Sensing: High Side vs. Low Side

Occasionally, you will be designing an electronic project, and there will be a need to to measure the current being drawn by a particular section, or  even the whole thing. When designing, prototyping, or even testing the design you can use a calibrated multimeter. In the field though, or inside a real product, how can you monitor current.

A very popular way is to use a very low value power resistor in series with the load you want to measure. As current flows through it it will induce an e.m.f (voltage) across the resistor. This resistor voltage can then be measured by an ADC in a microcontroller. The value will be linearly proportional to the current running through it. Using Ohm’s law you can deduce that the voltage across the the resistor is equal to the currentresistance. As the resistor may be slightly off, the device might need calibration.

An issue with this though, you want the minimum voltage drop possible across the resistor. This reduces power loss, and minimises the effect you will have on the load. For this reason a very small resistor needs to be used. There are plenty of resistors out there for this purpose, known as shunt or sense resistors. This tiny voltage could be as small as 0.1v, or maybe even lower, way too small for a standard ADC to pick up reliably. For this reason There is a need for an amplifier, to multiply this voltage by 20 or 50 times. This enables the swing to be measured across the range of the ADC. So if you have a 5v ADC, and the maximum voltage across the resistor will be 0.1v, the amplifier will need to have a gain of 50. There are two main categories of current sensors like this, High side and Low side.

schematics-project-1

The above image shows the basic configuration of these two types of measurement. The difference is based off on the location of the sense resistor. Low side sensing is between load and ground, with high side sensing between power and the load. It shouldn’t make much difference, the voltage across the Sense resistor will always be the same.

One reason for not using the the low side method is for the fact it is based off the ground reference. If anything between the power and the high side of the load is shorted, the current sensor wont pick it up. It is just one thing thing that you can’t then implement into your design.

Also be careful when choosing the amplifier and the resistors you intend to use. There are many amplifiers on the market designed for this specific purpose. The TSC101 is an amplifier I recently included in a project, for this exact purpose. A high side current sensor, with a precision trimmed preset gain of either 20, 50, or 100. Adding in a laser precision trimmed power resistor to this, and there is an output for a microcontroller, a very simple current sensing application. for less than £2 in your application.