Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 15 of 15

Thread: help with interrupts on t3

  1. #1
    Senior Member
    Join Date
    Nov 2012
    Location
    Los Angeles
    Posts
    100

    help with interrupts on t3

    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

  2. #2
    Senior Member
    Join Date
    Jun 2013
    Location
    So. Calif
    Posts
    2,828
    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.

  3. #3
    Senior Member
    Join Date
    Jan 2013
    Posts
    843
    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.

  4. #4
    Senior Member
    Join Date
    Nov 2012
    Location
    Los Angeles
    Posts
    100
    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 by virtualdave; 08-05-2013 at 02:03 PM.

  5. #5
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    20,566
    Quote Originally Posted by virtualdave View Post
    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;

  6. #6
    Senior Member
    Join Date
    Aug 2013
    Location
    Gothenburg, Sweden
    Posts
    293
    The serial ports have 64 byte receive buffer, so if port 2 only sends a few bytes you will not miss them.

  7. #7
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    20,566
    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.

  8. #8
    Senior Member
    Join Date
    Jun 2013
    Location
    So. Calif
    Posts
    2,828
    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.

  9. #9
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    20,566
    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.

  10. #10
    Senior Member
    Join Date
    Jan 2013
    Posts
    843
    Quote Originally Posted by PaulStoffregen View Post
    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.

  11. #11
    Senior Member
    Join Date
    Jun 2013
    Location
    So. Calif
    Posts
    2,828
    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).

  12. #12
    Senior Member
    Join Date
    Nov 2012
    Location
    Los Angeles
    Posts
    100
    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

  13. #13
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    20,566
    Quote Originally Posted by tni View Post
    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.

  14. #14
    Senior Member
    Join Date
    Jun 2013
    Location
    So. Calif
    Posts
    2,828
    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 by stevech; 08-05-2013 at 11:48 PM.

  15. #15

    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

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •