It has been a while since i've worked with the arduino, and I don't seem to find anything specifically addressing my question. I have heard "interrupt-driven serial", but software/firmware is not my strong suite.
I have a nmea mediatek gps, and via an "gps init" sequence I set it up as follows: 2Hz, RMC and GGA sentences only, and 57600 baud. works well, and the arduino reads it nicely with softwareserial (and a modified read buffer of 256).
((Arduino IDE 1.0.1, on a 2560 mega. Mediatek gps hooked to pins 12 (RX) and 13 (TX) ))
Now, the question!
I want the arduino to (on its own) recognize that a new gps nmea sentence has come in, and run my parsing code which then sets certain variables (lat, lon, etc) accordingly.
Is there any smooth way to do this, in an interrupt-driven style? so I can keep a "while (gpsSerial.available()) { ... }" style of code out of the void loop() function?
awjennin:
Is there any smooth way to do this, in an interrupt-driven style? so I can keep a "while (gpsSerial.available()) { ... }" style of code out of the void loop() function?
If that's not sarcasm, then my answer is "because of my OCD". If it is sarcasm, then I assume its not commonly done?
I would love to have my actual parsing code kick in automatically each time the arduino receives a gps tx, and then update the lat, lon, etc variables in the background, so next time I refer to them, they are current.
In general, there is no way to know what is "commonly done" because very few people publish their programs. However, it is safe to assume that what you want to do is not "commonly done" because...
but I would love to have my actual parsing code (not pictured here) kick in automatically each time the arduino receives a gps tx, and then update the lat, lon, etc variables in the background, so next time I refer to them, they are current.
One does not require the other. In other words, you gain nothing by parsing in an interrupt service routine but do add considerable risk and complexity.
Back to my previous question... Why on Earth would you want to do that?
You can create a function that you call from loop(). You can not hook into the serial interrupt that fires when a bit of serial data arrives.
The question is exactly what do you want to do? There are differences between a bit arriving, a byte arriving, and a complete packet arriving. The software serial interrupt handler deals with the bit arriving; you can not butt in on that. There are no interrupts when a complete byte arrives or when a complete packet arrives (for good reason).
Sorry, I dont have a good answer for you other than "thats how I feel that it should be done". I'm on the elec. engineering side of the equation and i guess I just have different opinions. Like I said, i'm trying to parse out a gps's nmea data. I like to build up "code modules" that I can deploy in various projects. nothing particularly special. also, my parsing code is a derivative of the work jordi and williams did, which i adapted to my needs mid 2011.
To me it just makes more sense that a processor would not be burdened by checking the serial status all the time, but rather should be interrupted when there is actually data there.
maybe my solution is a hardware one? build a small unity-gain opamp with its input tapping off the arduino's RX port.. have its output go into an ac->dc converter and then fed into a interrupt-capable pin. Then have the arduino waiting for a HI>LOW transition. then, drums fire code! what do you think?
No much of that idea. As I said, the software serial interrupt is triggered when a bit arrives. Why are you interested in that? You need 10 of them for a byte. You need several bytes for a packet.
Why are you trying to through hardware at a software issue? You still need the software to detect when the packet is complete, so you can parse it.
Well the point of the buffered ad->dc converter is that it would see a constant logic-high while the gps/uart devices is transmitting to the arduino, and when it stops the logic high goes to logic low. this HI>LO triggers the interrupt, and signals that the data is sent, the serial buffer is full, and time to call the parse function.
the reason for this is because i'm not good at software - When i do things like: figure out the serial buffer is too small (which was causing problems), and then find the file and modify it to something more appropriate for my needs, I feel like superman and iron-man combined! software isn't as intuitive to me as it is to you folks.
Is there any smooth way to do this, in an interrupt-driven style?
The short answer is yes.
Your question has two parts:
you want the avr to be notified of new data to be read from the gps. That can be done via an interrupt pin (INTx or PCINTx): the isr for that interrupt will trigger a transmission from the gps to the avr. But presumes that your gps has the hardware to change a pin.
you want a serial transmission to be done entirely in the background: it is fairly simple. The TXCIEn enable the uart transmission interrupt. All you do is to load up a byte from the transmission buffer (until its empty - at that point, you disable the uart transmission).
The beauty of this approach is that it frees up the mcu from long transmission sessions.
My original question was driven by the errors I was getting (serial read buffer of 64 bytes).
Once I increased the buffer to 256, I just call the "parse" function and it reads & delivers nice data.
Now I feel like that's similar to jamming a screwdriver in the ignition of a car - sure it works, but there is a better way.
maybe my question should be: "IS there a better way?" (better-meaning less processing intensive / less prone to issues)
(BTW - THANKS for taking the time to respond! this is helping me get a better understanding of how you folks see things, and how i should probably try seeing them as well)
My original question was driven by the errors I was getting (serial read buffer of 64 bytes).
The Arduino can get data out of the buffer orders of magnitude faster than it can be put into the buffer by the HardwareSerial or SoftwareSerial classes. IF you read it now and then. That the 64 byte was not enough implies that you are not reading often enough. Increasing the buffer size is generally not necessary.
So, what is your code diddling around doing, instead of reading the serial data?
You got it! I was reading it only once in a while. it worked when reading as fast as possible, but didn't work if I read it slower than every 20ms? and my code did this in the interim: delay();
I didn't like the idea of calling it all the time, allowing the code to sometimes run quick (when the read buffer was empty), and sometimes lagg (because its parsing code). I wanted to make it more rigid and dependable. Is this a poor school of thought for coding?
I wanted to get something together that would return gps variables no matter when I called the function, or which gps fix speed i used(1-10Hz). A lot of this is simply because I want to know - I dont have a secret project or anything. I just feel like ive been here a lot of times, and want to know the most correct way to go about data reading.
I didn't like the idea of calling it all the time, allowing the code to sometimes run quick (when the read buffer was empty), and sometimes lagg (because its parsing code). I wanted to make it more rigid and dependable. Is this a poor school of thought for coding?
You are mixing up the activity of the processor after it gets a complete packet with that of getting the data from the serial port. No parsing (of GPS data) typically takes place until the whole packet has been received.
Hehe! well, the delay() was " "simulating" " program activity.
I hate to say it but i'm still confused - how about this, what would you do...?
You are hell-bent on writing the most efficient code possible, and will be receiving data from a GPS via SoftwareSerial at intervals between 1 and 10 Hz and up to 200bytes long. How would you attack this problem while trying to keep microcontroller resources as free as possible?
Could you give your answer in a bulleted list? - i'm just trying to understand the train of thought. THANKS!
you want the avr to be notified of new data to be read from the gps. That can be done via an interrupt pin (INTx or PCINTx): the isr for that interrupt will trigger a transmission from the gps to the avr.
...
The beauty of this approach is that it frees up the mcu from long transmission sessions.
@dhenry: Both hardware and software serial already use interrupts. So I'm not sure what you are suggesting here.
The point is, which my colleagues have been trying to make, that on one interrupt you do not get the X, Y, Z coordinates from the GPS. You get a single byte. The techniques described in the link will show how to gradually assemble that into useful data. The existing interrupts will make sure you don't miss incoming bytes as you do something else (eg. calculations).
The existing use of delay is the sole reason the program is not working correctly.
Hehe! well, the delay() was " "simulating" " program activity.
Not really. It's simulating going to sleep at the bus stop. And then you complain when you miss the bus.
Here is an example of how a uart transmission can be done (largely) without cpu intervention.
//transmission interrupt
ISR(USART_TX_vect) {
_U0TX_ptr++; //increment to to the next char
if (*_U0TX_ptr) { //buffer has not been exhausted
UDR0 = *_U0TX_ptr; //load up the next char to be transmitted
} else {
UCSR0B &=~(1<<TXEN0); //disable the transmisster
_U0TX_DONE = 0; //signalling end of the transmission
}
}
//send a null-terminated string
//calling program needs to check _U0TX_DONE first
unsigned char uart0_puts(unsigned char * str) { //put a string
_U0TX_ptr = str;
_U0TX_DONE = 1; //transmission in progress
UCSR0B |= (1<<TXEN0); //enable the transmission module
UDR0 = *_U0TX_ptr; //load up the char and start the transmission
return _U0TX_DONE;
}
Your user code would look like this:
if (_U0TX_DONE==0) uart0_puts(mystring); //print mystring if uart0 isn't in use
Unlike the stock serial, this routine allows the mcu to do other things while uart transmission is in progress.
I provided just the sender side; the receiver side is almost identical.
dhenry:
Here is an example of how a uart transmission can be done (largely) without cpu intervention.
...
Unlike the stock serial, this routine allows the mcu to do other things while uart transmission is in progress.
Stock (hardware) serial allows the processor to do other things while the transmission is in progress. Serial.write puts the outgoing data into a buffer, and uses interrupts to send it.
Mr. Gammon, you have built up some great knowledge resources on your website and they easy to understand! I see what you are doing with the serial port. My question is: isn't this what softwareSerial is basically doing? and if it is, is my method of increasing buffer size and just polling sufficient?
and dhenry, i think I see what you are doing too - if I was to make the "RX" side of what you gave me, that would allow for a less mcu intensive operation! do you think it is less intensive that the softwareserial itself?
@ awjennin - I wish I did.
dhenry seems to be deliberately muddying the waters - I really wish I knew why.
Perhaps dhenry will be so good as to enlighten us.
From about IDE 1.0, both hardware serial transmission and reception have been buffered, interrupt-driven operations.