Microchip® Advanced Software Framework

Quick Start Guide for ADC - Callback

In this use case, the ADC will convert 128 samples using interrupt driven conversion.

When all samples have been sampled, a callback will be called that signals the main application that conversion is complete.

The ADC will be set up as follows:

  • VCC /2 as reference
  • Div 8 clock prescaler
  • 12-bit resolution
  • Window monitor disabled
  • 1/2 gain
  • Positive input on ADC PIN 0
  • Negative input to GND (single ended)
  • Averaging disabled
  • Oversampling disabled
  • Right adjust data
  • Single-ended mode
  • Free running disabled
  • All events (input and generation) disabled
  • Sleep operation disabled
  • No reference compensation
  • No gain/offset correction
  • No added sampling time
  • Pin scan mode disabled

Setup

Prerequisites

There are no special setup requirements for this use-case.

Code

Add to the main application source file, outside of any functions:

struct adc_module adc_instance;
#define ADC_SAMPLES 128
uint16_t adc_result_buffer[ADC_SAMPLES];

Callback function:

volatile bool adc_read_done = false;
void adc_complete_callback(
struct adc_module *const module)
{
adc_read_done = true;
}

Copy-paste the following setup code to your user application:

void configure_adc(void)
{
struct adc_config config_adc;
#if (!SAML21) && (!SAML22) && (!SAMC21) && (!SAMR30)
config_adc.gain_factor = ADC_GAIN_FACTOR_DIV2;
#endif
config_adc.clock_prescaler = ADC_CLOCK_PRESCALER_DIV8;
config_adc.reference = ADC_REFERENCE_INTVCC1;
#if (SAMC21)
config_adc.positive_input = ADC_POSITIVE_INPUT_PIN5;
#else
config_adc.positive_input = ADC_POSITIVE_INPUT_PIN6;
#endif
config_adc.resolution = ADC_RESOLUTION_12BIT;
#if (SAMC21)
adc_init(&adc_instance, ADC1, &config_adc);
#else
adc_init(&adc_instance, ADC, &config_adc);
#endif
adc_enable(&adc_instance);
}
void configure_adc_callbacks(void)
{
adc_register_callback(&adc_instance,
adc_complete_callback, ADC_CALLBACK_READ_BUFFER);
}

Add to user application initialization (typically the start of main()):

configure_adc();
configure_adc_callbacks();

Workflow

  1. Create a module software instance structure for the ADC module to store the ADC driver state while in use.
    struct adc_module adc_instance;
    Note
    This should never go out of scope as long as the module is in use. In most cases, this should be global.
  2. Create a buffer for the ADC samples to be stored in by the driver asynchronously.
    #define ADC_SAMPLES 128
    uint16_t adc_result_buffer[ADC_SAMPLES];
  3. Create a callback function that will be called each time the ADC completes an asynchronous read job.
    volatile bool adc_read_done = false;
    void adc_complete_callback(
    struct adc_module *const module)
    {
    adc_read_done = true;
    }
  4. Configure the ADC module.
    1. Create an ADC module configuration struct, which can be filled out to adjust the configuration of a physical ADC peripheral.
      struct adc_config config_adc;
    2. Initialize the ADC configuration struct with the module's default values.
      Note
      This should always be performed before using the configuration struct to ensure that all values are initialized to known default settings.
    3. Change the ADC module configuration to suit the application.
      #if (!SAML21) && (!SAML22) && (!SAMC21) && (!SAMR30)
      config_adc.gain_factor = ADC_GAIN_FACTOR_DIV2;
      #endif
      config_adc.clock_prescaler = ADC_CLOCK_PRESCALER_DIV8;
      config_adc.reference = ADC_REFERENCE_INTVCC1;
      #if (SAMC21)
      config_adc.positive_input = ADC_POSITIVE_INPUT_PIN5;
      #else
      config_adc.positive_input = ADC_POSITIVE_INPUT_PIN6;
      #endif
      config_adc.resolution = ADC_RESOLUTION_12BIT;
    4. Set ADC configurations.
      #if (SAMC21)
      adc_init(&adc_instance, ADC1, &config_adc);
      #else
      adc_init(&adc_instance, ADC, &config_adc);
      #endif
    5. Enable the ADC module so that conversions can be made.
      adc_enable(&adc_instance);
  5. Register and enable the ADC Read Buffer Complete callback handler.
    1. Register the user-provided Read Buffer Complete callback function with the driver, so that it will be run when an asynchronous buffer read job completes.
      adc_register_callback(&adc_instance,
      adc_complete_callback, ADC_CALLBACK_READ_BUFFER);
    2. Enable the Read Buffer Complete callback so that it will generate callbacks.

Use Case

Code

Copy-paste the following code to your user application:

adc_read_buffer_job(&adc_instance, adc_result_buffer, ADC_SAMPLES);
while (adc_read_done == false) {
/* Wait for asynchronous ADC read to complete */
}
while (1) {
/* Infinite loop */
}

Workflow

  1. Enable global interrupts, so that callbacks can be generated by the driver.
  2. Start an asynchronous ADC conversion, to store ADC samples into the global buffer and generate a callback when complete.
    adc_read_buffer_job(&adc_instance, adc_result_buffer, ADC_SAMPLES);
  3. Wait until the asynchronous conversion is complete.
    while (adc_read_done == false) {
    /* Wait for asynchronous ADC read to complete */
    }
  4. Enter an infinite loop once the conversion is complete.
    while (1) {
    /* Infinite loop */
    }