Electronic – Arduino – interrupt based decoding of serial data, race between ISR and loop()

arduinointerruptsRF

Trying to decode serial data (encoded using proprietary method) from a RF module.

Having switched back and forth between the fast-sampling (4x the fastest freq) and of late an interrupt based method, I've reached a dead-end and not sure what all I am doing wrong, because apparently people have been successful in decoding ISM-band RF data (Manchester, NRZ, VirtualWire and HT12E/PT2260-62 encoded), in software.

In my case, the RF modules's AGC (which I've no idea how to turn off, since there's no data-sheet, and the co. doesn't respond to emails… a cheap east-asian make) picks up noise and has the fastest level-transitions in durations ~40us, although valid (encoded data) transitions not lesser than 400us. My ISR has an elementary task of measuring the duration of last state to reject the spurious noise, and save only those transitions that could possibly be part of the encoded data. So far, I have not used a circular-queue (which is what I plan as the next step), but before doing that, I did a small experiment. In the ISR I check the state of a flag to see if it is true, and if true I do Serial.print("x"), else I set it true. In the loop(), I check if flag is true, then do Serial.print("-") and set it false. The flag is declared volatile. I find that I get very long continous streams of "x"s and very rare "-". My ISR is triggering on every transition of pin-2 (i.e. external interrupt#0).

Now my question is, would adding a queue really solve my problem, because apparently the ISR is running far too frequently and thereby starving my loop() of cycles. Counting the distance between the "x"s and "-", I was hoping to find the ideal queue length, but I can't figure out a pattern. Is there something inherently wrong in my approach ?

An outline of the source code is here.

Best Answer

You probably want to take the transmission time (and software UART management methods) of the Serial.print() routine into account.

If you have access to a scope, toggling I/O pins would be a lot faster. You could toggle one in the ISR and the other in the loop.

Or you could increment a counter in the ISR and sample/clear it in the loop (inside an interrupt disable/re-enable) then print it (after re-enabling interrupts). More sophisticated would be to only print the number if it exceeds the previous maximum.

However, such tests are fundamentally flawed unless the testing code constitutes a small fraction of the loop operations - just figuring out how many interrupts can occur during a max-check/print routine is meaningless in terms of the ultimate operation which would presumably be doing something else in the loop.

Generally, you will want to use circular buffering if you are acquiring data via an interrupt which must be aggregated into a larger assembly (bits into bytes, or bytes into buffers) and then acted upon in a way that is at all time consuming. It shouldn't be too hard to change your buffer size later to optimize memory consumption, especially if you keep them power-of-two sized. You may want to build in overflow alarms, and try running the finished system with an artificially reduced buffer size.

Do pay very careful attention to ensuring that the ISR can't jump in the middle of where the loop trying to update the buffer's tracking variables - if you do a read-modify-write, you'll need to protect it.