Adafruit FeatherM0 ASF Tutorials

The source code for the tutorials can be found on GitHub.

2017-07-27 04:20

05_ADC - Analog to Digital Converter

The goal of this project is to get familiar with the Analog-to-Digital Converter of the ATSAMD21.
We will use it to determine the charging state of an attached 2000mAh Lithium Polymer Battery.

Schematic Source: Adafruit, License: Attribution-ShareAlike Creative Commons

Battery Voltage

The FeatherM0 has the capability to measure the voltage of an attached Lithium Polymer Battery using the ADC of the MCU on AIN7 (pin PA07).

The Data Sheet of the 2000mAh 3.7V Lithium Polymer Battery I bought from Adafruit states a maximum voltage of 4.2V when fully loaded and a minimum voltage of 3.0V when empty. In the FeatherM0 Manual on page 23 we find the information that the protection circuit shuts down the power at 3.2V.

Since the maximum value to measure can not exceed the reference value and the reference value can not exceed VDDA-0.6V (=2.7V) (SAMD21 data sheet, "36.9.4 Analog-to-Digital (ADC) Characteristics" page 955), the battery value has to be scaled down to meet these limits.

The designers of the FeatherM0 took care of this for us, by using two 100k resistors (R6 and R3) to build a 1/2 voltage divider.

So the value to measure now ranges from 2.1V down to 1.6V.

Reference Voltage

For the ADC to work, it needs a reference voltage, which provides the upper limit for the value to measure.

The SAMD21 MCU provides three internal reference voltages:

And two inputs for the user to provide external reference voltages:

The developer has to choose the appropriate reference voltage for his application, taking the limits of the MCU into account:

The source code offers the option to use:

R1 and R2 are enforcing the limits for Vref.
For the values choosen: 1.34V <= Vref <= 2.67V
C1 helps to stabilize the reference voltage.

Using the internal reference has the advantage of a lower Bill Of Materials count, but:

Using the external reference has the advantage of being able to


The program will:

Adding a new Atmel Software Framework (ASF) Skeleton Project

Add a new ASF Skeleton Project for the 05_ADC tutorial to the existing solution.

See my Step-By-Step tutorial if you are not sure how to do this.

Choosing a startup project

To make sure AtmelStudio always compiles and runs the 05_ADC project we make it the StartUp Project.

Adding the project specific Atmel Software Framework (ASF) code

The next step is to import the ASF modules needed for the specific project.

or use the ASF Wizard entry in 05_ADC's context menu.

You should see two lists:

In Selected Modules we already see:

This is the standard framework needed for any ASF application.

In Available Modules find:

And Add>> the module to the project.

This driver provides a unified interface for the configuration and management of the ADC module.

Next find in Available Modules :

And Add>> the module to the project.

This driver provides easy access to the I/O pins.

Now in Available Modules find:

And Add>> the module to the project.

This driver allows easy usage of the MCU Real Time Clock.

Also in Available Modules find:

And Add>> this module to the project.

Which helps us using a SERCOM module in USART mode.

And again in Available Modules find:

And Add>> this module to the project.

While this driver is strictly speaking not necessary for the project, it provides the printf() function for printing to the serial terminal, making printing messages more convenient.

Now press Apply. This will add all the necessary ASF code to our project.

Using the DIT Adafruit Feather Library

For this project we need to:

See my Step-By-Step tutorial if you are unsure how to do this.

RTC Clock Setup

The clock system of the ATSAMD21 is very versatile, but it also can be quite confusing to set up for a beginner. There are a couple of moving parts involved. They have to be set up correctly for the RTC to function properly.

We need to set up:

  1. A System Clock Source (SYSTEM_CLOCK_SOURCE_XOSC32K)
    These are the actual oscillators / crystals that provide the good vibrations.
    Since the FatherM0 has especially for real time clock applications a 32.768kHz crystal attached to the MCU we are going to use it.
  2. The Generic Clock Source (GCLK), the RTC is connected to (GCLK_2).
  3. The connection between the GCLK_2 an the SYSTEM_CLOCK_SOURCE_XOSC32K

In the Solution Explorer find the generated conf_clocks.h.

For step 1 find the SYSTEM_CLOCK_SOURCE_XOSC32K section. The following values worked for me:

For step 2 and 3 find the SYSTEM_CLOCK_SOURCE_XOSC32K section.

We enable the Real Time Clock generic clock source in line 149 and connect it to the SYSTEM_CLOCK_SOURCE_XOSC32K in line 151.

Writing the application code

In the Solution Explorer find the generated main.c.

All we need to import to get the whole ASF magic working is the asf.h, that was generated by the ASF wizard. The entry should already be there. The wizard took care of that for us (line 53).

In lines 62..66 we define a couple of symbols that help us to keep the following code a little bit more readable.

By using or commenting out line 68 we select if the external or the internal reference voltage is used by the program.

If the external reference voltage is used (by using line 68):

If the internal reference voltage is used (by commenting out line 68):

These variables need to be globally accessible throughout this compilation unit, so they can be accessed from within the callback functions.

ADC callback

This is the function that gets called once the ADC has finished converting the input.

As the libraries imported for the project do not include a floating point version of the printf() function:

ADC configuration

This function configures and initializes the ADC.

RTC callback

rtc_callback_overflow (lines 121..129) is called when RTC counter reached the end of the configured period.


The call to adc_read_buffer_job() is non blocking, but might fail (e.g. if the ADC is still busy). That is why we embed the call in a loop, that repeats it until the job is accepted.

The ADC measurement job will call the adc_complete_callback(), once it is done.

RTC configuration

rtc_count_configure (lines 134..153) configures and initializes the RTC counter and the callback functions.


The main function in lines 158..201 is pretty strait forward now, since the program logic is already implemented in the ADC and RTC configuration and the callback functions.

  1. We declare a variable to hold the USART module data structure, for the serial communication module we use (line 160)
  2. In line 166 we initialize the board
  3. Line 167 enables interrupts globally
    If you forget to enable the interrupts the callback functions will not work.
  4. We use the DIT Adafruit Feather Library functions to:
  5. We do not configure any callback functions for the serial communication, since we are not actually interested in talking to the terminal.

  6. In line 177 and line 178 we print the welcome message
  7. Line 184 calls the adc_configure() function, that sets up the ADC
  8. Line 184 enables the ADC
  9. Line 187 registers the adc_complete_callback() function for the ADC_CALLBACK_READ_BUFFER event
  10. Line 188 enables the ADC_CALLBACK_READ_BUFFER callback for the event
  11. Line 191 calls the rtc_count_configure() function, that sets up the RTC counter and the callback functions
  12. Line 192 enables (starts) the RTC counter
  13. Finally we enter an infinite loop (lines 198..200) that has nothing to do