External Interrupt speed for Nano 33 BLE

I have an external sensor that needs at minimum, a 40 kHz oscillator which I made using a LTC6907. To read out the data, I need to be able to count pulses in order to synchronize to it and I chose the Nano33 BLE since it had a clock speed of 64 MHz. As a simple test, only the oscillator is connected to the Nano33 and the simplest amount of code is used. The oscillator is an external interrupt on D2, the routine then counts the number of interrupts. I expected 40,000 but instead, I got anywhere between 19,000 to 29,000 counts. I'm baffled. I would have thought it would be able to keep up. Any ideas why it's not even close? I verified the input frequency using a scope and everything is correct.

testnano33ble4xscan.ino (939 Bytes)

There is a minor thing getting in your way, it is called instruction cycle time. The Nano is not fast enough to do this as an interrupt. However you can do it with one of the counter timers, use it as an input to the counter, then set it to interrupt you at maybe 1000 counts or whatever will scale into your code. You can simply use the counter and simply read it as you need the informaiton.

Hi.
In theory the 64MHz nano would perform 1600 cycles every cycle of the input signal.
64,000,000 / 40,000 = 1,600.

But printing time can mess up this count.
My suggestion is to run a sketch like this:

And delay() gets in the way a lot.

const int CLK  = 2;    // D2
unsigned long fcnt = 0;   // to keep track of clk pulses
unsigned long myTime = 0;
//Interrupt routines --
/*********************************************************************/
void onCLK() 
/*********************************************************************/
{
    fcnt++;
}
/*********************************************************************/

/*********************************************************************/
void setup() 
/*********************************************************************/
{
    Serial.begin(115200);
    while (!Serial);
    pinMode(CLK, INPUT);
    attachInterrupt(digitalPinToInterrupt(CLK), onCLK, RISING);
    myTime = millis();
}
/*********************************************************************/
void loop()
{
 if (millis() - myTime > 1000)
  {
    Serial.print("Fcnt: " );
    Serial.println(fcnt);
    fcnt = 0;
    myTime = millis();
  }

}

@kdomkus, your topic has been moved to a more suitable location on the forum.

Please read How to get the best out of this forum and pay special attention how to post code using code tags; people using cellphones in general can't view ino attachments so by posting in the post instead of attaching more people might be able to help.

Below your code from the opening post

const int CLK  = 2;    // D2
volatile long fcnt = 0;   // to keep track of clk pulses
long lastcnt;

//Interrupt routines --
/*********************************************************************/
void onCLK() 
/*********************************************************************/
{
    fcnt++;
}
/*********************************************************************/

/*********************************************************************/
void setup() 
/*********************************************************************/
{
    Serial.begin(115200);
    while (!Serial);

    pinMode(CLK, INPUT);

    attachInterrupt(digitalPinToInterrupt(CLK), onCLK, RISING);

}
/*********************************************************************/
void loop()
{
  while(1)
  {
    Serial.println("Fcnt: " + String(fcnt) + " Delta Cnt: " + String(fcnt-lastcnt));
    lastcnt = fcnt;
    delay(1000);
  }

}

@kdomkus
If we assume that delay(1000) is nearly a second and the serial output happens only every second, there should not be such a big error. So from that point Your idea to use the Nano BLE should be OK. But You've got only half the interrupts You expected. That is really bad.
I have some similar problems with the Nano BLE, because I have some exactly timed libraries depending on the availability of the CPU, measured with the micros() function of Arduino. I detected, that the mbedOS destroys my timing, especially if I use the BLE library (I measured time outs up to 5 ms).
I do not know, which interrupt priority is used for the external interrupts when enabled via attachInterrupt(). But if that is lower than other interrupts managed by mbedOS, some of your interrupts may get lost.
I am still working on that problem (Bypassing mbedOS). On a lower layer (NVIC_xxx()) you can set the interrupt priorities (0 for highest priority). May be, that could help.

