Arduino nano ble timer interrupt in 100us

I would like to ask about the timer or interrupt of the arduino nano 33 ble.
I want to read an analog or a function exactly every 100 microseconds (µs), but there seems to be no more precise timer, and NRF52_MBED_TimerInterrupt only goes to milliseconds.
The nano 33 ble and UNO chips are not yet the same, resulting in no more examples to refer to, so I came here for help

simple code as a reference

void setup(){
  Serial.begin(115200);
  Timer1.initialzie(100);
  Timer1.attachInterrupt(readAnalog);
}

void readAnalog(){// nexactly every 500 microseconds (µs)
  sampleData[i][j] = analogRead(pin);
}

void loop(){
  //println(sampleData);
}

This thread may be of interest

https://forum.arduino.cc/t/increase-the-adc-sample-rate/701813

What I need is a "fixed cycle" to read the data
This is related to the accuracy of the sampled data

I have seen similar codes with Timer4 settings
But I don't know how he calls PPI to work
In my example in this article, the function to be interrupted will be passed in as a parameter

#define SAMPLES_PER_SECOND  (2000000)
#define PPI_CHANNEL_T4      (7)
#define PIN_GPIO_T4         (2)
#define PORT_GPIO_T4        (1)

void setup()
{
  initTimer4();
  initGPIOTE();
  initPPI();
}

void loop()
{
  
}


void initTimer4()
{
  NRF_TIMER4->MODE =     TIMER_MODE_MODE_Timer;
  NRF_TIMER4->BITMODE = TIMER_BITMODE_BITMODE_16Bit;
  NRF_TIMER4->SHORTS = TIMER_SHORTS_COMPARE0_CLEAR_Enabled << TIMER_SHORTS_COMPARE0_CLEAR_Pos;
  NRF_TIMER4->PRESCALER = 0;
  NRF_TIMER4->CC[0] = 16000000 / SAMPLES_PER_SECOND; // Needs prescaler set to 0 (1:1) 16MHz clock
  NRF_TIMER4->TASKS_START = 1;
}


void initGPIOTE()
{
  NRF_GPIOTE->CONFIG[0] = ( GPIOTE_CONFIG_MODE_Task       << GPIOTE_CONFIG_MODE_Pos ) |
                          ( GPIOTE_CONFIG_OUTINIT_Low     << GPIOTE_CONFIG_OUTINIT_Pos ) |
                          ( GPIOTE_CONFIG_POLARITY_Toggle << GPIOTE_CONFIG_POLARITY_Pos ) |
                          ( PORT_GPIO_T4                  << GPIOTE_CONFIG_PORT_Pos ) |
                          ( PIN_GPIO_T4                   << GPIOTE_CONFIG_PSEL_Pos );
}


void initPPI()
{
  // Configure PPI channel with connection between TIMER->EVENTS_COMPARE[0] and GPIOTE->TASKS_OUT[0]
  NRF_PPI->CH[PPI_CHANNEL_T4].EEP = ( uint32_t )&NRF_TIMER4->EVENTS_COMPARE[0];
  NRF_PPI->CH[PPI_CHANNEL_T4].TEP = ( uint32_t )&NRF_GPIOTE->TASKS_OUT[0];

  // Enable PPI channel
  NRF_PPI->CHENSET = ( 1UL << PPI_CHANNEL_T4 );
}

There is a precise clock/timer implemented in the MBED-OS through the HighResClock-class which offers precision to at least microseconds. This is quite simple and mostly handles same role as classic Arduino micros-function.

Better way is to use the Ticker-class to call a function periodically through an interrupt.

Example of calling a function every 100us:

#include <mbed.h>

mbed::Ticker counterTicker;

uint32_t count = 0;
void ISRcounter(void)
{
  count++;
}

void setup()
{
  Serial.begin(57600);
  counterTicker.attach_us( ISRcounter, 100 ); // Call ISRcounter function every 100 us. 
}

