4-bit Pic24 Alphanumeric LCD library

Looking all over the internet, I could not find for the life of me any existing Alphanumeric LCD library for the C30 compiler – so I made one, which requires 6 pins to operate. It only has 3 commands; lcd_print, lcd_command and lcd_init. For printing anything more complex than a string, the standard library can be included and sprintf is used to format a temporary string to be sent the LCD’s way. Files here are tested on a PIC24FJ64GB002, MPLAB X 1.10, C30 3.30c.

An example main.c file – Flashes an LED while displaying a switch position and ADC reading on an LCD:

#include "setup.h"
#include "lcd.h"
#include <stdio.h>

// Configuration setup
_CONFIG1( FWDTEN_OFF & GWRP_OFF & GCP_OFF & JTAGEN_OFF & ICS_PGx3 )  
_CONFIG2( FCKSM_CSDCMD & OSCIOFNC_ON & POSCMOD_HS & FNOSC_FRCPLL & I2C1SEL_PRI & PLL96MHZ_ON & PLLDIV_NODIV & IESO_OFF & IOL1WAY_OFF );  
_CONFIG3( SOSCSEL_IO );

int main() {  
    // Set the LED pin to an output
    TRISBbits.TRISB7 = 0;

    // Set the switch pin to input
    TRISBbits.TRISB8 = 1;

    lcd_init();

    //Initialize ADC

    AD1PCFGbits.PCFG9 = 0; // Enable analog pin 9

    AD1CON3bits.ADCS = 0b00111111; // Set ADC clock to 64 x Tcy
    AD1CON1bits.SSRC = 0b111; // Auto-conversion on
    AD1CON3bits.SAMC = 0b00011; // Time bits = 3 TAD

    AD1CHS = 9; // Use positive input 9 on channel 0, relative to Avss

    AD1CON1bits.ADON = 1; //Turn on ADC

    char final[32];

    while(1) {
        lcd_command( LINE1 ); // Print out the switch status
        lcd_print( "Switch: " );
        lcd_print( (PORTBbits.RB8 == 0)? "On  " : "Off  " );

        lcd_command( LINE2 ); //Print out the voltage on A9
        AD1CON1bits.SAMP = 1;
        while( !AD1CON1bits.DONE );
        float voltage = ((float)ADC1BUF0 * 3.3f)/1024.0f;
        sprintf( final, "Voltage: %3.2f  ", voltage );
        lcd_print( final );

        LATBbits.LATB7 = ~LATBbits.LATB7; //Toggle an LED

        __delay_ms( 100 );
    }
}

The library (and the example main file given) needs a setup.h file to be declared with some includes. I didn’t put the device configuration bits in here, otherwise there’s a linker error:

// Includes some commonly used libraries, sets device configuration,
// defines some useful things.

#ifndef SETUP_H
#define SETUP_H

#include "p24Fxxxx.h" // This header will choose the right device header

#define FCY 16000000UL // Running at 16 MIPS = Fosc/2

#include <PIC24F_plib.h>
#include <libpic30.h>

// Define a C++-like boolean - makes things more readable in some cases
typedef unsigned bool;  
#define true 1;
#define false 0;

#endif

Here’s your lcd.h code:

// (c) Seb Holzapfel, 2012
// A 6-pin Alphanumeric LCD interface library

#ifndef LCD_H
#define LCD_H

#include "setup.h"

// Change these to the pins you want
#define LCD_RS_PIN LATBbits.LATB0
#define LCD_E_PIN  LATBbits.LATB1
#define LCD_D4_PIN LATBbits.LATB2
#define LCD_D5_PIN LATBbits.LATB3
#define LCD_D6_PIN LATBbits.LATB4
#define LCD_D7_PIN LATBbits.LATB5

#define LCD_RS_TRIS TRISBbits.TRISB0
#define LCD_E_TRIS  TRISBbits.TRISB1
#define LCD_D4_TRIS TRISBbits.TRISB2
#define LCD_D5_TRIS TRISBbits.TRISB3
#define LCD_D6_TRIS TRISBbits.TRISB4
#define LCD_D7_TRIS TRISBbits.TRISB5

