From 164721657dd74d355427418d85d47abfdd8b9637 Mon Sep 17 00:00:00 2001 From: Hauke Petersen Date: Tue, 19 May 2015 18:12:40 +0200 Subject: [PATCH] cpu/samd21: added cpu clock configuration - choosable between PLL and internal 8MHz osciallator - configurable to a wide range of frequencies --- cpu/samd21/cpu.c | 99 ++++++++++++------- cpu/samd21/include/component/component_gclk.h | 2 + cpu/samd21/periph/i2c.c | 2 +- cpu/samd21/periph/spi.c | 14 ++- cpu/samd21/periph/timer.c | 34 ++++++- cpu/samd21/periph/uart.c | 50 ++-------- 6 files changed, 118 insertions(+), 83 deletions(-) diff --git a/cpu/samd21/cpu.c b/cpu/samd21/cpu.c index 75f44bcafa..432043d5eb 100644 --- a/cpu/samd21/cpu.c +++ b/cpu/samd21/cpu.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014 Freie Universität Berlin + * Copyright (C) 2015 Freie Universität Berlin * * This file is subject to the terms and conditions of the GNU Lesser * General Public License v2.1. See the file LICENSE in the top level @@ -14,22 +14,80 @@ * @brief Implementation of the CPU initialization * * @author Thomas Eichinger + * @author Hauke Petersen * @} */ #include "cpu.h" - -void clk_init(void); +#include "periph_conf.h" /** - * @brief Initialize the CPU, set IRQ priorities + * @brief Configure clock sources and the cpu frequency */ +static void clk_init(void) +{ + /* enable clocks for the power, sysctrl and gclk modules */ + PM->APBAMASK.reg = (PM_APBAMASK_PM | PM_APBAMASK_SYSCTRL | + PM_APBAMASK_GCLK); + + /* adjust NVM wait states, see table 42.30 (p. 1070) in the datasheet */ +#if (CLOCK_CORECLOCK > 24000000) + PM->APBAMASK.reg |= PM_AHBMASK_NVMCTRL; + NVMCTRL->CTRLB.reg |= NVMCTRL_CTRLB_RWS(1); + PM->APBAMASK.reg &= ~PM_AHBMASK_NVMCTRL; +#endif + + /* configure internal 8MHz oscillator to run without prescaler */ + SYSCTRL->OSC8M.bit.PRESC = 0; + SYSCTRL->OSC8M.bit.ONDEMAND = 0; + SYSCTRL->OSC8M.bit.RUNSTDBY = 0; + SYSCTRL->OSC8M.bit.ENABLE = 1; + while (!(SYSCTRL->PCLKSR.reg & SYSCTRL_PCLKSR_OSC8MRDY)); + +#if CLOCK_USE_PLL + /* reset the GCLK module so it is in a known state */ + GCLK->CTRL.reg = GCLK_CTRL_SWRST; + while (GCLK->STATUS.reg & GCLK_STATUS_SYNCBUSY); + + /* setup generic clock 1 to feed DPLL with 1MHz */ + GCLK->GENDIV.reg = (GCLK_GENDIV_DIV(8) | + GCLK_GENDIV_ID(1)); + GCLK->GENCTRL.reg = (GCLK_GENCTRL_GENEN | + GCLK_GENCTRL_SRC_OSC8M | + GCLK_GENCTRL_ID(1)); + GCLK->CLKCTRL.reg = (GCLK_CLKCTRL_GEN(1) | + GCLK_CLKCTRL_ID(1) | + GCLK_CLKCTRL_CLKEN); + while (GCLK->STATUS.reg & GCLK_STATUS_SYNCBUSY); + + /* enable PLL */ + SYSCTRL->DPLLRATIO.reg = (SYSCTRL_DPLLRATIO_LDR(CLOCK_PLL_MUL)); + SYSCTRL->DPLLCTRLB.reg = (SYSCTRL_DPLLCTRLB_REFCLK_GCLK); + SYSCTRL->DPLLCTRLA.reg = (SYSCTRL_DPLLCTRLA_ENABLE); + while(!(SYSCTRL->DPLLSTATUS.reg & + (SYSCTRL_DPLLSTATUS_CLKRDY | SYSCTRL_DPLLSTATUS_LOCK)) == + (SYSCTRL_DPLLSTATUS_CLKRDY | SYSCTRL_DPLLSTATUS_LOCK)); + + /* select the PLL as source for clock generator 0 (CPU core clock) */ + GCLK->GENDIV.reg = (GCLK_GENDIV_DIV(CLOCK_PLL_DIV) | + GCLK_GENDIV_ID(0)); + GCLK->GENCTRL.reg = (GCLK_GENCTRL_GENEN | + GCLK_GENCTRL_SRC_FDPLL96M | + GCLK_GENCTRL_ID(0)); +#else /* do not use PLL, use internal 8MHz oscillator directly */ + GCLK->GENDIV.reg = (GCLK_GENDIV_DIV(CLOCK_DIV) | + GCLK_GENDIV_ID(0)); + GCLK->GENCTRL.reg = (GCLK_GENCTRL_GENEN | + GCLK_GENCTRL_SRC_OSC8M | + GCLK_GENCTRL_ID(0)); +#endif + + /* make sure we synchronize clock generator 0 before we go on */ + while (GCLK->STATUS.reg & GCLK_STATUS_SYNCBUSY); +} + void cpu_init(void) { - /* pm clk enable */ - PM->APBAMASK.reg = PM_APBAMASK_PM; - /* port clk enable */ - PM->APBBMASK.reg |= PM_APBBMASK_PORT; /* disable the watchdog timer */ WDT->CTRL.bit.ENABLE = 0; /* initialize the Cortex-M core */ @@ -37,28 +95,3 @@ void cpu_init(void) /* Initialise clock sources and generic clocks */ clk_init(); } - -/** - * @brief Initialise clock sources and generic clocks - */ -void clk_init(void) -{ - PM->APBAMASK.reg |= PM_APBAMASK_SYSCTRL; - PM->APBAMASK.reg |= PM_APBAMASK_GCLK; - - SYSCTRL->OSC8M.bit.PRESC = 0; - SYSCTRL->OSC8M.bit.ONDEMAND = 1; - SYSCTRL->OSC8M.bit.RUNSTDBY = 0; - SYSCTRL->OSC8M.bit.ENABLE = 1; - - /* Software reset the module to ensure it is re-initialized correctly */ - GCLK->CTRL.reg = GCLK_CTRL_SWRST; - while (GCLK->CTRL.reg & GCLK_CTRL_SWRST); - - while (GCLK->STATUS.reg & GCLK_STATUS_SYNCBUSY); - /* Select the correct generator */ - *((uint8_t*)&GCLK->GENDIV.reg) = 0; - while (GCLK->STATUS.reg & GCLK_STATUS_SYNCBUSY); - GCLK->GENCTRL.reg = (GCLK_GENCTRL_GENEN | GCLK_GENCTRL_SRC_OSC8M | GCLK_CLKCTRL_GEN_GCLK0); - while (GCLK->STATUS.reg & GCLK_STATUS_SYNCBUSY); -} diff --git a/cpu/samd21/include/component/component_gclk.h b/cpu/samd21/include/component/component_gclk.h index 25279f3366..65b18b7720 100644 --- a/cpu/samd21/include/component/component_gclk.h +++ b/cpu/samd21/include/component/component_gclk.h @@ -176,6 +176,7 @@ typedef union { #define GCLK_GENCTRL_SRC_XOSC32K_Val 0x5u /**< \brief (GCLK_GENCTRL) XOSC32K oscillator output */ #define GCLK_GENCTRL_SRC_OSC8M_Val 0x6u /**< \brief (GCLK_GENCTRL) OSC8M oscillator output */ #define GCLK_GENCTRL_SRC_DFLL48M_Val 0x7u /**< \brief (GCLK_GENCTRL) DFLL48M output */ +#define GCLK_GENCTRL_SRC_FDPLL96M_Val 0x8u /**< \brief (GCLK_GENCTRL) FDPLL96M output */ #define GCLK_GENCTRL_SRC_XOSC (GCLK_GENCTRL_SRC_XOSC_Val << GCLK_GENCTRL_SRC_Pos) #define GCLK_GENCTRL_SRC_GCLKIN (GCLK_GENCTRL_SRC_GCLKIN_Val << GCLK_GENCTRL_SRC_Pos) #define GCLK_GENCTRL_SRC_GCLKGEN1 (GCLK_GENCTRL_SRC_GCLKGEN1_Val << GCLK_GENCTRL_SRC_Pos) @@ -184,6 +185,7 @@ typedef union { #define GCLK_GENCTRL_SRC_XOSC32K (GCLK_GENCTRL_SRC_XOSC32K_Val << GCLK_GENCTRL_SRC_Pos) #define GCLK_GENCTRL_SRC_OSC8M (GCLK_GENCTRL_SRC_OSC8M_Val << GCLK_GENCTRL_SRC_Pos) #define GCLK_GENCTRL_SRC_DFLL48M (GCLK_GENCTRL_SRC_DFLL48M_Val << GCLK_GENCTRL_SRC_Pos) +#define GCLK_GENCTRL_SRC_FDPLL96M (GCLK_GENCTRL_SRC_FDPLL96M_Val << GCLK_GENCTRL_SRC_Pos) #define GCLK_GENCTRL_GENEN_Pos 16 /**< \brief (GCLK_GENCTRL) Generic Clock Generator Enable */ #define GCLK_GENCTRL_GENEN (0x1u << GCLK_GENCTRL_GENEN_Pos) #define GCLK_GENCTRL_IDC_Pos 17 /**< \brief (GCLK_GENCTRL) Improve Duty Cycle */ diff --git a/cpu/samd21/periph/i2c.c b/cpu/samd21/periph/i2c.c index 04a2cb0b63..93718630bb 100644 --- a/cpu/samd21/periph/i2c.c +++ b/cpu/samd21/periph/i2c.c @@ -82,7 +82,7 @@ int i2c_init_master(i2c_t dev, i2c_speed_t speed) pin_scl = I2C_SDA; pin_sda = I2C_SCL; i2c_pins = I2C_0_PINS; - clock_source_speed = I2C_0_REF_F; + clock_source_speed = CLOCK_CORECLOCK; sercom_core = SERCOM3_GCLK_ID_CORE; sercom_gclk_id_slow = SERCOM3_GCLK_ID_SLOW ; break; diff --git a/cpu/samd21/periph/spi.c b/cpu/samd21/periph/spi.c index e5c65d156e..7b41029713 100644 --- a/cpu/samd21/periph/spi.c +++ b/cpu/samd21/periph/spi.c @@ -66,9 +66,19 @@ int spi_init_master(spi_t dev, spi_conf_t conf, spi_speed_t speed) f_baud = 1000000; break; case SPI_SPEED_5MHZ: +#if CLOCK_CORECLOCK >= 5000000 + f_baud = 5000000; + break; +#else return -1; +#endif case SPI_SPEED_10MHZ: +#if CLOCK_CORECLOCK >= 10000000 + f_baud = 10000000; + break; +#else return -1; +#endif } switch(conf) { @@ -172,7 +182,9 @@ int spi_init_master(spi_t dev, spi_conf_t conf, spi_speed_t speed) spi_dev->CTRLA.reg |= SERCOM_SPI_CTRLA_MODE_SPI_MASTER; while (spi_dev->SYNCBUSY.reg); - spi_dev->BAUD.bit.BAUD = (uint8_t) (((uint32_t) SPI_0_F_REF) / (2 * f_baud) - 1); /* Syncronous mode*/ + spi_dev->BAUD.bit.BAUD = (uint8_t) (((uint32_t)CLOCK_CORECLOCK) / (2 * f_baud) - 1); /* Syncronous mode*/ + + spi_dev->CTRLA.reg |= (SERCOM_SPI_CTRLA_DOPO(dopo)) | (SERCOM_SPI_CTRLA_DIPO(dipo)) | (cpha << SERCOM_SPI_CTRLA_CPHA_Pos) diff --git a/cpu/samd21/periph/timer.c b/cpu/samd21/periph/timer.c index 7a8dc1b565..862bfe4549 100644 --- a/cpu/samd21/periph/timer.c +++ b/cpu/samd21/periph/timer.c @@ -48,13 +48,27 @@ timer_conf_t config[TIMER_NUMOF]; */ int timer_init(tim_t dev, unsigned int us_per_ticks, void (*callback)(int)) { - /* configure GCLK0 to feed TC3, TC4 and TC5 */; - GCLK->CLKCTRL.reg = (uint16_t)((GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK0 | (TC3_GCLK_ID << GCLK_CLKCTRL_ID_Pos))); + /* at the moment, the timer can only run at 1MHz */ + if (us_per_ticks != 1) { + return -1; + } + +/* select the clock generator depending on the main clock source: + * GCLK0 (1MHz) if we use the internal 8MHz oscillator + * GCLK1 (8MHz) if we use the PLL */ +#if CLOCK_USE_PLL + /* configure GCLK1 (configured to 1MHz) to feed TC3, TC4 and TC5 */; + GCLK->CLKCTRL.reg = (uint16_t)((GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK1 | (TC3_GCLK_ID << GCLK_CLKCTRL_ID_Pos))); while (GCLK->STATUS.bit.SYNCBUSY); /* TC4 and TC5 share the same channel */ + GCLK->CLKCTRL.reg = (uint16_t)((GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK1 | (TC4_GCLK_ID << GCLK_CLKCTRL_ID_Pos))); +#else + /* configure GCLK0 to feed TC3, TC4 and TC5 */; + GCLK->CLKCTRL.reg = (uint16_t)((GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK0 | (TC3_GCLK_ID << GCLK_CLKCTRL_ID_Pos))); + /* TC4 and TC5 share the same channel */ GCLK->CLKCTRL.reg = (uint16_t)((GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK0 | (TC4_GCLK_ID << GCLK_CLKCTRL_ID_Pos))); +#endif while (GCLK->STATUS.bit.SYNCBUSY); - /* select the timer and enable the timer specific peripheral clocks */ switch (dev) { #if TIMER_0_EN @@ -68,8 +82,13 @@ int timer_init(tim_t dev, unsigned int us_per_ticks, void (*callback)(int)) while (TIMER_0_DEV.CTRLA.bit.SWRST); /* choosing 16 bit mode */ TIMER_0_DEV.CTRLA.bit.MODE = TC_CTRLA_MODE_COUNT16_Val; - /* sourced by 8MHz with Presc 64 results in 125kHz clk */ - TIMER_0_DEV.CTRLA.bit.PRESCALER = TC_CTRLA_PRESCALER_DIV64_Val; +#if CLOCK_USE_PLL + /* sourced by 1MHz with prescaler 1 results in... you know it :-) */ + TIMER_0_DEV.CTRLA.bit.PRESCALER = TC_CTRLA_PRESCALER_DIV1_Val; +#else + /* sourced by 8MHz with Presc 8 results in 1MHz clk */ + TIMER_0_DEV.CTRLA.bit.PRESCALER = TC_CTRLA_PRESCALER_DIV8_Val; +#endif /* choose normal frequency operation */ TIMER_0_DEV.CTRLA.bit.WAVEGEN = TC_CTRLA_WAVEGEN_NFRQ_Val; break; @@ -87,8 +106,13 @@ int timer_init(tim_t dev, unsigned int us_per_ticks, void (*callback)(int)) TIMER_1_DEV.CTRLA.bit.MODE = TC_CTRLA_MODE_COUNT32_Val; +#if CLOCK_USE_PLL + /* sourced by 1MHz and prescaler 1 to reach 1MHz */ + TIMER_1_DEV.CTRLA.bit.PRESCALER = TC_CTRLA_PRESCALER_DIV1_Val; +#else /* sourced by 8MHz with Presc 8 results in 1Mhz clk */ TIMER_1_DEV.CTRLA.bit.PRESCALER = TC_CTRLA_PRESCALER_DIV8_Val; +#endif /* choose normal frequency operation */ TIMER_1_DEV.CTRLA.bit.WAVEGEN = TC_CTRLA_WAVEGEN_NFRQ_Val; break; diff --git a/cpu/samd21/periph/uart.c b/cpu/samd21/periph/uart.c index 67e333a128..dfa8cb8578 100644 --- a/cpu/samd21/periph/uart.c +++ b/cpu/samd21/periph/uart.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014 Freie Universität Berlin + * Copyright (C) 2015 Freie Universität Berlin * * This file is subject to the terms and conditions of the GNU Lesser * General Public License v2.1. See the file LICENSE in the top level @@ -15,6 +15,7 @@ * * @author Thomas Eichinger * @author Troels Hoffmeyer + * @author Hauke Petersen * * @} */ @@ -53,8 +54,6 @@ static inline void irq_handler(uart_t uartnum, SercomUsart *uart); */ static uart_conf_t uart_config[UART_NUMOF]; -static uint64_t _long_division(uint64_t n, uint64_t d); - int uart_init(uart_t uart, uint32_t baudrate, uart_rx_cb_t rx_cb, uart_tx_cb_t tx_cb, void *arg) { /* initialize basic functionality */ @@ -81,11 +80,7 @@ int uart_init(uart_t uart, uint32_t baudrate, uart_rx_cb_t rx_cb, uart_tx_cb_t t int uart_init_blocking(uart_t uart, uint32_t baudrate) { - /* Calculate the BAUD value */ - uint64_t temp1 = ((16 * ((uint64_t)baudrate)) << 32); - uint64_t ratio = _long_division(temp1 , UART_0_REF_F); - uint64_t scale = ((uint64_t)1 << 32) - ratio; - uint64_t baud_calculated = (65536 * scale) >> 32; + uint32_t baud = ((((uint32_t)CLOCK_CORECLOCK * 10) / baudrate) / 16); switch (uart) { #if UART_0_EN @@ -97,9 +92,6 @@ int uart_init_blocking(uart_t uart, uint32_t baudrate) GCLK->CLKCTRL.reg = (uint16_t)((GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK0 | (SERCOM0_GCLK_ID_CORE << GCLK_CLKCTRL_ID_Pos))); while (GCLK->STATUS.bit.SYNCBUSY); - GCLK->CLKCTRL.reg = (uint16_t)((GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK0 | (SERCOM0_GCLK_ID_SLOW << GCLK_CLKCTRL_ID_Pos))); - while (GCLK->STATUS.bit.SYNCBUSY); - /* configure PINS to input/output*/ UART_0_PORT.DIRSET.reg = (1 << UART_0_TX_PIN); /* tx's direction is output */ UART_0_PORT.PINCFG[UART_0_RX_PIN % 32].bit.INEN = true; /* buffer rx pin's value */ @@ -117,20 +109,17 @@ int uart_init_blocking(uart_t uart, uint32_t baudrate) /* set to LSB, asynchronous mode without parity, PAD0 Tx, PAD1 Rx, * 16x over-sampling, internal clk */ UART_0_DEV.CTRLA.reg = SERCOM_USART_CTRLA_DORD \ - | SERCOM_USART_CTRLA_FORM(0x0) \ - | SERCOM_USART_CTRLA_SAMPA(0x0) \ - | SERCOM_USART_CTRLA_TXPO(0x0) \ | SERCOM_USART_CTRLA_RXPO(0x1) \ - | SERCOM_USART_CTRLA_SAMPR(0x0) \ + | SERCOM_USART_CTRLA_SAMPR(0x1) \ | SERCOM_USART_CTRLA_MODE_USART_INT_CLK; - /* Set baud rate */ - UART_0_DEV.BAUD.bit.BAUD = baud_calculated; + + UART_0_DEV.BAUD.FRAC.FP = (baud % 10); + UART_0_DEV.BAUD.FRAC.BAUD = (baud / 10); /* enable receiver and transmitter, one stop bit*/ UART_0_DEV.CTRLB.reg = (SERCOM_USART_CTRLB_RXEN | SERCOM_USART_CTRLB_TXEN); while(UART_0_DEV.SYNCBUSY.bit.CTRLB); - break; #endif } @@ -226,29 +215,4 @@ static inline void irq_handler(uint8_t uartnum, SercomUsart *dev) } } - - - -static uint64_t _long_division(uint64_t n, uint64_t d) -{ - int32_t i; - uint64_t q = 0, r = 0, bit_shift; - for (i = 63; i >= 0; i--) { - bit_shift = (uint64_t)1 << i; - - r = r << 1; - - if (n & bit_shift) { - r |= 0x01; - } - - if (r >= d) { - r = r - d; - q |= bit_shift; - } - } - - return q; -} - #endif /* UART_NUMOF */