Microchip® Advanced Software Framework

Free-running conversions with interrupt

In this use case, ADCA is configured for:

  • sampling on two channels (0 and 1) with respective inputs:
    • I/O pin as single-ended input (PA0)
    • two I/O pins as differential input w/ 2x gain (PA1 and PA5)
  • signed conversions
  • 12-bit resolution
  • internal 1V reference
  • free-running conversions
  • interrupt-based conversion handling

The ADC results are handled in an interrupt callback function which simply stores the result in one of two channel-specific, global variables.

Note
This use case assumes that the device has multiple ADC channels. Refer to the applicable device datasheet for information about the number of ADC channels.

Setup steps

Example code

Ensure that conf_adc.h contains:

#define CONFIG_ADC_CALLBACK_ENABLE
#define CONFIG_ADC_CALLBACK_TYPE int16_t

Add to application C-file:

#define MY_ADC ADCA
int16_t ch0_result;
int16_t ch1_result;
static void adc_handler(ADC_t *adc, uint8_t ch_mask, adc_result_t result)
{
switch (ch_mask) {
case ADC_CH0:
ch0_result = result;
break;
case ADC_CH1:
ch1_result = result;
break;
default:
break;
}
}
static void adc_init(void)
{
struct adc_config adc_conf;
struct adc_channel_config adcch_conf;
adc_read_configuration(&MY_ADC, &adc_conf);
adcch_read_configuration(&MY_ADC, ADC_CH0, &adcch_conf);
adc_set_clock_rate(&adc_conf, 5000UL);
adc_write_configuration(&MY_ADC, &adc_conf);
adcch_enable_interrupt(&adcch_conf);
adcch_write_configuration(&MY_ADC, ADC_CH0, &adcch_conf);
adcch_write_configuration(&MY_ADC, ADC_CH1, &adcch_conf);
}

Add to main():

adc_init();

Workflow

  1. Define a macro for the ADC to use, in case we want to change it later:
    • #define MY_ADC ADCA
  2. Define global variables to contain the ADC result of each channel:
    • int16_t ch0_result;
      int16_t ch1_result;
  3. Create an ADC interrupt callback function that stores the results in the channels' respective global variables:
    • static void adc_handler(ADC_t *adc, uint8_t ch_mask, adc_result_t result)
      {
      switch (ch_mask) {
      case ADC_CH0:
      ch0_result = result;
      break;
      case ADC_CH1:
      ch1_result = result;
      break;
      default:
      break;
      }
      }
      Note
      Refer to adc_callback_t for documentation on the interrupt callback function type.
  4. Create a function adc_init() to intialize the ADC:
    • static void adc_init(void)
      {
      // ...
      }
  5. Allocate configuration structs for ADC and channel, then initialize them:
  6. Set signed, 12-bit conversions with internal 1V voltage reference:
  7. Set free-running conversions on the first two ADC channels:
  8. Set ADC clock rate to maximum 5 KHz:
    • adc_set_clock_rate(&adc_conf, 5000UL);
      Note
      In free-running mode, it is wise to reduce the ADC clock so that the device has time to handle the results, e.g., channel 0 does not complete a new conversion before channel 1's result has been handled.
  9. Set the interrupt callback function to use for the ADC:
  10. Write the configuration to the ADC:
  11. Enable interrupts for the ADC channels:
  12. Set up single-ended input from pin 0 on port A, then write the config to the first channel (0):
  13. Set up differential input from pins 1 and 5 on port A, with 2x gain, then write the config to the second channel (1):
  14. Initialize the clock system, the ADC, and the PMIC since we will be using interrupts:

Usage steps

Example code

Add to main.c():

adc_enable(&MY_ADC);
do {
} while (true);

Workflow

  1. Enable interrupts globally to allow the ADC interrupts to be handled:
  2. Enable the ADC to start conversions:
    • adc_enable(&MY_ADC);
      Note
      When configured for free-running conversions, the ADC will start doing conversions as soon as it is enabled, so we do not need to do it manually.
  3. Enter a busy-loop while interrupts handle the ADC results:
    • do {
      } while (true);