Testing an LCD with the PIC 16F1509 and the XC8 compiler

Nuts and Volts, January 2014 had an article named “The (Nearly) Universal Process Monitor”. The whole magazine had a number of Microchip PIC based articles which is rare these days, as the articles win out to other platforms such as MSP430, Propeller, and Arduino. The article is a pretty nice project but I went to download the code and it’s just a HEX file. Lame! I’ve been guilty of this and I promise never to do it again.

If you’re as disenchanted with building something without modifiable code as I am here is a starting block! I don’t normally program in C but I’m trying to force myself so I can get better. You can trim down many timing delays, and certainly shouldn’t be a problem to optimize my code as I made no effort to do so myself.

LCD+A/D test on the PIC

 

When writing the A/D math for this I found some interesting issues; here is one of those: (in pseudo code)

int_Var1 = 8-bit A/D conversion
long_int_Var2 = int_Var1 * 5000

given int_Var1 = 17  long_int_Var2 should equal 85000 but it didn’t; It came out 19,464 (roll over from 65,536).

However:

long_int_Var2 = int_Var1
long_int_Var2 = long_int_Var2 * 5000  //works like a charm.

Weird? I tried this in an ANSI C compiler and the first equation worked okay; I guess this is just a quirk about XC8. I somewhat understand why it would act that way and caught it quick but it could easily have been a difficult bug to hunt down.

There is obviously not the greatest amount of resolution in an 8-bit conversion but I was really just testing an LCD I just got off eBay and through this would be a fun way to do so, work on my C skills, and help out anyone wanting a head-start on building their own metering application. If you don’t want to do the math yourself you’ll find you get about 20mV/step resolution (5000mV/256) at 8-bit.

 

The thousands position is moot and could be dropped. The resolution of the A/D being used in 8-bit doesn't allow for reliable usage in the thousandths.
The thousands position is moot and could be dropped. The resolution of the A/D being used in 8-bit doesn’t allow for reliable usage in the thousandths.

 

Notes: Refresh is slow, clearing the LCD line #2 is slow because I didn’t like the flash of clearing the whole display… it could have also been made more quick had I not written 16 spaces and just written enough to cover my 7 digits of output. I also should have dropped the LSB of my number because it’s a worthless bit considering my resolution. I will if this gets used on anything other than a breadboard test.

I used a standard 44780 16×2 LCD, a Tautic development board, and a PIC 16F1509 (happens to come with the dev board). For programming I used MPLAB X IDE v1.95 and XC8 v1.21 (free version) and the PICkit 3 programmer. The code is commented enough to figure out the hardware setup.

code (with whatever fixes since wiring the blog) can be found at : https://github.com/chasxmd/iradan.com-2014

at writing it looks like:

/* 
 * File: main.c
 * Author: Charles M Douvier
 * Contact at: http://iradan.com
 *
 * Created on January 18, 2014, 9:42 AM
 *
 * Target Device:
 * 16F1509 on Tautic 20 pin dev board
 *
 * Project:
 * A/D --> LCD Test
 * 8-bit resolution across Vdd to Vref (0-5V)
 * for 3.3V operation adjust A/D math
 *
 * LCD (44780 type) Test with XC8 compiler
 * LCD code ported from Mike Pearce's 2001 LCD code for HI-TECH C
 * as found on http://www.microchipc.com/
 */
#include <xc.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <plib.h>
//config bits
#pragma config FOSC=INTOSC, WDTE=OFF, PWRTE=OFF, MCLRE=ON, CP=OFF, BOREN=ON, CLKOUTEN=OFF, IESO=OFF, FCMEN=OFF
#pragma config WRT=OFF, STVREN=OFF, LVP=OFF
#define _XTAL_FREQ 4000000 //defined for delay
int an9_value; //value for a/d
 char buf[10]; //buff for iota
 long int fvar; //long for format math
 long int ones; //left of decm
 long int decm; //decimal places
 int tempi; //to add leadign zeros..
/*
 * LCD RS LATA.5
 * LCD EN LATA.4
 * LCD DATA4 LATC.0
 * LCD DATA5 LATC.1
 * LCD DATAT6 LATC.2
 * LCD DATA7 LATC.3
 * LED LATA.0 for scan rate/heartbeat
 */
