How to properly use Wire.onReceive in Arduino - arduino

I've got a stream of messages coming into an I2C slave from its master.
How do I properly resolve the below issues?
If I do:
Wire.onReceive(handler);
void handler(int howMany) {
byte message = Wire.read();
}
What should I do with message? I shouldn't process it in the ISR in order to keep the interrupt slim and fast. If I instead do:
volatile byte message;
and then set this from the ISR and read it inside loop(), I might not loop fast enough to read quickly incoming messages - thus losing messages.
So instead I can create a buffer, and push to it from inside the ISR, and then read from loop() but that's even more susceptible to race conditions - unless I disable interrupts whilst I take a local copy of the buffer - but with interrupts off I'll miss messages.
Wire has an internal rx buffer, so I'd have thought I could just call Wire.available() and Wire.read() from inside my loop, but the implementation of Wire.cpp states:
// must be called in:
// slave rx event callback
// or after requestFrom(address, numBytes)
int TwoWire::available(void);
So, what's the right way to do this? How do I handle/enqueue fast incoming i2c messages and process them all without dropping data?
Assume I can process messages faster than my rx buffer will fill up, messages will just come in bursts, so no need to care about overflows necessarily.

Related

PIC16 and EUSART

I'm trying to get the EUSART setup over RS485 on a PIC16F. For the RS485 i have an external circuit MAX3430 to make the conversion to RS485. I am able to both send and receive data. The problem I have is that as I do the switch back to RO after a TX transaction I notice the last two bytes don't get sent. It seems that if i do as below (I use pin RB6 as a switch between RO/TX) it's to fast and the last two bytes never get transmitted. What's the best practice here? I'm guessing adding some delay after last sent TX but i don't want to halt my entire program for this, unless that is the general recommendation? I am also exploring a timer as a possible solution, would just need a way to reset the timer so i always get the same delay in that case, currently working on this alternative. Any thoughts are appreciated.
IO_RB6_SetHigh(); // Enable TX mode
for(uint8_t i = 0; i < sizeof(msg); i++)
{
EUSART1_Write(msg[i]);
}
IO_RB6_SetLow(); // Enable Read mode
Thanks,
Marcus
You're switching to RX before transmission completes. You need to either wait two symbol times before calling IO_RB6_SetLow() or (better) check the status of USART Tx buffer to see if it has completed the transmission. There is a bit called "Transmit shift register status bit", in PIC16F877A it's called TXSTA.TRMT.
EUSART1_Write would do this check before sending the next character, you may want to take a look at the source to see how it's done.

Send data to Arduino at any time

