ATSAMD20 Xplained Pro

Created by Alex Aldag, last modified by Robert Nelson on Sep 08, 2017

This page is about the Atmel ATSAMD20 XPlained Pro board and how to get started with it.

Available Boards

ATSAMD20-XPRO (SAMD20 Xplained Pro) at Digi-Key

ATPROTO1-XPRO (Prototype extension board) at Digi-Key

ATIO1-XPRO (I/O extension board) at Digi-Key

ATOLED1-XPRO (128x32 OLED extension board) at Digi-Key

Features and Benefits

Using an ARM Cortex-M0+ core, the SAM D20 is one of the newest flash-based microcontrollers that Atmel has released. There are four different device families, the D10, the D11, the D20, and the D21. Coupled with their wide peripheral set and low power process, this makes a good addition to their product line.

The SAM D20 is able to operate up to a maximum speed of 48MHz. This has been coupled with the Atmel event system, a single-cycle 32x32 multiplier, a 2-stage pipeline, and single-cycle I/O access. All of the parts have the Atmel SERCOM (Serial Communications Interface) which is a very flexible serial interface able to be configured as I2C, SPI, or USART. Each interface can be assigned to different I/O pins through multiplexing, increasing the flexibility of the part. There are mulitple instances of 16-bit timer/counters, each of which can be programmed to perform frequency and waveform generation. There are two sleep modes, idle and standby, that are selectable via software.

Software

Atmel Studio 6.2 (latest version) - If you have previous versions of Studio 6.x, I would suggest that you uninstall those versions before installing the latest. I found an issue with upgrading from 6.1 (build 2562) where Atmel Software Framework (ASF) was essentially removed from Studio. I went back and removed Studio 6.1 (2562), 6.0, and 5.0 (just to be sure) and then installed the full version of 6.1 (2674). Version 3.9.1 of ASF was then installed and usable.

Hardware

If you’re familiar with the AVR Xplained kits, then you know that these were created to make it easier for the designer (you) to get started with various Atmel devices. Since it’s introduction, the number of boards that are available have expanded, primarily towards the high end product like the ATxmega, the UC3 and the SAM4. With these boards they created some add-on boards to show how to implement various sensors using the installed microcontroller. After getting feedback from the field and their customers, Atmel decided to come out with an Xplained Pro board, giving the user (again, you) more and easier access to the various GPIO and peripherals. Of course, when you do this, you have to have some boards that can be coonected to those same peripherals. So, Atmel developed add-on boards that fit the Xplained Pro boards (listed above under Available Boards). And to make it even easier for you, Atmel incorporated their SHA204 product into these new boards so Atmel Studio can identify which boards are connected. Atmel Studio will actually let you know which board you have connected to it as well as which extention board is attached.

Features of the SAMD20 Xplained Pro

  • Atmel ATSAMD20J18 microcontroller
  • Embedded debugger (EDBG)
    • USB interface
    • Programming and debugging on board SAM D20 through Serial Wire Debug (SWD)
    • Programming and debugging external targets through Serial Wire Debug (SWD)
  • Digital I/O
    • Two mechanical buttons (user and reset button)
    • One user LED
    • Three extension headers
  • Two possible power sources
    • External power
    • Embedded debugger USB
  • 32kHz crystal

Example Projects

All the examples that I’m going to show here were written and compiled for Atmel Studio. IAR and Keil may or may not have support for this chip and board. As of the current writing, I haven’t seen support available. If you want a list of currently available applications within the Atmel Software Framework, go to Atmel SAMD20 Pro ASF. You can also find more information about the ASF for the supported Atmel devices and boards.

LED Toggle

Perhaps the most basic and sought out program is to blink an LED. Why, you ask? Well, let’s think about it for a minute. What is really going on when you get an LED to toggle on a board? First, you have to initialize your system. This means that you have your clocks set up and running. It means that you have the GPIO pins initialized to be able to drive or sink current for the LED and, in some cases, have a button input. It also means that you have to have you interrupts set up. It could be from a button push or from a timer. If you’re using the timer (as shown in the next example), you also need to get that initialized as well. Essentially, you have to have some of the most important parts of the chip running to blink a LED. So, let’s get started.

