Measure 3 phase current sensor and capture the waveform?

Hello Everyone,

Currently, I work on nano 33 BLE Sense, I have a project with 3-phase motor control, first step I need to measure the current sensor and get the waveform in real-time, I got a reference to access ADC on Nano 33 BLE Sense for high-speed measurement using the register like this thread Increase the ADC sample rate, I try to modify the program on that thread as what a need, But the result is not what I expected, the serial monitor showed the data only Zero like the picture below:
serial_com

What is the problem? and how to fix this problem?
Thank you for your answers and help!

This is my modified program:

/*
  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  20000
#define PPI_CHANNEL         (7)

#define ADC_BUFFER_SIZE     3
volatile nrf_saadc_value_t adcBuffer[ADC_BUFFER_SIZE];
volatile bool adcFlag = false;

#define DATA_BUFFER_SIZE    500
volatile nrf_saadc_value_t dataBuffer[DATA_BUFFER_SIZE];
uint32_t dataBufferIndex = 0;
volatile nrf_saadc_value_t dataBuffer2[DATA_BUFFER_SIZE];
volatile nrf_saadc_value_t dataBuffer3[DATA_BUFFER_SIZE];

volatile uint32_t sampleCounter = 0;

#define DATA_SEND_INTERVAL  5

void setup()
{
  Serial.begin( 9600 );
  while ( !Serial );
  //Serial.println( "Arduino Nano 33 BLE (mbedOS)" );
  
  initADC();
  initTimer4();
  initPPI(); 
  Serial.println( "A0\tA1\tA2" );
}

void loop()
{
  enum APP_STATE_TYPE { APP_COLLECT_SAMPLES,
                        APP_SEND_DATA,                         
                        APP_STATE_RESTART = 255
                      };  
  static uint32_t state = 1;

  switch ( state )
  { 
    case APP_COLLECT_SAMPLES:
      if ( adcFlag )
      {
        if ( copyData() == 0 )
        {
          state++;
        }
        adcFlag = false;      
      }
      break;
    case APP_SEND_DATA:
      if ( sendData( DATA_SEND_INTERVAL ) == 0 )
      {
        state++;
      }
      break;
    default:
      dataBufferIndex = 0;
      state = 1;
      break;
  }
}


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;
  
  // P0.05 - AIN3 -> Pin A1
  NRF_SAADC->CH[3].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[3].PSELP = SAADC_CH_PSELP_PSELP_AnalogInput3 << SAADC_CH_PSELP_PSELP_Pos;
  NRF_SAADC->CH[3].PSELN = SAADC_CH_PSELN_PSELN_NC << SAADC_CH_PSELN_PSELN_Pos;
  
  // P0.30 - AIN6 -> Pin A2
  NRF_SAADC->CH[6].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[6].PSELP = SAADC_CH_PSELP_PSELP_AnalogInput6 << SAADC_CH_PSELP_PSELP_Pos;
  NRF_SAADC->CH[6].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 );
}

int32_t copyData()
{
  dataBuffer[dataBufferIndex] = adcBuffer[0];
  dataBuffer2[dataBufferIndex] = adcBuffer[1];
  dataBuffer3[dataBufferIndex] = adcBuffer[2];
  dataBufferIndex = ( dataBufferIndex + 1 ) % DATA_BUFFER_SIZE;
  return dataBufferIndex;  
}

int32_t sendData( uint32_t interval )
{
  static uint32_t previousMillis = 0;

  uint32_t currentMillis = millis();
  if ( currentMillis - previousMillis >= interval )
  {
    previousMillis = currentMillis;
    Serial.print( dataBuffer[dataBufferIndex] );
    Serial.print( '\t' );
    Serial.print( dataBuffer2[dataBufferIndex] );
    Serial.print( '\t' );
    Serial.println( dataBuffer3[dataBufferIndex] );
    
    dataBufferIndex = ( dataBufferIndex + 1 ) % DATA_BUFFER_SIZE;
    return dataBufferIndex;
  }
  return -1;
}

If you do not have a reasonable load on the three-phase power, there is NO current to be measured. Therefore, your results are correct.

Currently, I just connect the ADC input with a variable resistor to simulate the voltage input to the ADC. but the ADC value doesn't change when I turn the variable resistor.

Schematic with values & voltages required.

And you did check the actual voltage with your DVM, didn't you?

Yes I have checked the variable resistor voltage

schematic approxiately like this

and voltage I use 3.2 - 3.3 volt

And the voltage range you saw on the meter is WHAT?

You muse see zero at one end of travel and the power supply voltage connected to the opposite end. DO you have the power ground connected to one end of the pot?

image

Reference: Nano 33 BLE Sense Cheat Sheet | Arduino Documentation | Arduino Documentation

I do not have a Nano 33 BLE here, so outside of the simulator, no way to test.

But, I would suggest dumbing down the sketch to just reading A0 and printing that value. Rotate pot to determine full ranges of values.

Then add A1 and print. Same test with pot.

Then add A2 and print, Same test with pot.

Now, A0, A1, and A2 should follow the potentiometers. Add the remainder of your code.

The range is 0-3.2 volt, and I also check the output of the pot is 0-3.2 volt,

yes, I think the connection is okay, I have to check it several times,

I have tested for only 1 Analog input is okay,

but when I try to make add another analog input, the output will like what I got before,
for Program only 1 analog input I use this program:
Source

/*
  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 );
}

I tried to modify the program like this:

/*
  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     3
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" );
  Serial.println( "A0\tA1\tA2" );
  initADC();
  initTimer4();
  initPPI();
}

void loop()
{
    Serial.print( adcBuffer[0] );
    Serial.print( '\t' );
    Serial.print( adcBuffer[1] );
    Serial.print( '\t' );
    Serial.println( adcBuffer[2] );
}


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;
  
  // P0.05 - AIN3 -> Pin A1
  NRF_SAADC->CH[3].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[3].PSELP = SAADC_CH_PSELP_PSELP_AnalogInput3 << SAADC_CH_PSELP_PSELP_Pos;
  NRF_SAADC->CH[3].PSELN = SAADC_CH_PSELN_PSELN_NC << SAADC_CH_PSELN_PSELN_Pos;
  
  // P0.30 - AIN6 -> Pin A2
  NRF_SAADC->CH[6].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[6].PSELP = SAADC_CH_PSELP_PSELP_AnalogInput6 << SAADC_CH_PSELP_PSELP_Pos;
  NRF_SAADC->CH[6].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 );
}

I set the first pot (A0) at the minimum voltage, the second pot (A1) at around the middle voltage, and the third pot (A2) at maximum voltage, the result is like the picture below:

That looks like the A0, A1, and A2 stores in only adcBuffer[0], can anyone give a suggestion on what should I do?
Thanks

So, use the A0 working sketch to check A1, A2 just to insure the analog mux is working correctly.

Keep the sketch very basic, until all 3 analog channels work. Get rid of everything else...

When you get A0, A1, and A2 printing based on respective potentiometers, then get more sophisticated and determine where things break.

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