arduino-libraries / ArduinoBLE

ArduinoBLE library for Arduino
GNU Lesser General Public License v2.1
313 stars 205 forks source link

Arduino Nano BLE 33/Sense nRF52840 ADC sampling is interrupted by ArduinoBLE library #219

Open Cathay2017 opened 2 years ago

Cathay2017 commented 2 years ago

Hi, I use Arduino Nano BLE 33/Sense (as Bluetooth Periperal) to sample a full rectified sine voltage wave and real-time send it through bluetooth, then bluetooth central (Such as: pad) can show the waveform realtime. In this project,I find an important issue.

To get high ADC sampling rate, I can’t use AnalogRead() which is too slow for my project. Instead, I code based on the sketch which Klaus_K provided in the post (https://forum.arduino.cc/t/increase-the-adc-sample-rate/701813?u=xing_2019).

However, I find a critial issue: When I don’t use ArduinoBLE library, for example:BLE.begin(), nRF52840 ADC works fine,I can achieve high accuracy based on the ADC sampling data. I plot it by using a python script based on the data which my PC computer receive from Serial port of Arduino Nano BLE 33/Sense. Refer to Fig 1, it is a standard full-rectified sine wave (x axis: sample numbers; y axis: ADC value read by Arduino Nano BLE 33/Sense). Great. But when I use ArduinoBLE library, simply BLE.begin(), the wavform received from Bluetooth Central is distorted. There will no accuracy for later computation at all. It seems the ADC sampling is interrupted, refer to Fig 2. Notice: the actual input waveform to A0 is good, refer to Fig 3, just the ADC sampling is interrupted. I also did a further test, if I put BLE.end() after BLE.begin(), ADC sampling works fine. I duplicate this issue in different Arduino Nano BLE 33 uints.

My questions are:

  1. It seems ArduinoBLE library has some conflict with Nordic ADC function, why? Is there any wrong configuration in the code I used? I provide the code below. I appreciate if anyone can help me to review it.
  2. Is ArduinoBLE stopping any other interrupt? It seems ADC is interrupted by some action with higher priority, refer to Fig 4. I try to increase ADC priority: by change NVIC_setPriority(SAADC_IRQn, 1UL) to NVIC_setPriority(SAADC_IRQn, 0UL), however it doesn’t help. It seems the interruption has higher priority, forecly disable any other interrupt(?). I search in ArduioBLE library,but didn't get any clue on it. Also, the version of ArduinoBLE I use is the latest version: V1.2.1. Can anyone support me to review the BLE.begin() what happens after execute BLE.begin() ? How could it impact ADC sampling?
  3. Is Arduino Nano BLE 33/Sense support ADC sampling and real-time Bluetooth commuincation? Please do not use DC voltage to A0 to duplicate this issue, you won’t find this issue since DC voltage still can be plotted to a straight line even the ADC is interuppted.

I really appreciate if anyone can help me out in this issue.

I provide code below. You can just comment/uncomment “if(BLE.begin()) { Serial.println( "Bluetooth initialized successfully!");}” to deplicate this issue.

/*

  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.

*/

// make some change on the original experimental codes to demonstrate the ADC interrupted issue by Bluetooth,2022/01/17 9:55AM

#include "mbed.h"

#include <ArduinoBLE.h>

#define SAMPLES_PER_SECOND  20000

#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( 115200 );

  while ( !Serial );

  Serial.println( "Arduino Nano 33 BLE (mbedOS) example: Timer -> PPI -> SAADC" );

  if(BLE.begin())

  {

    Serial.println( "Bluetooth initialized successfully!");

  }

  initADC();

  initTimer4();

  initPPI();

}

void loop()

{

  static uint32_t previousMillis = 0;

  if ( adcFlag )

  {

    adcFlag = false;

    // if ( sampleCounter >= SAMPLES_PER_SECOND )

    if ( sampleCounter >= 1 )

    {

      // 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;

  // P0.04 - AIN2 -> Pin A0

  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 );

}

To duplicate this issue, I proivde minimum code below. Again, I make a little change based on Klaus_K’s code. For the hardware connection, I use a circuit to convert a sine wave(input frequency: 30 ~ 300Hz, amplitude: +/-2.5V) to fully recified waveform, then input to A0 of Arduino Nano BLE 33. You can duplicate Fig 1 and Fig2 by using 30Hz Sine waveform. I also attach a 100Hz Sine waveform for reference. OK. Any help, I really appreciate!!!

Fig 1. Singal Generator: 30Hz,+/-2.5V Sine waveform, after full rectified circuit, 60Hz full rectified sine wave to A0. Comment BLE.begin() in codes. image

Fig 2. Singal Generator: 30Hz,+/-2.5V Sine waveform, after full rectified circuit, 60Hz full rectified sine wave to A0. Uncomment BLE.begin() in codes. image

Fig 3. the actual waveform catched by oscilloscope. Blue waveform: 30Hz,+/-2.5V Sine waveform. Green waveform: 60Hz full rectified sine wave to Arduino A0 image

Fig 4.Singal Generator: 100Hz,+/-2.5V Sine waveform, after full rectified circuit, 200Hz full rectified sine wave to A0. Uncomment BLE.begin() in codes. The inerrupt seems periodic. image

wpilgri commented 1 year ago

Same issue here although my problem is reading the Arduino Nano 33 BLE onboard accelerometer. I can’t read at much more than 20 Hz or I see what you describe. Killing me as I’ve tried using mbed::ticker and other workarounds and no luck! BLE is a let down so far.