This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

SAADC Sampling with a 3-axis Accelerometer

Hello,

I am currently trying to implement SAADC sampling from three channels into the same sample buffer (device is a 3-axis accelerometer, ADXL335). The root objective of my project is to read from three TWI sensors, one analog 3-output sensor (hence the SAADC sampling) and send values from these sensors to a BLE central device using BLE uart. I've been integrating code from the twi_sensor example and SAADC_simpler example into the ble_uart example, and have successfully gotten the TWI interfaces to work. However, the SAADC has been more difficult. Only zeros are present in the buffer after sampling.

Here are the tools of my work:

Laird BL652 DVK kit (bluetooth module for nRF52832)

Keil uVision 5 IDE (ATMEL ATSAM3U is the JTAG (SWD rather) interface, no SWO support)

Nordic SDK 15.0.0, SD S132

I copied all of the SAADC initialization code from the SAADC_simpler example, then I call nrf_drv_saadc_sample() which, according to the Nordic documentation online, should sample all initialized channels.

I've enabled SAADC driver in sdk_config.h to be 14 bit accuracy, oversampling disabled, low power mode disabled, and interrupt priority of 7. Here are the initialization functions I have so far:

#define SAMPLES_IN_BUFFER 3


void saadc_callback(nrf_drv_saadc_evt_t const * p_event)
{
    if (p_event->type == NRF_DRV_SAADC_EVT_DONE)
    {
        ret_code_t err_code;
        
        err_code = nrf_drv_saadc_buffer_convert(p_event->data.done.p_buffer, SAMPLES_IN_BUFFER);
        APP_ERROR_CHECK(err_code);
        
        NRF_LOG_INFO("%d\r\n", p_event->data.done.p_buffer[0]);
    }
}
    
void saadc_init(void)
{
    ret_code_t err_code;
    
    nrf_saadc_channel_config_t channel_config_x \
    = NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(ARDUINO_A1_PIN); // nRF pin4
    nrf_saadc_channel_config_t channel_config_y \
    = NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(ARDUINO_A2_PIN); // nRF pin28
    nrf_saadc_channel_config_t channel_config_z \
    = NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(ARDUINO_A3_PIN); // nRF pin 29
    
    err_code = nrf_drv_saadc_init(NULL, saadc_callback);
    
    APP_ERROR_CHECK(err_code);
    
    err_code = nrf_drv_saadc_channel_init(0, &channel_config_x);
    APP_ERROR_CHECK(err_code);
    err_code = nrf_drv_saadc_channel_init(1, &channel_config_y);
    APP_ERROR_CHECK(err_code);
    err_code = nrf_drv_saadc_channel_init(2, &channel_config_z);
    APP_ERROR_CHECK(err_code);
    
    
    err_code = nrf_drv_saadc_buffer_convert(adxl_sample, SAMPLES_IN_BUFFER);
    APP_ERROR_CHECK(err_code);
}

Then, I implement these functions in my main function, along with the initialization functions I have for BLE and TWI sensors:

int main(void)
{
    bool erase_bonds;
    
    is_connected = false;
    
    // Initialize BLE
    uart_init();
    log_init();
    timers_init();
    buttons_leds_init(&erase_bonds);
    power_management_init();
    ble_stack_init();
    gap_params_init();
    gatt_init();
    services_init();
    advertising_init(); 
    conn_params_init();
    
    // Initialize IIC
    twi_APDS0_init();
    twi_APDS1_init();
    
    init_apds(&APDS0_twi, APDS_ADDR);
    init_apds(&APDS1_twi, APDS_ADDR);
    
    // Initialize SAADC Sampling
    saadc_init();
    bool is_busy = nrfx_saadc_is_busy();

    
    // Start execution.
    printf("\r\nUART started.\r\n");
    NRF_LOG_INFO("Debug logging for UART over RTT started.");
    advertising_start();

    // Point to the SAADC sample location
    uint8_t example[2];
    uint32_t interval = &example[1] - &example[0]; // hard arithmetic for memory location step between vector locations
    nrf_saadc_value_t *p_adxl_sample = &adxl_sample[0]; // point to the base of the sample
    uint16_t adxl_samples_16[3] = {((uint16_t)(*p_adxl_sample)), ((uint16_t)*(p_adxl_sample + interval)), ((uint16_t)*(p_adxl_sample + 2*interval))}; //dereference the sample values and put them into a convenient place
    uint8_t adxl_samples_8[6]; // vector for 8 bit wide sample values
    uint16_t *p_adxl_samples_16 = &adxl_samples_16[0]; // point to the base of the new sample vector
    uint8_t *p_adxl_samples_8[6]; // point to the base of the 8 bit wide sample vector
    p_adxl_samples_8[0] = (uint8_t *)p_adxl_samples_16; // write the base address of the 16 bit vector to the base address of the 8 bit vector 
    
    for(int i = 1; i < 6; i++) // write the correct addresses of each 8 bit location based on the interval found above
    {
        p_adxl_samples_8[i] = p_adxl_samples_8[i-1] + interval;
    }
    
    for(int i = 0; i < 6; i++) // dereference the values in adxl_samples_16 vector to the 8 bit vector
    {
        adxl_samples_8[i] = *p_adxl_samples_8[5-i]; // The reverse order is just for convenience when reading them after BLE send
    }
    // Enter main loop.
    for (;;)
    {
        if(!is_connected)
        {
            idle_state_handle();
        }
        
        if(is_connected)
        {
            if(is_busy)
            {
                uint32_t err_code = nrf_drv_saadc_buffer_convert(adxl_sample, SAMPLES_IN_BUFFER);
                nrf_delay_ms(1);
                err_code = nrf_drv_saadc_sample(); 
                nrf_delay_ms(1);
                send_iic_values(&m_nus, &adxl_samples_8[0], p_packet_size);
                nrf_delay_ms(2000);
            }
        is_busy = nrfx_saadc_is_busy();
    
        } //if(is_connected)
    } //for(;;)
} //main

The complex pointer manipulation of the adxl_sample is necessary because the ble_nus_data_send function can only send byte-sized values of type uint8_t. I've proven that the formula of pointers works by hard-writing values to the sample and sending them, but there are probably ways I could improve my work.

I should also mention that my board configuration is definitely correct. I've probed around the DVK board and found that the voltages into the selected inputs of the nRF52832 are definitely nonzero and reasonable. Also, as mentioned before, I've gotten the TWI to work so I'm familiar with how the pca10040.h file works.

Is there something I'm missing? What about calibration? The nordic documentation made this task seem straightforward but I've had a lot of trouble and I'm running out of ideas. Any help is appreciated.

  • Hey Benjamin, sorry for the late reply.

    I've got a hard time following your buffer set-up. The normal way is to just declare a buffer as an array of uint16's :

    uint16_t adc_buffer[3];

    Set it to zero with a call to memset(&adc_buffer, 0, SAMPLES_IN_BUFFER);

    The SAADC will place the 14 bit samples as 16 bit unsigned intergers in a consecutive array of unsigned 16 bit integers. This means that adc_buffer[0] contains the x value, [1] contains the y value, and [2] contains the z value. 

    Since you're reusing the buffer you need to cache the content between each sample:

    declare a uint16_t xyz_buffer;

    And copy the adc_buffer into the xyz_buffer in your adc event handler with a call to memcpy(&xyz_buffer, &adc_buffer, SAMPLES_IN_BUFFER);

Related