I was lucky enough to be able to have a pre-release version of the SAMD20 Xplained Pro board and the software to be able to use it. While not distinctly different from the board shown above, there were some differences in terms what pins went where. For our purposes, however, it won’t make a difference. There were a few differences in the code but not enough to make a difference. One is within the code block below. I think the version below is easier to understand over what they have in the updated version. If you’ve never used Atmel Studio before, I would suggest that you go to the View Help under the Help menu (or press CTRL-F1) and look through Atmel Stuido file and the Atmel Software Framework.file.

So, using Studio 6, I chose a New Example Project off of the Start Page, changed from All Projects and had it sorted by Kit. I chose the correct version of ASF, scrolled down until I found SAM D20 Xplained Pro and chose LED Toggle Application. Pretty much what this project does is when you push SW0, LED0 turns on. At this point you think “What’s the point in that? You can hard-wire it to do the same thing.” However, this is where Atmel makes it a little easier to change things and show some capabilities of the part.

in the main file (led_toggle.c), near the top are some definitions. By changing these #define statements, you can change how the part is going to react to that button push. It can react via the PORT driver, the EIC driver, using the SysTick handler, or using the EIC handler, using interrupts or not using interrupts. This gives you the capability to debug code which uses various ways to see a pin change.

/*
USE_INTERRUPTS      USE_EIC         Result
--------------      -------         ---------------------------------------------
false               false           Polled via PORT driver
false               true            Polled via EIC driver
true                false           Polled via PORT driver, using SysTick handler
true                true            Polled via EIC driver, using EIC handler
*/
  
#define USE_INTERRUPTS  true
#define USE_EIC         true

So, let’s break down the steps that Atmel takes to get this program working. The first step is the system initialization, system_init(). This function in turn calls the system_clock_init() and the system_board_init() functions.

#if CONF_CLOCK_XOSC_ENABLE == true
    struct system_clock_source_xosc_config xosc_conf;
    system_clock_source_xosc_get_default_config(&xosc_conf);
  
    xosc_conf.external_clock        = CONF_CLOCK_XOSC_EXTERNAL_CRYSTAL;
    xosc_conf.startup_time          = CONF_CLOCK_XOSC_STARTUP_TIME;
    xosc_conf.auto_gain_control     = CONF_CLOCK_XOSC_AUTO_GAIN_CONTROL;
    xosc_conf.frequency             = CONF_CLOCK_XOSC_EXTERNAL_FREQUENCY;
    xosc_conf.on_demand             = CONF_CLOCK_XOSC_ON_DEMAND;
    xosc_conf.run_in_standby        = CONF_CLOCK_XOSC_RUN_IN_STANDBY;
 
    system_clock_source_xosc_set_config(&xosc_conf);
    system_clock_source_enable(SYSTEM_CLOCK_SOURCE_XOSC);
#endif

The system_board_init() function is going to be totally dependent on the board that is being used. When making your own board, you’ll have to make your own board_init function. In this function, all Atmel did was set up the port pins for the LED output and the button input. I have to admit that they way they did it seems a bit complicated but with all the development boards they have it seems to work.

void system_board_init(void)
{
    struct port_config pin_conf;
    port_get_config_defaults(&pin_conf);
  
    /* Configure LEDs as outputs, turn them off */
    pin_conf.direction = PORT_PIN_DIR_OUTPUT;
    port_pin_set_config(LED_0_PIN, &pin_conf);
    port_pin_set_output_level(LED_0_PIN, LED_0_INACTIVE);
 
    /* Set buttons as inputs */
    pin_conf.direction = PORT_PIN_DIR_INPUT;
    pin_conf.input_pull = PORT_PIN_PULL_UP;
    port_pin_set_config(BUTTON_0_PIN, &pin_conf);
}

At this point the code starts going into a series of #if statements to determine how it’s supposed to react to the button push. So, depending on how you set up USE_INTERRUPTS and USE_EIC will determine if it uses the EIC, interrupts, and the systick.

#if USE_EIC == true
    configure_extint();
#endif
 
#if USE_INTERRUPTS == true
# if USE_EIC == false
    configure_systick_handler();
# else
    configure_eic_callback();
# endif
 
system_interrupt_enable_global();
 
while (true) {
/* Do nothing - use interrupts */
}
#else
# if USE_EIC == false
    while (true) {
    update_led_state();
    }
# else
    while (true) {
        if (extint_chan_is_detected(BUTTON_0_EIC_LINE)) {
            extint_chan_clear_detected(BUTTON_0_EIC_LINE);
        update_led_state();
        }
    }
# endif
#endif

Again, if you go through Atmel Studio and choose a New Example Project, you can find this one listed.

