help with interrupts on t3

Status
Not open for further replies.

virtualdave

Well-known member
Hi all,

Interrupts have always been one of those things I wished I paid more attention to, and looks like I have a project where I need a bit of guidance.

Quick run down...reading a ~120 byte ascii stream on Serial1 @961200 baud. After reading the data the micro does a bit of processing of the data (mostly gesture analysis). All in all it takes about 5ms to read the data and process the gestures. At the same time I'm "waiting" for a message on Serial2 from a remote system asking for an update on the state of each of the gestures. When this message comes in, I need to stop whatever it is I'm doing and read/respond to the incoming message, then resume whatever it was I was doing. Sounds like a job for an interrupt, eh? Only I'm very much in the dark on how to do this, especially in the realm of a teensy3.

I would be grateful for any pointers on where to read/learn more. I've been reading as much as I can on interrupts, but not sure what's AVR specific or might/might not be applicable to an input on a hardware serial port.

Thank you in advance for any pointers!

All the best,
David
 
To keep it simple: in your gesture loop, can you simply call Serial2.available()?
You can take data from the serial port, append it to a buffer, until you get a complete data frame/message, Then process it.
I've done things like this by writing a function that is a state machine to parse/process incoming bytes. The state # is a static variable in the function.
 
The serial ports don't have an event API. So, I think the easy choices are doing polling as mentioned in the previous post or if you only need to do minimal processing to parse the request / generate the response on Serial2 hack the existing interrupt handler (should be 'uart1_status_isr' in arduino/hardware/teensy/cores/teensy3/serial2.c).

Make sure the transmit buffer is big enough for the complete response message. Interrupts are asynchronous - so you you need proper synchronization/atomic updates for data you are going to access in your ISR.

(Interrupts are basically disabled while you are executing the 'uart1_status_isr' ISR - so you really only want to do very fast stuff in there...)

You do realize, T3 is ARM Cortex, not AVR, right? The interrupt stuff/peripheral drivers are quite different.
 
Thank you both for your feedback.

(I actually had replied to Steve's message but seems to have vanished into the ether, so trying again)

@Stevech: that has been my plan till someone put a bee in my bonnet (the developer who will be sending my the messages on serial2) about interrupts. My worry is if more than 1 byte comes in on Serial2 (or 3) while taking the 3ms to read the data coming in on serial1 (for example). Rather than interrupts I should probably look into increasing the size of the buffer on serial2 to something like 4-8 bytes (like the serial1 buffer) if that's possible. The slight 2-3ms delay in responding to the request on serial2 certainly wouldn't be an issue for this application.

@tni: very much aware of ARM vs. AVR :) just not how it might relate here. Thanks for your pointers!

Thanks again,
David
 
Last edited:
My worry is if more than 1 byte comes in on Serial2 (or 3) while taking the 3ms to read the data coming in on serial1 (for example). Rather than interrupts I should probably look into increasing the size of the buffer on serial2 to something like 4-8 bytes (like the serial1 buffer) if that's possible.

By default, Serial2 and Serial3 have 64 byte receive buffers and 40 byte transmit buffers.

If you need larger, you could increase up to 255 by edited serial2.c and serial3.c. Look for this in each file:

Code:
#define TX_BUFFER_SIZE 40
static volatile uint8_t tx_buffer[TX_BUFFER_SIZE];
static volatile uint8_t tx_buffer_head = 0;
static volatile uint8_t tx_buffer_tail = 0;
static volatile uint8_t transmitting = 0;

#define RX_BUFFER_SIZE 64
static volatile uint8_t rx_buffer[RX_BUFFER_SIZE];
static volatile uint8_t rx_buffer_head = 0;
static volatile uint8_t rx_buffer_tail = 0;
 
The serial ports have 64 byte receive buffer, so if port 2 only sends a few bytes you will not miss them.
 
Serial2 and Serial3 already use interrupts to place incoming data into the receive buffer and to automatically transmit whatever you put into the transmit buffer, with Serial2.print() or Serial2.write().

Unless you're an expert, I would not recommend fiddling with the serial interrupts. It may look simple, and you can probably achieve seemingly good results without much effort, but it's extremely difficult to make more complex data sharing and notification 100% reliable. Race conditions are almost impossible to diagnose and fix.
 
Most UARTs also have a FIFO to buffer 8 or so bytes (some have 16). Upon arrival of incoming bytes, the UART interrupt service "unloads" the FIFO in a single interrupt. For high baud rates, this greatly reduces the interrupts/second and overhead.

The opposite occurs for the UART's transmitter FIFO- it can be filled in one quick loop, then no interrupt until it becomes, say, half-empty.

Proper use of the FIFOs is up the the interrupt handler code - it is possible to not enable and exploit the benefits of the FIFOs.

So this may help the OP understand a bit more about serial I/O and interrupts.
A fundamental of interrupts is to do the bare minimum/as possible in an interrupt handler, rarely/never call library functions (that may not be reentrant), and always be mindful of atomic access to variables and mutual exclusion, etc.
 