uint32_t lastCount;
uint32_t diffCount;
void loop()
{
  diffCount = count - lastCount; // Count how many times ISRcounter was called.
  lastCount = count;
  Serial.println( String(count) + String(", ") + String(diffCount) );
  delay(1000); 
}

This gives output:

16:12:10.169 -> 11060988, 10152
16:12:11.128 -> 11071140, 10152
16:12:12.146 -> 11081295, 10155
16:12:13.164 -> 11091451, 10156

which is pretty close to the expected value of 10000 function calls per second considering that we are using delay() and Serial.print() for debugging. Remember to keep the called function minimal since it is run in the interrupt-context.

Btw. You might have to change the ADC settings to get the sampling rate high enough if you use this to read analog values.

1 Like

This is a good way.
I was looking for nrf52 related information before, I didn't know the keyword "mbed" at all.
You've saved my life.
arm mbed full API list

Forgot to update, Ticker cannot accept analogRead in the called function

Hey Jamakoiv,
I am trying to learn mdeb and interrupts.
To minimize processor loading I removed all serial stuff.
In the ISR I turned an LED on and then off.
The time from the leading edge to leading edge is the time from ISR to ISR.
The time is usually 99.6us but sometimes I see a time as short as 82us and as long as 104us.
Have you noticed this??
Here is the code.
I can send a scop trace if you care.



#include <mbed.h>

mbed::Ticker counterTicker;


uint32_t count = 0;

int LED4_PIN = 10;
int LED3_PIN = 11;

void ISRcounter(void)
{
  digitalWrite(10, HIGH);
  digitalWrite(11, HIGH);
  count++;
  digitalWrite(10, LOW);
  digitalWrite(11, LOW);

}

void setup()
{

  
  counterTicker.attach_us( ISRcounter, 100 ); // Call ISRcounter function every 100 us. 
  //LED4_PIN = 10;
  //LED3_PIN = 11;
  pinMode(10, OUTPUT);
  pinMode(11, OUTPUT);
  delay(1000);
//Serial.begin(1200);
   //delay(1000);
}

uint32_t lastCount;
uint32_t diffCount;
void loop()
{
  diffCount = count - lastCount; // Count how many times ISRcounter was called.
  lastCount = count;
  //Serial.println( String(count) + String(", ") + String(diffCount) );
  delay(1000); 
}

I decided to send the scopE trace.

I have not seen the short intervals when using the Ticker class.

I think that one area for you to explore is to run your test with Timer interrupts and not Ticker.

Ticker uses Timer1 which is shared by other services and the Ticker API may have some software involved.

You may want to explore direct hardware Timer interrupts using Timer 3 or 4.

There is a library which is available through the library manager if you want to use one.

https://github.com/khoih-prog/NRF52_MBED_TimerInterrupt

Cattledog,
Sorry to bother you.
I have no idea what I am doing with mBed.
I am using a NANO 33 BLE.
When I goto the github line and download the example "Argument_None.ino"
I get the following message :
#error TinyUSB Arduino Library does not support your core yet

Any guidance on what I am doing wrong?

Thanks,
Lalpace888

No. I just complied that example code Argument_None.ino for a Nano33 BLE with no errors. I used both IDE 2.0.4 and 1.8.19.

You should be using the latest version of this core package (Arduino Mbed OS Nano Boards by Arduino Version 4.0.2) available through the boards manager.

If the updated core files don't clear the error, then in the IDE pull down menu under File>Preferences check show verbose output for compile and attach the complete error message.

The latest version that I have (and can download) is 3.5.4.

I'll do an update and see what I get.

Thanks,
Laplace888

cattledog,
My BAOARDS MANAGER says Version 4.0.2 but the pull down says 3.5.4??
Do I have the latest version installed?

Thanks,
Larry

I think so. This is what I see in the Boards Manager with IDE 2.0.4

2.04 BoardsManager Capture

The detailed output from the compile will tell us for sure.

cattledog,
Looks like it was an operator error managing libraries,
Thanks,
Laplace888

cattledog,
My gut feeling is that the repeatability of the ISR depends on what the IRQ is interrupting.
I tried the interrupt thing and here is what I got.

