CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to CCS Technical Support

PIC16F877A UART

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
wordizlife



Joined: 08 Mar 2012
Posts: 38
Location: Canada

View user's profile Send private message

PIC16F877A UART
PostPosted: Wed Mar 21, 2012 12:48 pm     Reply with quote

Hey Everyone!
I am trying to receive data from a PIC16C505(ELM323) and have my PIC16F877A display the data on an LCD screen. This is for my school project which is an OBD2 interpreter.
The 16C505 is successfully transmitting data, I just cannot seem to get the 16F877A to get the data and send it to the LCD...
If I look at the 16F877A datasheet there is a whole procedure to be followed to set up the asynchronous UART properly.

If I read my PIC16F877A datasheet pg.118

http://ww1.microchip.com/downloads/en/devicedoc/39582b.pdf

It says that I have to set the SYNC, SPEN, RCIE, CREN bits of the TXSTA, RCSTA,... registers in order to receive serial data on my RX pin.

Is this necessary or does this code take care of all this: ??
Code:

#use rs232(baud=9600,parity=N,xmit=PIN_C6,rcv=PIN_C7,bits=8,stream=PORT1)


Here is my main code:

Please let me know if there is anything that might be causing my problems in it and also if it is necessary to setup the UART manually or the Project Wizard will do the job.

Code:
#include <main.h>
#include "D:\Winter_2012\Data_Acquisition\Custom_Include_Files\16F87_LCD_Header_Lab4.c"

#byte TRISD = 0x88
#byte PORTD = 0x08

#bit D0 = 0x8.0
#bit D1 = 0x8.1

char received;
int interrupt_flag;
int interrupt_flag2;

void get_data();

#int_RDA
void  RDA_isr()
{
interrupt_flag = 1;
get_data();
}

void main()
{
   enable_interrupts(INT_RDA);
   enable_interrupts(GLOBAL);

interrupt_flag2 = 0;

While(interrupt_flag2 ==0)
{
LCD_init(); // Initialize LCD
interrupt_flag2 = 1;
}
delay_ms(5000);
TRISD = 0x00; // Set Pot D as outputs
D1 = 1; // Turns on ELM323

}
void get_data(void) // Reads PIC16F877A RX Pin on Interrupt
{
received = getc();
text_string(received); // Sends received data to LCD
}

Here is my LCD Header file:
Code:
#byte TRISC = 0x87
#byte TRISB = 0x86


#bit LCD_RS = 0x6.3 // Register select PORT B
#bit LCD_EN = 0x6.2 // Enable PORT B

#bit LCD_D4 = 0x6.4 // Data bits PORT B
#bit LCD_D5 = 0x6.5 // Data bits PORT B
#bit LCD_D6 = 0x6.6 // Data bits PORT B
#bit LCD_D7 = 0x6.7 // Data bits PORT B

int16 data_val;
int16 num_val;
int16 ones;
int16 tens;
int16 hundreds;

int line_mode;

void pulse(void)
         {
         LCD_EN = 0;
         delay_ms(10);
         LCD_EN = 1;
         }

void set_lcd_pins(int data_val)
         {
         
         if( data_val & 0x80) LCD_D7=1; else LCD_D7=0;
         if( data_val & 0x40) LCD_D6=1; else LCD_D6=0;
         if( data_val & 0x20) LCD_D5=1; else LCD_D5=0;
         if( data_val & 0x10) LCD_D4=1; else LCD_D4=0;
         pulse();
         }

void send_nibbles(int data_val)

         {
         set_lcd_pins(data_val&0xf0);
         delay_ms(10);
         set_lcd_pins((data_val&0x0f)<<4);
         delay_ms(10);
         }
void shift_display_left(void)
{
   LCD_RS = 0;
   send_nibbles(0x18);//Shifts display left
}
void Entry_Mode(void)
{
   LCD_RS = 0;// COMAND MODE
   send_nibbles(0x07);//display moves with cursor & increments(L to R)
}
void Clear_LCD(void)

      {
      LCD_RS = 0;// COMAND MODE
      send_nibbles(0x01);// Clear LCD
      }
     
void LCD_On_Blink_Cursor(void)
      {
      LCD_RS = 0;// COMAND MODE
      send_nibbles(0x0f);// Turn LCD On and Set Blinking Cursor
      }
     
void set_line_mode(int line_mode)
      {
     
      LCD_RS = 0; // COMMAND MODE
     
      If (line_mode == 2)
      {
         send_nibbles(0x28); // Function set to 2 line mode
      }
      If (line_mode == 0)
      {
         send_nibbles(0x20); // Function set to 1 line mode
      }
      }

void set_disp_add_first_line(void)
      {
      LCD_RS = 0; // COMAND MODE
      send_nibbles(0x80); // Set Cursor to First Square
     }
