Skip to content

K20 general and timer adjustments #264

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Apr 22, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ extern "C" {
* \return Bus frequency
*/
static inline uint32_t bus_frequency(void) {
return SystemCoreClock / (((SIM->CLKDIV1 & SIM_CLKDIV1_OUTDIV4_MASK) >> SIM_CLKDIV1_OUTDIV4_SHIFT) + 1);
return SystemCoreClock / (((SIM->CLKDIV1 & SIM_CLKDIV1_OUTDIV2_MASK) >> SIM_CLKDIV1_OUTDIV2_SHIFT) + 1);
}

/*!
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
#define DEVICE_LOCALFILESYSTEM 0
#define DEVICE_ID_LENGTH 24

#define DEVICE_SLEEP 0
#define DEVICE_SLEEP 1

#define DEVICE_DEBUG_AWARENESS 0

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -165,3 +165,43 @@ void gpio_irq_set(gpio_irq_t *obj, gpio_irq_event event, uint32_t enable) {
// Interrupt configuration and clear interrupt
port->PCR[obj->pin] = (port->PCR[obj->pin] & ~PORT_PCR_IRQC_MASK) | irq_settings | PORT_PCR_ISF_MASK;
}

void gpio_irq_enable(gpio_irq_t *obj) {
switch (obj->port) {
case PortA:
NVIC_EnableIRQ(PORTA_IRQn);
break;
case PortB:
NVIC_EnableIRQ(PORTB_IRQn);
break;
case PortC:
NVIC_EnableIRQ(PORTC_IRQn);
break;
case PortD:
NVIC_EnableIRQ(PORTD_IRQn);
break;
case PortE:
NVIC_EnableIRQ(PORTE_IRQn);
break;
}
}

void gpio_irq_disable(gpio_irq_t *obj) {
switch (obj->port) {
case PortA:
NVIC_DisableIRQ(PORTA_IRQn);
break;
case PortB:
NVIC_DisableIRQ(PORTB_IRQn);
break;
case PortC:
NVIC_DisableIRQ(PORTC_IRQn);
break;
case PortD:
NVIC_DisableIRQ(PORTD_IRQn);
break;
case PortE:
NVIC_DisableIRQ(PORTE_IRQn);
break;
}
}
51 changes: 51 additions & 0 deletions libraries/mbed/targets/hal/TARGET_Freescale/TARGET_K20D5M/sleep.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/* 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.
*/
#include "sleep_api.h"
#include "cmsis.h"

//Normal wait mode
void sleep(void)
{
SMC->PMPROT = SMC_PMPROT_AVLLS_MASK | SMC_PMPROT_ALLS_MASK | SMC_PMPROT_AVLP_MASK;

//Normal sleep mode for ARM core:
SCB->SCR = 0;
__WFI();
}

//Very low-power stop mode
void deepsleep(void)
{
//Check if PLL/FLL is enabled:
uint32_t PLL_FLL_en = (MCG->C1 & MCG_C1_CLKS_MASK) == MCG_C1_CLKS(0);

SMC->PMPROT = SMC_PMPROT_AVLLS_MASK | SMC_PMPROT_ALLS_MASK | SMC_PMPROT_AVLP_MASK;
SMC->PMCTRL = SMC_PMCTRL_STOPM(2);

//Deep sleep for ARM core:
SCB->SCR = 1<<SCB_SCR_SLEEPDEEP_Pos;

__WFI();

//Switch back to PLL as clock source if needed
//The interrupt that woke up the device will run at reduced speed
if (PLL_FLL_en) {
if (MCG->C6 & (1<<MCG_C6_PLLS_SHIFT) != 0) /* If PLL */
while((MCG->S & MCG_S_LOCK0_MASK) == 0x00U); /* Wait until locked */
MCG->C1 &= ~MCG_C1_CLKS_MASK;
}

}
178 changes: 76 additions & 102 deletions libraries/mbed/targets/hal/TARGET_Freescale/TARGET_K20D5M/us_ticker.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,150 +18,120 @@
#include "PeripheralNames.h"
#include "clk_freqs.h"

static void pit_init(void);
static void lptmr_init(void);
#define PIT_TIMER PIT->CHANNEL[0]
#define PIT_TIMER_IRQ PIT0_IRQn
#define PIT_TICKER PIT->CHANNEL[1]
#define PIT_TICKER_IRQ PIT1_IRQn

static void timer_init(void);
static void ticker_init(void);


static int us_ticker_inited = 0;
static uint32_t pit_ldval = 0;
static uint32_t clk_mhz;

void us_ticker_init(void) {
if (us_ticker_inited)
return;
us_ticker_inited = 1;

pit_init();
lptmr_init();
}

static uint32_t pit_us_ticker_counter = 0;

void pit0_isr(void) {
pit_us_ticker_counter++;
PIT->CHANNEL[0].LDVAL = pit_ldval; // 1us
PIT->CHANNEL[0].TFLG = 1;

SIM->SCGC6 |= SIM_SCGC6_PIT_MASK; // Clock PIT
PIT->MCR = 0; // Enable PIT

clk_mhz = bus_frequency() / 1000000;

timer_init();
ticker_init();
}

/******************************************************************************
* Timer for us timing.
*
* The K20D5M does not have a prescaler on its PIT timer nor the option
* to chain timers, which is why a software timer is required to get 32-bit
* word length.
******************************************************************************/
static void pit_init(void) {
SIM->SCGC6 |= SIM_SCGC6_PIT_MASK; // Clock PIT
PIT->MCR = 0; // Enable PIT
static volatile uint32_t msb_counter = 0;
static uint32_t timer_ldval = 0;

static void timer_isr(void) {
msb_counter++;
PIT_TIMER.TFLG = 1;
}

pit_ldval = bus_frequency() / 1000000;
static void timer_init(void) {
//CLZ counts the leading zeros, returning number of bits not used by clk_mhz
timer_ldval = clk_mhz << __CLZ(clk_mhz);

PIT->CHANNEL[0].LDVAL = pit_ldval; // 1us
PIT->CHANNEL[0].TCTRL |= PIT_TCTRL_TIE_MASK;
PIT->CHANNEL[0].TCTRL |= PIT_TCTRL_TEN_MASK; // Start timer 1
PIT_TIMER.LDVAL = timer_ldval; // 1us
PIT_TIMER.TCTRL |= PIT_TCTRL_TIE_MASK;
PIT_TIMER.TCTRL |= PIT_TCTRL_TEN_MASK; // Start timer 0

NVIC_SetVector(PIT0_IRQn, (uint32_t)pit0_isr);
NVIC_EnableIRQ(PIT0_IRQn);
NVIC_SetVector(PIT_TIMER_IRQ, (uint32_t)timer_isr);
NVIC_EnableIRQ(PIT_TIMER_IRQ);
}

uint32_t us_ticker_read() {
if (!us_ticker_inited)
us_ticker_init();

uint32_t retval;
__disable_irq();
retval = (timer_ldval - PIT_TIMER.CVAL) / clk_mhz; //Hardware bits
retval |= msb_counter << __CLZ(clk_mhz); //Software bits

if (PIT_TIMER.TFLG == 1) { //If overflow bit is set, force it to be handled
timer_isr(); //Handle IRQ, read again to make sure software/hardware bits are synced
NVIC_ClearPendingIRQ(PIT_TIMER_IRQ);
return us_ticker_read();
}

return pit_us_ticker_counter;
__enable_irq();
return retval;
}

/******************************************************************************
* Timer Event
*
* It schedules interrupts at given (32bit)us interval of time.
* It is implemented used the 16bit Low Power Timer that remains powered in all
* power modes.
* It is implemented using PIT channel 1, since no prescaler is available,
* some bits are implemented in software.
******************************************************************************/
static void lptmr_isr(void);

static void lptmr_init(void) {
/* Clock the timer */
SIM->SCGC5 |= SIM_SCGC5_LPTIMER_MASK;

/* Reset */
LPTMR0->CSR = 0;
static void ticker_isr(void);

static void ticker_init(void) {
/* Set interrupt handler */
NVIC_SetVector(LPTimer_IRQn, (uint32_t)lptmr_isr);
NVIC_EnableIRQ(LPTimer_IRQn);

/* Clock at (1)MHz -> (1)tick/us */
/* Check if the external oscillator can be divided to 1MHz */
uint32_t extosc = extosc_frequency();

if (extosc != 0) { //If external oscillator found
OSC0->CR |= OSC_CR_ERCLKEN_MASK;
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);
NVIC_SetVector(PIT_TICKER_IRQ, (uint32_t)ticker_isr);
NVIC_EnableIRQ(PIT_TICKER_IRQ);
}

void us_ticker_disable_interrupt(void) {
LPTMR0->CSR &= ~LPTMR_CSR_TIE_MASK;
PIT_TICKER.TCTRL &= ~PIT_TCTRL_TIE_MASK;
}

void us_ticker_clear_interrupt(void) {
// we already clear interrupt in lptmr_isr
}

static uint32_t us_ticker_int_counter = 0;
static uint16_t us_ticker_int_remainder = 0;

static void lptmr_set(unsigned short count) {
/* Reset */
LPTMR0->CSR = 0;

/* Set the compare register */
LPTMR0->CMR = count;

/* Enable interrupt */
LPTMR0->CSR |= LPTMR_CSR_TIE_MASK;

/* Start the timer */
LPTMR0->CSR |= LPTMR_CSR_TEN_MASK;
inline static void ticker_set(uint32_t count) {
PIT_TICKER.TCTRL = 0;
PIT_TICKER.LDVAL = count;
PIT_TICKER.TCTRL = PIT_TCTRL_TIE_MASK | PIT_TCTRL_TEN_MASK;
}

static void lptmr_isr(void) {
// write 1 to TCF to clear the LPT timer compare flag
LPTMR0->CSR |= LPTMR_CSR_TCF_MASK;
static void ticker_isr(void) {
// Clear IRQ flag
PIT_TICKER.TFLG = 1;

if (us_ticker_int_counter > 0) {
lptmr_set(0xFFFF);
ticker_set(0xFFFFFFFF);
us_ticker_int_counter--;

} else {
if (us_ticker_int_remainder > 0) {
lptmr_set(us_ticker_int_remainder);
us_ticker_int_remainder = 0;

} else {
// This function is going to disable the interrupts if there are
// no other events in the queue
us_ticker_irq_handler();
}
// This function is going to disable the interrupts if there are
// no other events in the queue
us_ticker_irq_handler();
}
}

Expand All @@ -173,13 +143,17 @@ void us_ticker_set_interrupt(unsigned int timestamp) {
return;
}

us_ticker_int_counter = (uint32_t)(delta >> 16);
us_ticker_int_remainder = (uint16_t)(0xFFFF & delta);
if (us_ticker_int_counter > 0) {
lptmr_set(0xFFFF);
//Calculate how much falls outside the 32-bit after multiplying with clk_mhz
//We shift twice 16-bit to keep everything within the 32-bit variable
us_ticker_int_counter = (uint32_t)(delta >> 16);
us_ticker_int_counter *= clk_mhz;
us_ticker_int_counter >>= 16;

uint32_t us_ticker_int_remainder = (uint32_t)delta * clk_mhz;
if (us_ticker_int_remainder == 0) {
ticker_set(0xFFFFFFFF);
us_ticker_int_counter--;
} else {
lptmr_set(us_ticker_int_remainder);
us_ticker_int_remainder = 0;
ticker_set(us_ticker_int_remainder);
}
}