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!