void set_disp_add_second_line(void)
      {
      LCD_RS = 0; // COMAND MODe
      send_nibbles(0xC0);
      }
void LCD_Left_Shifting(void)
      {
      LCD_RS = 0;//Comand mode
      send_nibbles(0x18);
      }
void LCD_Right_Shifting(void)
      {
      LCD_RS = 0;//Comand mode
      send_nibbles(0x1C);
      }
void LCD_Cursor_Home(void)
      {
      LCD_RS = 0;//Comand mode
      send_nibbles(0x02);
      }
void LCD_init(void)
      {
     
      TRISB = 0x00;
      LCD_EN = 1;
      LCD_RS = 0;// COMAND MODE

      set_lcd_pins(0x30);
      set_lcd_pins(0x30);
      set_lcd_pins(0x30);
      set_lcd_pins(0x20);
     
      Clear_LCD();
      set_disp_add_first_line();
      LCD_On_Blink_Cursor();
      set_line_mode(2); // SET THE LINE MODE
      set_disp_add_first_line();
     
      }
     

     
 void text_string(char string1)
      {
      LCD_RS = 1;
      send_nibbles(string1);
      }
 void binary2dec(long value)
   {
   num_val=value;
   ones=num_val%10;
   num_val=num_val/10;
   tens=num_val%10;
   hundreds=num_val/10;
   
   send_nibbles(hundreds+0x30);
   send_nibbles(tens+0x30);
   send_nibbles(ones+0x30);
   }
 void disp_number(hundreds,tens,ones)
   {
   send_nibbles(hundreds+0x30);
   send_nibbles(tens+0x30);
   send_nibbles(ones+0x30);
   }
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Wed Mar 21, 2012 9:55 pm     Reply with quote

Quote:

#int_RDA
void RDA_isr()
{
interrupt_flag = 1;
get_data();
}


}
void get_data(void) // Reads PIC16F877A RX Pin on Interrupt
{
received = getc();
text_string(received); // Sends received data to LCD

}

Don't send data to the LCD from within the #int_rda routine.
The #int_rda should only get the characters and put them in a buffer.
Read the buffer and send the chars to the lcd, outside of the #int_rda
routine. See the CCS example file, Ex_sisr.c, which shows how to
implement a circular buffer with #int_rda:
Quote:

c:\program files\picc\examples\ex_sisr.c


Quote:

#use rs232(baud=9600,parity=N,xmit=PIN_C6,rcv=PIN_C7,bits=8,stream=PORT1,
ERRORS)

Add the ERRORS parameter to automatically clear any UART lockups
due to overrun or framing errors. You only have one #use rs232()
statement so you don't need to define a stream. You're not using
'streams' in your main code anyway(fgetc function, etc.). So you can
delete the stream parameter and name.
wordizlife



Joined: 08 Mar 2012
Posts: 38
Location: Canada

View user's profile Send private message

PostPosted: Thu Mar 22, 2012 2:23 pm     Reply with quote

Perfect, I will try that out tonight!

Thanks for the reply, it's very appreciated!
ckielstra



Joined: 18 Mar 2004
Posts: 3680
Location: The Netherlands

View user's profile Send private message

PostPosted: Thu Mar 22, 2012 3:53 pm     Reply with quote

Also check the end of your main function. Now it just ends there. The compiler adds a sleep instruction at the end and your processor stops.
Common practice is to have an empty forever loop at the end to prevent the processor from going to sleep, and then your interrupts can continue to do the real work.
wordizlife



Joined: 08 Mar 2012
Posts: 38
Location: Canada

View user's profile Send private message

PostPosted: Thu Mar 22, 2012 7:59 pm     Reply with quote

I am viewing the EX_SISR.C example file but I do not fully understand the code.

What I think it is doing is with this code:
Code:

#int_rda
void serial_isr(){
int t;

buffer[next_in]=getc();
t=next_in;
next_in=(next_in+1) % BUFFER_SIZE;
if(next_in==next_out)
next_in=t; // Buffer full!!
}
#define bkbhit (next_in!=next_out)

BYTE bgetc() {
BYTE c;

while(!bkbhit);
c=buffer[next_out];
next_out=(next_out+1) % BUFFER_SIZE;
return(c);
}

is t = the data received. So for example if I wanted to use my function to send the data to the LCD I would simply have to do this?:
Code:

Write_string_lcd(t);


This code is setting the buffer size?:
Code:

next_out=(next_out+1) % BUFFER_SIZE;


I am also having trouble understanding what use this code has and if I even need to use it:
Code:

