View previous topic :: View next topic |
Author |
Message |
wordizlife
Joined: 08 Mar 2012 Posts: 38 Location: Canada
|
PIC16F877A UART |
Posted: Wed Mar 21, 2012 12:48 pm |
|
|
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
|
|
Posted: Wed Mar 21, 2012 9:55 pm |
|
|
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
|
|
Posted: Thu Mar 22, 2012 2:23 pm |
|
|
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
|
|
Posted: Thu Mar 22, 2012 3:53 pm |
|
|
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
|
|
Posted: Thu Mar 22, 2012 7:59 pm |
|
|
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
|
|
Posted: Thu Mar 22, 2012 8:34 pm |
|
|
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
|
|
Posted: Fri Mar 23, 2012 6:50 am |
|
|
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: 1346
|
|
Posted: Fri Mar 23, 2012 6:57 am |
|
|
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
|
|
Posted: Wed Mar 28, 2012 3:02 pm |
|
|
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! |
|
|
|