Interrupts for UART using Teensy 3.1 or 3.0

Status
Not open for further replies.
If you *really* want to go down this difficult path, as the code in reply #15, at least grab the interrupt code from serial2.c, because you're using Serial2. Get it working as-is, without any modifications, using attachInterruptVector(). Then try to make changes only after you have the original code working in your program.

But really, why not at least give serialEvent a try? Really?
 
Duff

I have been experimenting with your SerialEvent library on the Teensy 3.1 and I am seeing a weird problem. I modified your example program slightly as shown below:

Code:
/*****************************************************
 * This SerialEvent example shows the "Read Bytes Until"
 * event where it will fire the RX event handler when
 * a termination character is detected or the buffer
 * is full.
 *
 * By using the loopback feature we can test
 * the sending and receiving without having to connect
 * up anything. If you want to disable this feature
 * comment it out.
 *****************************************************/
#include <SerialEvent.h>

Serial1Event Event1;

#define TX_BUFFER_SIZE 128
#define RX_BUFFER_SIZE 128

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
  Serial.begin(0);
  while(!Serial);
  //------------------------------------------------------------------------------------
  // Must declare TX buffer size in bytes, this must be as big as your largest packet
  SERIAL1_MEMORY_TX(TX_BUFFER_SIZE);
  // Must declare RX buffer size in bytes, will fire when buffer is full or term if declared
  SERIAL1_MEMORY_RX(RX_BUFFER_SIZE);
  Event1.loopBack = false;// internal loopback set / "default = false"
  Event1.txEventHandler = tx1Event;// event handler Serial1 TX
  Event1.rxEventHandler = rx1Event;// event handler Serial1 RX
  Event1.rxTermCharacter = '@';// this termination character will fire the RX event handler
  Event1.begin(9600);// start serial port
  //------------------------------------------------------------------------------------
  Serial.println("TX event will fire when termination character is detected");
}

void loop() {
  if (Serial.available()) {
    char c = Serial.read();
    Event1.print(c);
  }

//--------------------------------------Serial1 Events-------------------------------------
void tx1Event(void) {
  // TX Event function will fire when it is finished sending a packet
}

void rx1Event(void) {
  // RX Event function prints the buffer when it is full
     int len = strlen((char *) Event1.rxBuffer);
    for (int i = 0; i < len; i++) {
        Serial.printf("%x", Event1.rxBuffer[i]);
    }
    
    Serial.printf("Termination Character %#02X Event fired: %s\n", Event1.rxTermCharacter, Event1.rxBuffer);
    digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
}

That is, no loopback and I am using the @ sign as a termination character. My input is coming from an HC-05 Bluetooth module connected to Serial1 port on the Teensy 3.1. I've instrumented the rx1Event callback to print out the individual characters received.

I get very reliable triggering with the SerialEvent but the data being returned is odd. Say I send the string "mood@" via the BT module over and over. Here is the output I receive.

TX event will fire when termination character is detected
6d6f6f6440Termination Character 0X40 Event fired: mood@
da6d6f6f6440Termination Character 0X40 Event fired:
mood@
da6d6f6f6440Termination Character 0X40 Event fired:
mood@
da6d6f6f6440Termination Character 0X40 Event fired:
mood@
da6d6f6f6440Termination Character 0X40 Event fired:
mood@

The first time through the data is as I expected. All subsequent times the received data is prefixed by hex byte "da"

Do you have any idea why this might be? What am I doing wrong?

Thanks in advance for looking into this.

Craig Lindley
 
Never mind. It is probably my Bluetooth SPP client that is adding the da.

Sorry for the noise.
ok good, becareful in the Event functions since they are fired from the DMA isr. If you can, just copy the data and set a flag that handles the data in loop. I need to update the examples for that:)
 
Hi Duff,

Excellent library! An example of copying the data and setting a flag would definitely be very helpful.

-Jon
 
Ok, I think I finally have a working example below. It sets a flag the interrupt has triggered and compares the received serial string in the main loop. If you see anything that looks off please let me know as I do not have much experience working with interrupts.

Code:
/*****************************************************
 * This SerialEvent example shows the "Read Bytes Until"
 * event where it will fire the RX event handler when
 * a termination character is detected or the buffer
 * is full.
 *
 * By using the loopback feature we can test
 * the sending and receiving without having to connect
 * up anything. If you want to disable this feature
 * comment it out.
 *****************************************************/
#include <SerialEvent.h>

Serial1Event Event1;

#define TX_BUFFER_SIZE 128
#define RX_BUFFER_SIZE 128

volatile boolean rx_interrupt_fired = false;
volatile unsigned char * rx_received_string;

void setup() {
  Serial.begin(0);
  //while(!Serial);
  //------------------------------------------------------------------------------------
  // Must declare TX buffer size in bytes, this must be as big as your largest packet
  SERIAL1_MEMORY_TX(TX_BUFFER_SIZE);
  // Must declare RX buffer size in bytes, will fire when buffer is full or term if declared
  SERIAL1_MEMORY_RX(RX_BUFFER_SIZE);
  Event1.loopBack = false;// internal loopback set / "default = false"
  Event1.txEventHandler = tx1Event;// event handler Serial1 TX
  Event1.rxEventHandler = rx1Event;// event handler Serial1 RX
  Event1.rxTermCharacter = 10;// this termination character will fire the RX event handler
  Event1.begin(115200);// start serial port
  //------------------------------------------------------------------------------------
  Serial.println("TX event will fire when termination character is detected");
}