// LCD Command set
#define LINE1       0x80    // Set display to line 1 character 0
#define LINE2           0xC0    // Set display to line 2 character 0
#define FUNCTION_SET    0x28    // 4 bits, 2 lines, 5x7 Font
#define DISP_ON         0x0C    // Display on
#define DISP_ON_C       0x0E    // Display on, Cursor on
#define DISP_ON_B       0x0F    // Display on, Cursor on, Blink cursor
#define DISP_OFF        0x08    // Display off
#define DISP_CLR        0x01    // Clear the Display
#define ENTRY_INC       0x06    // Increment-mode, display shift OFF
#define ENTRY_INC_S     0x07    // Increment-mode, display shift ON
#define ENTRY_DEC       0x04    // Decrement-mode, display shift OFF
#define ENTRY_DEC_S     0x05    // Decrement-mode, display shift ON
#define DD_RAM_ADDR     0x80    // Least Significant 7-bit are for address

#define LCD_DATA_MODE 1
#define LCD_COMMAND_MODE 0

void lcd_command( unsigned command );

void lcd_print( char text[] );

void lcd_init();

#endif

And the lcd.c file:

// (c) Seb Holzapfel, 2012
// A 6-pin Alphanumeric LCD interface library

#include "lcd.h"
#include <string.h>

inline void lcd_toggle_enable() {  
    LCD_E_PIN = 1;
    LCD_E_PIN = 0;
}

void lcd_mode( unsigned mode ) {  
    LCD_RS_PIN = mode;
    __delay_us( 100 );
}

void lcd_clock_nibble( unsigned char nibble ) {

    // Set data pins according to bitmask
    LCD_D4_PIN = ((nibble & 0b1   ) > 0);
    LCD_D5_PIN = ((nibble & 0b10  ) > 0);
    LCD_D6_PIN = ((nibble & 0b100 ) > 0);
    LCD_D7_PIN = ((nibble & 0b1000) > 0);

    // Clock them
    lcd_toggle_enable();
}

void lcd_clock_byte( unsigned char byte ) {  
    lcd_clock_nibble(byte >> 4);
    __delay_us( 50 );
    lcd_clock_nibble(byte);
    __delay_us( 50 );
}

void lcd_print( char text[] ) {  
    // Incrementing pointers and checking for null doesn't work for some reason
    unsigned l = strlen(text); // So I used the inbuild length function.

    lcd_mode( LCD_DATA_MODE );

    unsigned i; // Declaration outside because we're not in C99 mode.
    for( i = 0; i != l; ++i ) {
        lcd_clock_byte( text[i] );
    }
}

void lcd_command( unsigned command ) {  
    lcd_mode( LCD_COMMAND_MODE );
    lcd_clock_byte( command );
    __delay_ms(2); // Wait a bit, some commands take a while
}

void lcd_init() {  
    __delay_ms( 20 );

    // Set all LCD pins to output mode
    LCD_RS_TRIS = 0;
    LCD_E_TRIS  = 0;
    LCD_D4_TRIS = 0;
    LCD_D5_TRIS = 0;
    LCD_D6_TRIS = 0;
    LCD_D7_TRIS = 0;

    LCD_E_PIN = 0;

    // Enter command mode
    lcd_mode( LCD_COMMAND_MODE );

    // Initialisation sequence

    // Clock 0x03 twice
    lcd_clock_nibble( 0x03 );
    __delay_ms( 5 );
    lcd_clock_nibble( 0x03 );
    __delay_us( 200 );

    // Enter 4-bit mode
    lcd_clock_nibble( 0x03 );
    __delay_us( 200 );
    lcd_clock_nibble( 0x02 );
    __delay_ms( 5 );

    // Set up some defaults
    lcd_clock_byte( FUNCTION_SET );
    lcd_clock_byte( DISP_OFF );
    lcd_clock_byte( DISP_ON );
    lcd_clock_byte( ENTRY_INC );
    lcd_clock_byte( DISP_CLR );
    lcd_clock_byte( LINE1 );

    __delay_ms( 5 );
}

Use my code for whatever you like (hobby use, commercial use, whatever) – If you use it for something interesting, be sure to let me know!

comments powered by Disqus