void lcd_strobe (void) //TOGGLE LCD_EN
{
 LATAbits.LATA4 = 0;
 __delay_ms(20);
 LATAbits.LATA4 = 1;
}
/* write a byte to the LCD in 4 bit mode */
void lcd_write(unsigned char c)
{
 LATC = c >> 4;
 lcd_strobe();
 LATC = c;
 lcd_strobe();
 __delay_us(100);
}
/*
 * Clear and home the LCD
 */
void lcd_clear(void)
{
 LATAbits.LATA5 = 0;
 lcd_write(0x1);
 __delay_ms(2);
}
/* write a string of chars to the LCD */
void lcd_puts(const char * s)
{
 LATAbits.LATA5 = 1; // write characters
 while(*s)
 lcd_write(*s++);
}
/*
 * Go to the specified position
 */
void lcd_goto(unsigned char pos)
{
 LATAbits.LATA5 = 0;
 lcd_write(0x80+pos);
}
/*
 * Write 16 spaces on LCD 2 to avoid blanking, (ugly CLEAR effect)
 * this is slow but work for my needs
 */
void lcd_clrline2(void)
{
 lcd_goto(40);
 lcd_puts(" ");
 lcd_goto(40);
}
/* initialise the LCD - put into 4 bit mode */
void lcd_init(void)
{
 LATAbits.LATA5 = 0; // write control bytes
 LATC = 0x03;
 __delay_ms(150); //power on delay
 lcd_strobe();
 __delay_ms(5);
 lcd_strobe();
 __delay_ms(5);
 lcd_strobe();
 __delay_ms(5);
 LATC = 0x02; // set 4 bit mode
 __delay_ms(5);
 lcd_strobe();
 __delay_ms(5);
 lcd_write(0x28); // 4 bit mode, 1/16 duty, 5x8 font
 lcd_write(0x08); // display off
 lcd_write(0x0C); // display on cursor+blink off
 lcd_write(0x06); // entry mode
}
int main(void) {
 // set up oscillator control register, using internal OSC at 4MHz.
 OSCCONbits.IRCF = 0x0d; //set OSCCON IRCF bits to select OSC frequency 4MHz
 OSCCONbits.SCS = 0x02; //set the SCS bits to select internal oscillator block
TRISCbits.TRISC0 = 0; // output
 TRISCbits.TRISC1 = 0; // output
 TRISCbits.TRISC2 = 0; // output
 TRISCbits.TRISC3 = 0; // output
 TRISAbits.TRISA0 = 0; // output
 TRISAbits.TRISA4 = 0; // output
 TRISAbits.TRISA5 = 0; // output
 TRISCbits.TRISC7 = 1; //analog input
 ANSELCbits.ANSC7 = 1; //...setup on PORTC.7/AN9
 LATAbits.LATA0 = 0; //LED Im-Alive test
__delay_ms(250); //let the power settle
lcd_init();
 __delay_ms(10);
 lcd_clear();
//display test message
 lcd_puts("Testing the LCD.");
 lcd_goto(40);
ADCON0 = 0b00100101; //select AN9 and enable
/* ADCON1
 * bit 7 ADFM: ADC Result Format Select bit
 * 0 = Left justified. Six Least Significant bits of ADRESL are set to ?0? when the conversion result is loaded.
 * bit 6-4 ADCS<2:0>: ADC Conversion Clock Select bits
 * 110 = FOSC/64
 * bit 3-2 Unimplemented: Read as ?0?
 * bit 1-0 ADPREF<1:0>: ADC Positive Voltage Reference Configuration bits
 * 00 = VREF+ is connected to VDD
 */
 ADCON1 = 0b01100000; //left justified, FOSC/64 speed Vref=Vdd
while(1)
 {
 LATAbits.LATA0 = 0; //debugging

 lcd_clrline2(); //clear LCD line 2 by writting " " and return
__delay_us(5); 
 GO = 1;
 while (GO) continue; //wait for conversion
 an9_value = ADRESH; //AN9 value
 //format value for LCD read out
 //value = AD_value * 5000 (because 5000mV is Vref)
 //value = value / 256 (8 bit number)
 fvar = an9_value;
 fvar = fvar * 5000;
 fvar = fvar / 256;
 ones = fvar / 1000;
 ones = ones % 10;
 decm = fvar % 1000;
LATAbits.LATA0 = 1; //LED Im-Alive test. I made it through conversion
//page 366 of XC8 user guide
 itoa(buf,ones,10); //int conv to buffer
 lcd_puts(buf); //outputs "1s" place to LCD.
 lcd_puts(".");
 //page 374 of XC8 user guide
 ltoa(buf,decm,10); //long conversion to buffer
 tempi=strlen(buf); //uh, adding leading zeros..
 tempi=3-tempi; //probably a better way of doing thing
 while (tempi) //first figure out how many zeros
 {
 lcd_puts("0"); //missed 3-string length
 tempi=tempi-1; //then send them until done
 }
 lcd_puts(buf); //output buffer to LCD
lcd_puts(" V"); //attach some units for display
 //delay
 __delay_ms(999); //LCD refresh rate
 }
 return (EXIT_SUCCESS);
}

