Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

LCD not continuously displaying serial data from DMM

Status
Not open for further replies.

Sodrohu

New Member
I've got a 16F877A reading serial data from a digital multimeter. The DMM data can be read using HyperT on the PC; the idea is to try using ucontrollers to decrease costs and try to display the available data on a 2x16 LCD. So far I've got a simulated circuit on Proteus and a physical circuit from a previous project. The current problem is this:

In the Proteus circuit, whenever I start the read, the LCD never reads the entire data being sent, just the first few characters. For example, suppose that the data has a format of +0000.32 DV. On HyperT the data is fully shown, but the LCD will only display '+00', then hangs.

Here's my code:
Code:
#include <stdio.h>
#include <htc.h>
#include "usart.h"

#ifndef _XTAL_FREQ
 // Unless specified elsewhere, 4MHz system frequency is assumed
 #define _XTAL_FREQ 20000000
#endif

#define rs          RB4 //RS pin of the LCD display
#define e           RB5 //E pin of the LCD display
#define lcd_data    PORTD       //LCD 8-bit data PORT

void delay(unsigned long data);         
void send_config(unsigned char data);
void send_char(unsigned char data);
void lcd_goto(unsigned char data);
void lcd_clr(void);
void send_string(const char *s);
void init_all(void);
void displaylcdchar(int line, unsigned short text);
void displaylcdint(int line, int text);
int i;

void main(void)
{
    unsigned char input;

    INTCON=0;   // purpose of disabling the interrupts.

    init_comms();   // set up the USART - settings defined in usart.h
    init_all();
    i =0;
    // Output a message to prompt the user for a keypress   
    printf("\rPress a key and I will echo it back:\n");
    lcd_goto(0);
    send_string("Press a key and");
    __delay_ms(500);
    __delay_ms(500);
    while(1)
    {
        if(i==16) 
        {   
            //printf("\n");
            i=0;
        }
            input = getch();
            lcd_goto(i);
            send_char(input);
            printf("\rI detected [%c]",input);
            i++;
    }
}

void init_all(void) // initialise all values and variables
{
TRISA = 0b00001111;
//PORTA = 0xFF;
TRISB = 0x00; // port B as output
PORTB = 0xFF; //clear portB
//TRISC = 0x00; // port C as output
//PORTC = 0x00; //clear portC
TRISD = 0b00000000; //port d as input
PORTD = 0x00;
TRISE = 0b00000000; //all outputs

//LCD initialise
send_config(0b00000001);        //clear display at lcd
send_config(0b00000010);        //lcd return to home 
send_config(0b00000110);        //entry mode-cursor increase 1
send_config(0b00001100);        //display on, cursor off and cursor blink off
send_config(0b00111000);        //function set

ADCON1 = 0x06;      //sets the PORTA to be all digital I/Os
}
void delay(unsigned long data)          //delay function, the delay time
{                                       //depend on the given value
    for( ;data>0;data--);
}

void send_config(unsigned char data)    //send lcd configuration 
{
    rs=0;                               //set lcd to configuration mode
    lcd_data=data;                      //lcd data port = data
    e=1;                                //pulse e to confirm the data
    delay(50);
    e=0;
    delay(50);
}

void send_char(unsigned char data)      //send lcd character
{
    rs=1;                               //set lcd to display mode
    lcd_data=data;                      //lcd data port = data
    e=1;                                //pulse e to confirm the data
    delay(10);
    e=0;
    delay(10);
}

void lcd_goto(unsigned char data)       //set the location of the lcd cursor
{                                       //if the given value is (0-15) the 
    if(data<16)                         //cursor will be at the upper line
    {                                   //if the given value is (20-35) the 
        send_config(0x80+data);         //cursor will be at the lower line
    }                                   //location of the lcd cursor(2X16):
    else                                // -----------------------------------------------------
    {                                   // | |00|01|02|03|04|05|06|07|08|09|10|11|12|13|14|15| |
        data=data-20;                   // | |20|21|22|23|24|25|26|27|28|29|30|31|32|33|34|35| |
        send_config(0xc0+data);         // -----------------------------------------------------    
    }
}

void lcd_clr(void)                      //clear the lcd
{
    send_config(0x01);
    delay(600); 
}

void send_string(const char *s)         //send a string to display in the lcd
{          
    while (s && *s)send_char (*s++);
}

void uart_send(unsigned char data)
{   
    while(TXIF==0);                     //only send the new data after 
    TXREG=data;                         //the previous data finish sent
}

I'm using the examples provided by Microchip, using MPLAB 8.xx. Here's the other files:
Code:
#include <htc.h>
#include <stdio.h>
#include "usart.h"

void 
putch(unsigned char byte) 
{
    /* output one byte */
    while(!TXIF)    /* set when register is empty */
        continue;
    TXREG = byte;
}

unsigned char 
getch() {
    /* retrieve one byte */
    while(!RCIF)    /* set when register is not empty */
        continue;
    return RCREG;   
}

