Reading and Writing ARM Cortex Pins using ASF

Created on: 31 May 2016

Part 8 of the ASF ARM Tutorial

How to read the state of a microcontroller pin and write to a microcontroller pin using ASF.

In this part of the ASF ARM tutorial, the IOPORT ASF module is used to read the state of a single microcontroller pin. The same module is used to write to a pin to set the voltage level of the pin to high or low.

Which ASF Module to Use for Pin I/O

When searching for IO in the ASF Wizard in Atmel Studio to find an input/output module for controlling and reading pins, three possible candidates appear in the results, namely, IOPORT, GPIO and PIO. IOPORT is the correct module to use in most cases.

The GPIO ASF module is marked as deprecated in the ASF documentation and should not be used in any projects. Use IOPORT instead.

PIO is classed as a driver in ASF and so is lower level than the IOPORT module which is classed as an ASF service. In fact PIO is a dependency of IOPORT and is therefore automatically installed in a project when the IOPORT module is added to the project.

Creating a Pin Read and Write ASF Project

The Atmel Studio ASF project that follows demonstrates how to read a single microcontroller pin and write to a single microcontroller pin. This is done by reading the on-board push-button switch attached to pin 30 of port A and writing to the on-board LED on pin 14 of port B of the SAM4N Xplained Pro board. If you are using a different board, make the necessary adjustments to the pin definitions in the project.

Follow the steps from the ASF project checklist from the previous part of this tutorial series to create the in_out_pin ASF project. Add the ASF IOPORT module to the project using the ASF Wizard.

Pins are defined as I/O pins in conf_board.h.

conf_board.h

#ifndef CONF_BOARD_H
#define CONF_BOARD_H

// output pin for LED
#define LED0 IOPORT_CREATE_PIN(PIOB, 14)
// input pin for switch
#define SW0 IOPORT_CREATE_PIN(PIOA, 30)

// clock resonators
#define BOARD_FREQ_SLCK_XTAL      (32768U)
#define BOARD_FREQ_SLCK_BYPASS    (32768U)
#define BOARD_FREQ_MAINCK_XTAL    (12000000U)
#define BOARD_FREQ_MAINCK_BYPASS  (12000000U)
#define BOARD_MCK                 CHIP_FREQ_CPU_MAX
#define BOARD_OSC_STARTUP_US      15625

#endif // CONF_BOARD_H

The IOPORT_CREATE_PIN() macro is used to define pin names in ASF and set their pin and port numbers.

Although the crystals on the board are defined in conf_board.h to prevent compiler warnings, conf_clock.h was not modified to use the external 12MHz crystal. The internal R/C oscillator and PLL is used in the project which is the default setting.

Disable the watchdog timer, initialize the IOPORT module and set the pin directions in init.c.

init.c

#include <asf.h>
#include <board.h>
#include <conf_board.h>

void board_init(void)
{
    WDT->WDT_MR = WDT_MR_WDDIS;                     // disable watchdog
    ioport_init();                                  // call before using IOPORT service
    ioport_set_pin_dir(LED0, IOPORT_DIR_OUTPUT);    // LED pin set as output
    ioport_set_pin_dir(SW0, IOPORT_DIR_INPUT);      // switch pin set as input
}

ioport_init() must be called before using functions from the IOPORT module to enable the IO module.

ioport_set_pin_dir() is used to specify whether the pins defined using IOPORT_CREATE_PIN() are to be input pins or output pins. The first parameter passed to this function is the pin name. Pin direction is specified by passing either IOPORT_DIR_OUTPUT for an output pin or IOPORT_DIR_INPUT for an input pin to this function.

Application code in main.c calls the necessary initialization functions and then reads the pin level of the input pin and writes this level to the output pin.

main.c

#include <asf.h>

int main (void)
{
    bool pin_level;

    sysclk_init();
    board_init();

    while (1) {
        pin_level = ioport_get_pin_level(SW0);
        ioport_set_pin_level(LED0, pin_level);	
    }
}

ioport_get_pin_level() reads the state of the push-button switch on the input pin and saves it to the pin_level variable.

ioport_set_pin_level() writes the state of the input pin stored in pin_level to the output pin which switches the LED.