Displaying a keypad input onto an LCD

Thread Starter

Jswale

Joined Jun 30, 2015
121
Hi everyone,

I'm using PIC16F1459 and MPLAB v8.92 with the XC8 compiler...

I am trying to display a variable onto an LCD, the variable is the number pressed on a keypad. I get a few numbers now and again and sometimes it lags or doesn't respond. It is definitely a bug in the code so have a look for yourself and see if you can spot something.


Code:
//--------------------Keypad Scan------------------------------------
void Scan(void)
{

numberpressed = 12;
while (1)                //Starting point, there is no number 12, means its polling
    {
    col1=0;
    col2=1;
    col3=1;                                //B4 is 0, scans for 1,4,7,*
       
    if (row1==0)   
        {
        __delay_ms(10);                    //used for de-bounce purposes
        while (row1==0);                //waits for switch to be released
        __delay_ms(10);                    //used for de-bounce purposes
        numberpressed=1;
        return;                            //'return' leaves current subroutine
        }
   
    if (row2==0)               
        {
        __delay_ms(10);
        while (row2==0);       
        __delay_ms(10);
        numberpressed=4;
        return;
        }
           
    if (row3==0)
        {
        __delay_ms(10);
        while (row3==0);
        __delay_ms(10);
        numberpressed=7;
        return;
        }
       
    if (row4==0)
        {
        __delay_ms(10);
        while (row4==0);
        __delay_ms(10);
        numberpressed=10;
        return;
        }
   

    col1=1;                                //B5 is 0, scans for 2,5,8,0
    col2=0;
    col3=1;                   
       
    if (row1==0)
        {
        __delay_ms(10);
        while (row1==0);
        __delay_ms(10);
        numberpressed=2;
        return;
        }
   
    if (row2==0)
        {
        __delay_ms(10);
        while (row2==0);
        __delay_ms(10);
        numberpressed=5;
        return;
        }       
   
    if (row3==0)
        {
        __delay_ms(10);
        while (row3==0);
        __delay_ms(10);
        numberpressed=8;
        return;
        }
       
    if (row4==0)
        {
        __delay_ms(10);
        while (row4==0);
        __delay_ms(10);
        numberpressed=0;
        return;
        }

   
    col1=1;                                    //B6 is 0, scans for 3,6,9,#
    col2=1;
    col3=0;               
       
    if (row1==0)
        {
        __delay_ms(10);
        while (row1==0);
        __delay_ms(10);
        numberpressed=3;
        return;
        }
   
    if (row2==0)
        {
        __delay_ms(10);
        while (row2==0);
        __delay_ms(10);
        numberpressed=6;
        return;
        }
           
    if (row3==0)
        {
        __delay_ms(10);
        while (row3==0);
        __delay_ms(10);
        numberpressed=9;
        return;
        }
       
    if (row4==0)
        {
        __delay_ms(10);
        while (row4==0);
        __delay_ms(10);
        numberpressed=11;
        return;
        }
    }
}

//======================= MAIN =====================================
void main (void)
{
    initIO();               // init the chip IO
    __delay_ms(300);        // power up delay for LCD - adjust as necessary
    lcd_init();             // init the LCD
    lcd_WriteStr("I am Working");
    lcd_SetCursor(lcdLINE2);
    
    while(1)                                       
    {   
    if (numberpressed==1) break;

        while(1)
        {
        Scan();                                //poll for number pressed
        num1=(numberpressed + 0x30);        //add 0x30 for ascii
        __delay_ms(1);                        //1ms delay
        lcd_SetCursor(lcdLINE2);            //set cursor to line1
        lcd_data(num1);                        //display key pressed
        break;                                //break out of while loop
        }    //while
    }    //while
   
    lcd_SetCursor(lcdLINE1);        //sets cursor to line1
    lcd_WriteStr("UNLOCKED     ");
    lcd_SetCursor(lcdLINE2);        //sets cursor to line2
    lcd_WriteStr("UNLOCKED     ");

    while(1);    // loop forever
}    //main
All LCD/keypad/Pic config is done earlier in the code but that is all correct, the problem lies somewhere in this code.

