View previous topic :: View next topic |
Author |
Message |
pilar
Joined: 30 Jan 2008 Posts: 197
|
Timeout with TMR3 for INT_RDA |
Posted: Thu Jan 30, 2014 3:16 pm |
|
|
Hello,
I'm using CCS V4.079 to implement a timeout of 30 seconds when I receives data by the UART, but I can not leave the loop reception, someone can tell me where is my mistake!
Here is my code:
Code: | #include <18F4520.h>
#fuses HS,NOWDT,NOPROTECT,NOLVP,NOBROWNOUT
#use delay(clock=20000000)
#use rs232(baud=19200, xmit=PIN_C6, rcv=PIN_C7, timeout=5000)// RS232 Estándar
int8 next_in = 0;
int8 next_out = 0;
int8 theChar = 0;
int8 i;
int8 buffer[32];
char Signal_VAL[6];
int32 TimeOUT3_Clock;
int32 TimeOUT3;
#INT_RDA
void serial_isr() {
int t;
buffer[next_in]=getc();
t=next_in;
next_in=(next_in+1) % 32;
if(next_in==next_out)
next_in=t;
}
#define bkbhit (next_in!=next_out)
int8 bgetc() {
BYTE c;
WHILE(!bkbhit) ;
c=buffer[next_out];
next_out=(next_out+1) % 32;
return(c);
}
void Clear_Buffer(){
for (i=0; i<32; i++){
buffer[i]= 0x00;
}
next_in = 0;
next_out = 0;
}
#INT_TIMER3
void Set_TimeOUT3() {
Set_Timer3(64911);
TimeOUT3_Clock++;
if (TimeOUT3_Clock >= 1000){
TimeOUT3++;
TimeOUT3_Clock = 0;
}
}
void Read_ISIG(){
Clear_Buffer();
TimeOUT3 = 0;
TimeOUT3_Clock = 0;
enable_interrupts(INT_TIMER3);
printf("AT_ISIG=1\r");
Delay_ms(100);
while ((theChar != '_') || (TimeOUT3 <= 30))theChar = bgetc();
theChar = bgetc();
if (theChar == 'I') {
theChar = bgetc();
if (theChar == 'S') {
theChar = bgetc();
if (theChar == 'I') {
theChar = bgetc();
if (theChar == 'G') {
theChar = bgetc();
if (theChar == ':') {
theChar = bgetc();
for (i=0; i<5; i++){
Signal_VAL[i] = bgetc();
}
}
}
}
}
}
disable_interrupts(INT_TIMER3);
}
void main() {
enable_interrupts(global);
enable_interrupts(int_rda);
setup_timer_3(T3_INTERNAL | T3_DIV_BY_8);
set_timer3(64911);
disable_interrupts(INT_TIMER3);
Read_ISIG();
while (TRUE){
delay_ms(200);
}
} |
|
|
|
asmboy
Joined: 20 Nov 2007 Posts: 2128 Location: albany ny
|
|
Posted: Thu Jan 30, 2014 3:21 pm |
|
|
what do you think happens in bgetc() if you call it and
no character is waiting in the buffer?? |
|
|
pilar
Joined: 30 Jan 2008 Posts: 197
|
|
Posted: Thu Jan 30, 2014 3:29 pm |
|
|
Precisely by this is TimeOUT3 produced by TMR3 |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19546
|
|
Posted: Thu Jan 30, 2014 3:50 pm |
|
|
The point is that instead of calling bgetc, when you want a character, you need to use bkbhit, to see if there _is_ a character.
So you use something like:
Code: |
int8 wait_getc(void)
{
while (TRUE)
{
if (bkbhit())
return bgetc();
if (TIMEOUT3>30)
return 0;
}
}
|
This will exit with a '0' character, if one doesn't arrive, and timeout3 gets to 31
bgetc, sits _waiting_ for a character if one is not available, so TIMEOUT3 is never checked in your code....
Best Wishes |
|
|
pilar
Joined: 30 Jan 2008 Posts: 197
|
|
Posted: Thu Jan 30, 2014 4:44 pm |
|
|
I made the changes that you suggest me but the problem persists or is there something I'm doing wrong?
Code: | #include <18F4520.h>
#fuses HS,NOWDT,NOPROTECT,NOLVP,NOBROWNOUT
#use delay(clock=20000000)
#use rs232(baud=19200, xmit=PIN_C6, rcv=PIN_C7, timeout=5000)// RS232 Estándar
int8 next_in = 0;
int8 next_out = 0;
int8 theChar = 0;
int8 i;
int8 buffer[32];
char Signal_VAL[6];
int32 TimeOUT3_Clock;
int32 TimeOUT3;
int8 wait_getc() ;
#INT_RDA
void serial_isr() {
int t;
buffer[next_in]=getc();
t=next_in;
next_in=(next_in+1) % 32;
if(next_in==next_out)
next_in=t;
}
#define bkbhit (next_in!=next_out)
int8 bgetc() {
BYTE c;
WHILE(!bkbhit) ;
c=buffer[next_out];
next_out=(next_out+1) % 32;
return(c);
}
int8 wait_getc()
{
while (TRUE)
{
if (bkbhit)
return bgetc();
if (TIMEOUT3>30)
return 0;
}
}
void Clear_Buffer(){
for (i=0; i<32; i++){
buffer[i]= 0x00;
}
next_in = 0;
next_out = 0;
}
#INT_TIMER3
void Set_TimeOUT3() {
Set_Timer3(64911);
TimeOUT3_Clock++;
if (TimeOUT3_Clock >= 1000){
TimeOUT3++;
TimeOUT3_Clock = 0;
}
}
void Read_ISIG(){
Clear_Buffer();
TimeOUT3 = 0;
TimeOUT3_Clock = 0;
enable_interrupts(INT_TIMER3);
printf("AT_ISIG=1\r");
Delay_ms(100);
while (theChar != '_') theChar = wait_getc();
theChar = wait_getc();
if (theChar == 'I') {
theChar = wait_getc();
if (theChar == 'S') {
theChar = wait_getc();
if (theChar == 'I') {
theChar = wait_getc();
if (theChar == 'G') {
theChar = wait_getc();
if (theChar == ':') {
theChar = wait_getc();
for (i=0; i<5; i++){
Signal_VAL[i] = wait_getc();
}
}
}
}
}
}
disable_interrupts(INT_TIMER3);
}
void main() {
enable_interrupts(global);
enable_interrupts(int_rda);
setup_timer_3(T3_INTERNAL | T3_DIV_BY_8);
set_timer3(64911);
disable_interrupts(INT_TIMER3);
Read_ISIG();
while (TRUE){
delay_ms(200);
}
} |
|
|
|
asmboy
Joined: 20 Nov 2007 Posts: 2128 Location: albany ny
|
|
Posted: Thu Jan 30, 2014 6:57 pm |
|
|
ok next item:
the structure/logic of
read_isig()
is fatally flawed in that it seems TOTALLY error intolerant.
by this i mean that if the order of receiving of ISIG:
does not occur as you seem to expect- then
you are unable to recover and search for a correct preamble.
ALSO in the problematic read_isig() function ,
do you really intend for this to be executed before all the "if tests ??
Code: |
theChar = wait_getc();
theChar = wait_getc();
|
are you expecting a junk character and wish to skip it ?
if so - that structure will not work as you intend either. |
|
|
pilar
Joined: 30 Jan 2008 Posts: 197
|
|
Posted: Thu Jan 30, 2014 7:51 pm |
|
|
Hi asmboy,
The possible answer that I can be received from device are:
or
Code: |
_ISIG: 45.7500000
_ISIG: 50.2000000
_ISIG: 52.6000000 |
......another possibility is that I did not receive any answer of device and the microcontroller remains in that condition in an infinite loop ...
I just want to capture only a (01) ISIG value with two decimal, then get out of the loop capture and also prevent the microcontroller hanging up in that infinite loop .... |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19546
|
|
Posted: Fri Jan 31, 2014 4:07 am |
|
|
Change a lot.....
1) Get rid of the timeout in the #use rs232. This may well cause problems. You are using interrupt driven serial, which means the 'getc' routine, will _only_ be called when a character _is_ available. So no need for a timeout. Having this implies the compiler will add timing code to the getc routine, which you do not want in an ISR, and may interact with other things....
2) Fundamentally re-think your approach. Do a search here for 'state machine'. You want something like:
Code: |
#include <18F4520.h>
#fuses HS,NOWDT,NOPROTECT,NOLVP,NOBROWNOUT
#use delay(clock=20000000)
#use rs232(baud=19200, UART1, ERRORS)
#include <ctype.h>
//states
enum (waiting,header,digit) read_state=waiting;
char header[]="ISIG: ";
//buffer definitions
int8 next_in = 0, next_out=0;
#define BUFFSIZ 32
int8 buffer[BUFFSIZ];
#define bkbhit (next_in!=next_out)
#define CLEAR_BUFF next_in=next_out
//you don't need to set the values to zero, just to each other.
//you also don't need to empty the buffer itself.
int8 bgetc(void)
{
BYTE c;
if(!bkbhit)
return 0; //should never get here
c=buffer[next_out];
if (++next_out==BUFFSIZ)
next_out=0;
return(c);
}
#INT_RDA
void serial_isr(void)
{
int t;
buffer[next_in]=getc();
t=next_in;
if (++next_in==BUFFSIZ)
next_in=0;
if(next_in==next_out)
next_in=t;
}
//clock definitions
int1 timeout=FALSE;
int8 waitfor=0;
#define PER_SEC 18 //19 ticks per second
#INT_TIMER3
void timer3_tick(void)
{
static tick=PER_SEC;
if (tick)
--tick;
else
{
if (waitfor)
--waitfor;
tick=PER_SEC;
}
}
int32 Read_ISIG(void)
{
int8 rxc;
int8 charnum=0;
int32 val=0;
int8 ctr=8; //much larger than needed
int1 rescan=FALSE;
//sit waiting for max 30 seconds for ISIG message
read_state=waiting;
timeout=30;
while (timeout)
{
if (bkbhit)
{
//now there is a character
rxc=bgetc(); //read it
do
{
if (rescan) //here a test has failed
{
rescan=FALSE;
state=waiting;
charnum=0;
}
switch (read_state) {
case waiting:
//here looking for the first character '_'
if (rxc=='_')
read_state++;
break;
case header:
//now need to look for the ISIG header
if (rxc==header[charnum])
{
charnum++;
if (header[charnum]=='\0') //end of header
read_state++;
}
else
{
//problem
rescan=TRUE;
//re-test the current character
}
break;
case digit:
//Now should only be receiving digits or DP
if (rxc=='.')
{
ctr=2; //number of digits to read after the decimal
}
else
{
if (isdigit(rxc))
{
//here we have a digit
rxc-='0'; //convert to decimal
val*=10;
val+=rxc;
if (--ctr==0)
{
//have read two digits after the decimal
return val;
}
}
else
{
//Here we have hit a non digit. Something wrong
//rescan the character
rescan=TRUE;
}
}
break;
}
if (read_state>digit)
read_state=waiting; //trap impossible situation
}
while (rescan); //loop if the character needs to try again
} //end of character tests
}//only loop here while timeout > 0
return 0;
}
void main() {
int32 isigval;
enable_interrupts(global);
enable_interrupts(int_rda);
setup_timer_3(T3_INTERNAL | T3_DIV_BY_4);
enable_interrupts(INT_TIMER3);
//timer 3 now ticks at 19/sec 2000000/(4*4*65536)
while (TRUE)
{
isigval=Read_ISIG()
//will return with an ISIG value if available, or '0' if timeout.
delay_ms(200);
}
}
|
Now, 'no warranties'. Typed this untested, to show the approach.
This uses a timer at 19Hz (keep things slow, unless they need to be fast), and will wait for 30 seconds to find an 'ISIG' message.
It decodes the message to an int32 value (*100). So your
"_ISIG: 52.6000000" message should return 5260.
There are certain other changes, the test for 'BUFFSIZ', rather than '%', which is safe for non binary buffer sizes (CCS should have a warning about this in the example, but don't...).
If a character fails to match, it jumps back and tests it against the '_' again, so should recover when unexpected data arrives.
Very different.....
Just realised I had omitted the ':' from the header. Have added it.
Best Wishes
Last edited by Ttelmah on Sun Feb 02, 2014 1:18 am; edited 1 time in total |
|
|
Mike Walne
Joined: 19 Feb 2004 Posts: 1785 Location: Boston Spa UK
|
|
Posted: Fri Jan 31, 2014 5:32 am |
|
|
I suggest you change the structure of your code. Work on the KISS principle.
Your very deep nesting is asking for trouble.
You also need to be clear on what you want to happen when:-
1) No data is received.
2) Wrong/garbled data is received during preamble. Continue waiting or re-start?
3) Wrong type of data received when expecting numerical digits.
4) Total time or time between characters is too long?
There are ways to go without requiring deep nesting, such as:-
a) Treat the preamble as a string and simply index through looking for the next character.
b) Create a state machine which looks for each preamble character in turn.
c) ...................
Then deal with the data.
Mike |
|
|
pilar
Joined: 30 Jan 2008 Posts: 197
|
|
Posted: Fri Jan 31, 2014 8:11 am |
|
|
Ttelmah and Mike, thanks for your feedback, I will try to explain what I intend to do ..
I have a device which I need to link to a microcontroller, this device only answer to queries made by the microcontroller using AT commands, if the micro no makes a query the device does not answer...
The problem is that the ranswer time to a command varies greatly depending on which is the query, there are answer that occur after 10 ms or 100ms as there are also answers that take up to 20 0 30 seconds ..
The number of query commands that need to send the micro is the amount, if trying to use a state machine for these characters, I would have to implement a state machine for each answer ...
What I need is:
1 - If no data is received, after 30 seconds out of the loop and continue with another query
2 - if wrong/garbled data is received, must make another query again.
3 - All possible ANSWERS are encodigos ascii.. |
|
|
Mike Walne
Joined: 19 Feb 2004 Posts: 1785 Location: Boston Spa UK
|
|
Posted: Fri Jan 31, 2014 8:48 am |
|
|
Assemble the reply as a string, then compare to either the expected or all the reply string(s).
Could be part of a state machine solution.
Depends on which you're most comfortable with.
Mike
If you know what the expected reponse time is, you don't have to always wait for the full 30s. |
|
|
asmboy
Joined: 20 Nov 2007 Posts: 2128 Location: albany ny
|
|
Posted: Fri Jan 31, 2014 1:29 pm |
|
|
pilar:
Mr. Ttelmah has done you a great service here,
both in terms of logical, error resistant,
self-recovering code, but also in illustrating program flow ,
that ought to guide you through projects well after this one has been
handed in. |
|
|
pilar
Joined: 30 Jan 2008 Posts: 197
|
|
Posted: Fri Jan 31, 2014 2:46 pm |
|
|
Thank you all.. |
|
|
|