unsigned char
getche(void)
{
    unsigned char c;
    putch(c = getch());
    return c;
}

This is the header where the baud rate and oscillator clocks are defined:

Code:
#ifndef _SERIAL_H_
#define _SERIAL_H_

#define BAUD 9600      
#define FOSC 20000000L
#define NINE 0     /* Use 9bit communication? FALSE=8bit */

#define DIVIDER ((int)(FOSC/(16UL * BAUD) -1))
#define HIGH_SPEED 1

#if NINE == 1
#define NINE_BITS 0x40
#else
#define NINE_BITS 0
#endif

#if HIGH_SPEED == 1
#define SPEED 0x4
#else
#define SPEED 0
#endif

#if defined(_16F87) || defined(_16F88)
    #define RX_PIN TRISB2
    #define TX_PIN TRISB5
#else
    #define RX_PIN TRISC7
    #define TX_PIN TRISC6
#endif

/* Serial initialization */
#define init_comms()\
    RX_PIN = 1; \
    TX_PIN = 1;       \
    SPBRG = DIVIDER;        \
    RCSTA = (NINE_BITS|0x90);   \
    TXSTA = (SPEED|NINE_BITS|0x20)

void putch(unsigned char);
unsigned char getch(void);
unsigned char getche(void);

#endif

I once tried changing the code a bit so that it reads and echoes keyboard chars, and the LCD works. If I press the keys one by one, it displays just fine. However, when I press the keys really fast, the LCD freezes again. I suspect the PIC didn't have enough time to read each cahracter being sent and this freezes up the PIC. Might be wrong though. I attached the Proteus circuit for reference.
 

Attachments

  • pic.png
    pic.png
    6.9 KB · Views: 155
This is why I always use sprintf(); I almost always send the contents of a buffer to the LCD, as the send_string() routine you've defined only works with constants

ie..
Code:
send_string("Hello world!!");
will work, but a string coming in from a stream, serially, will not...

Declare the function with a normal "char*" and not the "const char*" and use a buffer to print to it

Code:
char buffer[16];

sprintf(buffer,"Hello world!!");
send_string(buffer);

then you can send the serial data directly with..
Code:
 send_string(serial_input);

One last thing

Code:
while(s && *s)

is a bit spurious... just use
Code:
while(*s) {};
 
Last edited:
Declare the function with a normal "char*" and not the "const char*" and use a buffer to print to it

Code:
char buffer[16];

sprintf(buffer,"Hello world!!");
send_string(buffer);

then you can send the serial data directly with..
Code:
 send_string(serial_input);

Wait, I don't get this part, can you elaborate on this?
 
I assume you are placing the input string from the serial port onto the LCD! const char* is a pointer to ROM memory... "Hello World!" is placed in ROM, but a serial string will be placed in RAM...IDATA or UDATA... both have different pointers... You can't use const char* to point to RAM, as you found out it doesn't work..

So my answer was to convert the LCD string routine to a RAM buffer, and use sprintf(); to place information in it. ( sprintf is.. formatted print to a stream ).
 
I assume you are placing the input string from the serial port onto the LCD! const char* is a pointer to ROM memory... "Hello World!" is placed in ROM, but a serial string will be placed in RAM...IDATA or UDATA... both have different pointers... You can't use const char* to point to RAM, as you found out it doesn't work..

So my answer was to convert the LCD string routine to a RAM buffer, and use sprintf(); to place information in it. ( sprintf is.. formatted print to a stream ).

Yep. The input from getch() is supposed to be displayed on the LCD. What I don't get is how am I supposed to put the input from getch() into the send_string(char *s) ? Is it something like this?
Code:
input = getch();
sprintf(buffer,"%c", input);
send_string(buffer);

void send_string(char *s)			//send a string to display in the lcd
{          
  	while (*s)send_char (*s++);
}

I've tried this way and it still doesn't work, or am I doing it wrong?
 
OK... getch(); fetches a single character, not a string.

What data is arriving on the serial port? Do you need a readline function?

Code:
x = 0;
do
   {
    buffer[x] = getch();
   }WHILE(  buffer[x++] == 0xD);
buffer[x] = 0;

**There is no error checking, this is an example.. possible hang**
buffer will fill until a carriage return..


Just a question... Have you set up the serial port?... getch() and putch() have to be set up to use the comport
 
Last edited:
OK... getch(); fetches a single character, not a string.
What data is arriving on the serial port? Do you need a readline function?
Data is coming from a DMM, so the format is somethig like +0000.32 DCV. I need to extract just the nonzero values and the unit type (ACV,DCV,OHM and so...). Yeah, I'm trying to come up with a readline function now. I've tried your code, and it's not working...the loop runs for about 2 times before anging up on the third attempt and the DMM values just got dispalyed without being read...

Just a question... Have you set up the serial port?... getch() and putch() have to be set up to use the comport