Cheers
JSwale
 

JohnInTX

Joined Jun 26, 2012
4,787
Hi @Jswale ,
A couple of things:
You can't add 0x30 to anything >9 and get what you want. You should at least test for >9 and if so, poke in the character translation. A better way to do it is to use the character code (0-12) as an index into an array of printable characters. A bonus is that your scan codes don't have match what's printed on the button, just rearrange the order of the returned codes. For example if your '0' button returns scancode 9, just put '0' in the 10th entry of the table (index 9).

Like this:
C:
const char transTable[] = {'0','1','2','3','4','5','6','7','8','9','*','#','?'};
unsigned char ASCIIchar;

if(numberpressed>11) numberpressed = 12;  //qualify your index- will print '?' if unknown
ASCIIchar = transTable[numberpressed]; // translate to ASCII numbers and symbols
lcd_data(ASCIIchar); //write it
This looks like a combination lock. If that is the case, you don't have to convert the ASCII keys to numbers - ASCII compares just fine.

Your debouncing/delay stuff is not doing much for you.

I would write scan() to scan the array ONCE with no delays and return a key code or noKey. Use scan() like this:
1) If no switch closure is found, do nothing - keep scanning by calling scan() repeatedly. Do other things in between.
2) If a switch is closed, save its code and debounce by scanning a few more times with a short delay between scans. Compare the returned code with the first one. If its the same, continue debouncing. If it changes, start over.
3) Once the key is debounced - process the code while your finger is still on the button.
4) After processing, scan the keyboard until no keys are reported (finger off button). Again, do other things in between.
5) When no keys are reported, goto 1)

A good reason to write the scanner this way is that eventually you'd want to make it called by an interrupt driven timer, do all the debounce and key release detect and just emit keycodes into a FIFO (first in first out) buffer. Then your main routine can just call something .. well like getchar() which fetches characters out of the buffer or returns EMPTY if no chars. A more relevant reason here is that you are hanging in the scanner until someone hits a key - if the code wants to do something else while waiting, you are sunk.

That's how I'd do it.
Good luck.
 
Last edited:

Thread Starter

Jswale

Joined Jun 30, 2015
121
@JohnInTX Got it working quite nicely, thanks for the help.

I noticed you helped @nerdegutta out with some ADC stuff a few years back and if you can see why I am only getting 4 vertical lines and not a number between 0-1023 on my ADC?

code below;

Code:
TRISC=0b00000001;
    PORTC=0b00000000;
    ANSELC=0b00000001;
    unsigned int ADC_result;

void adc_init()
{
    ADCON0= 0b00010001;            //RC0 for ADC use
    ADCON1= 0b10010000;            //Right justified and use of Vsupply + clock conversion 8Fosc = 2us
    ADCON2= 0b00000000;           
}


while(1)
    {   
       
        ADCON0=0x83;                        //Sets Go_Done bit
        while(ADCON0==0x83);                //Waits for conversion to finish
        __delay_us(10);
        ADC_result=ADRESL+(ADRESH*256);            //gets 10 bit result
        lcd_cmd(0x80);                            //Start of Line 1
        lcd_WriteStr(ADC_result);                //writes the value (0-1023)
}
 

nerdegutta

Joined Dec 15, 2009
2,684
Hi.
Are you still moving your PIC back and forth to program it? I believe RC0 is on pin 16, which is for ICSPDAT. Keep that in mind. It is also the Vref+ pin. Remember to turn off the comparators.

Try to organize your code like in the other thread. With Include-statements, configurations, definitions and functions separated. It is much easier to read and understand.

If you at the same time add "=c" in the code tag - that would be awesome.

