====== Introduction ======
The SAM C21 family is based on the ARM Cortex-M0+. The specific architecture is [[arm:armv6-m|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.
* 2.8 V -- 5 V supply voltage
* 48 MHz core
====== Chapters of importance ======
15 - Gives an understanding of the clock generating and distribution.
====== SAMC21 Xplained Pro ======
[[http://ww1.microchip.com/downloads/en/devicedoc/Atmel-42460-SAM-C21-Xplained-Pro_User-Guide.pdf|User guide]] [[https://www.microchip.com/DevelopmentTools/ProductDetails/PartNO/ATSAMC21-XPRO| Product page]]
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 ======
[[https://microchipdeveloper.com/32arm:differences-among-arm-cortex-families|Developer portal]]
===== 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.
There seems to be a problem with the XOSC32K crystal on the Xplained board, it runs ocationally when touching the PCB.. 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);
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.
====== PORT peripheral ======
[[arm:atsamc21:pinout|ATSAMC21 pinout]]
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 ======
[[https://electronics.stackexchange.com/questions/478864/atsamc21-can-configuration-nominal-bit-timing-vs-data-bit-timing-time-qua|StackOverflow]] -- Undocumented baud calculation
''total_tq = sync_seg_tq + (phase_seg1_tq+1) + (phase_seg2_tq+1)'' where ''sync_seg_tq == 1'' always.
====== Programming ======
Good place: [[https://microchipdeveloper.com/32arm:sam-bare-metal-c-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: [[https://www.microchip.com/mplab/avr-support/atmel-studio-7|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 SMART ARM MCU
* Select the extension Atmel Software Framework and Example projects
* Install the drivers when asked to
- Open Atmel Studio
- Select ''Tools->Device pack manager''. In the window that opens search for ''ATSAMC21'' and install the latest release of ''ATSAMC21_DFP''. Restart atmel studio.
- Select ''Tools->Extensions and Updates''. In the window that opens choose ''Updates'' in 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 pick ''GCC C Executable Project''. Type a name for the project and press OK.
- Find the correct MCU in the list. The Xplained board has the ''ATSAMC21J18A'' MCU. 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.
Note that the Debug config by default has the -O1 optimization flag set.