Adafruit FeatherM0 ASF Tutorials

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

2017-07-04 00:55

03_Button - Digital Output

The goal of the third project is for the MCU to be notified when the state of a button changes.

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

We connect a push-button to JP3.10, which is connected to the MCU's PA15 port (that's pin 24 on an ATSAMD21G18_QFN).


Usually we would also connect a pull-up resistor of something like 100k between JP3.10 and Vcc to pull the MCU input pin to HIGH when the button is not pressed.
There is not need for that in this case though, since the ATSAMD21 MCUs have build in pull-up resistors, that can be activated and deactivated by software.

The program will:

Adding a new Atmel Software Framework (ASF) Skeleton Project

Add a new ASF Skeleton Project for the 03_Button 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 03_Button 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 03_Button'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 service implements the delay_ms() function.

Now in Available Modules find:

And Add>> the module to the project.

This driver allows us to assign call-back functions to external interrupt events, like the state change of a digital input pin.

Next in Available Modules find:

And Add>> the module to the project.

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

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.

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).

I/O pin definitions

The lines 63..65 are specific to the pin we have connected the button to.
These are the values you have to change, if you are using a different pin.

We could have used the specific values for the pin directly in the configuration function. But it is good practice to define verbose abstract names at the top of the file, assign the specific values to them and only use the abstract names in the rest of the code. First of all, if you have to change the values (e.g. because you later want to move the button to a different pin), it is much easier to find the assignment. And second, by using abstract names you only have to change the value assignment on top,. No need to go through the whole implementation and adjust all the references.

Some of the values are kind of hard to remember, but AtmelStudio will try to help you by making suggestions as soon as you start typing.

The declaration in line 64 is necessary, since modern MCUs have more function than they can provide pins for. PA15 could also be assigned to one of the lines for SERCOM 3 or 4 (pin multiplexing). So we have to tell the MCU that it should use the pin for the digital I/O line.


The lines 67..77 implement the callback function that we want the ASF external interrupt channel module to call whenever it detected that the I/O pin PA15 changed status.

Following the schematics, a pressed button connects pin PA15 to ground. So the pin is active LOW.

We read the current pin state in line 72 and send a message according to the button state to the serial terminal.


In lines 82..93 we implement the function that configures the MCU to detect the button state changes.


The main function in lines 98..154:

  1. Declares a variable to hold the USART module data structure, for the serial communication module we use (line 100)
  2. Declares a variable for return values (line 101)
  3. In line 107 we initialize the board
  4. Line 108 enables interrupts globally
    If you forget to enable the interrupts the callback functions will not work.
  5. Line 109 initializes the delay service
    If you forget this the delay_ms() functions will not work and the LED will not blink.
  6. We use the DIT Adafruit Feather Library functions to:
  7. We do not configure any callback functions for the serial communication, since we are not actually interested in talking to the terminal.

  8. In line 122 we print the welcome message
  9. Line 129 calls the button_init() function, that sets up the external interrupt channel for our button state change detection
  10. In Line 131 we register our button_on_detect() function as the callback function for button state changes and enable it in line 136.
    Both functions provide a return value that tells us if the operation (register/enable) was successful. Since there is no point in running the application if they are not, we retry any of them until we are successful.
  11. Finally we enter an infinite loop (lines 145..153) that blinks the LED.
    After a reboot this shows us when the MCU is ready for us to press the button and that the MCU can do other things while still detecting correctly when the button is pressed or released.