NRF51822 ADC data over the BLE link

Source Code Files for the ADC over BLE on Github.
ADC over BLE link established a communication link between NRF51822 board and a mobile device. Once the board is connected it will take 4 adc samples and send them to the mobile device. Once you compile and flash the program into your NRF51822 chip. Open a mobile app, connect to "CNK TECH Labs".

Nordic Semiconductor provides free app to view data shown below:

Analog to digital converter is configured to:

  • AIN2 is the ADC input on pin P0.01
  • Scale 1/1
  • Vref = 1.2V
  • 10 bit resolution

Analog to Digital Converter (ADC)

Configuration for ADC, inside the main():
static nrf_drv_adc_channel_t m_channel_config = NRF_DRV_ADC_DEFAULT_CHANNEL(NRF_ADC_CONFIG_INPUT_2);

ADC Initialization KEIL image

Macro for initializing the ADC channel with the default configuration

Set input voltage range

It is very important you configure the ADC so the input voltage range and the ADC voltage range is matching. If the input voltage range is lower than the ADC voltage range, the resolution will not be fully utilized. If the input voltage range is higher than the ADC voltage range, all values above the maximum ADC voltage range will be limited to the maximum value, also called the saturation point.

Input voltage range and saturation point depends on the configured ADC reference voltage and the chosen prescaling.
If the 1.2 V VBG internal reference voltage is used, the ADC range will be 0-1.2 V with a saturation point of 1.2 V.
This means that your AIN signal with 1/1 prescaling should be in the range 0-1.2 V in order to obtain proper conversion.
Input above 1.2 V will be converted to the maximum ADC value.
However, if you use, for example, 1/3 prescaling for your AIN input the input is scaled down to 1/3.
The effect is that your AIN voltage range is 0 - 3.6 V because the 3.6 V input voltage is scaled down to:
3.6 / 3 = 1.2 V.

Table below shows examples of reference voltage and prescaling settings and the corresponding saturation points for ADC AIN input.

Voltage divider

There are two rules to follow to find the maximum input voltage allowed on the AIN pins:

  1. The ADC should not be exposed to higher voltage than 2.4 V on an AIN pin after prescaling:
    Input voltage x prescaling = max. 2.4 V.
  2. A GPIO pin must not be exposed to higher voltage than VDD + 0.3 V, according to the Absolute maximum ratings from the nRF51x22 Product Specification.

Input impedance

To achieve the ADC error specifications stated in the nRF51822 Product Specification, the output impedance of the connected voltage source must be 1 k-Ω or lower. Another advantage if the output impedance is 1 k-Ω or lower is that different prescaling settings for the ADC input will have no practical effect on the ADC accuracy.

If a voltage source with higher impedance is applied, additional gain and offset error is introduced, which also will vary for different prescaling settings.

When the ADC is not sampling the AIN input pin has very high impedance and can be regarded as open circuit. Table 5 shows the statistics for the internal impedance for different prescaling settings. 99.7% of devices (+- 3 sigma) are expected to be within 6.3%, for example for 1/1 prescaling => [121.5, 137.9] k-Ω.

If you open nrf_drv_adc.h file you will find NRF_DRV_ADC_DEFAULT_CHANNEL macro for ADC initialization:

/**@brief Macro for initializing the ADC channel with the default configuration. */
#define NRF_DRV_ADC_DEFAULT_CHANNEL(analog_input)          \
 {{{                                                       \
    .resolution = NRF_ADC_CONFIG_RES_10BIT,                \
    .reference  = NRF_ADC_CONFIG_REF_VBG,\   
    .ain        = (analog_input)                           \
 }}, NULL}

Inside the nrf_adc.h file you will find different options that can be used in the shown above NRF_DRV_ADC_DEFAULT_CHANNEL macro:

