Before someone tells me I should not do an AnalogRead in an ISR, I have good reasons. I do this on the UNO with no issues. I need to do it likewise on the Nano 33 BLE. My ISR period is MUCH larger than the ~100usec period of each AnalogRead.
Folks have suggested the underlying OS on the Nano 33 BLE is the cause. Regardless, has someone come up with a workaround...short of programming the Nano 33 BLE outside of the Arduino architecture?
Could you please describe what you are trying to achieve and what your good reasons are? Maybe this will help us find out whether your reasons are good and find a solution for your issue.
sbailey64:
I do this on the UNO with no issues.
The two processors are quite different. I think it is like saying you can drive around in your car with open windows and therefore you should be able to do this with an airplane.
sbailey64:
My ISR period is MUCH larger than the ~100usec period of each AnalogRead.
There are other potential reasons for the issue for instance processor and NVIC priority level.
sbailey64:
Folks have suggested the underlying OS on the Nano 33 BLE is the cause.
With an OS you get rules. If you break the rules you get into trouble. This is true even if you think the rules are stupid for your application. Maybe we can bend the rules or maybe you need a different RTOS with different rules.
No blocking code in ISR: avoid any call to wait, infinite while loop or blocking calls in general.
No printf, malloc or new in ISR: avoid any call to bulky library functions. In particular, certain library functions (such as printf, malloc and new) are non re-entrant, and their behavior could be corrupted when called from an ISR.
For printfs from interrupt context, use Event instead.
The call is conditional. Maybe when you create an analogIn global outside the ISR the issue can be avoided. See example in the mbedOS documentation.
Thank you Klaus_K. That worked as far as compiling. Using mbed Analogin.read() function in my ISR still crashes my system. Bummer. I thought this might work.
analogPin.read_u16() // this is normalized to a full 16-bit range (you do not get more resolution but the highest value is 0xFFFF and the lowest is 0x0000)
or
analogPin.read() // float value (range 0.0 to 1.0)
Yes...I tried both functions to no avail. Still crashes. Below is my code. Came from one of the Arduino examples for Nano 33 BLE. Maybe I am setting something up incorrectly?
void setup()
{
Serial.begin(115200);
while (!Serial);
delay(100);
Serial.print(F("\nStarting TimerInterruptLEDDemo on ")); Serial.println(BOARD_NAME);
Serial.println(NRF52_MBED_TIMER_INTERRUPT_VERSION);
// configure pin in output mode
pinMode(LED_BUILTIN, OUTPUT);
// Interval in microsecs
if (ITimer.attachInterrupt(TIMER_FREQ_HZ, TimerHandler))
{
Serial.print(F("Starting ITimer OK, millis() = "));
Serial.println(millis());
}
else
Serial.println(F("Can't set ITimer. Select another freq. or timer"));
}
void loop()
{
/* Nothing to do all is done by hardware. Even no interrupt required. */
}
Thanks...but the link you provided explains the same problem with no solution. I am thinking the Nano 33 BLE is not going to work for my applications. I was attracted to the potential 200k sample time at 12 bit resolution. Oh well
I have written an experimental example that shows you how to use a Timer and PPI to control the ADC. This will avoid the need to use analogRead in the timer interrupt. The timer is starting the conversion automatically and the ADC uses EasyDMA to transfer the data to RAM.
This is not a beginners example. Please read the notes and let me know if you have any questions.
/*
This experimental code shows how to control the nRF52840 SAADC using a Timer and PPI.
The SAADC uses EasyDMA to copy each sample into a variable in memory.
The ADC samples pin A0.
Note:
- the maximum sampling rate is 200kSamples/s
- only one sample per second it printed
- this code has not been tested with a debugger, some samples might be lost due to mbedOS (needs further testing)
- this code will likely not work when using analogRead() on other pins
The circuit:
- Arduino Nano 33 BLE/ BLE Sense board.
This example code is in the public domain.
*/
#include "mbed.h"
#define SAMPLES_PER_SECOND 180000
#define PPI_CHANNEL (7)
#define ADC_BUFFER_SIZE 1
volatile nrf_saadc_value_t adcBuffer[ADC_BUFFER_SIZE];
volatile bool adcFlag = false;
volatile uint32_t sampleCounter = 0;
void setup()
{
Serial.begin( 9600 );
while ( !Serial );
Serial.println( "Arduino Nano 33 BLE (mbedOS) example: Timer -> PPI -> SAADC" );
initADC();
initTimer4();
initPPI();
}
void loop()
{
static uint32_t previousMillis = 0;
if ( adcFlag )
{
adcFlag = false;
if ( sampleCounter >= SAMPLES_PER_SECOND )
{
uint32_t sampleCount = sampleCounter;
uint32_t currentMillis = millis();
uint32_t runTime = currentMillis - previousMillis;
Serial.print( "Samples: " );
Serial.print( sampleCount );
Serial.print( " run time: " );
Serial.print( runTime );
Serial.print( " ms A0: " );
Serial.println( adcBuffer[0] );
previousMillis = currentMillis;
sampleCounter = 0;
}
}
}
extern "C" void SAADC_IRQHandler_v( void )
{
if ( NRF_SAADC->EVENTS_END != 0 )
{
NRF_SAADC->EVENTS_END = 0;
adcFlag = true;
sampleCounter++;
}
}
void initADC()
{
nrf_saadc_disable();
NRF_SAADC->RESOLUTION = NRF_SAADC_RESOLUTION_12BIT;
NRF_SAADC->CH[2].CONFIG = ( SAADC_CH_CONFIG_GAIN_Gain1_4 << SAADC_CH_CONFIG_GAIN_Pos ) |
( SAADC_CH_CONFIG_MODE_SE << SAADC_CH_CONFIG_MODE_Pos ) |
( SAADC_CH_CONFIG_REFSEL_VDD1_4 << SAADC_CH_CONFIG_REFSEL_Pos ) |
( SAADC_CH_CONFIG_RESN_Bypass << SAADC_CH_CONFIG_RESN_Pos ) |
( SAADC_CH_CONFIG_RESP_Bypass << SAADC_CH_CONFIG_RESP_Pos ) |
( SAADC_CH_CONFIG_TACQ_3us << SAADC_CH_CONFIG_TACQ_Pos );
NRF_SAADC->CH[2].PSELP = SAADC_CH_PSELP_PSELP_AnalogInput2 << SAADC_CH_PSELP_PSELP_Pos;
NRF_SAADC->CH[2].PSELN = SAADC_CH_PSELN_PSELN_NC << SAADC_CH_PSELN_PSELN_Pos;
NRF_SAADC->RESULT.MAXCNT = ADC_BUFFER_SIZE;
NRF_SAADC->RESULT.PTR = ( uint32_t )&adcBuffer;
NRF_SAADC->EVENTS_END = 0;
nrf_saadc_int_enable( NRF_SAADC_INT_END );
NVIC_SetPriority( SAADC_IRQn, 1UL );
NVIC_EnableIRQ( SAADC_IRQn );
nrf_saadc_enable();
NRF_SAADC->TASKS_CALIBRATEOFFSET = 1;
while ( NRF_SAADC->EVENTS_CALIBRATEDONE == 0 );
NRF_SAADC->EVENTS_CALIBRATEDONE = 0;
while ( NRF_SAADC->STATUS == ( SAADC_STATUS_STATUS_Busy << SAADC_STATUS_STATUS_Pos ) );
}
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 initPPI()
{
NRF_PPI->CH[PPI_CHANNEL].EEP = ( uint32_t )&NRF_TIMER4->EVENTS_COMPARE[0];
NRF_PPI->CH[PPI_CHANNEL].TEP = ( uint32_t )&NRF_SAADC->TASKS_START;
NRF_PPI->FORK[PPI_CHANNEL].TEP = ( uint32_t )&NRF_SAADC->TASKS_SAMPLE;
NRF_PPI->CHENSET = ( 1UL << PPI_CHANNEL );
}