Table of Contents
Introduction
<WRAP group> <WRAP half column> The SAM C21 family is based on the ARM Cortex-M0+. The specific architecture is ARMv6-M.
The SAM C21 microcontrollers use little endian.
There are two buses present; AHB, Advanced High-performance Bus and APB, Advanced Peripheral Bus. Some peripherals are connected to both of the buses, most are only connected to the APB.
There is also something called IOBUS, that seem to be tightly coupled to the ARM core. It seems that peripherals connected to this can be affected directly with an instruction and change in a single clock-cycle.
</WRAP>
- 2.8 V – 5 V supply voltage
- 48 MHz core
<WRAP half column> </WRAP> </WRAP>
Chapters of importance
15 - Gives an understanding of the clock generating and distribution.
SAMC21 Xplained Pro
Based on ATSAMC21J18A
The SAM21 Xplained Pro development board has some peripherals attached:
- A LED is connected to PA15.
- A button is connected to PA28 to GND, no external pull-up
- A CAN interface is connected to PA24(TX) and PA25(RX)
- A LIN interface is connected to PA06(TX), PA07(RX) and PB22(EN)
- A QTouch capacitive touch button to PA05
Some lines are connected to the debugger-MCU that can pass communication to PC. The UART is accessible with any serial monitor. The other debug ports is accesible through a plugin in Atmel studio called Data Visualizer and can be found under the Tools menu.
- CDC_UART PB10(MCU→PC) and PB11(PC→MCU) (SERCOM0)
- DGI_I2C PA12(SCL) and PA13(SDA)
- DGI_SPI PB23(EN), PB01(SCK), PB00(MOSI), PB02(MISO)
- DGI_GPIO_LEVEL PB12, PB16, PB17, PA28
Using peripherals
Clocks and synchronisation
At startup the clock is configured to 4 MHz.
The initial clock setup is described in chapter 15.7 and chapter 12.
All peripherals interfaces two different clock-domains; their individual General Clock source, and the synchronized APB, or AHB, clock that is interfaced by the CPU. Both of these clock sources need to be configured properly.
A General Clock source is configured by a GCLK_XXXX_CORE register, see chapter 16.8.4 table 16-9 for the register mappings. A peripheral can require several General Clock sources.
The synchronized clock is controlled by a bit mask in the Main Clock system, MCLK chapter 17. To quickly find the relevant registers, refer to the peripherals chapter x.5.3 Product Dependencies - Clocks.
The registers in the peripheral that runs in the General Clock domain is referred to as Core registers.
Each core register has its own synchronization mechanism, which makes it possible to write to different core registers consecutively without problems.
Maximum clock frequencies for different parts of the controller can be found in table 45-8 in chapter 45.6.
MCLK, Main Clock system
Three equivalent examples for enabling the synchronized bus clock for the timer TC0.
MCLK->APBCMASK.bit.TC0_ = 1;
MCLK->APBCMASK.reg |= MCLK_APBCMASK_TC0;
REG_MCLK_APBCMASK |= MCLK_APBCMASK_TC0;
GCLK, Generic Clock system
There are nine Generic Clock Generators. Generator 0 is dedicated to source the Main Clock system and is enabled by default.
Each Generic Clock Generator can be sourced from one of several clock sources. These Generators all have their individual clock divider. Generator 1 is the only generator that can be used as clock source for other generators.
There are several Peripheral Channels. The number is dependent on the number of peripherals in the chip. Maximum 64. There are 41 in the ATSAMC21J18A.
Each Peripheral Channel can be sourced from any one of the Generic Clock Generators. Each Peripheral Channel is harwired to one peripheral. The Peripheral Channel consists of a MUX and a clock gate.
The GENCTRL[x] must be written to as one whole 32-bit register, because of the synchronization mechanism.
Example of how to hook up TC0 to Generator 2
GCLK->GENCTRL[2].reg = GCLK_GENCTRL_DIV(100) | GCLK_GENCTRL_GENEN | GCLK_GENCTRL_SRC(GCLK_GENCTRL_SRC_OSC48M_Val); GCLK->PCHCTRL[TC0_GCLK_ID].bit.CHEN = GCLK_PCHCTRL_CHEN | GCLK_PCHCTRL_GEN(GCLK_PCHCTRL_GEN_GCLK2_Val);
Step-by-step
- (A running clock source.)
- A Generic Clock Generator must be enabled and configured to use a running clock source.
- The Generic Clock Generator must be connected to the desired peripheral
- To interface the peripheral from the CPU, the corresponding synchronized clock must be unmasked in the Main Clock registers.
OSC32KCTRL 32.768 Hz oscillator
Three oscillators: External crystal oscillator XOSC32K, Internal high accuracy oscillator OSC32K and Internal ultra low-power oscillator OSCULP32K
All can run at the same time and be used as sources for different generators.
<wrap hi>There seems to be a problem with the XOSC32K crystal on the Xplained board, it runs ocationally when touching the PCB..</wrap> Hmm.. It seems to be a problem only when step-by-step debugging trhough the synchronizing code while(!OSC32KCTRL→STATUS.bit.XOSC32KRDY);. Now it runs rock solid.
Example code to enable the internal high accuracy oscillator and wait for it to stabilize.
OSC32KCTRL->OSC32K.reg = OSC32KCTRL_OSC32K_ENABLE | OSC32KCTRL_OSC32K_EN32K | OSC32KCTRL_OSC32K_EN1K | OSC32KCTRL_OSC32K_STARTUP(7); while(!OSC32KCTRL->STATUS.bit.OSC32KRDY);
Same goal but different register access:
OSC32KCTRL->OSC32K.bit.STARTUP = 7; OSC32KCTRL->OSC32K.bit.EN1K = 1; OSC32KCTRL->OSC32K.bit.EN32K = 1; OSC32KCTRL->OSC32K.bit.ENABLE = 1; while(!OSC32KCTRL->STATUS.bit.OSC32KRDY);
<wrap hi>Note that none of the internal oscillators seems to be very accurate without calibration by means of the calibration registers. Not accurate at all. The OSC32K oscillator swung at 45 kHz instead of 32.768 for me.</wrap>
PORT peripheral
Only requires APB clock, CLK_PORT_APB, to be interfaced. This is enabled by default at reset. There is no Generic Clock for the PORT peripheral.
All pins are configured to tri-state mode and the input drivers are disabled, with potential exceptions for JTAG-pins.
Some ways of setting PA15 as output and toggle it:
Using direct register access
REG_PORT_DIRSET0 = PORT_PA15; while (1) { REG_PORT_OUTTGL0 = PORT_PA15; for(int i = 400000; i ; --i); }
Using indirect register access
PORT->Group[0].DIRSET.reg = PORT_PA15; /* Replace with your application code */ while (1) { PORT->Group[0].OUTTGL.reg = PORT_PA15; for(int i = 400000; i ; --i); }
TC peripheral
The APB clock, CLK_TCx_APB, is required for the peripheral to be interfaced. This is disabled by default at reset. Can be accessed through MCLK.APBCMASK.bit.TCx_.
The Generic Clock, GCLK_TCx, is required for the timer to count.
Since the peripheral has several clock sources, some register accesses require synchronization.
uint16_t last_time, now; last_time = now = 0; // Set LED pin, PA15, as output PORT->Group[0].DIRSET.reg = PORT_PA15; // Setup internal 32k oscillator OSC32KCTRL->OSC32K.reg = OSC32KCTRL_OSC32K_ENABLE | OSC32KCTRL_OSC32K_EN32K | OSC32KCTRL_OSC32K_EN1K | OSC32KCTRL_OSC32K_STARTUP(7); // Enable Generic Clock Generator 3, sourced from the internal 32khz oscillator GCLK->GENCTRL[3].reg = GCLK_GENCTRL_DIV(0) | GCLK_GENCTRL_GENEN | GCLK_GENCTRL_SRC(GCLK_GENCTRL_SRC_OSC32K_Val); // Connect the timer 0, TC0, to Generator 3 GCLK->PCHCTRL[TC0_GCLK_ID].reg = GCLK_PCHCTRL_CHEN | GCLK_PCHCTRL_GEN(GCLK_PCHCTRL_GEN_GCLK3_Val); // Enable bus clock for TC0 MCLK->APBCMASK.bit.TC0_ = 1; // Configure timer 0 for 16-bit counter mode TC0->COUNT16.CTRLA.bit.MODE = TC_CTRLA_MODE(TC_CTRLA_MODE_COUNT16_Val); // Configure prescaler and start timer TC0->COUNT16.CTRLA.reg |= TC_CTRLA_ENABLE | TC_CTRLA_PRESCALER(TC_CTRLA_PRESCALER_DIV1_Val); // Wait for the timer to start while(TC0->COUNT32.SYNCBUSY.bit.ENABLE); while (1) { // Request read synchronization of the COUNT register TC0->COUNT16.CTRLBSET.reg = TC_CTRLBSET_CMD(TC_CTRLBSET_CMD_READSYNC_Val); // Wait for the command to be registered while(TC0->COUNT16.SYNCBUSY.bit.CTRLB); // Wait for the COUNT register to be synchronized while(TC0->COUNT16.SYNCBUSY.bit.COUNT); // Read the COUNT register now = TC0->COUNT16.COUNT.reg; if((uint16_t)(now - last_time) > 32768u) // Should be 1 sek { PORT->Group[0].OUTTGL.reg = PORT_PA15; last_time = now; } }
CAN Bus Peripheral
StackOverflow – Undocumented baud calculation
total_tq = sync_seg_tq + (phase_seg1_tq+1) + (phase_seg2_tq+1) where sync_seg_tq == 1 always.
Programming
ASF - Atmel Software Framework
Since the acquisition by Microchip this is referred to as Advanced Software Framework.
OpenOCD
$ openocd -f "interface/cmsis-dap.cfg" -f "target/at91samdXX.cfg"
$ telnet localhost 4444 > reset halt > at91samd chip-erase > flash write_bank 0 main.bin 0
$ arm-none-eabi-gdb main.elf --eval-command="target remote localhost:3333"
A file named openocd.cfg in the same folder as openocd is started in is supposed to be loaded automatically. But sometimes this doesn't seem to work. In this case run openocd -f script_file.fcg
# Filename: openocd.cfg # or envoke with openocd -f filename.cfg # Explicit debugger and target config files #source [find interface/cmsis-dap.cfg] #source [find target/at91samdXX.cfg] # Readily available config instead of the explicit setup above source [find board/atmel_samc21_xplained_pro.cfg] # Some of the sleep commands seems necessary, otherwise the MCU doesnt seem to react properly # Set to known state init reset init sleep 25 # Erase flash at91samd chip-erase sleep 25 # Write firmware flash write_bank 0 flash_image.bin 0 sleep 25 # Reset MCU reset sleep 25 # Shut down openocd server shutdown sleep 25
Getting started
Download and install Atmel Studio
- Download Atmel Studio: Atmel Studio 7 Download
- Run the installer
- Uncheck the option to send anonymous information if this is not desired
- Make sure to check the architecture <wrap hi>SMART ARM MCU</wrap>
- Select the extension <wrap hi>Atmel Software Framework and Example projects</wrap>
- Install the drivers when asked to
- Open Atmel Studio
- Select
Tools→Device pack manager. In the window that opens search forATSAMC21and install the latest release ofATSAMC21_DFP. Restart atmel studio. - Select
Tools→Extensions and Updates. In the window that opens chooseUpdatesin the left panel. Apply all available updates. Restart Atmel Studio and repeat until there are no updates available. - Start a project
Start a new bare metal project
- Open Atmel Studio and choose
File→New→Project… - In the left panel select
Installed→C/C++and pickGCC C Executable Project. Type a name for the project and press OK. - Find the correct MCU in the list. The Xplained board has the
ATSAMC21J18AMCU. Press OK. - The project should now be set up and you can press F5 to start the empty program in debug mode if the Xplained board is connected.
<wrap hi>Note that the Debug config by default has the -O1 optimization flag set.</wrap>