/****************************************************************************************************************************
  Argument_None.ino
  For NRF52 boards using mbed-RTOS such as Nano-33-BLE
  Written by Khoi Hoang

  Built by Khoi Hoang https://github.com/khoih-prog/NRF52_MBED_TimerInterrupt
  Licensed under MIT license

  Now even you use all these new 16 ISR-based timers,with their maximum interval practically unlimited (limited only by
  unsigned long miliseconds), you just consume only one NRF52 timer and avoid conflicting with other cores' tasks.
  The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers
  Therefore, their executions are not blocked by bad-behaving functions / tasks.
  This important feature is absolutely necessary for mission-critical tasks.
*****************************************************************************************************************************/

/*
   Notes:
   Special design is necessary to share data between interrupt code and the rest of your program.
   Variables usually need to be "volatile" types. Volatile tells the compiler to avoid optimizations that assume
   variable can not spontaneously change. Because your function may change variables while your program is using them,
   the compiler needs this hint. But volatile alone is often not enough.
   When accessing shared variables, usually interrupts must be disabled. Even with volatile,
   if the interrupt changes a multi-byte variable between a sequence of instructions, it can be read incorrectly.
   If your data is multiple variables, such as an array and a count, usually interrupts need to be disabled
   or the entire sequence of your code which accesses the data.
*/

// This sketch does only Timer0 as a proff of concept.

#if !( ARDUINO_ARCH_NRF52840 && TARGET_NAME == ARDUINO_NANO33BLE )
  #error This code is designed to run on nRF52-based Nano-33-BLE boards using mbed-RTOS platform! Please check your Tools->Board setting.
#endif


// To be included only in main(), .ino with setup() to avoid `Multiple Definitions` Linker Error
#include "NRF52_MBED_TimerInterrupt.h"


volatile uint32_t Timer0_Old_Cnt = 0; //micros() leaving ISR
volatile uint32_t Timer0_New_Cnt = 0; //micros() entering ISR 
volatile uint32_t Timer0_Delta_Cnt = 0;//



// Init NRF52 timer NRF_TIMER1
NRF52_MBED_Timer ITimer0(NRF_TIMER_4);






void TimerHandler0()
{
Timer0_New_Cnt = micros();
  digitalWrite(10, HIGH);
  digitalWrite(11, HIGH);

  if (Timer0_New_Cnt>Timer0_Old_Cnt)
     { (Timer0_Delta_Cnt =Timer0_New_Cnt - Timer0_Old_Cnt); }
  digitalWrite(10, LOW);
  digitalWrite(11, LOW);
  Timer0_Old_Cnt = Timer0_New_Cnt;
  
}



void setup()
{
    pinMode(11, OUTPUT);  //LED3
    pinMode(10, OUTPUT);  //LED4
    pinMode( 4,  INPUT_PULLUP);
  Serial.begin(115200);

  while (!Serial && millis() < 5000);

  delay(100);

  
(ITimer0.attachInterruptInterval( 100, TimerHandler0));// 100usec
}

void loop()
{
  if (digitalRead(4)==0) {return;}
 Serial.print(150);
Serial.print(",");
Serial.print(100);
Serial.print(",");
Serial.println(Timer0_Delta_Cnt);
  
}

Here is a screen shot of the monitor.

And here is the scope.
The horz is 20us/div not 10 as the previous trend.

Both sorta agree,

I wonder what's going on???

I have never really used that library, and I don't think the timer set up is correct for the short interrupts. The library was configured for longer intervals.

I think that you either want to go back to exploring Ticker, or else dig into the data sheets and online references and set up a timer interrupt with low level code.

I'll take a look at some different things, but I don't have much time to work on this.

One observation I have is that the Nano 33 BLE is best used for BLE, and trying to use this device for tightly timed hardware functions can be troublesome. I'm not certain if you can disable the BLE when working on these timer interrupts or how to set up the different priorities for the RTOS.

Good Luck.

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