void loop() {
  if (Serial.available()) {
    char c = Serial.read();
    Event1.print(c);
  }
  compareString();
}

//--------------------------------------Serial1 Events-------------------------------------
void tx1Event(void) {
  // TX Event function will fire when it is finished sending a packet
}

void rx1Event(void) {
  // RX Event function prints the buffer when it is full
  Serial.printf("Termination Character %#02X Event fired: %s\n", Event1.rxTermCharacter, Event1.rxBuffer);
  //digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
  rx_interrupt_fired = true;
  rx_received_string = Event1.rxBuffer;
}

void compareString(){
  cli();
  String rx_received_string_copy((char *)rx_received_string);
  boolean rx_interrupt_fired_copy = rx_interrupt_fired;
  sei();
  
  if(rx_interrupt_fired_copy == true){
    Serial.println("String Compare Started");
    cli();
    rx_interrupt_fired = false;
    sei();

    if(rx_received_string_copy=="ON\n") {
      digitalWrite(LED_BUILTIN, HIGH);
      Serial.println("LED ON");
    }
    else if(rx_received_string_copy=="OFF\n") {
      digitalWrite(LED_BUILTIN, LOW);
      Serial.println("LED OFF");
    }
  }
}
 
Try this sketch out and let me know, I set the loopback in this sketch so you try it from the same teensy, just type into the serial monitor and set the monitor to terminate with a "newline".

Code:
#include <SerialEvent.h>


Serial1Event Event1;


#define TX_BUFFER_SIZE 32
#define RX_BUFFER_SIZE 32


volatile boolean rx_interrupt_fired = false;
volatile uint32_t bufSize = 0;
char rx_received_string[RX_BUFFER_SIZE];


void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
  Serial.begin(0);
  //------------------------------------------------------------------------------------
  // Must declare TX buffer size in bytes, this must be as big as your largest packet
  SERIAL1_MEMORY_TX(TX_BUFFER_SIZE);
  // Must declare RX buffer size in bytes, will fire when buffer is full or term if declared
  SERIAL1_MEMORY_RX(RX_BUFFER_SIZE);
  Event1.loopBack = true;// internal loopback set / "default = false"
  Event1.txEventHandler = tx1Event;// event handler Serial1 TX
  Event1.rxEventHandler = rx1Event;// event handler Serial1 RX
  Event1.rxTermCharacter = 10;// this termination character will fire the RX event handler
  Event1.begin(115200);// start serial port
  //------------------------------------------------------------------------------------
  Serial.println("TX event will fire when termination character is detected");
}


void loop() {
  if (Serial.available()) {
    char c = Serial.read();
    Event1.print(c);
  }
  compareString();
}


//--------------------------------------Serial1 Events-------------------------------------
void tx1Event(void) {
  // TX Event function will fire when it is finished sending a packet
}


void rx1Event(void) {
  // return if still processing data
  if(rx_interrupt_fired) return;
  
  // get size of the buffer
  bufSize = Event1.rxBufferSize;
  
  // signal that data is ready to process
  rx_interrupt_fired = true;
}


void compareString(){
  // process data if term charater is found from SerialEvent
  if(rx_interrupt_fired == true) {
    // reset user buffer
    memset(rx_received_string, 0, RX_BUFFER_SIZE);
    
    Serial.print("Coping String: ");
    
    // Copy buffer to user array: rx_received_string
    for(int i = 0; i < bufSize; i++) rx_received_string[i] = Event1.rxBuffer[i];
    
    Serial.print(rx_received_string);
    //remeber to the last byte is located ("Returned SerialEvent Buffer Size" - 1)
    if(rx_received_string[bufSize-1] != '\n') Serial.println();


    // data handler code below
    String rx_received_string_copy((char *)rx_received_string);
    Serial.print("String Compare Started... ");
    if(rx_received_string_copy=="ON\n") {
      digitalWrite(LED_BUILTIN, HIGH);
      Serial.println("LED ON");
    }
    else if(rx_received_string_copy=="OFF\n") {
      digitalWrite(LED_BUILTIN, LOW);
      Serial.println("LED OFF");
    }
    else {
      Serial.println("No Match Found");
    }
    
    Serial.println();
    // update rx fired flag
    rx_interrupt_fired = false;
  }
}
 
Hi Duff,

Thanks for the example, it is very helpful!

Looks I had a lot of things incorrect in my sketch like thinking I needed to use cli(); and sei(); (why wouldn't these be needed?). I'm surprised it even worked at all : )

-Jon
 
single byte event

Hello Duff,

first of all, the UartEvent lib is exactly what i was looking for, thank you so much.
i played around with the examples and i may be blind, but there is one thing i am not sure about.

how do i listen to a single byte event?
i tried to shrink the rx buffer size in UartEvent.h to 1, but that won't work.

