How do I use registers to configure SAADC for my Arduino Nano 33?

The documentation is a little unclear. I am using Arduino Nano 33 BLE. Using the analogread() functions of Arduino I get a much worse performance than the nRF is supposed to handle. How can I use the registers with the void setup() and void loop() to leverage the full performance of SAADC? I want to make the ADC in Nano BLE 33 faster.
I am using Arduino on PlatformIO IDE on VSCode.
This is the closest think I have found: NordicSnippets/main.c at master · andenore/NordicSnippets · GitHub
But it is not working as I am too inexperienced to figure out the GPIO.

You are in luck, I have just written an experimental example for another user that shows you how to use a Timer and PPI to control the ADC.
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 );
}
4 Likes

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