Delay Example

Or as we all like to say, “Blinky”. The same reasoning applies to this one as to the previous one. You’re really exercising a major portion of the chip to get this to work. So, just like before, I went into Studio 6.1 and chose to make a New Example Project. In this case, I went with the Delay Service Example - SAM D20 Xplained Pro. Reading the description you read “Initialize the system clock and blink a LED at a constant 1Hz frequency.” That’s pretty much Blinky right there. And this is where I ran into a little snag. Like any example project, you expect to see a file with Main in it, usually right in front of you. In this case, not so much. This is what I ended up looking at.

So, since there wasn’t an obvious file that showed Main, I had to dig through the Solution Explorer to find it. It ended up under ASF>common2>services>delay>example labeled delay_example.c.

So, just like the LED Toggle, you have the system_init() function which calls the system_clock_init() and system_board_init() functions. Now, since this is a “Delay Example”, you need to set up something to get that delay. This particular example has a delay_init() call after the system_init() is done.

The first line in this function is actually calling another function that returns a frequency. In this case, system_gclk_gen_get_hz(0) reads the clock control register for channel 0 and returns the clock frequency in hertz. The next two lines gets you the number of cycles per millisecond and microsecond, respectively.

The last line in this initialization is for the SysTick, which is the ARM implimentation of a system timer within the core. When enabled, it is a pretty standard count-down timer that sets a flag once it rolls over. All that is happening on this line is that it’s loading what clock source it’s using and enabling the SysTick. In this case, the SysTick_CTRL_CLKSOURCE_Msk is set to one which means it’s using the processor clock rather than the reference clock.

void delay_init(void)
{
    cycles_per_ms = system_gclk_gen_get_hz(0);
    cycles_per_ms /= 1000;
    cycles_per_us = cycles_per_ms / 1000;
     
    SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_ENABLE_Msk;
}

So, the next set of lines are setting up the port pins. Something that isn’t mentioned in ASF or the code anywhere is that this was written to be used with the OLED1 board. What I added, as can be seen below, is setting up PA14 so that USER LED0 is used as well. If you have the OLED1 board, then both LED0 and LED2 are going to be used.

So, within ASF, the port pins already have a default set-up, which is as an input with the internal pull-ups enabled. The first line sets up a port_config structure called pin. The next line gets the default configuration, which is direction and input_pull, and stores it within the pin struct. Every time a Port pin or group is going to be changed, the port_get_config_defaults() function needs to be called before being modified, thus giving you a set pin set up. The last line changes the the pin direction from an input to an output.

The port_pin_set_config() function will write the new configuration to the respective port pins, in this case PA13 and PA14. They make a note in that function stating that if the pin direction is set as an output, the pull-up/down input configuration is ignored. However, that’s not really accurate. After looking through that function, the internal pull-up is still enabled and it’s still written back to pin register when the function is called. Now, as an output, it doesn’t really make a difference if the pull-up is active or not. When you set the output level high, it goes high. When you set it low, it goes low.

On the other hand, there are some handy registers in place on these parts to make it easier to switch between input and output. There is the DIRCLR, the DIRSET, and the DIRTGL registers for each port. DIRCLR is Port Data Direction Clear, SET is set and TGL is toggle. So, when you write a one to a bit in CLEAR, it will clear the corresponding bit in the DIR register, configuring that pin as an input. Write a one to a bit in SET, it will set that bit in the DIR register making it an output. And when you write a one to a bit in TOGGLE, it will toggle between being an input/output to being an output/input. So, when you start messing around with these particular registers, you need to keep in mind the internal pull-ups are probably still enabled.

Getting back to the code as shown, all the last two lines are doing are setting the output of the pins to a high. This function uses logic levels to set or clear an output, true = 1, false = 0.

struct port_config pin;
port_get_config_defaults(&pin);
pin.direction = PORT_PIN_DIR_OUTPUT;
 
port_pin_set_config(PIN_PA13, &pin);
port_pin_set_config(PIN_PA14, &pin);
port_pin_set_output_level(PIN_PA13, true);
port_pin_set_output_level(PIN_PA14, true);

So, after all of this is done, you can finally get the LED(s) to blink.

I have also added PA14 to these functions to make LED0 blink. So, within for() loops the port pins are having their output levels toggled. These registers are similar to the direction registers of those same pins, to SET, CLEAR, or TOGGLE a port pin. In this instance, the function will write a one to the corresponding bit in the OUTTGL register to toggle that pin.

