Using the following code in V5.0 firmware on Electron to measure pulse widths from 100 - 600us to demodulate OOK signals from RFM69 radio.
It seems to return values of 10000 us all the time when triggered.
micros() appears to be working but concerned something is not triggering interrupt properly.
Watching with scope and 3.3v waveform input to pin B0 starts with 4 - 600us pulses which always show 10000us widths.
Pulse widths are decoding in background loop().
Upgraded to 5.1 and same issue exists. Very strange that over a period of 140ms with many pulses are sent that an interrupt only occurs every 10000us - 14 times equaling the length of many pulses. Why exactly 10000us?
// Interrupt service
void isr_rfm69 () {
uint32_t now = micros();
pulse_width = now - last_edge;
last_edge = now;
}
// Interrupt initialization
static void setupPinChangeInterrupt () {
pinMode(B0, INPUT_PULLUP);
attachInterrupt(B0, isr_rfm69,CHANGE);
}
Your ISR variable last_edge is declared volatile
?
Yes it is as below.
volatile uint16_t pulse_width;
volatile uint32_t last_edge = 0;
And your setupPinChangeInterrupt()
… Why are you declaring that static, it appears to not be a member function… Can you give the context of that function, are you attaching and detaching interrupts?
Maybe posting a basic complete program that demonstrated the problem is in order.
That was just because I merged code from another application. Changing to non static makes no difference to results. The code is large but I have commented out most of it to try and find this problem.
The basics are I attach the interrupt and feed a pulse stream to the pin and it the background check the values of pulse width to decode the OOK data stream. I used the basic idea from a arduino app Github except using a digital input pin - https://github.com/JorjBauer/acurite_decoder/blob/master/acurite_decoder.ino
How are you powering the RFM69?
How are you powering the Electron?
If you should happen to power the RFM69 with 5V (which I suppose it should be - or are you using the 3.3V version?) then you should not use INPUT_PULLUP
on the interrupt pin.
https://docs.particle.io/datasheets/electron-datasheet/#peripherals-and-gpio
You could also increase the interrupt priority
attachInterrupt(B0, isr_rfm69,CHANGE, 0); // highest allowed for GPIO interrupts via attachInterrupt
Very strange. I set up up an Electron with an CHANGE interrupt on B0. I used micros()
to measure the time since the last change interrupt and saved the pulse width in a circular buffer, which is read out of loop and printed to serial.
I fed in a 900 Hz square wave signal, and I got the values that I would have expected.
#include "Particle.h"
/**
* Thread and interrupt-safe (with caveats) circular buffer (ring buffer) class
*
* This class assumes a single reader thread and a single writer thread (or interrupt). For example, it
* works great if you read out of loop() and write from a single interrupt handler. It is not safe for
* multiple reader or multiple writer use cases!
*
*/
template <class T>
class RingBuffer {
public:
RingBuffer(T *elems, size_t size) : elems(elems), size(size) {};
~RingBuffer() {};
/**
* Returns the number of elements that can be read right now (0 = nothing can be read right now)
*
* This is mainly for informational purposes. It's more efficient to call preRead() and check for a non-NULL
* return value than it is to call availableForRead().
*/
size_t availableForRead() const {
return (size + head - tail) % size;
}
/**
* Non-copy version of read. Returns a pointer to the data to be read next or NULL if there is no data right now.
*
* If preRead() returns a non-null value you must call postRead() to consume the data, otherwise the next time
* you call preRead() you'll get the same data back. Don't call postRead() if you get NULL back from preRead()!
*/
T *preRead() const {
if (head == tail) {
return NULL;
}
else {
return &elems[tail];
}
}
/**
* Indicates that you have finished reading the data in the pointer returned by preRead() and it can be reused.
* Only call postRead() if preRead() returned a non-null value!
*/
void postRead() {
size_t next = (tail + 1) % size;
tail = next;
}
/**
* Read with copy. You can use this instead of preRead() and postRead(). Returns true if an element was copied
* or false if there was no data to read.
*/
bool read(T *elem) {
T *src = preRead();
if (src != NULL) {
*elem = *src;
postRead();
return true;
}
else {
return false;
}
}
/**
* Non-copy version of write. Returns a pointer to the buffer to write to or NULL if there is no space.
*
* If preWrite() returns a non-null value you must call postWrite() to commit the data, otherwise the data
* will not be saved. Don't call postWrite() if you get NULL back from preWrite()!
*/
T *preWrite() const {
size_t next = (head + 1) % size;
if (next == tail) {
// No space to write
return NULL;
}
return &elems[head];
}
/**
* Commits the write. Only call if preWrite() returned a non-NULL value.
*/
void postWrite() {
size_t next = (head + 1) % size;
head = next;
}
/**
* Write with copy. You can use this instead of preWrite() and postWrite(). elem is copied. Returns true if
* the operation succeeded (there was space in the buffer).
*/
bool write(const T *elem) {
T *dst = preWrite();
if (dst != NULL) {
*dst = *elem;
postWrite();
return true;
}
else {
// No room to write
return false;
}
}
private:
T *elems;
size_t size;
volatile size_t head = 0;
volatile size_t tail = 0;
};
void isr(); // forward declaration
const size_t RING_BUFFER_SIZE = 1000;
uint32_t ringBufferData[RING_BUFFER_SIZE];
RingBuffer<uint32_t> ringBuffer(ringBufferData, RING_BUFFER_SIZE);
volatile uint32_t lastInterrupt = 0;
void setup() {
Serial.begin(9600);
pinMode(B0, INPUT_PULLUP);
attachInterrupt(B0, isr, CHANGE);
}
void loop() {
uint32_t width;
while(ringBuffer.read(&width)) {
Serial.printlnf("%u", width);
}
}
void isr() {
// This is an interrupt service routine. Be careful what you put in this function!
if (lastInterrupt != 0) {
uint32_t width = micros() - lastInterrupt;
ringBuffer.write(&width);
}
lastInterrupt = micros();
}
And the serial output looks like:
553
556
553
556
554
555
554
555
554
555
554
555
554
557
2 Likes
Currently USB is powering Electron and 3.3v from Electron is powering the RFM69.
I also tried setting the priority 0.
Thanks for trying that. I must have something wrong I am not seeing. I also tried a fifo previously as I thought it was a interrupt problem. I will try your code and see if I can trace the problem.