Then your code is like this:
C:
TRISC=0b00000001;
    PORTC=0b00000000;
    ANSELC=0b00000001;
    unsigned int ADC_result;
void adc_init()
{
    ADCON0= 0b00010001;            //RC0 for ADC use
    ADCON1= 0b10010000;            //Right justified and use of Vsupply + clock conversion 8Fosc = 2us
    ADCON2= 0b00000000;        
}
while(1)
    {
     
        ADCON0=0x83;                        //Sets Go_Done bit
        while(ADCON0==0x83);                //Waits for conversion to finish
        __delay_us(10);
        ADC_result=ADRESL+(ADRESH*256);            //gets 10 bit result
        lcd_cmd(0x80);                            //Start of Line 1
        lcd_WriteStr(ADC_result);                //writes the value (0-1023)
}
 

Thread Starter

Jswale

Joined Jun 30, 2015
121
Hi,

Yes I am moving it back and forth so ICSPDAT isn't a problem. I only pasted a small portion of my code. I think I am not breaking down each individual number that is in the result so will be adding;
C:
char *temp="0000";

temp[0] = ADC_result/1000 +48;
temp[1] = (ADC_result/100)%10 +48;
temp[2] = (ADC_result/10)%10 +48;
temp[3] = ADC_value %10 +48;

lcd_WriteStr(temp);
Also it wont let me changer the code tag and C isn't in the language options.
C:
//Author: Jack Swale
//July 2015
// PIC16F1459 Configuration Bit Settings
// 'C' source line config statements
#include <xc.h>
// #pragma config statements should precede project file includes.
// Use project enums instead of #define for ON and OFF.
// CONFIG1
#pragma config FOSC = INTOSC    // Oscillator Selection Bits (INTOSC oscillator: I/O function on CLKIN pin)
#pragma config WDTE = OFF       // Watchdog Timer Enable (WDT disabled)
#pragma config PWRTE = ON       // Power-up Timer Enable (PWRT enabled)
#pragma config MCLRE = OFF       // MCLR Pin Function Select (MCLR/VPP pin function is Digital Input)
#pragma config CP = OFF         // Flash Program Memory Code Protection (Program memory code protection is disabled)
#pragma config BOREN = OFF      // Brown-out Reset Enable (Brown-out Reset disabled)
#pragma config CLKOUTEN = OFF   // Clock Out Enable (CLKOUT function is disabled. I/O or oscillator function on the CLKOUT pin)
#pragma config IESO = ON        // Internal/External Switchover Mode (Internal/External Switchover Mode is enabled)
#pragma config FCMEN = ON       // Fail-Safe Clock Monitor Enable (Fail-Safe Clock Monitor is enabled)
// CONFIG2
#pragma config WRT = OFF        // Flash Memory Self-Write Protection (Write protection off)
#pragma config CPUDIV = NOCLKDIV// CPU System Clock Selection Bit (NO CPU system divide)
#pragma config USBLSCLK = 48MHz // USB Low SPeed Clock Selection bit (System clock expects 48 MHz, FS/LS USB CLKENs divide-by is set to 8.)
#pragma config PLLMULT = 3x     // PLL Multipler Selection Bit (3x Output Frequency Selected)
#pragma config PLLEN = ENABLED  // PLL Enable Bit (3x or 4x PLL Enabled)
#pragma config STVREN = ON      // Stack Overflow/Underflow Reset Enable (Stack Overflow or Underflow will cause a Reset)
#pragma config BORV = LO        // Brown-out Reset Voltage Selection (Brown-out Reset Voltage (Vbor), low trip point selected.)
#pragma config LPBOR = OFF      // Low-Power Brown Out Reset (Low-Power BOR is disabled)
#pragma config LVP = OFF        // Low-Voltage Programming Enable (High-voltage on MCLR/VPP must be used for programming)
#define _XTAL_FREQ 4000000      // 4MHz for now
//---------------------INIT IO -----------------------------------
void initIO(void)
{
    OSCCON = 0b00110100;  // Internal oscillator = 4MHz (for now)
    TRISC=0b00000001;
    PORTC=0b00000000;
    ANSELC=0b00000001;
    TRISB=0b00000000;
    PORTB=0b00000000;
    ANSELB=0b00000000;
    TRISA=0b11111111;
    PORTA=0b00000000;
    ANSELA=0b00000000;
}