As you can see, each for() loop has it’s own delay function: delay_s(), delay_ms(), and delay_cycles(). The function names are pretty self explanatory, delay_s() will delay so many seconds and so on. However, this is where I think they’ve gotten too complicated. First, they define delay_s(delay) as cpu_delay_s(delay) which is defined as delay_cycles_ms(1000 * delay). This function is a while() loop which is a count down that has the function delay_cycles(cycles_per_ms).

So I found this function within the cycle_counter.h file. This is a static inline void function which sets up the SysTick counter and waits for it to run down. It’s interesting to note that delay_s, delay_ms, and delay_us (not used here) all end up going to the delay_cycles() function. Of course, delay_ms and delay_us are defined as cpu_delay_ms and cpu_delay_us which are defined as delay_cycles_ms and delay_cycles_us. These, of course end up calling the same static inline function for the SysTick. If you recall from above, cycles_per_ms is equal to system clock in hertz divided by 1000. The function delay_cycles_ms() uses that value to call the delay_cycles() function to determine how many cycles to delay. Similarly, the delay_cycles_us() takes the cycles_per_us value shown above to determine how many cycles the delay_cycles() function will wait.

So, with the way that they set this while() loop up, the very last loop isn’t going to be reached if you run it as is. If you comment out one of the first two, then the last one will run. You won’t really notice that they both are on since it’s only a 5000 count for 100 clock cycles. If you up it to 50000 you have a much better time seeing it.

while (true) {
    for (int i = 0; i < 5; i++) {
        port_pin_toggle_output_level(PIN_PA13);
        port_pin_toggle_output_level(PIN_PA14);
        delay_s(1);
    }
 
    for (int i = 0; i < 50; i++) {
        port_pin_toggle_output_level(PIN_PA13);
        port_pin_toggle_output_level(PIN_PA14);
        delay_ms(100);
    }
 
    for (int i = 0; i < 5000; i++) {
        port_pin_toggle_output_level(PIN_PA13);
        port_pin_toggle_output_level(PIN_PA14);
        delay_cycles(100);
    }
}

So, to combat the fact that the last function won’t be reached with the code as is, I did a little bit of tweaking to this. I made i a volatile variable within main() so that all three for() loops can be reached. I also decided to change up the initial output level for PA14 (LED0) so that it’s off when PA13 (LED2) is on. This way the two LEDs are on at opposite times.

int main(void)
{
    system_init();
    delay_init();
 
    volatile int i;
 
    struct port_config pin;
    port_get_config_defaults(&pin);
    pin.direction = PORT_PIN_DIR_OUTPUT;
 
    port_pin_set_config(PIN_PA13, &pin);
    port_pin_set_config(PIN_PA14, &pin);
    port_pin_set_output_level(PIN_PA13, true);
    port_pin_set_output_level(PIN_PA14, false);
 
    while (true) {
        for (i = 0; i < 5; i++) {
            port_pin_toggle_output_level(PIN_PA13);
            port_pin_toggle_output_level(PIN_PA14);
            delay_s(1);
        }
 
        for (i = 0; i < 50; i++) {
            port_pin_toggle_output_level(PIN_PA13);
            port_pin_toggle_output_level(PIN_PA14);
            delay_ms(100);
        }
 
        for (i = 0; i < 50000; i++) {
            port_pin_toggle_output_level(PIN_PA13);
            port_pin_toggle_output_level(PIN_PA14);
            delay_cycles(100);
        }
    }
}

Conclusion

These two example projects are pretty much the most basic ones that you can get. The first one you push a button and the LED comes on. The second one uses timers to blink the LED. There are a more projects listed within ASF and I’m sure that there will be more pages dealing with the ATSAMD20 Xplained Pro board and the extension boards.

Authors Note

This page was started and is maintained by the Digi-Key Applications Engineering group. It is one of the many ways that we provide support for engineers, hobbyists, entrepreneurs, and other technical minded people. We continue to try to expand and update the content in these pages but we need to know when our work has been useful in any way. In this way we can justify our efforts in working on the eewiki. Please post any comments or questions or you can send them to the link below. If there is something specific you want to address to the author, please include his name in the subject line. As time goes on I will be adding more content on the ATSAMD20 boards and chips.

Comments/Questions

For questions or feedback about information on this or any other page, please go to the TechForum: TechForum