How to use Dual channel or triple ADC?

Hi, I'm using SDK17.02 on nRF52810,and I have enabled AIN6 reading the voltage of battery successfully.

Now I need to read another voltage outside at the same time, so what should I do?

Here is the ADC code:

#include "nrf_drv_saadc.h"
#include "nrf_log.h"
#include "app_timer.h"
#include "sta_app.h"

#define BATTERY_LEVEL_MEAS_INTERVAL     APP_TIMER_TICKS(10000)              /**< Battery level measurement interval (ticks). */
static nrf_saadc_value_t m_adc_buf;
#define ADC_REF_VOLTAGE_IN_MILLIVOLTS   600                               /**< Reference voltage (in milli volts) used by ADC while doing conversion. */
#define ADC_PRE_SCALING_COMPENSATION    6                                 /**< The ADC is configured to use VDD with 1/3 prescaling as input. And hence the result of conversion is to be multiplied by 3 to get the actual value of the battery voltage.*/
#define DIODE_FWD_VOLT_DROP_MILLIVOLTS  0                               /**< Typical forward voltage drop of the diode . */
#define ADC_RES_10BIT                   1024                              /**< Maximum digital value for 10-bit ADC conversion. */
/**@brief Macro to convert the result of ADC conversion in millivolts.
 *
 * @param[in]  ADC_VALUE   ADC result.
 *
 * @retval     Result converted to millivolts.
 */
#define ADC_RESULT_IN_MILLI_VOLTS(ADC_VALUE)\
        ((((ADC_VALUE) * ADC_REF_VOLTAGE_IN_MILLIVOLTS) / ADC_RES_10BIT) * ADC_PRE_SCALING_COMPENSATION)

APP_TIMER_DEF(m_battery_timer_id);

/**@brief Function for handling the Battery measurement timer timeout.
 *
 * @details This function will be called each time the battery level measurement timer expires.
 *
 * @param[in] p_context  Pointer used for passing some arbitrary information (context) from the
 *                       app_start_timer() call to the timeout handler.
 */
static void m_battery_level_meas_timeout_handler(void * p_context)
{
    UNUSED_PARAMETER(p_context);
    
    ret_code_t err_code = nrf_drv_saadc_buffer_convert(&m_adc_buf, 1);
    APP_ERROR_CHECK(err_code);
    
    err_code = nrf_drv_saadc_sample();
    APP_ERROR_CHECK(err_code);
}
/**@brief Function for handling the ADC interrupt.
 *
 * @details  This function will fetch the conversion result from the ADC, convert the value into
 *           percentage and send it to peer.
 */
void saadc_event_handler(nrf_drv_saadc_evt_t const * p_event)
{
    if (p_event->type == NRF_DRV_SAADC_EVT_DONE)
    {
        nrf_saadc_value_t adc_result;
        uint16_t batt_lvl_in_milli_volts;
        uint8_t percentage_batt_lvl;

        adc_result = p_event->data.done.p_buffer[0];
				
        batt_lvl_in_milli_volts =
            (ADC_RESULT_IN_MILLI_VOLTS(adc_result) + DIODE_FWD_VOLT_DROP_MILLIVOLTS)*2;
				if(batt_lvl_in_milli_volts>=4100) percentage_batt_lvl=100;
				else if((batt_lvl_in_milli_volts>=3900)&((batt_lvl_in_milli_volts<4100))) percentage_batt_lvl=75;
				else if((batt_lvl_in_milli_volts>=3700)&((batt_lvl_in_milli_volts<3900))) percentage_batt_lvl=50;
				else if((batt_lvl_in_milli_volts>=3550)&((batt_lvl_in_milli_volts<3700))) percentage_batt_lvl=25;
				else if(batt_lvl_in_milli_volts<3550) percentage_batt_lvl=0;
				m_tag_type.battery = batt_lvl_in_milli_volts;
//        NRF_LOG_INFO("Battery Level : %d [mV]  %d%%", batt_lvl_in_milli_volts,percentage_batt_lvl);
    }
    else if (p_event->type == NRF_DRV_SAADC_EVT_CALIBRATEDONE)
    {
        NRF_LOG_INFO("STA SAADC calibration complete");
    }
}
/**@brief Function for configuring ADC to do battery level conversion.
 */