BYTE bgetc() {
BYTE c;

while(!bkbhit);
c=buffer[next_out];
next_out=(next_out+1) % BUFFER_SIZE;
return(c);


Also do I need to clear the buffer after I have gotten the data I needed from it or will it over write itself when new data comes in?

Thanks again!
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Thu Mar 22, 2012 8:34 pm     Reply with quote

You are looking at things at a low level. You don't need to do that.
You don't need to understand the #int_rda code. Effectively it could be
a black box. All you need to understand is the interface. You can ignore
the internal code in the Ex_sisr.c functions.

In your main() code, you call bkbhit() to see if there is a character
available in the buffer. If so, you call bgetc() to get it. Then you can do
something with the character that you got, or you can wait until more
of them come in, and get them too. You could put them in an array,
and write a 0x00 byte at the end. Then you would have a string in an
array and you could do something with it in your main() code.
wordizlife



Joined: 08 Mar 2012
Posts: 38
Location: Canada

View user's profile Send private message

PostPosted: Fri Mar 23, 2012 6:50 am     Reply with quote

Ok, and do you recommend I simply copy paste this code into mine or "#include" it?

Also, do I need to clear the buffer after I have put the data into an array or will it ovewrite itself automatically?

You are saving my life, I have been messing around with this for two weeks now :P (still a beginner when it comes to programming) Thank you!!
jeremiah



Joined: 20 Jul 2010
Posts: 1344

View user's profile Send private message

PostPosted: Fri Mar 23, 2012 6:57 am     Reply with quote

As a note, if you use the ex_sisr.c code, make sure that BUFFER_SIZE is a power of 2 (2,4,8,16,32,64,...). The modulus operator (%) is a slow operation (usually a DIV instruction is used) UNLESS you are using a power of 2. Then it replaces the modulus operation with an AND operation, which is much faster than the DIV

EDIT:
I would do copy/paste what you need. The bgetc() function will handle "erasing" the buffer by moving the next_out indicator, freeing up the previous location to be overwritten.
wordizlife



Joined: 08 Mar 2012
Posts: 38
Location: Canada

View user's profile Send private message

PostPosted: Wed Mar 28, 2012 3:02 pm     Reply with quote

Sorry I have not had much time to work on the project but here is what I have coded without any successful results. I copy pasted sections of the sample code and use this function to send the data to my lcd screen.
Code:

While(1)
{
while(bkbhit)
   { 
      For (i = 0; i<=24; i++)
      {
      received_data[i] = bgetc();
      }
   }
      For (j = 0; j<=24; j++)
      {
      text_string(received_data[j]);
      }   
}

I created an array and I am using the bgetc() function to get the data but only giberish shows up on the LCD....?

You mentioned setting up the BUFFER_SIZE as a power of 2. How would I go about doing this?

Main Code:
Code:

#include <main.h>
#include "D:\Winter_2012\Project_2\Project2_LCD_Driver.c"
/**** Required Registers ****/

#byte TRISA = 0x85

#bit A5 = 0x5.5

char received;
int interrupt_flag;
int interrupt_flag2;
int i;
int j;

#int_rda
void serial_isr() {
   int t;

   buffer[next_in]=getc();
   t=next_in;
   next_in=(next_in+1) % BUFFER_SIZE;
   if(next_in==next_out)
     next_in=t;           // Buffer full !!
}

#define bkbhit (next_in!=next_out)

BYTE bgetc() {
   BYTE c;

   while(!bkbhit) ;
   c=buffer[next_out];
   next_out=(next_out+1) % BUFFER_SIZE;
   return(c);
}

char received_data[24]; //16F877A data Array

void main()
{
   enable_interrupts(int_rda);
   #if defined(__PCD__)
   enable_interrupts(intr_global);
   #else
   enable_interrupts(global);
   #endif

interrupt_flag2 = 0;

While(interrupt_flag2 ==0)
   {
   LCD_init(); // Initialize LCD
   set_disp_add_first_line();//set display address first line
   interrupt_flag2 = 1;
   }
delay_ms(5000);
TRISA = 0x00; // Set Pot D as outputs
A5 = 1; // Turns on ELM323
i = 0;
j = 0;
While(1)
{
while(bkbhit)
   { 
      For (i = 0; i<=24; i++)
      {
      received_data[i] = bgetc();
      }
   }
      For (j = 0; j<=24; j++)
      {
      text_string(received_data[j]);
      }   
}
}

Main Code Header File:
Code:

#if defined(__PCM__)
#include <16F877A.h>
#device adc=16


#FUSES NOWDT                    //No Watch Dog Timer
#FUSES HS                       //High speed Osc (> 4mhz for PCM/PCH) (>10mhz for PCD)
#FUSES NOBROWNOUT               //No brownout reset
#FUSES NOLVP                    //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#FUSES NOPROTECT

#use delay(clock=20000000)
#use rs232(baud=9600,parity=N,xmit=PIN_C6,rcv=PIN_C7,bits=8,ERRORS)

#endif

#define BUFFER_SIZE 32
BYTE buffer[BUFFER_SIZE];
BYTE next_in = 0;
BYTE next_out = 0;

There is progress!! At least the LCD is displaying something! Smile
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group