Faster ADC on Arduino R4 Wifi

Hello everyone,
My name is Jonas and I'm working with measuring periods in a simple pendulum. The pendulum is equipped with two sensors, a central sensor, which reads when the pendulum's bob is at the highest speed, and a lateral one, which reads when the pendulum's bob has a speed tending to zero.
The sensors are connected to analog inputs A0 and A2. Carrying out some tests and reading the material in some topics on this forum, I verified that the reading of the analog inputs has a delay of 21 microseconds. As there are some analog readings throughout the program, this may compromise the calculation of the gravity acceleration error.
I noticed that @susan-parker carried out some implementations to make the ADC faster in previous topics on this forum. However, I have no experience working with register manipulation. There are many implementations on Arduino R3 that I was able to perform. However, I was unable to understand the implementations for the Arduino R4. Could anyone help me with this issue?
Thanks in advance.

What are the sensors reading? the presence of the pendulum's bob (true, false) or the speed at that point (how?) ? ➜ why do you need an analog reading?

how fast is really this pendulum moving ?

Hi J-M-L Jackson
Below is the code I am using:

// Analogic ports
const int sensor_cent = A0;   // analog port for central sensor
const int sensor_lat = A2;    // analog port for side sensor

// Sensor threshold
const int lim_cent = 458;     // central threshold  //458
const int lim_lat = 460;      // side threshold  //460

// number of oscillations
const int n_oscilacao = 20;

// time interval for the period calculation 
unsigned long tempo_cent [n_oscilacao + 1];   // central sensor time
unsigned long tempo_lat [n_oscilacao + 1];    // side sensor time
unsigned long periodo_cent [n_oscilacao];     // central oscillation period
unsigned long periodo_lat [n_oscilacao];      // side oscillation period
unsigned long periodo_medio_cent;             // central oscillation period mean
unsigned long periodo_medio_lat;              // side oscillation period mean

// for the first oscillation
bool chave_cent = 1;
bool chave_lat = 1;
bool n1 = 1;

/*$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ FUNÇÃO SETUP $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*/
void setup() {
  Serial.begin(9600);  

  // checks whether the pendulum has passed the sensors
  while(chave_cent || chave_lat)
  {
    if(analogRead(A2) < lim_lat)
    {
      chave_lat = 0;
    }
    if(analogRead(A0) > lim_cent)
    {
      chave_cent = 0;  
    }
  }

  // checks whether the pendulum has passed the central sensor
  while(n1)
  {
    if(analogRead(A0) < lim_cent)
    {
      n1 = 0;
    }
  }

  // counting cycles and recording oscillation times
  for(int i=0; i < n_oscilacao + 1; i++)
  {
    while(analogRead(A0) < lim_cent)
    {
      // Doesn't do anything until the central sensor reads
    }
    tempo_cent [i] = micros(); // records the time since the start of the program, in us, on the central sensor

    while(analogRead(A2) < lim_lat)
    {
      // Doesn't do anything until the side sensor reads
    }
    tempo_lat [i] = micros(); // records the time since the start of the program, in us, on the side sensor

    while(analogRead(A0) < lim_cent)
    {
      // waits for the pendulum to return to the central sensor
    }

    while(analogRead(A0) > lim_cent)
    {
      // waits for the pendulum to pass through the central sensor
    }
  }

  // period calculations
  for(int i=0; i < n_oscilacao; i++)
  {
    periodo_cent [i] = tempo_cent [i+1] - tempo_cent [i];
    periodo_lat [i] = tempo_lat [i+1] - tempo_lat [i];
  }

  periodo_medio_cent = (tempo_cent [n_oscilacao] - tempo_cent [0]) / (n_oscilacao);
  periodo_medio_lat = (tempo_lat [n_oscilacao] - tempo_lat [0]) / (n_oscilacao);

  // Write data
  Serial.print("Period number;");
  Serial.print("periodo central;");
  Serial.println("periodo lateral");
  for(int i=0; i < n_oscilacao; i++)
  {
    Serial.print(i+1);
    Serial.print(";");
    Serial.print(periodo_cent [i]);
    Serial.print(";");
    Serial.println(periodo_lat [i]);
  }
  Serial.println();
  Serial.print("periodo medio central;");
  Serial.println("periodo medio lateral");
  Serial.print(periodo_medio_cent);
  Serial.print(";");
  Serial.println(periodo_medio_lat);
  Serial.println();
  Serial.println("--------------------------------------");

} //$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ END FUNÇÃO SETUP


/*$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ FUNÇÃO LOOP $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*/
void loop() {
  
    
  // loop infinito
  

} // $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ END FUNÇÃO LOOP

The sensor reading is based on the presence of the pendulum's bob, i.e. whether the pendulum is at the sensor or not. The principle of the sensor is based on voltage variation, from 0 to 5 V, which is being read by the analog input. I understand that digital input would be much faster for reading, but I don't know how to adapt to digital input.
The sensor has an oscillation speed of approx. 2 m/s on the central sensor.

