Interrupts for UART using Teensy 3.1 or 3.0

Status
Not open for further replies.

vinbob

Member
Hello everyone ! :cool:

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 :D
 
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 :D


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.
 
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!
 
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.
 
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.
 
#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
}
 
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
  }
}
 
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
 
First off wrap your code in code blocks "
Code:
" [COLOR=#ff0000]your code here![/COLOR] "[\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?
 
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
 
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.
 
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.
 
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;

}
 
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());

}
 
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.
 
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?
 
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);

???
 
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.
 
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.
 
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.
 
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);

???
 
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.
 
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.

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,
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.
 
Status
Not open for further replies.
Back
Top