typedef enum
    NRF_ADC_CONFIG_RES_8BIT  = ADC_CONFIG_RES_8bit,  /** < 8 bit resolution. */
    NRF_ADC_CONFIG_RES_9BIT  = ADC_CONFIG_RES_9bit,  /** < 9 bit resolution. */
    NRF_ADC_CONFIG_RES_10BIT = ADC_CONFIG_RES_10bit, /** < 10 bit resolution. */
} nrf_adc_config_resolution_t;
 * .input
 * @enum nrf_adc_config_scaling_t
 * @brief Scaling factor of the analog-to-digital conversion.
 * GPIO pin should not be exposed to higher voltage than VDD+0.3V, see Aboslute maximum ratings in 
 * the nRF51822/nRF51422 Product Specification
 * The ADC should not be exposed to higher voltage than 2.4V on an ADC AIN pin after prescaling
 * For example, when having 2/3 prescaling you can expose 2.4V/(2/3)=3.6V to an AIN pin. 
 * For not to violate rule 2, VDD should be 3.3V or higher in this case.
 * VDD 3.6, prescaling 1/1: AIN max 2.4V  (rule 1 limitation)  
 * VDD 3.6, prescaling 2/3: AIN max 3.6V  (rule 1 limitation)  
 * VDD 3.6, prescaling 1/3: AIN max 3.9V  (rule 2 limitation)  
 * VDD 3.3, prescaling 1/1: AIN max 2.4V  (rule 1 limitation)  
 * VDD 3.3, prescaling 2/3: AIN max 3.6V  (rule 1 and rule 2 limitation)  
 * VDD 3.3, prescaling 1/3: AIN max 3.6V  (rule 2 limitation)  
 * VDD 1.8, prescaling 1/1: AIN max 2.1V  (rule 2 limitation)  
 * VDD 1.8, prescaling 2/3: AIN max 2.1V  (rule 2 limitation)  
 * VDD 1.8, prescaling 1/3: AIN max 2.1V  (rule 2 limitation)
typedef enum
    NRF_ADC_CONFIG_SCALING_INPUT_FULL_SCALE  = ADC_CONFIG_INPSEL_AnalogInputNoPrescaling, /**< Full scale input.*/
    NRF_ADC_CONFIG_SCALING_INPUT_TWO_THIRDS  = ADC_CONFIG_INPSEL_AnalogInputTwoThirdsPrescaling, /**< 2/3 scale input.*/
    NRF_ADC_CONFIG_SCALING_INPUT_ONE_THIRD   = ADC_CONFIG_INPSEL_AnalogInputOneThirdPrescaling, /**< 1/3 scale input.*/
    NRF_ADC_CONFIG_SCALING_SUPPLY_TWO_THIRDS = ADC_CONFIG_INPSEL_SupplyTwoThirdsPrescaling, /**< 2/3 of supply.*/
    NRF_ADC_CONFIG_SCALING_SUPPLY_ONE_THIRD  = ADC_CONFIG_INPSEL_SupplyOneThirdPrescaling /**< 1/3 of supply.*/
} nrf_adc_config_scaling_t;

 * .reference
 * @enum nrf_adc_config_reference_t
 * @brief Reference selection of the analog-to-digital converter.
typedef enum
    NRF_ADC_CONFIG_REF_VBG              = ADC_CONFIG_REFSEL_VBG,                      /**< 1.2 V reference. */
    NRF_ADC_CONFIG_REF_SUPPLY_ONE_HALF  = ADC_CONFIG_REFSEL_SupplyOneHalfPrescaling,  /**< 1/2 of power supply. */
    NRF_ADC_CONFIG_REF_SUPPLY_ONE_THIRD = ADC_CONFIG_REFSEL_SupplyOneThirdPrescaling, /**< 1/3 of power supply. */
                                          ADC_CONFIG_EXTREFSEL_AnalogReference0 << ADC_CONFIG_EXTREFSEL_Pos, /**< External reference 0. */
    NRF_ADC_CONFIG_REF_EXT_REF1 = ADC_CONFIG_REFSEL_External | ADC_CONFIG_EXTREFSEL_AnalogReference1 << ADC_CONFIG_EXTREFSEL_Pos, /**< External reference 0. */
} nrf_adc_config_reference_t;


Data is sent from the ADC interrupt handler located in the main.c

 * @brief ADC interrupt handler.
 * Prints ADC results on hardware UART and over BLE via the NUS service.
static void adc_event_handler(nrf_drv_adc_evt_t const * p_event)
    uint8_t adc_result[ADC_BUFFER_SIZE*2];
    uint8_t Result = 0;
    if (p_event->type == NRF_DRV_ADC_EVT_DONE)
        if (adc_event_counter < 255)
            adc_event_counter++;	// 0 - 255
            adc_event_counter = 0;
        NRF_LOG_PRINTF("ADC event counter: %d\r\n", adc_event_counter); // Print event counter value
        uint32_t i;
        for (i = 0; i < p_event->data.done.size; i++)
            //p_event is a 16 bit storage. Print ADC result over the UART PC terminal
            printf("Sample value %d: %d\r\n", i+1, p_event->data.done.p_buffer[i]);
            //LSB byte of the ADC result in adc_result[(i*2)]
            adc_result[(i*2)] = p_event->data.done.p_buffer[i] >> 8;	
            //MSB byte of the ADC result in adc_result[(i*2)+1]
            adc_result[(i*2)+1] = p_event->data.done.p_buffer[i];	
            adc_data_to_char = (uint32_t) (p_event->data.done.p_buffer[i]);//Load
			hexdec_long( adc_data_to_char );//Convert
			ble_nus_string_send(&m_nus, Rx_buf, 10);//Send