I am working on an embedded system whose microcontroller is an Arduino chip (Arduino Nano ATmega328P).
I can connect to it with a FTDI cable to read its outputs using a serial terminal. Now I want to communicate with it the other way around, that is, sending it messages or codes in order to change its global parameters.
I know it is possible to do so if I write some code where the Arduino is doing nothing but listening to the serial port. But I would like to be able to send it message at any time, even when it is doing something else. Sending it a message could trigger an interrupt, then the Arduino would execute a parallel script where it listens to the serial port for some time...
Can I do that, or is it not possible with an Arduino ?
Thanks :)
As far as I know (could be wrong), you can not "multi-thread" an arduino (don't know of any microcontrollers that support this). Inherently microcontrollers should be coded in a different style from computers. You can not assume infinite resources and infinite threads. Instead it is all about your main loop speed and how to distribute your resources. The arduino example (blink without delay) is an example of how this is done with timers. Basically, you put all of your processes inside their own timers so that they execute once per timer interval and are skipped at all other times. Assuming none of your processes are in the main loop, this will allow context switching after each and every process, as the program will cycle back through the main loop looking for the next task timer to come up. If you put all of your cyclical processes on timers like this, then put your serial availability check in another (pretty high frequency) timer, and grab serial any time you see it, it will I believe give you the results that you are after. Keep in mind though that serial read and write is a relatively slow process and so after reading or writing you may need to be careful about other processes that depend on a tight frequency call (like reading a mic to determine sound frequency for instance would need to be reset every time you read serial).
--edit
I was literally working on this today anyways, so here you go:
*note that i had no need to read incoming serial, so left that bit to you to figure out. (but showed where it would be done)
boolean resetMicRecording = true;
unsigned long microphoneTimer = 0;
unsigned long microphonePeriod = 100; //us
unsigned long serialTimer = 0;
unsigned long serialPeriod = 500; //ms
void setup() {
Serial.begin(9600);
}
void loop() {
if (micros() - microphoneTimer >= microphonePeriod)
{
microphoneTimer = micros();
//do microphone task
if(resetMicRecording)//first time flag
{
//set inital recording stuff
resetMicRecording = false;
}
else//consistent frequency sampling
{
//precision analog reading, logging, and processing if nec
}
}
if (millis() - serialTimer >= serialPeriod)
{
serialTimer = millis();
if(Serial.available()>0)
{
//read your incoming serial here
//reset sensitive device timers to startup
resetMicRecording = true;
}
}
}

Interrupting with SoftwareSerial on Arduino

I'm using Bluetooth serial port profile to communicate with Arduino. The bluetooth module (HC-06) is connected to my digital pins 10 and 11 (RX, TX). The module is working properly, but I need an interrupt on data receive. I can't periodically check for incoming data as Arduino is working on a time-sensitive task (music-playing through a passive buzzer) and I need control signals to interrupt immediately on receive. I've looked through many documents including Arduino's own site, and they all explain how to establish regular communication using checking for serialPort.available() periodically. I've found one SO question Arduino Serial Interrupts but that's too complicated for my level. Any suggestions on reading realtime input through serial?
Note that the current version of SoftSerial actually uses PCINT to detect the individual bits. Hence I believe defining it again at the main loop would conflict with the SoftSerial's actual detection of bits.
I am reluctant to suggest this as it is modifying a core library. Which is difficult not to do when sharing interrupts. But if desperate, you could modify that routine, with your need.
within
\arduino-1.5.7\hardware\arduino\avr\libraries\SoftwareSerial\SoftwareSerial.cpp.
//
// The receive routine called by the interrupt handler
//
void SoftwareSerial::recv()
{
...
// if buffer full, set the overflow flag and return
if ((_receive_buffer_tail + 1) % _SS_MAX_RX_BUFF != _receive_buffer_head)
{
// save new data in buffer: tail points to where byte goes
_receive_buffer[_receive_buffer_tail] = d; // save new byte
_receive_buffer_tail = (_receive_buffer_tail + 1) % _SS_MAX_RX_BUFF;
#ifdef YOUR_THING_ENABLE
// Quickly check if it is what you want and DO YOUR THING HERE!
#endif
}
...
}
But beware your are still in a ISR and all Interrupts are OFF and you are blocking EVERYTHING. One should not lollygag nor dilly dally, here. Do you something quick and get out.

Overrun errors with two USART interrupts

Using two USARTs running at 115200 baud on a STM32F2, one to communicate with a radio module and one for serial from a PC. The clock speed is 120MHz.
When receiving data from both USARTs simultaneously overrun errors can occur on one USART or the other. Doing some quick back of the envelope calculations there should be enough time to process both, as the interrupts are just simple copy the byte to a circular buffer.
In both theory and from measurement the interrupt code to push byte to buffer should/does run in the order of 2-4µS, at 115200 baud we have around 70us to process each char.
Why are we seeing occassional OREs on one or other USART?
Update - additional information:
No other ISRs in our code are firing at this time.
We are running Keil RTX with systick interrupt configured to fire every 10mS.
We are not disabling any interrupts at this time.
According this book (The Designer's Guide to the Cortex-M Processor Family) the interupt latency is around 12cycles (not really deadly)
Given all the above 70us is at least a factor of 10 over the time we take to clear the interrupts - so I'm not sure its is so easy to explain. Should I be concluding that there must be some other factor I am over looking?
MDK-ARM is version 4.70
The systick interrupt is used by the RTOS so cannot time this the other ISRs take 2-3µS to run per byte each.
I ran into a similar problem as yours a few months ago on a Cortex M4 (SAM4S). I have a function that gets called at 100 Hz based on a Timer Interrupt.
In the meantime I had a UART configured to interrupt on char reception. The expected data over UART was 64 byte long packets and interrupting on every char caused latency such that my 100 Hz update function was running at about 20 Hz. 100 Hz is relatively slow on this particular 120 MHz processor but interrupting on every char was causing massive delays.
I decided to configure the UART to use PDC (Peripheral DMA controller) and my problems disappeared instantly.
DMA allows the UART to store data in memory WITHOUT interrupting the processor until the buffer is full saving lots of overhead.
In my case, I told PDC to store UART data into an buffer (byte array) and specified the length. When UART via PDC filled the buffer the PDC issued an interrupt.
In PDC ISR:
Give PDC new empty buffer
Restart UART PDC (so can collect data while we do other stuff in isr)
memcpy full buffer into RINGBUFFER
Exit ISR
As swineone recommended above, implement DMA and you'll love life.
Had a similar problem. Short solution - change oversampling to 8 bits which makes USART clock more precise. And choose your MCU clock wisely!
huart1.Init.OverSampling = UART_OVERSAMPLING_8;
Furthermore, add USART error handler and mechanism to check that your data valid such as CRC16. Here is example for the STM32F0xx series, I am assuming it should be pretty similar across the series.
void UART_flush(void) {
// Flush UART RX buffer if RXNE is set
if READ_BIT(huart1.Instance->ISR, USART_ISR_RXNE) {
SET_BIT(huart1.Instance->RQR, UART_RXDATA_FLUSH_REQUEST);
}
// Not available on F030xx devices!
// SET_BIT(huart1.Instance->RQR, UART_TXDATA_FLUSH_REQUEST);
// Clear All Errors (if needed)
if (READ_BIT(huart1.Instance->ISR, USART_ISR_ORE | USART_ISR_FE | USART_ISR_NE)) {
SET_BIT(huart1.Instance->ICR, USART_ICR_ORECF | USART_ICR_FECF | USART_ICR_NCF);
}
}
// USART Error Handler
void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart) {
if(huart->Instance==USART1) {
// See if we have any errors
if (READ_BIT(huart1.Instance->ISR, USART_ISR_ORE | USART_ISR_FE | USART_ISR_NE | USART_ISR_RXNE)) {
// Flush errors
UART_flush();
// Raise Error Handler
_Error_Handler(__FILE__, __LINE__);
}
}
}
DMA might help as well. My problem was related to USART clock tolerances which might even cause overrun error with DMA implemented. Since it is USART hardware problem. Anyway, hope this helps someone out there! Cheers!
I have this problem recently, so I implemented a UART_ErrorCallback function that had was not implanted yet (just the _weak version).
Is like this:
void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart)
{
if(huart == &huart1)
{
HAL_UART_DeInit(&huart1);
MX_USART1_UART_Init(); //my initialization code
...
And this solve the overrun issue.

Arduino interrupt handling

Background
I have need for a data logging application running on a "Arduino compatible" chipKit UNO32 board, with a connected sensor. Data should be logged to an SD-card found on a "Arduino Wireless SD shield".
The sensor is connected via I2C.
My problem is that when I use the Arduino SD library writing is slow: 25 ms per print() operation, which gives me a maximum of 40 Hz which is laughable compared to the 100-800Hz data rate of my sensor.
My faulty solution
Luckily the sensor comes equipped with both an on-chip FIFO that can store 32 sensor values. This means I can go to at least 200Hz without any troubles since the time to fill the FIFO is way larger than the time to write to the card.
But I'd still really like to get to at least 400Hz, so I thought I'd have the following setup:
Tell sensor to put data in the FIFO
When the FIFO is almost full, the sensor triggers an interrupt (sensor does this, and it works, I can catch the interrupt)
When the Arduino receives the interrupt, it polls the sensor for data (via I2C) and stores the data in a buffer in SRAM.
When the SRAM buffer is getting full, write its contents to the SD-card.
Unfortunately, this does not seem to work since the Arduino Wire library that handles I2C use interrupts, and can not be called from within an interrupt handler. I have tried it, and it freezes the microcontroller.
My question
There seems to be other I2C libraries for Arduino that do not rely on interrupts. Should I try that route?
Or is my way of thinking (grabbing a load of data in an ISR) bad from start? Is there another approach I should take?
Just use the interrupt to set a flag and finish the ISR. from the main loop do the calling I2C instead of directly calling from ISR.
boolean fifoFull = false;
void fifoISR() {
fifoFull = true;
}
void loop() {
if(fifoFull) {
// Do I2C
fifoFull = false;
}
}

Resources