void m_adc_init(void)
{
    ret_code_t err_code = nrf_drv_saadc_init(NULL, saadc_event_handler);
    APP_ERROR_CHECK(err_code);

    nrf_saadc_channel_config_t config =
        NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN6);

    config.burst = NRF_SAADC_BURST_ENABLED;

    err_code = nrf_drv_saadc_channel_init(0, &config);
    APP_ERROR_CHECK(err_code);

    err_code = nrf_drv_saadc_calibrate_offset();
    APP_ERROR_CHECK(err_code);
	
		// Create timers.
    err_code = app_timer_create(&m_battery_timer_id,
                                APP_TIMER_MODE_REPEATED,
                                m_battery_level_meas_timeout_handler);
    APP_ERROR_CHECK(err_code);
		
		// Start application timers.
    err_code = app_timer_start(m_battery_timer_id, BATTERY_LEVEL_MEAS_INTERVAL, NULL);
    APP_ERROR_CHECK(err_code);
		NRF_LOG_INFO("ADC initialized.");
}

Parents
  • You need to call nrf_drv_saadc_channel_init() on another channel, up to 7 more times, for a total of 8 channels.

    Once a channel is enabled it will, along any other enabled channel, get sampled once when the SAMPLE task is triggered. The result is stored sequentially in RAM once the buffer is full. See SAADC — Successive approximation analog-to-digital converter for more details. 

  • Hi, could you show me how to use dual channel in my project? I just want to read the voltage that both NRF_SAADC_INPUT_AIN6 and NRF_SAADC_INPUT_AIN7. I have tried but failed(Fatal Error).

    #include "nrf_drv_saadc.h"
    #include "nrf_log.h"
    #include "app_timer.h"
    #include "tag_app.h"
    
    #define BATTERY_LEVEL_MEAS_INTERVAL     APP_TIMER_TICKS(2000)              /**< Battery level measurement interval (ticks). */
    static nrf_saadc_value_t m_adc_buf[2];
    #define ADC_REF_VOLTAGE_IN_MILLIVOLTS   600                               /**< Reference voltage (in milli volts) used by ADC while doing conversion. */
    #define ADC_PRE_SCALING_COMPENSATION    6                                 /**< The ADC is configured to use VDD with 1/3 prescaling as input. And hence the result of conversion is to be multiplied by 3 to get the actual value of the battery voltage.*/
    #define DIODE_FWD_VOLT_DROP_MILLIVOLTS  0                               /**< Typical forward voltage drop of the diode . */
    #define ADC_RES_10BIT                   1024                              /**< Maximum digital value for 10-bit ADC conversion. */
    /**@brief Macro to convert the result of ADC conversion in millivolts.
     *
     * @param[in]  ADC_VALUE   ADC result.
     *
     * @retval     Result converted to millivolts.
     */
    #define ADC_RESULT_IN_MILLI_VOLTS(ADC_VALUE)\
            ((((ADC_VALUE) * ADC_REF_VOLTAGE_IN_MILLIVOLTS) / ADC_RES_10BIT) * ADC_PRE_SCALING_COMPENSATION)
    
    APP_TIMER_DEF(m_battery_timer_id);
    
    /**@brief Function for handling the Battery measurement timer timeout.
     *
     * @details This function will be called each time the battery level measurement timer expires.
     *
     * @param[in] p_context  Pointer used for passing some arbitrary information (context) from the
     *                       app_start_timer() call to the timeout handler.
     */
    static void m_battery_level_meas_timeout_handler(void * p_context)
    {
        UNUSED_PARAMETER(p_context);
        ret_code_t err_code;
    	
        err_code = nrf_drv_saadc_buffer_convert(&m_adc_buf[0], 1);
        APP_ERROR_CHECK(err_code);
    
    		err_code = nrf_drv_saadc_buffer_convert(&m_adc_buf[1], 1);
        APP_ERROR_CHECK(err_code);
        
        err_code = nrf_drv_saadc_sample();
        APP_ERROR_CHECK(err_code);
    }
    /**@brief Function for handling the ADC interrupt.
     *
     * @details  This function will fetch the conversion result from the ADC, convert the value into
     *           percentage and send it to peer.
     */
    void saadc_event_handler(nrf_drv_saadc_evt_t const * p_event)
    {
        if (p_event->type == NRF_DRV_SAADC_EVT_DONE)
        {
            nrf_saadc_value_t adc_result[2];
    
            uint16_t batt_lvl_in_milli_volts;
    				uint16_t charge_lvl_in_milli_volts;
            uint8_t percentage_batt_lvl;
    
            adc_result[0] = p_event->data.done.p_buffer[0];
    				adc_result[1] = p_event->data.done.p_buffer[1];
    			
            batt_lvl_in_milli_volts =
                (ADC_RESULT_IN_MILLI_VOLTS(adc_result[0]) + DIODE_FWD_VOLT_DROP_MILLIVOLTS)*2;
    				if(batt_lvl_in_milli_volts>=4100) percentage_batt_lvl=100;
    				else if((batt_lvl_in_milli_volts>=3900)&((batt_lvl_in_milli_volts<4100))) percentage_batt_lvl=75;
    				else if((batt_lvl_in_milli_volts>=3700)&((batt_lvl_in_milli_volts<3900))) percentage_batt_lvl=50;
    				else if((batt_lvl_in_milli_volts>=3550)&((batt_lvl_in_milli_volts<3700))) percentage_batt_lvl=25;
    				else if(batt_lvl_in_milli_volts<3550) percentage_batt_lvl=0;
    				m_tag_type.battery = batt_lvl_in_milli_volts;
    //        NRF_LOG_INFO("Battery Level : %d [mV]  %d%%", batt_lvl_in_milli_volts,percentage_batt_lvl);
        }
        else if (p_event->type == NRF_DRV_SAADC_EVT_CALIBRATEDONE)
        {
            NRF_LOG_INFO("TAG SAADC calibration complete");
        }
    }
    /**@brief Function for configuring ADC to do battery level conversion.
     */
    void m_adc_init(void)
    {
        ret_code_t err_code = nrf_drv_saadc_init(NULL, saadc_event_handler);
        APP_ERROR_CHECK(err_code);
    
        nrf_saadc_channel_config_t config =
            NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN6);
    		config.burst = NRF_SAADC_BURST_ENABLED;
    	
    		nrf_saadc_channel_config_t config_charge =
            NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN7);
    		config_charge.burst = NRF_SAADC_BURST_ENABLED;
    	
        err_code = nrf_drv_saadc_channel_init(0, &config);
        APP_ERROR_CHECK(err_code);
    
        err_code = nrf_drv_saadc_calibrate_offset();
        APP_ERROR_CHECK(err_code);
    	
    		// Create timers.
        err_code = app_timer_create(&m_battery_timer_id,
                                    APP_TIMER_MODE_REPEATED,
                                    m_battery_level_meas_timeout_handler);
        APP_ERROR_CHECK(err_code);
    		
    		// Start application timers.
        err_code = app_timer_start(m_battery_timer_id, BATTERY_LEVEL_MEAS_INTERVAL, NULL);
        APP_ERROR_CHECK(err_code);
    		NRF_LOG_INFO("ADC initialized.");
    }
    
    

  • Hi, and sorry for the long delay.

    This ticket seems to have slipped between the cracks. Do you still have an issue, or did you manage to resolve it on your own?

    Best regards,

    Didrik

  • Thanks for your reply.

    I still have this issue, I can not read two channels  voltage by that way. 

Reply Children
  • I notice in your code that you only init one of the channels. Have you tried to init the other channel as well?

        nrf_saadc_channel_config_t config =
            NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN6);
    		config.burst = NRF_SAADC_BURST_ENABLED;
    	
    		nrf_saadc_channel_config_t config_charge =
            NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN7);
    		config_charge.burst = NRF_SAADC_BURST_ENABLED;
    	
        err_code = nrf_drv_saadc_channel_init(0, &config);
        APP_ERROR_CHECK(err_code);
        
        // Try calling nrf_drv_saadc_channel_init(1, &config_charge) here

Related