[KL25Z] Fix us_ticker_api implementation and improve documentation

pull/1/head
Emilio Monti 2013-03-05 18:16:46 +00:00
parent c906468071
commit 5f09c37d0f
1 changed files with 110 additions and 81 deletions

View File

@ -17,50 +17,17 @@
#include "us_ticker_api.h" #include "us_ticker_api.h"
#include "PeripheralNames.h" #include "PeripheralNames.h"
static void (*us_ticker_interrupt_handler)(void) = NULL; /******************************************************************************
* Timer for us timing.
static uint16_t us_ticker_int_counter = 0; *
static uint16_t us_ticker_int_remainder = 0; * Need to have 32bit resolution, we are using the PIT (2x32bit timers) for
* that. All the other timers have only 16bit resolution.
void lptmr_isr(void) { * Unfortunately, the PIT does not have a prescaler, therefore it ticks at the
// write 1 to TCF to clear the LPT timer compare flag * bus clock of (24)MHz.
LPTMR0->CSR |= LPTMR_CSR_TCF_MASK; * To keep 32bit resolution we are chaining the 2 32bit timers together dividing
* the final result by 24.
if (us_ticker_int_counter > 0) { * NOTE: The PIT is a countdown timer.
us_ticker_int_counter--; ******************************************************************************/
} else {
if (us_ticker_int_remainder > 0) {
us_ticker_int_remainder = 0;
LPTMR0->CMR = us_ticker_int_remainder;
} else {
if (us_ticker_interrupt_handler != NULL) {
us_ticker_interrupt_handler();
}
}
}
}
static int lptmr_inited = 0;
void lptmr_init(void) {
lptmr_inited = 1;
/* Clock the timer */
SIM->SCGC5 |= SIM_SCGC5_LPTMR_MASK;
/* Reset */
LPTMR0->CSR = 0;
/* Set interrupt handler */
NVIC_SetVector(LPTimer_IRQn, (uint32_t)lptmr_isr);
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
}
static int us_ticker_running = 0; static int us_ticker_running = 0;
static void us_ticker_init(void) { static void us_ticker_init(void) {
@ -96,25 +63,52 @@ uint32_t us_ticker_read() {
return (uint32_t)(0xFFFFFFFF & ticks); return (uint32_t)(0xFFFFFFFF & ticks);
} }
static void us_ticker_set_interrupt(unsigned int timestamp) {
if (!lptmr_inited)
lptmr_init();
int delta = (int)(timestamp - us_ticker_read()); /******************************************************************************
if (delta < 0) { * Timer Event
if (us_ticker_interrupt_handler != NULL) { *
us_ticker_interrupt_handler(); * 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.
******************************************************************************/
static void lptmr_isr(void);
static void lptmr_irq_handler(void);
static void us_ticker_set_interrupt(unsigned int timestamp);
static ticker_event_handler event_handler = NULL;
void us_ticker_set_handler(ticker_event_handler handler) {
event_handler = handler;
} }
return;
} static uint32_t us_ticker_int_counter = 0;
us_ticker_int_counter = (uint16_t)(delta >> 16); static uint16_t us_ticker_int_remainder = 0;
us_ticker_int_remainder = (uint16_t)(0xFFFF & delta);
if (us_ticker_int_counter > 0) { static int lptmr_inited = 0;
LPTMR0->CMR = 0xFFFF; void lptmr_init(void) {
} else { lptmr_inited = 1;
LPTMR0->CMR = us_ticker_int_remainder;
/* Clock the timer */
SIM->SCGC5 |= SIM_SCGC5_LPTMR_MASK;
/* Reset */
LPTMR0->CSR = 0;
/* Set interrupt handler */
NVIC_SetVector(LPTimer_IRQn, (uint32_t)lptmr_isr);
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
} }
static void lptmr_set(unsigned short count) {
/* Reset */
LPTMR0->CSR = 0;
/* Set the compare register */
LPTMR0->CMR = count;
/* Enable interrupt */ /* Enable interrupt */
LPTMR0->CSR |= LPTMR_CSR_TIE_MASK; LPTMR0->CSR |= LPTMR_CSR_TIE_MASK;
@ -122,19 +116,35 @@ static void us_ticker_set_interrupt(unsigned int timestamp) {
LPTMR0->CSR |= LPTMR_CSR_TEN_MASK; LPTMR0->CSR |= LPTMR_CSR_TEN_MASK;
} }
static void us_ticker_disable_interrupt(void) { static void lptmr_isr(void) {
LPTMR0->CSR &= ~LPTMR_CSR_TIE_MASK; // write 1 to TCF to clear the LPT timer compare flag
LPTMR0->CSR |= LPTMR_CSR_TCF_MASK;
if (us_ticker_int_counter > 0) {
lptmr_set(0xFFFF);
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
lptmr_irq_handler();
}
}
} }
static ticker_event_handler event_handler;
static ticker_event_t *head = NULL; static ticker_event_t *head = NULL;
void irq_handler(void) { static void lptmr_irq_handler(void) {
/* Go through all the pending TimerEvents */ /* Go through all the pending TimerEvents */
while (1) { while (1) {
if (head == NULL) { if (head == NULL) {
// There are no more TimerEvents left, so disable matches. // There are no more TimerEvents left, so disable matches.
us_ticker_disable_interrupt(); LPTMR0->CSR &= ~LPTMR_CSR_TIE_MASK;
return; return;
} }
@ -143,9 +153,9 @@ void irq_handler(void) {
// point to the following one and execute its handler // point to the following one and execute its handler
ticker_event_t *p = head; ticker_event_t *p = head;
head = head->next; head = head->next;
if (event_handler != NULL) {
event_handler(p->id); // NOTE: the handler can set new events event_handler(p->id); // NOTE: the handler can set new events
}
} else { } else {
// This event and the following ones in the list are in the future: // This event and the following ones in the list are in the future:
// set it as next interrupt and return // set it as next interrupt and return
@ -155,9 +165,26 @@ void irq_handler(void) {
} }
} }
void us_ticker_set_handler(ticker_event_handler handler) { static void us_ticker_set_interrupt(unsigned int timestamp) {
event_handler = handler; if (!lptmr_inited)
us_ticker_interrupt_handler = irq_handler; lptmr_init();
int delta = (int)(timestamp - us_ticker_read());
if (delta <= 0) {
// This event was in the past:
lptmr_irq_handler();
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);
us_ticker_int_counter--;
} else {
lptmr_set(us_ticker_int_remainder);
us_ticker_int_remainder = 0;
}
} }
void us_ticker_insert_event(ticker_event_t *obj, unsigned int timestamp, uint32_t id) { void us_ticker_insert_event(ticker_event_t *obj, unsigned int timestamp, uint32_t id) {
@ -198,12 +225,14 @@ void us_ticker_remove_event(ticker_event_t *obj) {
__disable_irq(); __disable_irq();
// remove this object from the list // remove this object from the list
if (head == obj) { // first in the list, so just drop me if (head == obj) {
// first in the list, so just drop me
head = obj->next; head = obj->next;
if (obj->next != NULL) { if (obj->next != NULL) {
us_ticker_set_interrupt(head->timestamp); us_ticker_set_interrupt(head->timestamp);
} }
} else { // find the object before me, then drop me } else {
// find the object before me, then drop me
ticker_event_t* p = head; ticker_event_t* p = head;
while (p != NULL) { while (p != NULL) {
if (p->next == obj) { if (p->next == obj) {