I've been gradually moving the SAMD10 Blink example to a non-ASF form, and adding Arduino-like functionality (say what you want about Arduino, but implementing the "core" is a good way to learn a bunch of stuff about a new chip family.)
I've got clock_init() replaced, pinMode(), digitalWrite(), Polled UART, millis/micros/delay/etc (using the RTC)
A program that uses them all is about 1.5k. Which is pretty good, since the original was over 2k to do just the blink.
There are some useful threads over in the SAMD subsection of AVRFreaks.
You don't actually have to do much to get an LED blinking; setting up the core clock is about it (and you COULD default to a lot more than I do here...)
void system_clock_init()
{
#ifdef USEPLL
#define EXTCLK_DIV ((EXTCLK_FREQ/(2*F_INTERM)) - 1) // Slightly complicated
//Enable the 8MHz oscillator input from SAMD10 XMini
SYSCTRL->XOSC.reg = (SYSCTRL_XOSC_ENABLE);
//Wait for the crystal oscillator to start up
while((SYSCTRL->PCLKSR.reg & (SYSCTRL_PCLKSR_XOSCRDY)) == 0);
//Set flash wait state to 1, which we need to do at 48MHz
REG_NVMCTRL_CTRLB |= (NVMCTRL_CTRLB_RWS(1));
//Configure the DPLL
SYSCTRL->DPLLCTRLA.reg = 0; // Defaults!
/*
* The output of DPLL is supposed to be between 48MHz and 96MHz.
* By setting the PLL output to 2*F_CPU, we have easy acess to
* most of the "interesting" frequencies between 24MHz and 48MHz
* Lower F_CPU can be obtained by increasing the GCLK0 divisor.
*/
SYSCTRL->DPLLRATIO.reg = SYSCTRL_DPLLRATIO_LDR(2*F_CPU/F_INTERM);
SYSCTRL->DPLLCTRLB.reg = SYSCTRL_DPLLCTRLB_DIV(EXTCLK_DIV) | /* Refclock divider */
SYSCTRL_DPLLCTRLB_REFCLK(SYSTEM_CLOCK_SOURCE_DPLL_REFERENCE_CLOCK_XOSC);
SYSCTRL->DPLLCTRLA.reg = SYSCTRL_DPLLCTRLA_ENABLE;
while(!(SYSCTRL->DPLLSTATUS.reg & (SYSCTRL_DPLLSTATUS_CLKRDY | SYSCTRL_DPLLSTATUS_LOCK)) ==
(SYSCTRL_DPLLSTATUS_CLKRDY | SYSCTRL_DPLLSTATUS_LOCK));
//For generic clock generator 0, select the DPLL Clock as input, divide by 2
GCLK->GENDIV.reg = ((2 << GCLK_GENDIV_DIV_Pos) | (0 << GCLK_GENDIV_ID_Pos));
GCLK->GENCTRL.reg = ((0 << GCLK_GENCTRL_ID_Pos) | (GCLK_SOURCE_FDPLL << GCLK_GENCTRL_SRC_Pos)| (GCLK_GENCTRL_GENEN));
GCLK->CLKCTRL.reg = ((GCLK_CLKCTRL_GEN_GCLK0) | (GCLK_CLKCTRL_CLKEN)) ;
#endif /* USEPLL */
}
/*
* Map Arduino pin numbers to hardware specific values.
* In the case of the D10, all the pins bits in a single GPIO port
* so it's efficient to map to bit numbers (which an ARM can easily
* convert to a bitmask, since it has a barrel shifter.)
* -1 means the digital pin doesn't exist.
*/
const int8_t digital_pin_to_pinno[] = {
11, 10, 16, 17,
27, 25, 30, 31,
-1, -1, 23, 22,
24, 9, 02, 03,
04, 05, 06, 07,
15, 14
};
/*
* digitalWrite(pinno, level)
* This is logically similar to the ASF function port_pin_set_output_level(),
* but uses a different mechanism for mapping the "pinno" to the port/bit.
*/
void digitalWrite(uint8_t pinno, uint8_t level)
{
PortGroup *const port_base = &(PORT->Group[0]); /* port_get_group_from_gpio_pin(1); */ // any pin will work.
uint32_t pin_mask = (1UL << (digital_pin_to_pinno[pinno]));
if (level) {
port_base->OUTSET.reg = pin_mask;
} else {
port_base->OUTCLR.reg = pin_mask;
}
}
void pinMode(uint8_t pinno, uint8_t direction)
{
PortGroup *const port_base = &(PORT->Group[0]); /* port_get_group_from_gpio_pin(1); */ // any pin will work.
uint32_t pin_mask = (1UL << (digital_pin_to_pinno[pinno]));
if (direction == OUTPUT) {
port_base->DIRSET.reg = pin_mask;
} else {
port_base->DIRCLR.reg = pin_mask;
}
}