From aa0f7a7026eed06c12b2f9b13ba864163db9096a Mon Sep 17 00:00:00 2001 From: Sissors Date: Sat, 11 Jan 2014 19:10:36 +0100 Subject: [PATCH] Added clk_freqs.h for clock related functions Serial now also works when PLL/FLL is disabled and extosc is available. us_ticker tries to divide extosc to 1MHz, otherwise use fast internal oscillator --- .../TARGET_KL25Z/analogin_api.c | 3 +- .../TARGET_Freescale/TARGET_KL25Z/clk_freqs.h | 91 +++++++++++++++++++ .../TARGET_Freescale/TARGET_KL25Z/i2c_api.c | 3 +- .../TARGET_KL25Z/serial_api.c | 9 +- .../TARGET_Freescale/TARGET_KL25Z/spi_api.c | 3 +- .../TARGET_Freescale/TARGET_KL25Z/us_ticker.c | 36 +++++++- 6 files changed, 135 insertions(+), 10 deletions(-) create mode 100644 libraries/mbed/targets/hal/TARGET_Freescale/TARGET_KL25Z/clk_freqs.h diff --git a/libraries/mbed/targets/hal/TARGET_Freescale/TARGET_KL25Z/analogin_api.c b/libraries/mbed/targets/hal/TARGET_Freescale/TARGET_KL25Z/analogin_api.c index 973900fae4..f45fb05ad9 100644 --- a/libraries/mbed/targets/hal/TARGET_Freescale/TARGET_KL25Z/analogin_api.c +++ b/libraries/mbed/targets/hal/TARGET_Freescale/TARGET_KL25Z/analogin_api.c @@ -18,6 +18,7 @@ #include "cmsis.h" #include "pinmap.h" #include "error.h" +#include "clk_freqs.h" #define MAX_FADC 6000000 @@ -58,7 +59,7 @@ void analogin_init(analogin_t *obj, PinName pin) { } // bus clk - uint32_t PCLK = SystemCoreClock / (((SIM->CLKDIV1 & SIM_CLKDIV1_OUTDIV4_MASK) >> SIM_CLKDIV1_OUTDIV4_SHIFT) + 1); + uint32_t PCLK = bus_frequency(); uint32_t clkdiv; for (clkdiv = 0; clkdiv < 4; clkdiv++) { if ((PCLK >> clkdiv) <= MAX_FADC) diff --git a/libraries/mbed/targets/hal/TARGET_Freescale/TARGET_KL25Z/clk_freqs.h b/libraries/mbed/targets/hal/TARGET_Freescale/TARGET_KL25Z/clk_freqs.h new file mode 100644 index 0000000000..4273872b70 --- /dev/null +++ b/libraries/mbed/targets/hal/TARGET_Freescale/TARGET_KL25Z/clk_freqs.h @@ -0,0 +1,91 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2013 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBED_CLK_FREQS_H +#define MBED_CLK_FREQS_H + +#ifdef __cplusplus +extern "C" { +#endif + +//Get the peripheral bus clock frequency +static inline uint32_t bus_frequency(void) { + return SystemCoreClock / (((SIM->CLKDIV1 & SIM_CLKDIV1_OUTDIV4_MASK) >> SIM_CLKDIV1_OUTDIV4_SHIFT) + 1); +} + +//Get external oscillator (crystal) frequency +static uint32_t extosc_frequency(void) { + uint32_t MCGClock = SystemCoreClock * (1u + ((SIM->CLKDIV1 & SIM_CLKDIV1_OUTDIV1_MASK) >> SIM_CLKDIV1_OUTDIV1_SHIFT)); + + if ((MCG->C1 & MCG_C1_CLKS_MASK) == MCG_C1_CLKS(2)) //MCG clock = external reference clock + return MCGClock; + + if ((MCG->C1 & MCG_C1_CLKS_MASK) == MCG_C1_CLKS(0)) { //PLL/FLL is selected + uint32_t divider, multiplier; + if ((MCG->C6 & MCG_C6_PLLS_MASK) == 0x0u) { //FLL is selected + if ((MCG->S & MCG_S_IREFST_MASK) == 0x0u) { //FLL uses external reference + divider = (uint8_t)(1u << ((MCG->C1 & MCG_C1_FRDIV_MASK) >> MCG_C1_FRDIV_SHIFT)); + if ((MCG->C2 & MCG_C2_RANGE0_MASK) != 0x0u) + divider <<= 5u; + /* Select correct multiplier to calculate the MCG output clock */ + switch (MCG->C4 & (MCG_C4_DMX32_MASK | MCG_C4_DRST_DRS_MASK)) { + case 0x0u: + multiplier = 640u; + break; + case 0x20u: + multiplier = 1280u; + break; + case 0x40u: + multiplier = 1920u; + break; + case 0x60u: + multiplier = 2560u; + break; + case 0x80u: + multiplier = 732u; + break; + case 0xA0u: + multiplier = 1464u; + break; + case 0xC0u: + multiplier = 2197u; + break; + case 0xE0u: + default: + multiplier = 2929u; + break; + } + + return MCGClock * divider / multiplier; + } + } else { //PLL is selected + divider = (1u + (MCG->C5 & MCG_C5_PRDIV0_MASK)); + multiplier = ((MCG->C6 & MCG_C6_VDIV0_MASK) + 24u); + return MCGClock * divider / multiplier; + } + } + + //In all other cases either there is no crystal or we cannot determine it + //For example when the FLL is running on the internal reference, and there is also an + //external crystal. However these are unlikely situations + return 0; +} + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libraries/mbed/targets/hal/TARGET_Freescale/TARGET_KL25Z/i2c_api.c b/libraries/mbed/targets/hal/TARGET_Freescale/TARGET_KL25Z/i2c_api.c index 01a6c31437..110f7942fc 100644 --- a/libraries/mbed/targets/hal/TARGET_Freescale/TARGET_KL25Z/i2c_api.c +++ b/libraries/mbed/targets/hal/TARGET_Freescale/TARGET_KL25Z/i2c_api.c @@ -18,6 +18,7 @@ #include "cmsis.h" #include "pinmap.h" #include "error.h" +#include "clk_freqs.h" static const PinMap PinMap_I2C_SDA[] = { {PTE25, I2C_0, 5}, @@ -206,7 +207,7 @@ void i2c_frequency(i2c_t *obj, int hz) { uint32_t ref = 0; uint8_t i, j; // bus clk - uint32_t PCLK = SystemCoreClock / (((SIM->CLKDIV1 & SIM_CLKDIV1_OUTDIV4_MASK) >> SIM_CLKDIV1_OUTDIV4_SHIFT) + 1); + uint32_t PCLK = bus_frequency(); uint32_t pulse = PCLK / (hz * 2); // we look for the values that minimize the error diff --git a/libraries/mbed/targets/hal/TARGET_Freescale/TARGET_KL25Z/serial_api.c b/libraries/mbed/targets/hal/TARGET_Freescale/TARGET_KL25Z/serial_api.c index 5b8d4f4ad8..fe986519bc 100644 --- a/libraries/mbed/targets/hal/TARGET_Freescale/TARGET_KL25Z/serial_api.c +++ b/libraries/mbed/targets/hal/TARGET_Freescale/TARGET_KL25Z/serial_api.c @@ -23,6 +23,7 @@ #include "cmsis.h" #include "pinmap.h" #include "error.h" +#include "clk_freqs.h" /****************************************************************************** * INITIALIZATION @@ -70,7 +71,10 @@ void serial_init(serial_t *obj, PinName tx, PinName rx) { obj->uart = (UARTLP_Type *)uart; // enable clk switch (uart) { - case UART_0: SIM->SOPT2 |= SIM_SOPT2_PLLFLLSEL_MASK | (1<C1 & MCG_C1_CLKS_MASK) == MCG_C1_CLKS(0)) //PLL/FLL is selected + SIM->SOPT2 |= SIM_SOPT2_PLLFLLSEL_MASK | (1<SOPT2 |= (2<SCGC5 |= SIM_SCGC5_PORTA_MASK; SIM->SCGC4 |= SIM_SCGC4_UART0_MASK; break; case UART_1: SIM->SCGC5 |= SIM_SCGC5_PORTC_MASK; SIM->SCGC4 |= SIM_SCGC4_UART1_MASK; break; case UART_2: SIM->SCGC5 |= SIM_SCGC5_PORTD_MASK; SIM->SCGC4 |= SIM_SCGC4_UART2_MASK; break; @@ -119,8 +123,7 @@ void serial_baud(serial_t *obj, int baudrate) { // Disable UART before changing registers obj->uart->C2 &= ~(UART_C2_RE_MASK | UART_C2_TE_MASK); - // [TODO] not hardcode this value - uint32_t PCLK = (obj->uart == UART0) ? SystemCoreClock : SystemCoreClock / (((SIM->CLKDIV1 & SIM_CLKDIV1_OUTDIV4_MASK) >> SIM_CLKDIV1_OUTDIV4_SHIFT) + 1); + uint32_t PCLK = (obj->uart == UART0) ? SystemCoreClock : bus_frequency(); // First we check to see if the basic divide with no DivAddVal/MulVal // ratio gives us an integer result. If it does, we set DivAddVal = 0, diff --git a/libraries/mbed/targets/hal/TARGET_Freescale/TARGET_KL25Z/spi_api.c b/libraries/mbed/targets/hal/TARGET_Freescale/TARGET_KL25Z/spi_api.c index 429d38f968..f039a0468c 100644 --- a/libraries/mbed/targets/hal/TARGET_Freescale/TARGET_KL25Z/spi_api.c +++ b/libraries/mbed/targets/hal/TARGET_Freescale/TARGET_KL25Z/spi_api.c @@ -20,6 +20,7 @@ #include "cmsis.h" #include "pinmap.h" #include "error.h" +#include "clk_freqs.h" static const PinMap PinMap_SPI_SCLK[] = { {PTA15, SPI_0, 2}, @@ -145,7 +146,7 @@ void spi_frequency(spi_t *obj, int hz) { uint8_t ref_prescaler = 0; // bus clk - uint32_t PCLK = SystemCoreClock / (((SIM->CLKDIV1 & SIM_CLKDIV1_OUTDIV4_MASK) >> SIM_CLKDIV1_OUTDIV4_SHIFT) + 1); + uint32_t PCLK = bus_frequency(); uint8_t prescaler = 1; uint8_t divisor = 2; diff --git a/libraries/mbed/targets/hal/TARGET_Freescale/TARGET_KL25Z/us_ticker.c b/libraries/mbed/targets/hal/TARGET_Freescale/TARGET_KL25Z/us_ticker.c index 6b2e0798df..2b33130b2f 100644 --- a/libraries/mbed/targets/hal/TARGET_Freescale/TARGET_KL25Z/us_ticker.c +++ b/libraries/mbed/targets/hal/TARGET_Freescale/TARGET_KL25Z/us_ticker.c @@ -16,6 +16,7 @@ #include #include "us_ticker_api.h" #include "PeripheralNames.h" +#include "clk_freqs.h" static void pit_init(void); static void lptmr_init(void); @@ -43,8 +44,7 @@ static void pit_init(void) { PIT->CHANNEL[1].TCTRL |= PIT_TCTRL_TEN_MASK; // Start timer 1 // Use channel 0 as a prescaler for channel 1 - uint32_t PCLK = SystemCoreClock / (((SIM->CLKDIV1 & SIM_CLKDIV1_OUTDIV4_MASK) >> SIM_CLKDIV1_OUTDIV4_SHIFT) + 1); - PIT->CHANNEL[0].LDVAL = PCLK / 1000000 - 1; + PIT->CHANNEL[0].LDVAL = bus_frequency() / 1000000 - 1; PIT->CHANNEL[0].TCTRL = PIT_TCTRL_TEN_MASK; // Start timer 0, disable interrupts } @@ -77,8 +77,36 @@ static void lptmr_init(void) { NVIC_EnableIRQ(LPTimer_IRQn); /* Clock at (1)MHz -> (1)tick/us */ - LPTMR0->PSR = LPTMR_PSR_PCS(3); // OSCERCLK -> 8MHz - LPTMR0->PSR |= LPTMR_PSR_PRESCALE(2); // divide by 8 + /* Check if the external oscillator can be divided to 1MHz */ + uint32_t extosc = extosc_frequency(); + + if (extosc != 0) { //If external oscillator found + if (extosc % 1000000u == 0) { //If it is a multiple if 1MHz + extosc /= 1000000; + if (extosc == 1) { //1MHz, set timerprescaler in bypass mode + LPTMR0->PSR = LPTMR_PSR_PCS(3) | LPTMR_PSR_PBYP_MASK; + return; + } else { //See if we can divide it to 1MHz + uint32_t divider = 0; + extosc >>= 1; + while (1) { + if (extosc == 1) { + LPTMR0->PSR = LPTMR_PSR_PCS(3) | LPTMR_PSR_PRESCALE(divider); + return; + } + if (extosc % 2 != 0) //If we can't divide by two anymore + break; + divider++; + extosc >>= 1; + } + } + } + } + //No suitable external oscillator clock -> Use fast internal oscillator (4MHz) + MCG->C1 |= MCG_C1_IRCLKEN_MASK; + MCG->C2 |= MCG_C2_IRCS_MASK; + LPTMR0->PSR = LPTMR_PSR_PCS(0) | LPTMR_PSR_PRESCALE(1); + } void us_ticker_disable_interrupt(void) {