Hi Delta_G
Well, I have no knowledge in electronics, especially in manipulating registers. I checked a code in which @susan-parker mentions that it is possible to change the registers to make the A/D conversion faster. The code she created is this:

analog_read_value = *ADC140_ADDR00; // Internal 16bit register read
*ADC140_ADCSR |= (0x01 << 15); // Write to register to start next ADC conversion
// ADC total time = 290nS

Maybe I misinterpreted, but...
Is it possible to implement this to make sensor reading faster?
If the answer is yes, how do I implement this?

can you explain why you think analogRead is too slow?

the code would be better written as a state machine (Yet another Finite State Machine introduction) rather than having all those blocking while loops

The ADC read is intended to be inside a fast loop e.g. a 24kHz timer PWM interrupt, where one cannot afford to wait for an ADC conversion.
I read the previous reading, then start a new conversion, so there is no blocking.
The code above also needs the ADC init code in my void setup_adc(void) call.

I do the same thing with code running on an ATmegaXXX processor, split the ADC functions in this manner. Here is core code:

/*  Arduino UNO R3 test code for fast ATmega split ADC and digital pin operation
 *  Susan Parker - 10th June 2023.
 *
 * This code is "AS IS" without warranty or liability. 

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
*/
/* For external aref - ADR4540 - Ultralow Noise, High Accuracy Voltage Reference
   Using an aref gives stable 10bit values on USB power
https://www.analog.com/media/en/technical-documentation/data-sheets/adr4520_4525_4530_4533_4540_4550.pdf
*/


// Direct register bit set/clear/test/wait macros (found code):
#define _BV(bit) (1 << (bit))                                             // Convert a bit number into a byte value.
#define  clr_bit(sfr, bit)  (_SFR_BYTE(sfr) &= ~_BV(bit))
#define  set_bit(sfr, bit)  (_SFR_BYTE(sfr) |=  _BV(bit))
#define  is_bit_set(sfr, bit)    (_SFR_BYTE(sfr) & _BV(bit))              // bit_is_set ?
#define  is_bit_clr(sfr, bit)  (!(_SFR_BYTE(sfr) & _BV(bit)))             // bit_is_clear ?
#define  loop_until_bit_set(sfr, bit)  do{}while(is_bit_clr(sfr, bit))    // loop_until_bit_is_set
#define  loop_until_bit_clr(sfr, bit)  do{}while(is_bit_set(sfr, bit))    // loop_until_bit_is_clear

#include <avr/wdt.h>          // Enable WatchDog timer - use minimum safe timeout to stop runaway charge cycle
#include <stdlib.h>
#include <Arduino.h>
#include "avr/pgmspace.h"

#define SERIAL_BAUD    (115200)      // Serial COM port speed

static unsigned int analogAdcValue = 0;

void setup(void)
  {
  byte temp_reg = 0;
  Serial.begin(SERIAL_BAUD);      // connect to the serial port
  set_bit(DDRD, 4); // set Port D Pin 4 as output = Arduino Pin 4
  setup_adc();
  }

void loop()  // Background tasks 
  {
  run_adc();
  Serial.println(analogAdcValue);
  delay(100);
  }

void run_adc(void)  // This code would go inside an interrupt call e.g. PWM timer
  {
  set_bit(PORTD, 4);                 // set Digital Pin 4 high - to observe timing with a scope
  analogAdcValue = (int)ADCL + ((int)ADCH << 8);  // read result from previous ADC conversion
  ADCSRA |= (1 << ADSC);                          // start new ADC measurement (single shot)
  // Other code would go here
  clr_bit(PORTD, 4);                 // clr Digital Pin 4 - place at last line of interupt

  set_bit(PORTD, 4);    // Extra flick to show fast port bit operation on 'scope.
  clr_bit(PORTD, 4);    //   "     "   outside of ADC operation
  }
  
void setup_adc(void)  // ADC conversion takes 13 (prescaled) clock cycles
  {
  ADCSRA = 0;             // clear ADCSRA register
  ADCSRB = 0;             // clear ADCSRB register
  ADMUX = 0;              // set A0 analog input pin
  DIDR0 = 0x01;           // Digital Input Disable Register 0 for Analog pin 0 
  ADMUX |= (1 << REFS0);  // Internal reference - Comment out with external Aref 
  // ADCSRA |= (1 << ADPS1) | (1 << ADPS0);                   //   8 prescaler (equiv 153.8 KHz)
  // ADCSRA |= (1 << ADPS2);                                  //  16 prescaler (equiv  76.9 KHz)
  ADCSRA |= (1 << ADPS2) | (1 << ADPS0);                   // 32 prescaler (equiv  38.5 KHz)
  // ADCSRA |= (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0);    // 128 prescaler (equiv 8 KHz)
  ADCSRA |= (1 << ADEN);  // enable ADC
  ADCSRA |= (1 << ADSC);  // start ADC measurements
  }

/* End Code */