i'm creating a sketch that is reading/writing data with I2C as well as 1wire. Beside that, I have a communication interface connected to the hardware serial.
Sending/Reading the hardware serial has a higher priority as reading/writing I2C and 1wire. I have to get each and every byte on UART.
The problem now is:
Reading/writing I2C and 1wire takes a lot of time. depending on the connected device, up to several milliseconds. At 19200baud, a single bytes need ~580µs... So doing something else for several milliseconds does not sound like a good idea. But how do I overcome this? Maybe there is a receive buffer? But how much "time" can be bufferred when data is coming in with 19200 baud?
My first thought: Use an interrupt. That will "interrupt" any other "blocking" thing in loop(), let me receive the data, and continue with loop().
Arduino for 328p and 32u4 is looking in the main() for new data and calls SerialEvent() before/after loop() is called. So there is no real "interrupt driven data reception". If loop blocks for 10ms, I may miss a lot of bytes on UART.
I guess this is the same for samd21?
So my question is: Is there, for SAMD21, a way to get notified by a real interrupt when data is received on UART? If yes: Could you please point me to some helpful - beginner friendly - documentation?
Every time the SERCOM (configured as a UART) receives a character, it calls its associated SERCOM interrupt service routine (ISR).
void SERCOM5_Handler()
{
Serial.IrqHandler();
}
This interrupt handler function then in turn calls on Serial's IrqHandler function that places the character in a 64 byte buffer. This buffer can then be read using Serial.read(). Note that the Serial object is created (instantiated) from the UART class:
void Uart::IrqHandler()
{
if (sercom->availableDataUART()) {
rxBuffer.store_char(sercom->readDataUART());
}...
The SERCOM handler functions can be found in the "variant.cpp" file located (on my Windows machine) at:
To use the interrupt handler for a given Serial port you need to simply comment it out in "variant.cpp" file, paste it into your own sketch and add your own handler code.
Note that a new update to the Arduino core (currently version 1.6.8) will overwrite your changes to the "variant.cpp" file.
SERCOM0_Handler and SERCOM5_Handler() is triggered by the underlying IRQ of the serial port.
If I now use one of the other available serial ports, I have to write my "own" SERCOMx_Handler() funktion and forward the call to SerialX.IrqHandler()..
So at least for non-default serials/sercoms, it seems that I can add an own function-call to the interrupt, but just adding to SERCOMx_Handler() function.
The question is: Is it somehow possible to "overwrite" the default SERCOMx_Handler in variant.cpp, without modifying the core files?!
The question is: Is it somehow possible to "overwrite" the default SERCOMx_Handler in variant.cpp, without modifying the core files?!
No, unfortunately there isn't. However, it's possible if you configure one of the free sercom perhipherals, that aren't already configured by the Arduino Zero's core. In this case the unused Sercom1 and Sercom2 peripherals. This allows you to implement the sercom's interrupt handler in your sketch without resorting to changing the "variant.cpp" file.
To prevent buffer overflow it's necessary to read the Serial's 64 byte ring buffer quicker than it's being filled with incoming data. One method that I've used for a GPS running at 9600bps, is to set-up one of the SAMD21's TC timers to call an interrupt service routine that reads the buffer (with Serial.read()) slightly faster than the time it takes to receive a character. It's less efficient than using the sercom's interrupt service routine though.
Will that only disable things like timer or also the internal SERCOMx_Handler() interrupt call?
noInterrupts() prevents any interrupts from occuring. It should be OK, as long as it's used only briefly. Pending interrupts are serviced once the interrupts() function reinstates them.
I know this is an old post but just in case someone wants to implement an interrupt driven reception with Arduino and SAMD21 variants. The following is what worked for me:
you have to add the attribute((weak)) attribute to the SERCOM you want to use if you want to keep you variant.cpp compatible with other codes:
SERCOM sercom0( SERCOM0 ) __attribute__((weak));
In the case of arduino:
void UART_Handler(void) __attribute__((weak));
To add a new sercom to samd you can start reading:
After adding the weak attribute you have to link the sercom you want to use in your main code and override the interrupt service routine (ISR) by using:
extern SERCOM sercom0; //put your sercom here to use it in your main code
Then override the ISR:
void SERCOM0_Handler()
{
if (sercom0.availableDataUART())
{
sercom0.readDataUART(); //here you get the new char from uart, use your handler here
}
}
As an example, I'm using the TinyGPS++ Library and I wanted to updates the GPS using an interrupt to prevent character loss so in my ISR I have:
void SERCOM0_Handler()
{
if (sercom0.availableDataUART())
{
gps.encode(sercom0.readDataUART());
}
}
I hope this can help.
PD. If you need a full code using the TinyGPS++ just let me know.