On Teensy3, Serial1 has a 8 byte FIFOs. The code in serial1.c uses these to good effect to reduce the number of interrupts. Serial1 is best for fast baud rates.

Serial2 and Serial3 are ordinary double-buffered UARTs. In typical usage, there's 1 interrupt per byte. The interrupt routines are pretty efficient, so even 115200 baud is not a problem, unless you have other code or libraries that disable interrupts for too long.
 
My worry is if more than 1 byte comes in on Serial2 (or 3) while taking the 3ms to read the data coming in on serial1 (for example). Rather than interrupts I should probably look into increasing the size of the buffer on serial2 to something like 4-8 bytes (like the serial1 buffer) if that's possible.
By default, Serial2 and Serial3 have 64 byte receive buffers and 40 byte transmit buffers.
The Teensy has 1 byte hardware buffers on serial 2/3 and 8 bytes on serial 1. Those 64/40 byte buffers Paul talks about sit in the serial drivers and will do just fine, as long as you don't disable interrupts for a long period of time.

Paul does make a good point, if you don't have experience in interrupt or multi-threaded development, it's really easy to introduce nasty bugs where things work 99.999% of the time but fail every once in a while.
 
Ah, in my comment above, I should have, now that you mention it - point out that the Freescale UARTs in the T3's CPU implement deep FIFOs only on one of the three UARTs.
(I'm too habituated to the NXP CPUs which don't have this limitation - all are standard 16550 UARTs with 16 deep FIFOs).
 
Thanks again all for the input. Sounds like I don't need to touch a thing, which is quite lovely. It would not be pretty.

Thanks,
David
 
it's really easy to introduce nasty bugs where things work 99.999% of the time but fail every once in a while.

Back in the early 90s when the 8051 still dominated the microcontroller market, I learned this lesson the hard way. One nasty bug I wrote would strike about once per month... about once in every 700 hours of continuous (actually busty data flow) operation at 9600 baud. But when it did strike, the result was getting stuck in an infinite loop. A power cycling or hard reset was needed to recover. I even built boards with an external watchdog timer. After about 2 years of frustration, I finally found and fixed the bug.
 
I've had the same experience for many years. The famous saying in embedded system/RTOSes...
The last 1% of the bugs takes 99% of the debugging time. The solution always comes after a big Eureka! moment.

Someday, I'd like to learn how pacemaker firmware is tested. Without a large variety of branches in logic flow- the watchdog timer would prevent a loss of pacing, and risk to patient (though most patients just revert to about 40bpm). But they also have to never corrupt the event history log file that the cardiologist relies upon. And too, think about the implanted defibrillator kind of pacemaker. It just HAS to work. Curiously, these microprocessors are probably, on average, 20 years old, due to the R&D time then some government approval time, etc. Then the 5-12 year battery life time begins. A few mA*mSec, 70/minute.
These pacemakers have a magnetic inductive-loop serial data interface too - used a few times a year for checkups and reprogramming settings. These things cost the insurance companies like $15,000. I bet the BOM cost is $500. But design, design, design; test, test, test, test is costly.

Far less critical, but consider too airplanes and cars. Especially airplanes, if you look at what is automated on, say, the 787 - everything! So automated, that airlines seem to hire pilots that are "afraid" of Visual Flight Rules.
 
Last edited:
processing data from UART

Hi All,

I just registered so this my very first posting. I am a bit of a "greenhorn"...

Although this is quite an old posting and it has been discussed in at least 2 threads, I do want to make a few remarks for those that read this thread.

First of all, there is one very basic rule that somebody suggested in a previous posting, which is "Thou Shalt Not poll in a Real Time OS" : you will be notified through a ISR that something happened instead of checking that (in this case) UART's control or status register. When poling means also that the processor is àlways running while when using proper ISR to handle event means that the processor is only running when it is required

Second remark that I want to make is that it seems that some of you are doing the processing of the data received from the UART in the UART's ISR itself which will block ie the second UART's ISR until the first ISR number crunching or something finishes. It is uncommon to do any data processing in the UARTs ISR: all you do is the bear bear minimum because generally speaking, no other interrupt(s) are handled as long a ISR runs. This means that the processing of the data is done within a task and any task can be interrupted to yes indead handle ie an ISR for an UART to get proper response time.

So the trick is to accumulate one logical chunk of data from the serial port and than to hand it over to a task that will do some crunching of the data via a QUEUE. The chrunching results can be stored in a second queue.

For the second UART a similar strategy can be used: the second queue receives a short command to return certain data: that data can be fetched from the queue where the first task is writing to.

This is a very basic approach on UARTS, ISR, Queue's and task or threads to handle data from a serial port the proper way without blocking any other device that has it's own ISR

So second "design" rule would be Thou Shalt keep code of ISR a short as possible (process data ònly in a task or thread)

may be a very basic demo sample code that handles two UARTs would be desirable

Any comment is appriciated on above suggestion
 
Status
Not open for further replies.
Back
Top