//---------------------ADC Definitions---------------------------
void adc_init()
{
    ADCON0= 0b00010001;            //RC0 for ADC use
    ADCON1= 0b10010000;            //Right justified and use of Vsupply + clock conversion 8Fosc = 2us
    ADCON2= 0b00000000;      
}
//---------------------IO DEFINITIONS  --------------------------
#define lcd_port  LATC        // write to LAT, read from PORT
// RC7-4 is the LCD 4 bit databus
#define LCD_RSout LATC2     // moved from ICSP
#define LCD_ENout LATC3

#define col1    PORTBbits.RB4
#define col2    PORTBbits.RB5
#define col3    PORTBbits.RB6
#define row1    PORTAbits.RA0
#define row2    PORTAbits.RA1
#define row3    PORTAbits.RA3
#define row4    PORTAbits.RA4

int numberpressed;
const char transTable[] = {'0','1','2','3','4','5','6','7','8','9','*','#','?'};
unsigned char ASCIIchar;
unsigned int ADC_result;
//-------------------- DISPLAY SETTINGS  ---------------------
// Define some display settings.
#define lcdLINE1 0x00       // where line 1 begins
#define lcdLINE2 0x40       // where line 2 begins
//--------------------- STROBE LCD ---------------------------
// Pulses E line on LCD to write
int strobeLCD(void)
{
LCD_ENout = 1;
__delay_us(2);     // Added a little here
LCD_ENout = 0;
}
//--------------------- WRITE 8 BIT DATA TO LCD  -----------------
// Assumes LCD is ready and RS is set to correct value
// LCD data bus is RC4-RC7
void writeLCD(unsigned char dat)
{
    lcd_port &= 0x0f;               // get current port, clear upper bits
    lcd_port |= (dat & 0xf0);       // combine w/upper nibble, leave lower same
    strobeLCD();
    lcd_port &= 0x0f;               // get current port, clear upper bits
    lcd_port |= ((dat <<4) & (0xf0)); // combine w/lower nibble, leave lower port same
    strobeLCD();
    __delay_ms(2);                // wait for display to process
}
//-------------------- WRITE LCD COMMAND  -------------------------
// Write cmd to LCD with RS=0
// Assumes E is low and display is NOT busy
void lcd_cmd (unsigned char cmd)
{
    LCD_RSout = 0;       // select command register
    writeLCD(cmd);
}
//---------------------- WRITE LCD DATA  --------------------------
// Write dat to LCD with RS=1
// Assumes E is low and display is NOT busy
void lcd_data (unsigned char dat)
{
    LCD_RSout = 1;       // select data register
    writeLCD(dat);
}
//-------------------- RESET/CONFIGURE LCD  -------------------------
void lcd_init(void)
{
    lcd_port &= 0x0f;   // clear upper bits of LCD port
    lcd_port |= 0x30;   // direct data to LCD DB7-4
    LCD_RSout = 0;
    strobeLCD();        // write 3h, wait 10ms
    __delay_ms(10);
    strobeLCD();        // write 3h, wait..
    __delay_ms(10);
     strobeLCD();       // write 3h
    __delay_ms(10);
   lcd_port &= 0x0f;    // clear upper bits of LCD port
   lcd_port |= 0x20;    // direct data to LCD DB7-4
   strobeLCD();         // write 2h
   __delay_ms(10);
    lcd_cmd(0x28);       // Funciton Set: 4-bit mode - 2 line - 5x7 font.
    lcd_cmd(0x0e);       // DisplayON, cursor ON, blink OFF
    lcd_cmd(0x06);       // Automatic Increment - No Display shift.
    lcd_cmd(0x80);       // Address DDRAM with 0 offset 80h.
}
//----------------------- WRITE STRING TO LCD  ---------------------
// Writes null terminated string to LCD from ROM
void lcd_WriteStr(const unsigned char *c)
{
    while(*c != '\0'){
        lcd_data(*c);
        c++;
    }
}
//--------------------  SETS CURSOR ANYWHERE IN DISPLAY MEMORY  ---------
// Valid locations are 0-79 decimal.  This doesn't check for valid location
void lcd_SetCursor(unsigned char loc)
{
    lcd_cmd(loc | 0x80);        // form and send 'set DDRAM address' cmd
}
//----------------- CANNED LINE COMMANDS  -------------------------
// For a 2 line display
void lcd_LINE1(void)
{
    lcd_SetCursor(lcdLINE1);
}
void lcd_LINE2(void)
{
    lcd_SetCursor(lcdLINE2);
}