Yep. I'm using Hi-Tech's samples for this. Youi can see them on the first post up there. I've tried these before to communicate with the PC so I know it's working.
 
Instead of checking for a carriage return... Test for a NULL ( 0x00 ) or the "V" to end the line
Code:
x = 0;
do
   {
    buffer[x] = getch();
   }WHILE(  buffer[x++] == 0x00);
buffer[x] = 0;

At this point I don't think that a null checking method is going to work...since the do-while loop only loops for one, two times before it got stuck. The output of the DMM from the virtual port is correct, like this:
+0034. 5 DCV
+0034. 5 DCV
+0034. 5 DCV
+0034. 5 DCV

However, since the do-while loop onl;y runs for at most 2 loops, nothing is being shown on the transmit terminal. Does this mean there is a overrun problem?
 
Maybe! When I write for the pic18, I make my own routines and the first thing I do in the ISR is check the serial port for errors. I am going to look into this for you.. For now, set up a timeout flag, so when the comm port errors you can disable CREN and re-enable it again.
 
Maybe! When I write for the pic18, I make my own routines and the first thing I do in the ISR is check the serial port for errors. I am going to look into this for you.. For now, set up a timeout flag, so when the comm port errors you can disable CREN and re-enable it again.

Some updates..
What I did was that I added Interrupt handler that checks whether CREN is off or not, and if so, rectifies the problem. I also changed the reading loop quite a bit, so that the buffer size is limited to 11 only. As it turned out, the code works....kind of. It's not displaying the entire value of the DMM (since I made it to only display 11 chars instead of all 16), but otherwise it is displaying the correct data and updating correctly.

This is my current code:
Code:
void main(void)
{          unsigned char input;
            unsigned char buffer[16];
            unsigned char croot = " 040.09E-3";
            INTCON=0;        // purpose of disabling the interrupts.
            init_comms();    // set up the USART - settings defined in usart.h
            init_all();
            init_timers();
            i = 0;
            lcd_goto(0);
            send_string("Press a key");        
            while(1)
            {           if(i==10) 
                        {           
                          lcd_clr;
                        }
                        if(i==11) 
                        {           
                                    lcd_goto(0);
                                    send_string(buffer);
                                    i = 0;
                                    j++;
                        }
                                    buffer[i] = getch();
                                    PORTB+=1;
                                    i++;
            }
}

void init_timers(void) //initialise interrupts
{PS0 = 0;
PS1 = 0;
PS2 = 0;
PSA = 0;
T0CS = 0;
TMR0IE = 1;
PEIE = 1;
GIE = 1;
//TMR0ON = 1;
T1CKPS0 = 1; //prescaler as 8
T1CKPS1 = 1;
TMR1CS= 0;
T1OSCEN = 0;
TMR1IE = 1;
TMR1L = 0x00;
TMR1H = 0x00;
}

void interrupt ISR() //interrupt function for timer0 and 1
{
 if (T0IE && T0IF)
            {
            if(OERR)
            {CREN=0;
            CREN=1;
            }
            if(FERR)
            {RCREG= RCREG;
            RCREG = RCREG;
            RCREG = RCREG;
            }
            T0IF = 0;
            }
            if (TMR1IF)
            {           TMR1IF = 0;
            }
}

Strangely the Proteus simulation showed the results albeit with the data being displayed in "running lights" mode rather than displaying constantly. I'll have to change the code so that it displays the full 16 chars, but I don't have my programmer with me right now.
 
I think I did it!

I changed the previous method of using getch() and instead used the Hitech's function gets(). The code reads an entire line without errors(mostly). It also displayed correctly on the LCD. I got the data to convert to a float type, before comparing it with certain min/max values before displaying the results on the LCD.
Code:
void main(void)
{
	unsigned char input;
	unsigned char buffer[16];
	unsigned char buffer2[16];
	unsigned char buffer3[16];
	unsigned char neg_state[16] = "Value out range"; 
	unsigned char pos_state[16] = "Value in range"; 
	//int data_value = 45;
	float data_value;

	INTCON=0;	// purpose of disabling the interrupts.

	init_comms();	// set up the USART - settings defined in usart.h
	init_all();
	init_timers();
	i = 0;
	while(1)
	{	gets(buffer);
		data_value = atof(buffer);
		if(data_value != 0.000000)
		{	//printf("\r %f \n",data_value);
			if(data_value < FLO_VALUE)
			{
				lcd_clr();
				lcd_goto(20);
				send_string(neg_state);
				__delay_ms(1);
			}
			else if(data_value > CEI_VALUE)
			{
				lcd_clr();
				lcd_goto(20);
				send_string(neg_state);
				__delay_ms(1);
			}
			else
			{
				lcd_clr();
				lcd_goto(0);
				send_string(buffer);
				lcd_goto(20);
				send_string(pos_state);
				__delay_ms(1);
			}
		}
	}
}
 
Status
Not open for further replies.

New Articles From Microcontroller Tips

Back
Top