Hello everybody,
My serial ISR is as below. When I send a string to the microcontroller some characters are not received. The characters are lost in a peculiar way. eg. 1234567890123456 is received as 12356789012456 & 6543210987654321 is received as 65421098765321. Exactly 4th & 13th characters are lost in the mentioned 16 character string, not tested further.
void ISR_Serial (void) interrupt 4 // using 2 { unsigned char in_byte; if (RI){ // If data is received.. RI = 0; in_byte = SBUF; // get character in var RxBuffer[ RxInChar ] = in_byte; // save it in the buffer at RxInChar location if(RxInChar==RX_BUF_SIZE) // if buffer full, (care should be taken by sending app) RxInChar=0; // then make it 0 else if(in_byte==0x0D){ // whether byte was CR? RxBuffer[ RxInChar ] = '\0'; // & replace it by NULL char RxInChar=0; // then make pointer to 0 (for next transmission) if(!pc_mode_active) strcpy(tmpdata,RxBuffer); ser_data_avail=1; // set data available flag for main routine } else RxInChar++; // increase the location pointer } if( TxEnabled ){ // If data is transmitted if (TI){ // if previous byte is finished transmitting TI = 0; if (TxOutChar != TxInChar){ // adjust queue and transmit character... SBUF = TxBuffer [TxOutChar++]; TxOutChar %= TX_BUF_SIZE;//% is the modulus operator } else TxEnabled = 0; // nothing else to transmit, so disable function... } } }
If I comment out following two lines, there is no problem. (though pc_mode_active is true at this time).
RxInChar=0; // then make pointer to 0 (for next transmission) // if(!pc_mode_active) // strcpy(tmpdata,RxBuffer); ser_data_avail=1; // set data available flag for main routine
The baudrate is 115200. RCAP2H = 0xFF & RCAP2L = 0xFD, in VB I've used "115200,N,8,1"
As pc_mode_active is true at default, the 'if loop' should not execute. Similarly there should not be a problem because the code will reach to this point only on receiveing CR which is at the last position. Please help.
Thanks in advance.
Thanks Eric, I was really thinking to search for the meaning of KISS. But I am really frustrated to solve my current problem, so I postponed it.
But I am really frustrated to solve my current problem
that is the problem, instead ignore what you have and rewrite your small program [section] from scratch using ring buffers instead of trying "to solve my current problem"
Many have been stuck "trying to fix" something where the code was good, but the concept was wrong. That is not a good route.
Erik
PS -one mans opinion- "so I postponed it" never do that for more than a day or two, you will never get ahead by putting problems aside
Sorry, some confusion is here (may be my English). I have not postponed my original problem, I had just postponed to search for the meaning for KISS.
In this case, you seem to have forgotten that the GPS can send multiple lines every second, depending on what NMEA strings you have enabled. So there will only be inter-character delays from when it has received the first line until the ISR is busy filling that same line buffer with characters for the next NMEA string.
Your main loop main see the initial characters from the second string and then somewhere the data in the buffer will switch back to the remaining characters from the previous line. It is only the last text line in each burst you get (once/second) that your main loop is able to correctly process - unless your code have managed to clear your "i got data" flag for the new line when it was done with the previous line - but after the ISR had already detected the end-of-line for the next line.
The special thing with interrupts and real hardware is that the hardware doesn't stop just because you look at a piece of code in your main loop. Don't expect that the main loop code can do its work undisturbed - it gets constantly interrupted by new data comming in (or outgoing data being sent).
You must always be structured when looking at the code and trying to figure out what the code _can_ do. Interrupt-based code, just like threaded code, requires that you write the code with proper synchornization. Your code isn't infinitely fast. So when you call a function, the hardware will still have time to tick the timers, baudrate generators etc while that function runs.
You write that the GPS sends this, every second:
$GPGGA,120058.000,2400.0000,N,12100.0000,E,0,00,0.0,0.0,M,0.0,M,,0000*67 $GPGSA,A,1,,,,,,,,,,,,,0.0,0.0,0.0*30 $GPGSV,1,1,01,04,00,000,00*4C $GPRMC,120058.000,V,2400.0000,N,12100.0000,E,000.0,000.0,280606,,,N*76 $GPVTG,000.0,T,,M,000.0,N,000.0,K,N*02
But when the ISR gets the $GPGSV string and then sets the flag that it has received a full line, the UART may have time to receive $GPRMC before your main loop looks at the buffer - so it falsely think you have a GPRMC string to decode.
In the same way - when that $GPRMC string is finally received and flagged as available, the UART is already busy getting the start of the $GPVTG string - which will destroy the $GPRMC string in your buffer if the main loop wasn't fast enough.
It is possible to have a small state machine in the ISR that just trigs on $GPRMC and starts receiving data after this prefix is found. Resets whenever a newline is seen. That would block overwrites at the cost of that little state machine. Or the ISR need to buffer data in a ring buffer big enough for the main loop poll frequency.
The above overwrites should be obvious if thinking about the timing of your code. A printout of your line now and then should be able to show you that you are likely to have the start from one string and the end from a different string. And real funny issues if the ISR manages to overwrite the terminating zero in the buffer before the main loop was done with the processing - suddenly you can get the code to start hunting for a random string termination in the processor memory.
Example of a state machine for NMEA decoding:
www.visualgps.net/.../NMEAParser.html
And look in the downloads section of this site for an example of interrupt-driven, ring-buffered serial comms...
Thanks all for your reply.
As per Per's remarks, the NMEA sentences are really overwritten/missed, because the technique I used is not perfect besides I also enable/disable the ES0 register in main loop. But still it is acceptable to me if I get proper sentence once in 3/4 seconds. Which was achieved at the cost of corruption in PC communication.
Now I decided to concentrate on the PC communication problem when gps code is still there in ISR.
One thing I forget to tell you that, I was using baud rate of 9600 for gps whereas 115200 for PC mode. I was changing the value of RCAP2L register on switching. Now I have tried 2 things and the results are as..
1. I reduced the baud rate in PC comm to 57,600. The missing character problem was solved.
2. As lowering baud will take long time to read the EEPROM content into the PC, I decided not to lower the baud rate but use the X2 mode which I HAVE NOT USED EARLIER. Now total PC communication is lost??? I have set 115200 in both mcu & PC. Any idea?
With X2 Mode the reload value of Timers should be re-calculated. ^_^
Thanks, now I can communicate properly in PC mode at 115,200 baud rate. GPS code is still there in ISR. So now I can concentrate on a single problem (receiving $GPRMC string properly).
www.mcu4u.eu/.../
Thanks everybody.
Per Westermark, ur replies r always very informative !!!
I have used X2 mode & solved the problem of missing characters in PC communication, when some additional code was added to the serial ISR. It could also be solved by lowering baud rate.
Then I concentrated on only GPS communication. In the ISR, I discarded all those string that does not have start char = '$' & 4th char = 'R'
I was getting PERFECT data per second.
Then I removed the strcpy() function from the ISR and it is still working PERFECTLY!!!
Thanks!!!
Nice.
But as you can see, the prio 1 step when something isn't working is to take one step back and try to analyze the problem and understand exactly _why_ it isn't working.
That makes it much easier to create a custom fix that solves the problem without breaking something else. Or that doesn't just _seem_ to solve the problem because of some unexpected "luck".
It is too easy to just test something because the developer feels "stressed". But it seldom saves time to just test solutions. Figuring out the real issues means that you know how to test your program after having added any required fixes.
I do not know the source, but one of my favorite quotes is:
"succesful testing does not prove the abscence of bugs, it only proves the abscence of known bugs"
Thus I would never believe in "testing a bug away" if you can not analyze the source of the bug and THEN remove it you have no knowledge whatsoever if one of these "about once a week it fails" may exist.