//--------------------Keypad Scan------------------------------------
void Scan(void)
{


    col1=0;
    col2=1;
    col3=1;                                //B4 is 0, scans for 1,4,7,*
  
    if (row1==0)
        {
        numberpressed=1;
        return;                            //'return' leaves current subroutine
        }

    if (row2==0)          
        {
        numberpressed=4;
        return;
        }
      
    if (row3==0)
        {
        numberpressed=7;
        return;
        }
  
    if (row4==0)
        {
        numberpressed=10;
        return;
        }


    col1=1;                                //B5 is 0, scans for 2,5,8,0
    col2=0;
    col3=1;              
  
    if (row1==0)
        {
        numberpressed=2;
        return;
        }

    if (row2==0)
        {
        numberpressed=5;
        return;
        }  

    if (row3==0)
        {
        numberpressed=8;
        return;
        }
  
    if (row4==0)
        {
        numberpressed=0;
        return;
        }


    col1=1;                                    //B6 is 0, scans for 3,6,9,#
    col2=1;
    col3=0;          
  
    if (row1==0)
        {
        numberpressed=3;
        return;
        }

    if (row2==0)
        {
        numberpressed=6;
        return;
        }
      
    if (row3==0)
        {
        numberpressed=9;
        return;
        }
  
    if (row4==0)
        {
        numberpressed=11;
        return;
        }
}

//-----------------------verification--------------------------
void Verify (void)
{
numberpressed=50;   
lcd_WriteStr("Press 0 to cont");
while(numberpressed!=0)
{
Scan();
}
lcd_cmd(0x01);
}
//======================= MAIN =====================================
void main (void)
{
    initIO();               // init the chip IO
    __delay_ms(300);        // power up delay for LCD - adjust as necessary
    lcd_init();             // init the LCD
    adc_init();                //ADC init
    Verify();                //Demonstrates LCD powering up
   
while(1)
    {
  
        ADCON0=0x83;                        //Sets Go_Done bit
        while(ADCON0==0x83);                //Waits for conversion to finish
        __delay_us(10);
        ADC_result=ADRESL+(ADRESH*256);            //gets 10 bit result
        lcd_cmd(0x80);                            //Start of Line 1
        lcd_WriteStr(ADC_result);                //writes the value (0-1023)

        Scan();
        if(numberpressed>11) numberpressed = 12;  //qualify your index- will print '?' if unknown
        ASCIIchar = transTable[numberpressed]; // translate to ASCII numbers and symbols
        lcd_cmd(0xC0);                            //Starts Line 2
        lcd_WriteStr("The number is ");
        lcd_data(ASCIIchar); //write it

    if (numberpressed==1) break;
    }    //while

    lcd_SetCursor(lcdLINE1);        //sets cursor to line1
    lcd_WriteStr("UNLOCKED        ");
    lcd_SetCursor(lcdLINE2);           //sets cursor to line2
    lcd_WriteStr("UNLOCKED        ");

    while(1);    // loop forever
}    //main
The rest of the code is here^^^