is it supposed to work like this, or is there another way to listen to single byte events?

best regards
Stefan
 
i'll take a look at this later today, maybe I broke something in the latest update but i'll figure it out.
 
thank you. here comes my test code. the led blinks with rx buffer size = 2 or higher.
but nothing happens with rx buffer size = 1.

test setup: the external device which sends the serial generates a lot of 0x3E bytes. ( it generates packets where 0x3e belongs to the header. unfortunately it does not use a termination character at the end of a packet. that's why i want to use the single byte event )

Code:
#include <UartEvent.h>

Uart2Event Event2;

volatile bool print_flag = false;

void setup() {
    pinMode(LED_BUILTIN, OUTPUT);
    
    Event2.txEventHandler = tx2Event; // event handler Serial2 TX
    Event2.rxEventHandler = rx2Event; // event handler Serial2 RX
    
    Event2.begin(250000);             
    
    delay(1000);
}

void loop() {
    if(print_flag) {
        digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
        print_flag = false;
    }
}

void tx2Event(void) {
}

void rx2Event(void) {
    byte c = Event2.read();
    if(c == 0x3e) print_flag = true;
}
 
Last edited:
could it be overload?

i tried 125kBauds instead of 250. now i get one led toggle once when the transmitter starts sending and once when it stops sending.
the transmitter only supports 125k and 250k unfortunately.
 
hello duff, thank you so much. i did a couple quick tests that worked very well and will soon continue on that project. thanks again!
 
@Stefan I think fixed the bug when setting the rx buffer size to 1, give it try. The new version is 6.1

I'm joining in on the question regarding the cli() and sei() - why don't we have to wrap our access to volatile variables (shared with the ISR) with those?

Another question - how can I act upon the 1 Byte Received interrupt? I can't find an example for that.

Thanks, awesome library.
 
Another question - how can I act upon the 1 Byte Received interrupt? I can't find an example for that.

for the 1 byte functionality: edit the UartEvent.h
you will find this piece of code, where you can shrink individual rx buffers to 1 byte:

Code:
////////////////////////////////////////////////////////////////
// Tunable parameters (relatively safe to edit these numbers)
////////////////////////////////////////////////////////////////
#define TX0_BUFFER_SIZE 64 // Uart1 outgoing buffer size, must be power of 2
#define RX0_BUFFER_SIZE 64 // Uart1 incoming buffer size, must be power of 2

#define TX1_BUFFER_SIZE 64 // Uart2 outgoing buffer size, must be power of 2
#define RX1_BUFFER_SIZE 1 // Uart2 incoming buffer size, must be power of 2

#define TX2_BUFFER_SIZE 64 // Uart3 outgoing buffer size, must be power of 2
#define RX2_BUFFER_SIZE 64 // Uart3 incoming buffer size, must be power of 2

from there you only need the standard setup:

Code:
#include <UartEvent.h>
Uart2Event Event2;
void setup() {
    Event2.txEventHandler = tx2Event; // event handler Serial2 TX
    Event2.rxEventHandler = rx2Event; // event handler Serial2 RX
    Event2.begin(250000);             
}
void tx2Event(void) {
}
void rx2Event(void) {
    // ISR Code eg:
    byte c = Event2.read();
}

best regards
Stefan
 
Last edited:
for the 1 byte functionality: edit the UartEvent.h
you will find this piece of code, where you can shrink individual rx buffers to 1 byte.

Thanks, I thought you can call that interrupt even with buffers bigger than 1. I'll change the buffer size for now.
 
Thanks, I thought you can call that interrupt even with buffers bigger than 1. I'll change the buffer size for now.
The buffer size is related to when it will generate a event (isr). So if your buffer is 64 bytes it will generate a event once 64 bytes are received unless you specify a termination character. Then it will generate an event when its sees that term character or when the buffer is full.

If you have just one byte buffer you have to setup your own buffer also to capture the incoming packets. Make sure the rx event function is not doing much processing on incoming data since you can lose data. My next update I'm considering a doube buffering but with only a 1 byte buffer that mean you only still have 2 bytes if double buffered.
 
Make sure the rx event function is not doing much processing on incoming data since you can lose data.

i am interested on what the critical load on the ISR roughly is, given a specific baudrate. there will be several factors influencing this for sure, but i might do some simple tests just to get an idea.
 
you have to setup your own buffer also to capture the incoming packets.
I'm currently using a QueueList as a buffer to hold all the bytes received. It makes it really easy to count them and pop them for use later. Should I assign it as volatile, given that I push to it from the ISR, count it from the ISR and poping from it from the regular sketch?
 
I'm currently using a QueueList as a buffer to hold all the bytes received. It makes it really easy to count them and pop them for use later. Should I assign it as volatile, given that I push to it from the ISR, count it from the ISR and poping from it from the regular sketch?

yes i would
 
Hi Duff,
your library is great, but won't you use the newlib-inbuilt-memcpy instead ? It's assembler-hand-optimized by the ARM-guys.
Even there is no great difference - it would save some space at least.
 
Last edited:
Status
Not open for further replies.
Back
Top