Quickstart guide to a basic PIC based robots – Part 1

While browsing for “goodies” on eBay I ran across a $9 robot chassis. How can you go wrong for $9? I haven’t built a robot since Talus my sumo Roomba, so I thought why not.

If you’re looking for something like the model I purchased, check out eBay and do a search for “Robot Chassis”.  Adam Fabio of TheRegnineer.com mentioned he has almost completed working on a product that is similar. I’m guessing you can look for Adam’s product at his Tindie Store once he has finished it.

.. Fast forward three weeks for shipping from China.

None of my sensors have arrived but you’ll end up wanting to customize you robot for whatever sensor pack you are interested in anyhow. With that said I haven’t written any code for handling any sensors and at the end of the day this robot is just going to drive straight forward all day long. You can follow the code at the repository I set up for it on github to get the latest updates at…

The software:

https://github.com/chasxmd/16F1509_Rover_Robot

The hardware:

I’m using the TAUTIC 20 pin PIC development board as it’s only $10 and takes care of the reset switch capacitor and comes on a nice plug-in board if you solder the connectors the same way I did. Use whatever you like but notice I’m using an internal oscillator so you don’t have to worry about getting a specific development board or having a crystal.

I’m also using the Pololu Quick 2s9v1 Dual Serial Motor Controller which is cost me $25, a little on the high side, but I had one from another project so I’m just re-using it and it makes life a lot easier for development.

Check out the photo at the bottom of the post for the basic schematic.

Beginners:

If you don’t already have MPLABX you’re looking at an upwards battle. However if you don’t mind watching a few YouTube videos it’s not up a creek.

1. Download and install MPLABX and the HI-TECH C compiler.

2. Start a project within MPLABX; selecting PIC 16F1509 and choosing the HI-TECH compiler, and create a main file.

3. Copy my code off the github site and drop it into your main file and compile.

You can download the free version of HI-TECH C and MPLABX which is also free. They are both obtained from http://www.microchip.com. MPLABX is an IDE (integrated development environment) which means it’s a program for programming, compiling and loading your software. HI-TECH is a C compiler which works with MPLABX. There are other programming language options such as BASIC and even assembly (I use assembly most of the time). I don’t know of any free BASIC compilers but I’m guessing someone has one. Google might be your best friend if  you’re looking to go that route.

If you don’t have MPLABX you probably don’t have a PICKit 3 (or other hardware programming tool). I like the PICKit 3 because it will program all the newer Microchip PICs and it’s just about the most affordable tool for PICs. PICKit3 comes from Microchip or Digi-key. There are alternatives.

The Build:

It took me about 30 minutes to put the chassis kit together last night. It was pretty obvious how it went together which was good because there were no directions. The encoder wheels don’t really attach other than compression, I see issues with that if I end up using them… that’s what you get for $9?

The code I put together this morning in about an hour or two after reviewing the manual for the motor driver online; you should also review this document. The idea with the code was just to test the motors and motor driver. I also selected a high/half/normal speed which you can adjust for your motor’s needs. I believe the highest speed setting you can select is 0x7F, I chose 0x4F for my high speed based on my anticipated desire. Adjust as needed.. but keep in mind your low speed should be set so it doesn’t stall on carpet or whatever you want your robot to be driving around on.

That’s about it for now. Once I get some sensors in I’ll add them and then write the code. I’m still new to C programming but I felt it was a better choice since it seems most popular; I usually write everything in assembler. Check out the photo I posted as it has a basic layout of the circuit and how I set mine up on a breadboard until I receive some prototyping PCBs I ordered.

First step of my cheap chassis robot build... getting the motors turning.
First step of my cheap chassis robot build… getting the motors turning.

 

…. to be continued