JSwale.
 
Last edited:

JohnInTX

Joined Jun 26, 2012
4,787
Yes, you have to convert the binary ADC result to individual ASCII chars but you have issues with your routine:
C:
unsigned char temp[5]; // declare characters - you declared an array of pointers to char
temp[0] = ADC_result/1000 + '0';  // a better way to declare that you are converting to ASCII '0' evaluates to 30h
temp[1] = (ADC_result/100)%10 +48;
temp[2] = (ADC_result/10)%10 +48;
temp[3] = ADC_value %10 +48;
temp[4]= '\0';  // NULL terminate the string as lcd_WriteStr requires
lcd_WriteStr(temp); // the name of the char array is a pointer to char so this is OK
You have to manually edit the code tag once you paste the code - which I see that @nerdegutta already pointed out.
 

Thread Starter

Jswale

Joined Jun 30, 2015
121
I am still a little rusty on Pointers as I have never really used them in my programs.

I'll try that code now
 

JohnInTX

Joined Jun 26, 2012
4,787
I am still a little rusty on Pointers as I have never really used them in my programs.
The compiler will identify potential problems with warnings about 'illegal pointer conversion' or some such verbiage. That will help you. NEVER ignore such warnings (or any other compiler warning for that matter).
 

Thread Starter

Jswale

Joined Jun 30, 2015
121
What is displayed on the screen is just unrecognisable characters so something must be wrong, i am guessing with the configuration.
 

JohnInTX

