Forum Rule: Always post complete source code & details to reproduce any issue!
Page 1 of 4 1 2 3 ... LastLast
Results 1 to 25 of 83

Thread: Interrupts for UART using Teensy 3.1 or 3.0

  1. #1
    Junior Member
    Join Date
    Nov 2013
    Posts
    18

    Interrupts for UART using Teensy 3.1 or 3.0

    Hello everyone !

    I am trying to implement an Interrupt-routine for a project of mine.
    I use a Teensy 3.1 and a Bluetooth low energy module from Sparkfun for communication with iOS devices. The Teensy and the BLE module communicate through a serial uart port. We use Interrupts to detect if there is new data available at the serial port but they donít work with the teensy 3.x. they compile and work just fine with the teensy 2 as well as all other arduino boards.

    By the way, is there anyone who has any experiences with implementing an interrupt when new data is available at an UART serial port ???
    it should be triggered always like: if(Uart.available()){ interrupt routine }
    Thanks and best wishes,

    Vincent

  2. #2
    Senior Member duff's Avatar
    Join Date
    Jan 2013
    Location
    Las Vegas
    Posts
    970
    Quote Originally Posted by vinbob View Post
    By the way, is there anyone who has any experiences with implementing an interrupt when new data is available at an UART serial port ???
    it should be triggered always like: if(Uart.available()){ interrupt routine }
    Thanks and best wishes,
    Vincent

    I wrote a library that you can do this called SerialEvent. But interrupts don't work this way: if(Uart.available()){ interrupt routine }, that is just polling.

  3. #3
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    20,684
    Quote Originally Posted by vinbob View Post
    We use Interrupts to detect if there is new data available at the serial port but they donít work with the teensy 3.x. they compile and work just fine with the teensy 2 as well as all other arduino boards.
    Always post complete source code & details to reproduce any issue!

  4. #4
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    20,684
    Also, before reporting a problem, please make sure you're using the latest Teensyduino version, which is 1.20. To check, in Arduino, use Help > About.

    Often newer versions fix known issues, so if you're having trouble using an older version, always try the latest to see if the problem has already been solved. I know this sounds overly obvious, but every week we get problems posted here which are already fixed by the latest version.

    If the problem persists with 1.20, the only way any progress will be made is if you post a (hopefully small) complete program that reproduces the problem.

  5. #5
    Junior Member
    Join Date
    Nov 2013
    Posts
    18
    Thanks Duff for the hint.
    Yes i know the phrasing might be irritating. I know how interrupts work. I need an Interrupt which triggers a routine when there is new data available at a serial port.
    I have also the latest version of Teensyduino. I'll post a code snipped as soon as possible.

    best
    vinc.

  6. #6
    Junior Member
    Join Date
    Nov 2013
    Posts
    18
    #include <avr/interrupt.h>
    #include <avr/io.h>
    void setup()
    {
    pinMode(13, OUTPUT);
    interrupts();
    }

    void loop()
    {

    }

    ISR(USART0_RX_vect)
    {
    digitalWrite(13, HIGH); // set the LED on
    delay(1000); // wait for a second
    }

  7. #7
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    20,684
    This AVR specific code is never going to work on any ARM or non-AVR chip.

    For an example of similar code (very specific to Teensy 3.1), look in hardware/teensy/cores/teensy3/serial1.c.

    You can use attachInterruptVector() to cause your own function to run, instead of the default uart0_status_isr(). For example:

    Code:
    void setup() {
        Serial1.begin(9600);
        attachInterruptVector(IRQ_UART0_STATUS, myfunction);
    }
    
    void myfunction(void)
    {
      if (UART0_S1 & (UART_S1_RDRF | UART_S1_IDLE)) {
        // receive
      }
      uint8_t c = UART0_C2;
      if ((c & UART_C2_TIE) && (UART0_S1 & UART_S1_TDRE)) {
        // transmit
      }
      if ((c & UART_C2_TCIE) && (UART0_S1 & UART_S1_TC)) {
        // all previous transmit completed
      }
    }

  8. #8
    Junior Member
    Join Date
    Nov 2013
    Posts
    18
    Thanks,

    i tried to implement it like this:
    But i didn't succeed. After void myfunction(void) gets called the program seems to crash and stop.
    Where can i find a documentation to get to know how to use the attachInterruptVector() method and all interrupt-Vectors ?


    #include <avr/interrupt.h>
    #include <avr/io.h>

    void setup() {

    Serial.begin(9600);
    Serial2.begin(57600);
    interrupts();
    attachInterruptVector(IRQ_UART1_STATUS, myfunction);

    }

    void loop(){
    Serial.println("[+] loop");delay(500);

    }

    void myfunction(void)
    {
    Serial.println("[+] myfunction");

    if (UART0_S1 & (UART_S1_RDRF | UART_S1_IDLE)) {
    // receive
    Serial.println("receive");
    }
    uint8_t c = UART0_C2;
    if ((c & UART_C2_TIE) && (UART0_S1 & UART_S1_TDRE)) {
    // transmit
    Serial.println("transmit");
    }
    if ((c & UART_C2_TCIE) && (UART0_S1 & UART_S1_TC)) {
    // all previous transmit completed
    Serial.println("all previous transmit completed");
    }
    }


    best, Vincent

  9. #9
    Senior Member duff's Avatar
    Join Date
    Jan 2013
    Location
    Las Vegas
    Posts
    970
    First off wrap your code in code blocks "[CODE]" your code here! "[\CODE]" so it is easier to read! Second you have not started any of the uart clocks and setup the baud rate and such and you have code for Hardware Serial1 and Hardware Serial2 intermingled together, not going to work at all. As far as changing the default interruptVectors the only examples I know of are DMAChannels and my SDI-12 library, but unless you want to write all the low level drivers just try my library it does all this for you, plus gives you access to the interrupt through a callback and setup events to call those interrupts, its all documented in the examples. Not trying to push you that way but from your code it might be faster to get things working or if you want to write your own low level uart routines then you can, maybe thats what you want?

  10. #10
    Junior Member
    Join Date
    Nov 2013
    Posts
    18
    ok, cool
    thanks.
    It seems that OneByteReceive_Event is the best way to go but its not exactly what i need ...
    I have some functions running in my loop() function and if there is Data available i need all functions to stop immediately and read the incoming data.
    What would you recommend to go with ?
    best

  11. #11
    Senior Member duff's Avatar
    Join Date
    Jan 2013
    Location
    Las Vegas
    Posts
    970
    Quote Originally Posted by vinbob View Post
    I have some functions running in my loop() function and if there is Data available i need all functions to stop immediately and read the incoming data.
    best
    Thats what it does, the event handler function is called from the DMA RX interrupt so make sure whatever code is in there it is fast as possible. Is your serial data terminated? if so it would be better to set the termination character event and then read the whole data. But any how make sure you post short & simple code of what you want to accomplish, without that know one can really help you.

  12. #12
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    20,684
    Quote Originally Posted by vinbob View Post
    After void myfunction(void) gets called the program seems to crash and stop.
    Attempting to access any disabled peripheral causes a memory fault. The default fault handler function is an infinite loop which attempts to finish any USB and serial communication, and remain responsive to the USB audo-reboot request, but otherwise just waits forever. For all practical purposes, your program crashes when you read or write ANY register in another peripheral that is disabled.

    In the code you posted on #8, you changed from Serial1 to Serial2. But you still have a number of "UART0" registers, which are for Serial1. Reading or writing ANY of those registers for Serial1 when Serial1 isn't enabled will crash your program.

  13. #13
    Junior Member
    Join Date
    Nov 2013
    Posts
    18
    First of all thanks for your help !
    I reduced the code to this. I think i do misunderstand how this works at this moment.

    I run this code now and when i transmit data through serial2 to program crashes ...
    Heres my code in brackets


    Code:
    
    
    #include <avr/interrupt.h> 
    #include <avr/io.h> 
    
    volatile int interruptflag = 0;
    
    void setup() {
      
      Serial2.begin(57600); // start serial communication on serial 2
      Serial.begin(9600); // start serial communication for Serial Monitor
      
      interrupts();  // enable Interrupts
      attachInterruptVector(IRQ_UART1_STATUS, myfunction);
    
    }
    
    void loop(){
     
        Serial.print("[+] interruptflag:  ");Serial.println(interruptflag);
        delay(500);
    
    }
    
    void myfunction(void)
    {
       
        interruptflag = 1;
    
    }

  14. #14
    Junior Member
    Join Date
    Nov 2013
    Posts
    18
    I also tried something like this ...
    I transmit an Integer value which should exactly fit the buffer size of 4 ("#define RX_BUFFER_SIZE 4").
    So as i get it the "void rx2Event(void)" routine gets called when the buffer is full. So far everything works just fine but when i try to read the data which should be available at the serial
    "serial.available" returns 0. so i guess it has been already red and stored somewhere. How can i exit this integer.
    and how can i call an interrupt routine whenever there is data available no matter if the buffer is full or not.

    Sorry for not getting the hang of to

    Code:
    
    
     
    #include <SerialEvent.h>
    
    Serial2Event Event2;
    
    #define TX_BUFFER_SIZE 128
    #define RX_BUFFER_SIZE 4
    
    void setup() {
      Serial.begin(9600);
      
      SERIAL2_MEMORY_TX(TX_BUFFER_SIZE);
      SERIAL2_MEMORY_RX(RX_BUFFER_SIZE);
    
      
      //Event2.loopBack = true;// internal loopback set / "default = false"
      Event2.txEventHandler = tx2Event;// event handler Serial2 TX
      Event2.rxEventHandler = rx2Event;// event handler Serial2 RX
      Event2.begin(57600);// start serial port
    
    }
    
    
    void loop() {
    
      int input = 0;
      int buffer[4] = {};
      int transmittedNumber = 0;
    
      if(Event2.available()) {
        
        input = Event2.available();
        
      }
      
      
      if(input == 4){
       
          for(int i=0; i<input ; i++){
            buffer[i] = Event2.read();
          } 
          
          transmittedNumber = buffer[0]|buffer[1]<<8|buffer[2]<<16|buffer[3]<<24;
          Serial.print("transmittedNumber:  ");Serial.println(transmittedNumber);
        
      }
      
    }
    
    //--------------------------------------Serial1 Events------------------------------------
    void tx2Event(void) {
      // TX Event function will fire when it is finished sending a packet
    }
    
    void rx2Event(void) {
    
      Serial.println("rx2Event");
      Serial.print("available:  ");Serial.println(Event2.available());
    
    }

  15. #15
    Junior Member
    Join Date
    Nov 2013
    Posts
    18
    I also figured out to manage by doing something like this.

    Code:
    
    
    #include <stdbool.h>
    #include <avr/pgmspace.h>
    
    HardwareSerial2 Uart = HardwareSerial2();
    
    long debouncing_time = 1;
    volatile unsigned long last_micros;
    int interruptflag = 0;
    int transmittedNumber = 0;
    
    void setup() {
      
      delay(500);
      Serial.begin(9600);
      Uart.begin(57600);
      delay(10);
      
      interrupts(); 
      attachInterrupt(9, interruptDidGetCalled, RISING);
    }
    
    void loop() {
      delay(100);
      readData();
      
    }
    
    void readData(){
       
        int input = 0;
        int buffer[8] = {};
        transmittedNumber = 0;
        
        if(Uart.available()){
          input = Uart.available();
          Serial.print("Uart.available():  ");Serial.println(Uart.available());
        }else{
            Serial.println("UART not available");
        }
       
       if(input >= 4){
       
          for(int i=0;i<input;i++){
            buffer[i] = Uart.read();
          } 
             transmittedNumber = buffer[0]|buffer[1]<<8|buffer[2]<<16|buffer[3]<<24;
             Serial.print("transmittedNumber1:  ");Serial.println(transmittedNumber);
             input = 0;
       }else{
          for(int i=0;i<input;i++){
            buffer[i] = Uart.read();
          } 
       }
    }
    
    void interruptDidGetCalled(){
      if((long)(micros() - last_micros) >= debouncing_time * 1000) {
        OnceInterrupt();
        last_micros = micros();
      }
    }
    
    void OnceInterrupt(){
      Serial.println("[+] OnceInterrupt");
    }

    but this seems not to be a very elegant way
    So i am very exited about getting any advice.

  16. #16
    Junior Member
    Join Date
    Nov 2013
    Posts
    18
    no more hints ?

  17. #17
    Senior Member pictographer's Avatar
    Join Date
    May 2013
    Location
    San Jose, CA
    Posts
    664
    Looks like you've got a potential buffer overrun in readData.

  18. #18
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    20,684
    Writing your own interrupt code is pretty advanced programming. It certainly can be done, but there are many challenges. It's never easy, even for experts.

    Arduino has serialEvent for this sort of thing. I'd recommend trying that first. It's performance isn't as low latency as writing an interrupt, but it's still quite good. Maybe it will be enough?

  19. #19
    Junior Member
    Join Date
    Nov 2013
    Posts
    18
    ok, thanks.
    but as far as i know serial event is called every time between two loop iterations. So it is no real interrupt ...
    Is there maybe a full example how to use the
    Code:
    attachInterruptVector(IRQ_UART1_STATUS, myfunction);
    ???

  20. #20
    Senior Member
    Join Date
    Jun 2013
    Location
    So. Calif
    Posts
    2,828
    Quote Originally Posted by PaulStoffregen View Post
    Writing your own interrupt code is pretty advanced programming. It certainly can be done, but there are many challenges. It's never easy, even for experts.

    Arduino has serialEvent for this sort of thing. I'd recommend trying that first. It's performance isn't as low latency as writing an interrupt, but it's still quite good. Maybe it will be enough?
    I dimly recall that a user implemented for Teensy a serial event call-back mechanism.

    Having a call-back for serial events in the official Teensy baseline is quite desirable. App would register/deregister the call back and it would run in the ISR context. So its use would be with such caveats.

  21. #21
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    20,684
    Quote Originally Posted by vinbob View Post
    but as far as i know serial event is called every time between two loop iterations. So it is no real interrupt ...
    Arduino only checks at the end of loop().

    Teensyduino also does this checking inside delay(), Serial.print() (when it must wait) and numerous other blocking cases. It's still not a true interrupt, but much better than only at the end of loop() like Arduino.

    Please, at least give the easy way a try.

  22. #22
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    20,684
    Quote Originally Posted by stevech View Post
    Having a call-back for serial events in the official Teensy baseline is quite desirable. App would register/deregister the call back and it would run in the ISR context. So its use would be with such caveats.
    Long term, I'm considering more ways to provide callbacks like this.

    But for data reception, interrupt context is a very sharp double-edge sword. A couple years ago, when serialEvent was first proposed (to be interrupt context), the example code written by the Arduino devs and other knowledgeable developers all had subtle bugs due to not properly handling data sharing with atomic data access and proper volatile types. A huge part of the problem is lack of interrupt safety in many of the data mangement APIs, like String and even malloc. In recent years, I've put quite a lot of work into making more of the Teensyduino software infrastructure safer for interrupt context, but it's a work-in-progress with many areas still very unsafe (but at least much better than official Arduino).

    Today, serialEvent is quite responsive on Teensy, due to calling the events from yield(), and pervasive use of yield() in all blocking APIs. My long-term strategy is to continue improving this much safer, much more beginner friendly approach. Experience has shown even experts tend to make mistakes with the difficult and dangerous interrupt context approach.

  23. #23
    Junior Member
    Join Date
    Nov 2013
    Posts
    18
    ok, thanks.
    but as far as i know serial event is called every time between two loop iterations. So it is no real interrupt ...
    Is there maybe a full example how to use the
    Code:
    attachInterruptVector(IRQ_UART1_STATUS, myfunction);
    ???

  24. #24
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    20,684
    Quote Originally Posted by vinbob View Post
    but as far as i know serial event is called every time between two loop iterations. So it is no real interrupt ...
    Teensyduino calls serialEvent MUCH more often than only between every iteration of loop().

    Why will you not at least give serialEvent a try on Teensy to evaluate if its performance is good enough for your project? Really, why?



    Is there maybe a full example how to use the attachInterruptVector( ...
    No. The only code that exists is in serial1.c. This is very advanced programming, which is generally not done by beginners. The normal way to do this from Arduino is serialEvent.

  25. #25
    Senior Member
    Join Date
    Nov 2012
    Location
    Boston, MA, USA
    Posts
    1,108
    Quote Originally Posted by PaulStoffregen View Post
    Today, serialEvent is quite responsive on Teensy, due to calling the events from yield(), and pervasive use of yield() in all blocking APIs. My long-term strategy is to continue improving this much safer, much more beginner friendly approach. Experience has shown even experts tend to make mistakes with the difficult and dangerous interrupt context approach.
    Quote Originally Posted by PaulStoffregen View Post
    Arduino only checks at the end of loop().

    Teensyduino also does this checking inside delay(), Serial.print() (when it must wait) and numerous other blocking cases. It's still not a true interrupt, but much better than only at the end of loop() like Arduino.
    In response to which,
    Quote Originally Posted by vinbob View Post
    as far as i know serial event is called every time between two loop iterations.
    vinbob, I guess either you a) did not read what Paul wrote, or b) didn't understand it, or c) think that it is wrong, or d) think it doesn't apply because you are not using a Teensy.

Posting Permissions

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