ADC Asynchronous Driver

The Analog to Digital Converter (ADC) asynchronous driver adc_async_read_channel function will attempt to read the required number of conversion results from a ring buffer. It will return immediately after call (i.e. not block) if the requested number of data is not available. If data was not available, or less data than requested was available, this will be indicated in the function's return value.

The asynchronous driver uses interrupts to communicate that the ADC data is available, and the IRQ handler reads data from hardware and into ring buffers in memory. These ring buffers decouple the generation of data in the ADC with the timing of the application request for this data. In a way, the producer and consumer events are asynchronous to each other. The user can register a callback function to piggyback on the IRQ handler routine for communicating with the main application.

Summary of the API's Functional Features

The API provides functions to:
  • Initialize and deinitialize the driver and associated hardware

  • Select single shot or free running conversion modes

  • Configure major ADC properties such as resolution and reference source

  • Hookup callback handlers on conversion done, error, and monitor events

  • Start ADC conversion

  • Read back conversion results

Summary of Configuration Options

Below is a list of the main ADC parameters that can be configured in START. Many of these parameters are used by the adc_async_init function when initializing the driver and underlying hardware. Most of the initial values can be overridden and changed runtime by calling the appropriate API functions, such as adc_async_set_resolution.
  • Selecting which ADC input channels to enable for positive and negative input

  • Which clock source and prescaler the ADC uses

  • Various aspects of Event control

  • Single shot or free running conversion modes

  • Sampling properties such as resolution, window mode, and reference source

  • Run in Standby or Debug mode

Driver Implementation Description

Channel Map

The ADC asynchronous driver uses a channel map buffer to map the channel number of each enabled channel and the index of the descriptor for the channel.

The channel map is an array defined as follows:

            static uint8_t ADC_0_map[ADC_0_CH_MAX+1];
          

The index of the channel map buffer is the channel number, and the value of the channel map buffer is the index of the channel descriptor. For example, when registering (using adc_async_register_channel_buffer) channel_1, channel_5, and channel_9 in sequence, the value of channel_map[1] is 0, the value of channel_map[5] is 1, and the value of channel_map[9] is 2.

Ring Buffer

The ADC asynchronous driver uses a ring buffer to store ADC sample data. When the ADC raise the sample complete the interrupt, and a copy of the ADC sample register is stored in the ring buffer at the next free location. This will happen regardless of the ADC being in one shot mode or in free running mode. When the ring buffer is full, the next sample will overwrite the oldest sample in the ring buffer.

The adc_async_read_channel function reads bytes from the ring buffer. For ADC sample size larger than 8-bit the read length has to be a power-of-two number greater than or equal to the sample size.

Example of Usage

The following shows a simple example of using the ADC. The ADC must have been initialized by adc_async_init. This initialization will configure the operation of the ADC, such as single-shot or continuous mode, etc.

The example hooks up a callback handler to be called every time conversion is complete, thereafter enables channel 0 of ADC0, and finally starts a conversion on this channel.

          uint32_t number_of_conversions = 0;
          uint8_t  buffer[32];
          /*
           * Example of callback function called by the ADC IRQ handler when conversion is complete.
           * The printf-string is printed every time a conversion is complete.
           */
          static void convert_cb_ADC_0(const struct adc_async_descriptor *const descr, const uint8_t channel)
          {
              number_of_conversions++;
              printf("Number of conversions is now: %d\n", number_of_conversions);
          }
          /**
           * Example of using ADC_0 to generate waveform.
           */
          void ADC_0_example(void)
          {
              adc_async_register_callback(&ADC_0, 0, ADC_ASYNC_CONVERT_CB, convert_cb_ADC_0);
              adc_async_enable_channel(&ADC_0, 0);
              adc_async_start_conversion(&ADC_0);
              /* Attempt to read 4 conversion results into buffer */
              int32_t conversions_read = adc_async_read_channel(&ADC_0, 0, buffer, 4);
          }
        

Dependencies

  • The ADC peripheral and its related I/O lines and clocks

  • The NVIC must be configured so that ADC interrupt requests are periodically serviced