Joined Jun 26, 2012
4,787
What is displayed on the screen is just unrecognisable characters so something must be wrong, i am guessing with the configuration.
I doubt it. Probably something in your ADC->text routine (I didn't check the arithmetic but it looks OK).
As a test, load the array with known text in the conversion routine and null terminate that. Call the routine and see if it shows the text. If it does, your arithmetic needs work. If not, fix the text display.

Note that even 'garbage' on the screen has real numerical values. Pull the display datasheet and look at the character map - match up what you are seeing and it will tell you what codes were written.

You can simulate the conversion to text in MPSIM. Put a breakpoint before and after calling the routine. Use a watch window with the ADC value int and char array watched. On the first break right click the ADC value and enter something. Run to the next break point and inspect the array of text. That should be the ASCII value of whatever the input number was. Make a dummy loop and try various values. Step through the arithmetic one line at a time, watching the result. Your problem should become apparent.

Good luck

EDIT: Is the compiler throwing warnings about lcd_WriteStr? IIRC, its declared to use a const char * (i.e. pointer to a string in ROM). It may not like a string in RAM. XC8 can use both in one routine but you have to declare the parameter qualifier accordingly. I don't know what the syntax is off hand but its in the manual. EDIT Sec 5.5
 
Last edited:

nerdegutta

Joined Dec 15, 2009
2,684
Not quite what you are looking for, but this little program displays the ADC value and the corresponding voltage. The voltage is approximate. I'm using my version of xlcd.h and xlcd.c along with the standard libraries. I got:
RS=>RA0
RW=>RA1
E=>RA2
D4=>RC0
D5=>RC1
D6=>RC2
D7=>RC3

I got a pot connected to RA4, as analog input.
PIC16F688, 16x02LCD, PICKit3, MPLABX XC8 compiler
C:
// INCLUDES
#include <stdio.h>
#include <stdlib.h>
#include <xc.h>
#include "my_xlcd.h"

// CONFIGURATION BITS
#pragma config FOSC = INTOSCIO  // Oscillator Selection bits (INTOSCIO oscillator: I/O function on RA4/OSC2/CLKOUT pin, I/O function on RA5/OSC1/CLKIN)
#pragma config WDTE = OFF       // Watchdog Timer Enable bit (WDT enabled)
#pragma config PWRTE = OFF      // Power-up Timer Enable bit (PWRT disabled)
#pragma config MCLRE = ON       // MCLR Pin Function Select bit (MCLR pin function is MCLR)
#pragma config CP = OFF         // Code Protection bit (Program memory code protection is disabled)
#pragma config CPD = OFF        // Data Code Protection bit (Data memory code protection is disabled)
#pragma config BOREN = ON       // Brown Out Detect (BOR enabled)
#pragma config IESO = ON        // Internal External Switchover bit (Internal External Switchover mode is enabled)
#pragma config FCMEN = ON       // Fail-Safe Clock Monitor Enabled bit (Fail-Safe Clock Monitor is enabled)

// DEFINITIONS
#define _XTAL_FREQ 4000000

// GLOBAL VARIABLES
unsigned int adc_raw;
unsigned char adc_value[5];
int millivolt;
char message[] = "0.00";

// PROTOTYPE FUNCTIONS
void init_XCLD(void);
void DelayFor18TCY(void);
void DelayPORXLCD(void);
void DelayXLCD(void);
void Delay10KTCYx(unsigned char);
void init_OSC(void);
void init_ADC(void);
void read_ADC(void);


// FUNCTIONS
void init_XLCD(void){
    OpenXLCD(FOUR_BIT & LINES_5X7);
    while (BusyXLCD());
    WriteCmdXLCD(0x06);
    WriteCmdXLCD(0x0C);
}

void DelayFor18TCY(void){
    NOP(); NOP(); NOP(); NOP(); NOP(); NOP(); NOP();
    NOP(); NOP(); NOP(); NOP(); NOP(); NOP(); NOP();
    return;
}
void DelayPORXLCD(void){
    __delay_ms(15);
}

void DelayXLCD(void){
    __delay_ms(5);
}

void Delay10KTCYx(unsigned char){
    __delay_ms(10);
}

void init_OSC(void){
    OSCCONbits.IRCF0 = 0b110; // Internal Osc Freq Select bit => 4MHz
    OSCCONbits.HTS = 1;       // Status bit, high freq => Stable
    OSCCONbits.OSTS = 0;      // Osc startup time out => dev run from internal
    OSCCONbits.LTS = 1;       // Low freq bit => stable
    OSCCONbits.SCS = 1;       // Internal osc used for system clock
}

void init_ADC(void){
    ADCON0bits.ADFM = 1;        // Right justified
    ADCON0bits.VCFG = 0;        // Voltage ref => Vdd
    ADCON0bits.CHS = 3;         // Analog channel => AN3
    ADCON0bits.ADON = 1;        // ADC enable bit
    ADCON1bits.ADCS = 0b001;    // A/D Conversion clock bit
}

void read_ADC(void){
    ADCON0bits.GO = 1;          // A/D conversion is in progress.
    __delay_us(10);
    while(ADCON0bits.nDONE);    // Wait for AD to complete
    __delay_us(10);
}
/*
*   MAIN PROGRAM
*/
int main(int argc, char** argv) {

    TRISA = 0x00000000;         // All output
    TRISC = 0x00000000;
  
    PORTA = 0b00000000;         // All low
    PORTC = 0b00000000;
    TRISAbits.TRISA4 = 1;       // TRISA4 set to input

    ANSEL = 0b00000000;         // Disabling all analog input
    ANSELbits.ANS3 = 1;         // Enabling analog input on ANS3
  
    CMCON0 = 0x07;              // Turn off comparator
  
    init_ADC();                 // Init ADC
    init_XLCD();                // Init XLCD
    putrsXLCD("I'm alive.");    // Alive message
    __delay_ms(1000);
    while(1){
        read_ADC();             // read ADC
        WriteCmdXLCD(0x01);     // Clear LCD home cursor
        adc_raw = (unsigned int) ADRESH;    // Combining ARDRESL & ADRESH
        adc_raw = adc_raw * 256;            // Thanks to JohnInTX
        adc_raw += (unsigned int) ADRESL;   // for this
      
        itoa(adc_value, adc_raw, 10);       // convert int to ascill
      
        putrsXLCD(adc_value);   // Writes ADC value on LCD

        SetDDRamAddr(0x40);     // Goto line 2, pos 1
      
        millivolt = (adc_raw * 49) / 10;    // convert ADC to millivolts
    
        message[0] = millivolt / 1000 + 48;        // Sorting out the volt
        message[2] = (millivolt / 100) % 10 + 48;  // the hundreds
        message[3] = (millivolt / 10) % 10 + 48;   // the tenths
      
        putrsXLCD(message);    // Prints the volts ln LCD line 2
     
        __delay_ms(500);        // Waits for 0.5 sec
     
    }
  
    return (EXIT_SUCCESS);
}
I think this program can be made a little smoother, but not now. :)
 

Thread Starter

Jswale

Joined Jun 30, 2015
121
It looks like it works to some extent but it doesnt stay between the boundaries of 0-1023 that you would expect of a 10 bit result. Also it doesnt change much and the MSB flickers between 9 and 1. I am using a POT to vary vin between 0V and 5V.

Code used;

C:
    ADCON0=0x83;                        //Sets Go_Done bit
        while(ADCON0==0x83);                //Waits for conversion to finish
        __delay_us(10);
        ADC_result=ADRESL+(ADRESH*256);            //gets 10 bit result
     
         itoa(temp, ADC_result, 10);
 
    lcd_cmd(0x80);                            //Start of Line 1
        lcd_WriteStr(temp);                //writes the value (0-1023)

EDIT:
I also tried the text @JohnInTX by using 'H E L P' but the '? = A C' was displayed
 
Last edited:

Thread Starter

Jswale

Joined Jun 30, 2015
121
At once extreme it is 907 and at the other extreme it is 107 (with a little flickering). But it is never anything inbetween.


SOLVED: i was setting the ADCON=0x83 in the while loop instead of the single GO/DONE bit which was causing a problem. All works now!

@JohnInTX, what i have now got thanks to your help (and @nerdegutta) is a rectified and smoothed waveform that is measured by the Pic16F and is then displayed on the LCD also with a Keypad that displays number pressed . It all works perfectly, which gives me a good base to complicate it even more with interrupts and more features. :)

Regards JSwale
 
Last edited:

Thread Starter

Jswale

Joined Jun 30, 2015
121
Just a quick question.

I have never really used printf() except with Arduino and an LCD library and I see it is quite popular, how would I implement that into displaying it onto an LCD? Would I need an LCD library with it to associate the data bus and enable/RS pins too?
 

ErnieM

Joined Apr 24, 2011
8,377
A quick answer is to NEVER use printf() on a micro controller. That function (and those similar) while seemingly very useful and powerful are actually too powerful and contain lots and lots of code you will simply never use. I've seen such functions add 3 or 5 K to my build. The only time I used such was when I had a PIC32 with 512K words of memory to play in.

Keep to lightweight functions such as atoi() and lcd_WriteStr() and you will have room to do other useful things in your processor.
 
Last edited:

Thread Starter

Jswale

Joined Jun 30, 2015
121
Yeah I agree. Coding my own functions or using the lightweight functions are the way to go!

Printf() just seems an easy way out.
 

JohnInTX

Joined Jun 26, 2012
4,787
@ErnieM makes a good point for most compilers. Microchip addreseses this in XC8 by breaking printf into many small routines that are used only as required. I did a few tests awhile back and found simpler integer ops to take a few hundred instruntions, floats pushed it over 1K.. Still significant but not the big monolithic blocks of some other compilers. I posted the particulars but don't them handy. You can find out for yourself by trying test builds and looking at the dashboard in MPLABX or the compiler output.

FWIW In MikroC, a simple printf immediately pushed the code to past the free limit for the same code which is in line with ErnieM's remarks.

YMMV
 
Top