|
|
View previous topic :: View next topic |
Author |
Message |
nacho72
Joined: 20 Oct 2014 Posts: 13
|
Problems with INT_RDA. PIC 16F877. |
Posted: Tue Oct 21, 2014 5:34 am |
|
|
Hello,
I'm trying to send and receive data using the UART of my PIC16F877, through the pins RC7 and RC6. To do so I designed a circuit, where the user can press 3 different buttons, generating a diferent signal in the TX pin.
I joined with a wire the TX pin with the RX pin, so I will be able to read exactly what I send, and finallt display the message in an LCD.
But there is a problem with the INT_RDA, because it never reach the "READ CHAR TO DISPLAY IN LCD" part.
Here is the code I have writtem:
Code: |
#include "16F877.h"
#fuses NOWDT, HS
#use delay (clock=20000000)
#include <string.h>
#include <stdlib.h>
#include "lcd_ATE8.c" //for the LCD
#use RS232(UART, baud = 115200, xmit = PIN_C6 ,rcv = PIN_C7, PARITY = N, bits = 8, stop = 1, stream = BLUETOOTH)
#use fast_io(B)
#use fast_io(C)
#byte PORTB=0x06
#bit RB0=PORTB.0
#bit RB1=PORTB.1
#bit RB2=PORTB.2
#bit RB3=PORTB.3
#bit RB4=PORTB.4
#bit RB5=PORTB.5
#bit RB6=PORTB.6
#bit RB7=PORTB.7
#byte PORTC=0x07
#bit RC7=PORTC.7
#INT_RB
void key()
{
int btn; /* Determinan el boton pulsado */
delay_ms(20); // Espera de 20ms para evitar rebotes.
if(RB4) btn=0;
else if(RB5) btn=1;
else if(RB6) btn=2;
else return;
switch (btn)
{
case 0: lcd_putc("\f"); lcd_putc("H was pressed"); fprintf(BLUETOOTH,"H"); break;
case 1: lcd_putc("\f"); lcd_putc("W was pressed"); fprintf(BLUETOOTH,"W");break;
case 2: lcd_putc("\f"); lcd_putc("G was pressed"); fprintf(BLUETOOTH,"G");break;
}
}
#INT_RDA
void bt()
{
int x = 0;
if(RC7==1) //READ CHAR TO DISPLAY IN LCD
{
lcd_putc("\f");
lcd_putc("we get something");
//x=getc();
//lcd_putc(x)
}else
{
x=getc();
}
}
void main()
{
lcd_init();
lcd_putc("\f");
lcd_putc("START...");
PORTB=0;
set_tris_b(0b11110000);
set_tris_c(0b10010000);
enable_interrupts(GLOBAL);
enable_interrupts(INT_RB);
enable_interrupts(INT_RDA);
while(TRUE)
{
}
} |
Can anyone tell me what am I doing wrong with the INT_RDA?
Thank you very much for your time.
**This is my first post, in case I need to add further information, or something I said is not accurate, please let me know to edit the post or to write additional information below** [/img] |
|
|
Gabriel
Joined: 03 Aug 2009 Posts: 1067 Location: Panama
|
|
Posted: Tue Oct 21, 2014 6:40 am |
|
|
You are printing inside the ISR... this is bad.
Look at circular buffers.... and process data or display data outside the ISR.
also you seem to be using bluetooth... get this to work first using plain wiered conections.... then go for bluetooth _________________ CCS PCM 5.078 & CCS PCH 5.093 |
|
|
nacho72
Joined: 20 Oct 2014 Posts: 13
|
|
Posted: Tue Oct 21, 2014 7:16 am |
|
|
Hello Gabriel,
Uhm I'm not sure how else can I print the information. I read for the UART the easiest way is using a stream.
And yes, as you said I was planning to add a Bluetooth module here, but firstly I just pluged with a wire the TX pine with the RX, so see if the interruption detects when a signal is arriving to RX. But there is the problem.
I used an oscilloscope to check the signal in RX and TX, and I saw it is always high, but it moves to low level when some bits are transmited or recieved.
So my plan was to include and if, that just store the result of getc() when the level of RC7 (RX pin) is low.
But it is not working. |
|
|
nacho72
Joined: 20 Oct 2014 Posts: 13
|
|
Posted: Tue Oct 21, 2014 7:20 am |
|
|
Hello Gabriel,
I think I just understood why you mean with printing outside of the ISR.
Do you mean, in the if clause, make a call to another method, for example DoPrint(). And in DoPrint() make the printing? Like this:
Code: | void DoPrinting()
{
lcd_putc("\f");
lcd_putc("Something to print");
}
#INT_RDA
void bt()
{
int x = 0;
if(RC7==1)//()
{
DoPrinting();
}else
{
x=getc();
}
} |
|
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9229 Location: Greensville,Ontario
|
|
Posted: Tue Oct 21, 2014 7:33 am |
|
|
No he doesn't...
ISRs must be small and fast !
Inside the USR set a flag then exit.
This 'flag' is just a variable (say 'uart_char_yes')
Within 'main()' check the status of that flag and act accordingly...
if it's set...do something....clear variable.
any 'printing' takes a lot of time and should NOT be done inside any ISR. The same is true about 'math' operations,esp. floating point!
Simply set a 'flag' and exit.
If you search this forum there's 1000s of examples of this.....
jay |
|
|
Gabriel
Joined: 03 Aug 2009 Posts: 1067 Location: Panama
|
|
Posted: Tue Oct 21, 2014 8:57 am |
|
|
Code: | #INT_RDA
void SerialInt()
{
Receive_String[counter_read]=getchar(); // Gets chars from uart
counter_read++; // Increment counter
if(counter_read==SIZE_BUFFER)counter_read=0; // Circle Buffer
} |
This is my default Serial ISR... others might have their own. this is what I use in most of my code.
Basically it stores in "Receive_String" all incoming serial data and when it fills up it loops back (circular buffer) to the first position and starts over writing the oldest data.
when i say printing out of the ISR is have a function in your MAIN or other places NOT IN THE ISR or even CALLED from the ISR, that prints the contents of the Buffer.
you should not call any functions from your ISR.... as temtronic says.. use flags.
GET IN GET OUT FAST - like a bad date.
G. _________________ CCS PCM 5.078 & CCS PCH 5.093 |
|
|
ezflyr
Joined: 25 Oct 2010 Posts: 1019 Location: Tewksbury, MA
|
|
Posted: Tue Oct 21, 2014 8:58 am |
|
|
Hi,
No, that is NOT what Gabriel meant at all! What you need to do is 'buffer' the incoming serial data, and then print it outside of the interrupt as time allows. If you bog the interrupt down with printing, or calls to printing, inside the interrupt, then you are guaranteed to lose data! Look at CCS example 'ex_SISR.c' to see the proper way to setup an interrupt driven serial buffer!
Also, this topic has been hashed and rehashed literally thousands of times. You really should spend an hour or two looking at the forum archives! Trust me, it will be worth the effort!
John |
|
|
nacho72
Joined: 20 Oct 2014 Posts: 13
|
|
Posted: Tue Oct 21, 2014 9:38 am |
|
|
Thank you very much to all of you for your effort and recommendations. I will follow all of them and test them before posting a new version of my code.
But I would like to add something else I found in another answer from this forum.
The issue in that topic, was the code goes inside the INT_RDA so often, probably beause the pin was catching always some noise, so to avoid that situation and don't buffer noise, someone suggested to include an if() inside the INT_RDA, to check the state of the pin RX before buffering anything. They also mentioned to flush the RDA interruption a getc() is necessary, even if you don't want to use the data.
In addition, I was using an oscilloscope to measure the signal in RX, and I saw it was held in around 2V, moving from there to almost 0 repeatedly while receiving something. [something like 111111011010010011111111]
So I was thinking to write:
Code: | #INT_RDA
void SerialInt()
{
if(RC7==0)
{
Receive_String[counter_read]=getchar(); // Gets chars from uart
counter_read++; // Increment counter
if(counter_read==SIZE_BUFFER)counter_read=0; // Circle Buffer
}else{
getc()
}
} |
But I'm not sure if that change from 2.2V to almost 0V is going to be enough to be detected as a logical 0.
I will test these things, and keep looking for information, as you recommended me, before posting anything else.
Thanks again. |
|
|
ezflyr
Joined: 25 Oct 2010 Posts: 1019 Location: Tewksbury, MA
|
|
Posted: Tue Oct 21, 2014 10:34 am |
|
|
Hi,
You are talking 'bandaids' here. I've got dozens of designs, and thousands of deployed units that don't require what you are describing. What have you got connected to the Rx input pin on the PIC? Most of the time this input is connected to a MAX232 type IC if you are connected to a PC, and directly if you are connecting to a peripheral device (GPS, GSM modem, Bluetooth interface, etc.). In the direct connection case, be sure the PIC and peripheral are operating at the same voltage, or be prepared to do some level shifting of the interface signals.
John |
|
|
nacho72
Joined: 20 Oct 2014 Posts: 13
|
|
Posted: Tue Oct 21, 2014 11:54 am |
|
|
Hi,
Well I was reading several posts, trying the solutions they give and changing my code. But I still can't solve something as easy as triggering the INT_RDA several times.
The program goes inside the INT_RDA just the first time I launch the code, but it doesn't come back again. For testing your ideas and recommendations I wrote this simple code:
Code: | #include "16F877.h"
#fuses NOWDT, HS
#use delay (clock=20000000)
#include "lcd_ATE8.c"
#use RS232(UART, baud = 9600 /*115200*/, xmit = PIN_C6 ,rcv = PIN_C7, PARITY = N, bits = 8, stop = 1, stream = BLUETOOTH)
#use fast_io(B)
#use fast_io(C)
#byte PORTB=0x06
#bit RB0=PORTB.0
#bit RB1=PORTB.1
#bit RB2=PORTB.2
#bit RB3=PORTB.3
#bit RB4=PORTB.4
#bit RB5=PORTB.5
#bit RB6=PORTB.6
#bit RB7=PORTB.7
#byte PORTC=0x07
#bit RC7=PORTC.7 // Se definen los puertos
#INT_RB
void tecla()
{
int boton; /* Determinan el boton pulsado */
delay_ms(20); // Espera de 20ms para evitar rebotes.
if(RB4) boton=0;
else if(RB5) boton=1;
else if(RB6) boton=2;
else return;
switch (boton)
{
case 0: RB3=1; fprintf(BLUETOOTH,"HHHHHHHHHHHHHHHH\r\n"); break;
case 1: RB3=0; fprintf(BLUETOOTH,"MMMMMMMMMMMMMMMM\r\n");break;
case 2: RB3=1; fprintf(BLUETOOTH,"ZZZZZZZZZZZZZZZZ\r\n");break;
}
}
#INT_RDA
void bt()
{
char z;
z==getc();
new_char = 1;
}
void main()
{
lcd_init(); // Inicializa el LCD.
lcd_putc("\f"); // Borramos el display
lcd_putc("STARTING...");
PORTB=0;
set_tris_b(0b11110000); // 1 es entrada, 0 es salida
//set_tris_c(0b10010000);
enable_interrupts(INT_RB);
enable_interrupts(INT_RDA);
enable_interrupts(GLOBAL);
while(TRUE)
{
if(new_char==1)
{
lcd_putc("\f");
lcd_putc("GETTING SOMETHING");
delay_ms(1000);
lcd_putc("\f");
new_char=0;
}
}
} |
So in this code, when pressing the buttons, something is sent throught TX, and with the oscilloscope I have checked it is recieved in the pin RX. But the only time the program goes inside RDA is at the begining. Then never again appears the message "GETTING SOMETHING".
I commented the instruction set_tris_c(0b10010000); Because in other post recommend to trust in the compiler to solve it, avoiding an extra source of problems.
I also changed the baud rate to several different values (but I think that should not change anything, after all, the pin TX is wired to RX for this testing step).
Can anyone tell me where could be the problem? I think I followed all the recommendations above, and some others that appear in related topics, but I'm sure I'm still missing something.
Thank you! |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19520
|
|
Posted: Tue Oct 21, 2014 12:58 pm |
|
|
When INT_RDA triggers it is saying 'there is a character that needs to be read now'.
Your interrupt handler needs to do this. No delays, no prints etc..
It should just read the character. Set a flag to say 'I have a character - handle it', and immediately exit.
Your main code then reacts to the flag, inspects the character, delays, and prints.
At 9600bps, characters can arrive in just over 1mSec. Your 20mSec delay, can miss 9 characters.
Then the prints take over another 18mSec. Another 17 characters missed.
You just cannot handle things like this.
Repeat the mantra 50 times.
Keep interrupt handlers short. Just do what the interrupt requires. Nothing else. |
|
|
nacho72
Joined: 20 Oct 2014 Posts: 13
|
|
Posted: Tue Oct 21, 2014 1:37 pm |
|
|
But Ttelmah, that is for another interrupt, that one is to detect if the buttons have been pressed (and which one).
But below that I wrote the UART interruption, and it is as short as possible.
But anyway, I think I found the mistake (Or at least one possible mistake).
As I understood John said above the voltage level should be the same in RX and TX. And in my case it is not, because in the circuit I designed there are 2 resistors to adapt what in the future will be a Bluetooth module, so in TX it is around 5 but in RX it is around 3.3
Before leaving the labs I made short circuit between both, adding in the code a loop with a putc("A") and I think with that the INT_RDA was reached, but I will need to test it again tomorrow with more time.
Last edited by nacho72 on Tue Oct 21, 2014 5:36 pm; edited 1 time in total |
|
|
alan
Joined: 12 Nov 2012 Posts: 357 Location: South Africa
|
|
Posted: Tue Oct 21, 2014 1:51 pm |
|
|
Once you are in a interrupt you can not serve another, only when you exit.
Thus if you wait 20ms in the one interrupt you cannot get to the RDA interrupt.
Regards |
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9229 Location: Greensville,Ontario
|
|
Posted: Tue Oct 21, 2014 2:03 pm |
|
|
You now tell us about the classic 5 vs 3 problem...
5 volt PIC, 3 volt peripheral ( bluetooth module ).
There's probably 1000+ threads about this here....
You're going to run into MORE 'fun' trying to interface them.
rule #1 . use a 3 volt PIC with 3 volt peripherals if possible. Any 'L' version PIC will work fine at 3 volts.Since 99% of all new peripherals are 3 volts the simplest,easiest, most reliable solution is to buy and use an 'L' type PIC.
rule #2. if you don't follow rule #1 , then you MUST have some form of 'level translation' between the 5 V PIC and 3 V peripheral.
rule #3, if you don't follow rule # 1 or rule #2 then you will NEVER get the project running correctly, you'll waste days if not weeks of your time, and the end result is a failure.
hth
jay |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19520
|
|
Posted: Tue Oct 21, 2014 2:20 pm |
|
|
Also, search the forum for why you don't use INT_RB to detect switches.
Using a delay here ruins everything.
You either need hardware debounce, or to use a timer interrupt for debounce, and get out of the interrupt ASAP. |
|
|
|
|
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
|