Using the millis() idea improved it but it still doesn't count them all. I got an average fcnt of 37800 which means it's not able to keep up. I suspect that interrupt priority would affect it given that almost nothing else is happening.
I tried a Mega and an Uno which have a clock speed of 16 MHz and got a better result: average fcnt of 39800.
The Nano33 must be very inefficient - very disappointing. I'll have to figure out some sort of work around.

I have had the same experience with poor interrupt performance on the Nano 33. The Arduino core overlay to the mbed os makes it difficult to optimize.

If you can figure out how to use the timer/counter and the input signal as an external clock source you might have a better approach.

Use the Nano33 if you need BLE, but as a general high clock speed processor it is not a great choice.

The Teensy family of boards from PJRC is worth a look. They can use the Arduino IDE, there is good technical support online, and are low cost. The Teensy 3.2 is less than $20 US and has many capabilities.
https://www.pjrc.com/store/teensy32.html

I chose the nano33 because it also has the BLE so I'll probably keep going with the development until I find it won't work. I had gotten the sensor interface to work just fine with the ESP32 and I had gotten the bluetooth part working fine, it's just that when I tried to put them together, the interrupt routine stopped. Apparently the BT library used the interrupts. So my next choice was this.
I might get away with dividing the clock by 2. Run the sensor at 40kHz but the interrupt at 20kHz as long as they are synchronous.
I'll also look into using a timer but the documentation for using them is rather poor.

I would think that you would have a better chance of success with the ESP32. With the dual core, you can have the interrupts on core1 not interactive with the Bluetooth which is by default on core 0.

In general, the esp32 is going to be better documented and have more avenues of programming support than the Nano 33 BLE.

I tried setting a PWM output which then with an interrupt let me count pulses. The interrupt timing results were very erratic at high speeds. I kept reducing the timing until it was able to catch up. Unfortunately I had to go pretty far down. The PWM output though was very good (checked with scope). (see graph of f vs fcnt, where f (PWMf) = frequency of PWM timer, vs fcnt which is interrupt counting of the same). e.g. At 50kHz PWM, fcnt return ~40,000, but strangely at 75kHz PWM, returned 90,000.
f vs fcnt

#include "mbed.h"
#define CLK 2
volatile long fcnt = 0; //count clock cycles so as to know when to start something
unsigned long myTime = 0;

mbed::PwmOut ClkPin(digitalPinToPinName(CLK));

/*********************************************************************/
void onCLK()  // interrupt routine
/*********************************************************************/
{
    fcnt++;   // count clock cycles
}
/*********************************************************************/

/*********************************************************************/
void setup() 
/*********************************************************************/
{
    double PWMf;
    Serial.begin(115200);
    Serial.println("Begin...");

    PWMf = 50000.0f;          // 50kHz     
    ClkPin.period( 1.0f/PWMf );    
    ClkPin.write(0.50f);      // 50% duty cycle, relative to period

    attachInterrupt(digitalPinToInterrupt(CLK), &onCLK, RISING);

    myTime = millis();
}
/*********************************************************************/

/*********************************************************************/
void loop() 
/*********************************************************************/
{
    if (millis() - myTime >= 1000)
    {
      Serial.println("Millis: " + String(millis() - myTime) + "Fcnt: " + String(fcnt));
      fcnt = 0;
      myTime = millis();
    }
}
/*********************************************************************/

The ESP32 also has Hardware Pulse Counters that might provide considerably better performance and make the job easier.

Yes. Between the more easily used peripherals, the rtos, and the dual cores I can not help but think that you would be better going back to the ESP32.

I have not been able to find the way to use an external clock source counter with the mbed OS on the Nano 33 BLE, but I do think it is somewhere in there.

I think you're right. I'll try this again using the ESP. In the long run I'll have to figure out how to separate the code to run on separate cores. First glance it looked messy. Hopefully it's doable.

xTaskCreatePinnedToCore().
As mentioned, use Core 1 for the pulse counting stuff and let BLE run in Core 0.

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.