Rework us_ticker and rtc_api/lp_ticker | Fix compiler warnings for spi_api

Simplify tickers to stop emulating 1us ticks by using native timer resolutions.
Implement ticker get info functions.
Separate rtc and lp ticker init status by implementing separate functions.
Add mbed_critical include file to disable compiler warning messages.
pull/6697/head
Keyur Hariya 2018-03-09 16:58:35 -06:00 committed by adbridge
parent fac4ed0a27
commit a6cc79faf1
3 changed files with 137 additions and 356 deletions

View File

@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (C) 2016 Maxim Integrated Products, Inc., All Rights Reserved.
* Copyright (C) 2016,2018 Maxim Integrated Products, Inc., All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
@ -36,33 +36,24 @@
#include "rtc.h"
#include "lp.h"
#define PRESCALE_VAL RTC_PRESCALE_DIV_2_0 // Set the divider for the 4kHz clock
#define SHIFT_AMT (RTC_PRESCALE_DIV_2_12 - PRESCALE_VAL)
// LOG2 for 32-bit powers of 2
#define LOG2_1(n) (((n) >= (1 << 1)) ? 1 : 0)
#define LOG2_2(n) (((n) >= (1 << 2)) ? ( 2 + (LOG2_1((n) >> 2))) : LOG2_1(n))
#define LOG2_4(n) (((n) >= (1 << 4)) ? ( 4 + (LOG2_2((n) >> 4))) : LOG2_2(n))
#define LOG2_8(n) (((n) >= (1 << 8)) ? ( 8 + (LOG2_4((n) >> 8))) : LOG2_4(n))
#define LOG2(n) (((n) >= (1 << 16)) ? (16 + (LOG2_8((n) >> 16))) : LOG2_8(n))
#define WINDOW 1000
#define LP_TIMER_FREQ_HZ 4096
#define LP_TIMER_PRESCALE RTC_PRESCALE_DIV_2_0
#define LP_TIMER_RATE_HZ (LP_TIMER_FREQ_HZ >> LP_TIMER_PRESCALE)
#define LP_TIMER_WIDTH 32
static int rtc_inited = 0;
static volatile uint32_t overflow_cnt = 0;
static uint64_t rtc_read64(void);
static volatile int rtc_inited = 0;
static volatile int lp_ticker_inited = 0;
//******************************************************************************
static void overflow_handler(void)
static void init_rtc(void)
{
overflow_cnt++;
RTC_ClearFlags(MXC_F_RTC_FLAGS_ASYNC_CLR_FLAGS);
}
//******************************************************************************
void rtc_init(void)
{
if (rtc_inited) {
return;
}
rtc_inited = 1;
overflow_cnt = 0;
/* Enable power for RTC for all LPx states */
MXC_PWRSEQ->reg0 |= (MXC_F_PWRSEQ_REG0_PWR_RTCEN_RUN |
MXC_F_PWRSEQ_REG0_PWR_RTCEN_SLP);
@ -70,21 +61,12 @@ void rtc_init(void)
/* Enable clock to synchronizers */
CLKMAN_SetClkScale(CLKMAN_CLK_SYNC, CLKMAN_SCALE_DIV_1);
// Prepare interrupt handlers
NVIC_SetVector(RTC0_IRQn, (uint32_t)lp_ticker_irq_handler);
NVIC_EnableIRQ(RTC0_IRQn);
NVIC_SetVector(RTC3_IRQn, (uint32_t)overflow_handler);
NVIC_EnableIRQ(RTC3_IRQn);
// Enable wakeup on RTC rollover
LP_ConfigRTCWakeUp(0, 0, 0, 1);
/* RTC registers are only reset on a power cycle. Do not reconfigure the RTC
* if it is already running.
*/
if (!RTC_IsActive()) {
rtc_cfg_t cfg = {0};
cfg.prescaler = PRESCALE_VAL;
rtc_cfg_t cfg = { 0 };
cfg.prescaler = LP_TIMER_PRESCALE;
cfg.snoozeMode = RTC_SNOOZE_DISABLE;
int retval = RTC_Init(&cfg);
@ -96,163 +78,128 @@ void rtc_init(void)
}
//******************************************************************************
void lp_ticker_init(void)
static void overflow_handler(void)
{
rtc_init();
MXC_RTCTMR->comp[1] += ((UINT32_MAX >> LOG2(LP_TIMER_RATE_HZ)) + 1);
RTC_ClearFlags(MXC_F_RTC_FLAGS_ASYNC_CLR_FLAGS);
}
//******************************************************************************
void rtc_init(void)
{
if (rtc_inited) {
return;
}
NVIC_SetVector(RTC3_IRQn, (uint32_t)overflow_handler);
NVIC_EnableIRQ(RTC3_IRQn);
// Enable wakeup on RTC overflow
LP_ConfigRTCWakeUp(lp_ticker_inited, 0, 0, 1);
init_rtc();
rtc_inited = 1;
}
//******************************************************************************
void rtc_free(void)
{
if (RTC_IsActive()) {
// Clear and disable RTC
MXC_RTCTMR->ctrl |= MXC_F_RTC_CTRL_CLEAR;
RTC_Stop();
if (rtc_inited) {
rtc_inited = 0;
if (lp_ticker_inited) {
RTC_DisableINT(MXC_F_RTC_FLAGS_OVERFLOW);
} else {
MXC_RTCTMR->ctrl |= MXC_F_RTC_CTRL_CLEAR;
RTC_Stop();
}
}
}
//******************************************************************************
int rtc_isenabled(void)
{
return RTC_IsActive();
}
//******************************************************************************
time_t rtc_read(void)
{
uint32_t ovf_cnt_1, ovf_cnt_2, timer_cnt;
uint32_t ovf1, ovf2;
// Make sure RTC is setup before trying to read
if (!rtc_inited) {
rtc_init();
}
// Ensure coherency between overflow_cnt and timer
do {
ovf_cnt_1 = overflow_cnt;
ovf1 = RTC_GetFlags() & MXC_F_RTC_FLAGS_OVERFLOW;
timer_cnt = RTC_GetCount();
ovf2 = RTC_GetFlags() & MXC_F_RTC_FLAGS_OVERFLOW;
ovf_cnt_2 = overflow_cnt;
} while ((ovf_cnt_1 != ovf_cnt_2) || (ovf1 != ovf2));
// Account for an unserviced interrupt
if (ovf1) {
ovf_cnt_1++;
}
return (timer_cnt >> SHIFT_AMT) + (ovf_cnt_1 << (32 - SHIFT_AMT));
}
//******************************************************************************
static uint64_t rtc_read64(void)
{
uint32_t ovf_cnt_1, ovf_cnt_2, timer_cnt;
uint32_t ovf1, ovf2;
uint64_t current_us;
// Make sure RTC is setup before trying to read
if (!rtc_inited) {
rtc_init();
}
// Ensure coherency between overflow_cnt and timer
do {
ovf_cnt_1 = overflow_cnt;
ovf1 = RTC_GetFlags() & MXC_F_RTC_FLAGS_OVERFLOW;
timer_cnt = RTC_GetCount();
ovf2 = RTC_GetFlags() & MXC_F_RTC_FLAGS_OVERFLOW;
ovf_cnt_2 = overflow_cnt;
} while ((ovf_cnt_1 != ovf_cnt_2) || (ovf1 != ovf2));
// Account for an unserviced interrupt
if (ovf1) {
ovf_cnt_1++;
}
current_us = (((uint64_t)timer_cnt * 1000000) >> SHIFT_AMT) + (((uint64_t)ovf_cnt_1 * 1000000) << (32 - SHIFT_AMT));
return current_us;
return rtc_inited;
}
//******************************************************************************
void rtc_write(time_t t)
{
// Make sure RTC is setup before accessing
if (!rtc_inited) {
rtc_init();
}
RTC_Stop();
RTC_SetCount(t << SHIFT_AMT);
overflow_cnt = t >> (32 - SHIFT_AMT);
RTC_Start();
}
//******************************************************************************
void lp_ticker_set_interrupt(timestamp_t timestamp)
{
uint32_t comp_value;
uint64_t curr_ts64;
uint64_t ts64;
// Note: interrupts are disabled before this function is called.
// Disable the alarm while it is prepared
RTC_DisableINT(MXC_F_RTC_INTEN_COMP0);
curr_ts64 = rtc_read64();
ts64 = (uint64_t)timestamp | (curr_ts64 & 0xFFFFFFFF00000000ULL);
// If this event is older than a recent window, it must be in the future
if ((ts64 < (curr_ts64 - WINDOW)) && ((curr_ts64 - WINDOW) < curr_ts64)) {
ts64 += 0x100000000ULL;
}
uint32_t timer = RTC_GetCount();
if (ts64 <= curr_ts64) {
// This event has already occurred. Set the alarm to expire immediately.
comp_value = timer + 1;
} else {
comp_value = (ts64 << SHIFT_AMT) / 1000000;
}
// Ensure that the compare value is far enough in the future to guarantee the interrupt occurs.
if ((comp_value < (timer + 2)) && (comp_value > (timer - 10))) {
comp_value = timer + 2;
}
MXC_RTCTMR->comp[0] = comp_value;
MXC_RTCTMR->flags = MXC_F_RTC_FLAGS_ASYNC_CLR_FLAGS;
RTC_EnableINT(MXC_F_RTC_INTEN_COMP0);
// Enable wakeup from RTC
LP_ConfigRTCWakeUp(1, 0, 0, 1);
MXC_RTCTMR->comp[1] = t - (MXC_RTCTMR->timer >> LOG2(LP_TIMER_RATE_HZ));
// Wait for pending transactions
while (MXC_RTCTMR->ctrl & MXC_F_RTC_CTRL_PENDING);
}
//******************************************************************************
time_t rtc_read(void)
{
if (!rtc_inited) {
rtc_init();
}
return (MXC_RTCTMR->timer >> LOG2(LP_TIMER_RATE_HZ)) + MXC_RTCTMR->comp[1];
}
//******************************************************************************
void lp_ticker_init(void)
{
if (lp_ticker_inited) {
return;
}
NVIC_SetVector(RTC0_IRQn, (uint32_t)lp_ticker_irq_handler);
NVIC_EnableIRQ(RTC0_IRQn);
init_rtc();
lp_ticker_inited = 1;
}
//******************************************************************************
uint32_t lp_ticker_read(void)
{
return MXC_RTCTMR->timer;
}
//******************************************************************************
void lp_ticker_set_interrupt(timestamp_t timestamp)
{
MXC_RTCTMR->comp[0] = timestamp;
MXC_RTCTMR->flags = MXC_F_RTC_FLAGS_ASYNC_CLR_FLAGS;
MXC_RTCTMR->inten |= MXC_F_RTC_INTEN_COMP0;
// Enable wakeup from RTC compare 0
LP_ConfigRTCWakeUp(1, 0, 0, rtc_inited);
// Wait for pending transactions
while (MXC_RTCTMR->ctrl & MXC_F_RTC_CTRL_PENDING);
}
//******************************************************************************
void lp_ticker_disable_interrupt(void)
{
RTC_DisableINT(MXC_F_RTC_INTEN_COMP0);
}
//******************************************************************************
void lp_ticker_clear_interrupt(void)
{
RTC_ClearFlags(MXC_F_RTC_FLAGS_ASYNC_CLR_FLAGS);
}
//******************************************************************************
void lp_ticker_fire_interrupt(void)
{
NVIC_SetPendingIRQ(RTC0_IRQn);
}
//******************************************************************************
inline void lp_ticker_disable_interrupt(void)
const ticker_info_t *lp_ticker_get_info(void)
{
RTC_DisableINT(MXC_F_RTC_INTEN_COMP0);
}
static const ticker_info_t info = {
LP_TIMER_RATE_HZ,
LP_TIMER_WIDTH
};
//******************************************************************************
inline void lp_ticker_clear_interrupt(void)
{
RTC_ClearFlags(MXC_F_RTC_FLAGS_ASYNC_CLR_FLAGS);
}
//******************************************************************************
inline uint32_t lp_ticker_read(void)
{
return rtc_read64();
return &info;
}

View File

@ -32,6 +32,7 @@
*/
#include "mbed_assert.h"
#include "mbed_critical.h"
#include "spi_api.h" // mbed HAL
#include "spim_regs.h" // bare metal
#include "spim.h" // Maxim CMSIS driver

View File

@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2016 Maxim Integrated Products, Inc., All Rights Reserved.
* Copyright (c) 2016,2018 Maxim Integrated Products, Inc., All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
@ -32,251 +32,84 @@
*/
#include <stddef.h>
#include "mbed_error.h"
#include "mbed_critical.h"
#include "us_ticker_api.h"
#include "PeripheralNames.h"
#include "tmr.h"
#include "assert.h"
#define US_TIMER MXC_TMR0
#define US_TIMER_IRQn TMR0_0_IRQn
#define US_TIMER MXC_TMR0
#define US_TIMER_IRQn TMR0_0_IRQn
#define US_TIMER_PRESCALE TMR_PRESCALE_DIV_2_5
#define US_TIMER_MODE TMR32_MODE_COMPARE
#define US_TIMER_WIDTH 32
static int us_ticker_inited = 0;
static uint32_t ticks_per_us;
static uint32_t tick_win;
static volatile uint64_t current_cnt; // Hold the current ticks
static volatile uint64_t event_cnt; // Holds the value of the next event
#define MAX_TICK_VAL ((uint64_t)0xFFFFFFFF * ticks_per_us)
//******************************************************************************
static inline void inc_current_cnt_no_crit(uint32_t inc)
{
// Overflow the ticker when the us ticker overflows
current_cnt += inc;
if (current_cnt > MAX_TICK_VAL) {
current_cnt -= (MAX_TICK_VAL + 1);
}
}
//******************************************************************************
static inline void inc_current_cnt(uint32_t inc)
{
core_util_critical_section_enter();
inc_current_cnt_no_crit(inc);
core_util_critical_section_exit();
}
//******************************************************************************
static inline int event_passed(uint64_t current, uint64_t event)
{
// Determine if the event has already happened.
// If the event is behind the current ticker, within a window,
// then the event has already happened.
if (((current < tick_win) && ((event < current) ||
(event > (MAX_TICK_VAL - (tick_win - current))))) ||
((event < current) && (event > (current - tick_win)))) {
return 1;
}
return 0;
}
//******************************************************************************
static inline uint64_t event_diff(uint64_t current, uint64_t event)
{
// Check to see if the ticker will overflow before the event
if(current <= event) {
return (event - current);
}
return ((MAX_TICK_VAL - current) + event);
}
//******************************************************************************
static void tmr_handler(void)
{
uint32_t cmp = TMR32_GetCompare(US_TIMER);
TMR32_SetCompare(US_TIMER, 0xFFFFFFFF); // reset to max value to prevent further interrupts
if (TMR32_GetFlag(US_TIMER)) {
inc_current_cnt_no_crit(cmp);
}
TMR32_ClearFlag(US_TIMER);
NVIC_ClearPendingIRQ(US_TIMER_IRQn);
if (event_passed(current_cnt + TMR32_GetCount(US_TIMER), event_cnt)) {
// the timestamp has expired
event_cnt = 0xFFFFFFFFFFFFFFFFULL; // reset to max value
us_ticker_irq_handler();
} else {
uint64_t diff = event_diff(current_cnt, event_cnt);
if (diff < (uint64_t)0xFFFFFFFF) {
// the event occurs before the next overflow
TMR32_SetCompare(US_TIMER, diff);
// Since the timer keeps counting after the terminal value is reached, it is possible that the new
// terminal value is in the past.
if (TMR32_GetCompare(US_TIMER) < TMR32_GetCount(US_TIMER)) {
// the timestamp has expired
TMR32_SetCompare(US_TIMER, 0xFFFFFFFF); // reset to max value to prevent further interrupts
TMR32_ClearFlag(US_TIMER);
NVIC_ClearPendingIRQ(US_TIMER_IRQn);
event_cnt = 0xFFFFFFFFFFFFFFFFULL; // reset to max value
us_ticker_irq_handler();
}
}
}
}
static volatile int us_ticker_inited = 0;
//******************************************************************************
void us_ticker_init(void)
{
tmr32_cfg_t cfg;
if (us_ticker_inited) {
return;
}
us_ticker_inited = 1;
current_cnt = 0;
event_cnt = 0xFFFFFFFFFFFFFFFFULL; // reset to max value
ticks_per_us = SystemCoreClock / 1000000;
tick_win = SystemCoreClock / 100; // Set the tick window to 10ms
int retval = TMR_Init(US_TIMER, TMR_PRESCALE_DIV_2_0, NULL);
MBED_ASSERT(retval == E_NO_ERROR);
tmr32_cfg_t cfg;
cfg.mode = TMR32_MODE_CONTINUOUS;
cfg.mode = US_TIMER_MODE;
cfg.polarity = TMR_POLARITY_UNUSED;
cfg.compareCount = 0xFFFFFFFF;
cfg.compareCount = UINT32_MAX;
TMR_Init(US_TIMER, US_TIMER_PRESCALE, NULL);
TMR32_Config(US_TIMER, &cfg);
NVIC_SetVector(US_TIMER_IRQn, (uint32_t)tmr_handler);
NVIC_SetVector(US_TIMER_IRQn, (uint32_t)us_ticker_irq_handler);
NVIC_EnableIRQ(US_TIMER_IRQn);
TMR32_EnableINT(US_TIMER);
TMR32_Start(US_TIMER);
}
//******************************************************************************
void us_ticker_deinit(void)
{
TMR32_Stop(US_TIMER);
TMR32_DisableINT(US_TIMER);
TMR32_ClearFlag(US_TIMER);
us_ticker_inited = 0;
}
//******************************************************************************
uint32_t us_ticker_read(void)
{
uint64_t current_cnt1, current_cnt2;
uint32_t cmp, cnt;
uint32_t flag1, flag2;
static uint32_t last = 0;
if (!us_ticker_inited) {
us_ticker_init();
}
// Ensure coherency between current_cnt and TMR32_GetCount()
do {
current_cnt1 = current_cnt;
flag1 = TMR32_GetFlag(US_TIMER);
cmp = TMR32_GetCompare(US_TIMER);
cnt = TMR32_GetCount(US_TIMER);
flag2 = TMR32_GetFlag(US_TIMER);
current_cnt2 = current_cnt;
} while ((current_cnt1 != current_cnt2) || (flag1 != flag2));
// Account for an unserviced interrupt
if (flag1) {
// Clear peripheral interrupt flag; leaving NVIC pending set
TMR32_ClearFlag(US_TIMER);
// Advance global count
inc_current_cnt(cmp + cnt);
current_cnt1 += cmp;
}
current_cnt1 += cnt;
assert(last <= (current_cnt1 / ticks_per_us));
last = (current_cnt1 / ticks_per_us);
return last;
return US_TIMER->count32;
}
//******************************************************************************
void us_ticker_set_interrupt(timestamp_t timestamp)
{
// Note: interrupts are disabled before this function is called.
TMR32_Stop(US_TIMER);
if (TMR32_GetFlag(US_TIMER)) {
TMR32_ClearFlag(US_TIMER);
NVIC_ClearPendingIRQ(US_TIMER_IRQn);
inc_current_cnt(TMR32_GetCompare(US_TIMER));
}
// add and reset the current count value
inc_current_cnt(TMR32_GetCount(US_TIMER));
TMR32_SetCount(US_TIMER, 0);
// add the number of cycles that the timer is disabled here for
inc_current_cnt(200);
event_cnt = (uint64_t)timestamp * ticks_per_us;
// Check to see if the event has already passed
if (!event_passed(current_cnt, event_cnt)) {
uint64_t diff = event_diff(current_cnt, event_cnt);
if (diff < (uint64_t)0xFFFFFFFF) {
// the event occurs before the next overflow
TMR32_SetCompare(US_TIMER, diff);
} else {
// the event occurs after the next overflow
TMR32_SetCompare(US_TIMER, 0xFFFFFFFF); // set to max
}
} else {
// the requested timestamp occurs in the past
// set the timer up to immediately expire
TMR32_SetCompare(US_TIMER, 1);
}
TMR32_Start(US_TIMER);
US_TIMER->ctrl = 0;
US_TIMER->term_cnt32 = timestamp;
US_TIMER->inten = MXC_F_TMR_INTEN_TIMER0;
US_TIMER->ctrl = MXC_F_TMR_CTRL_ENABLE0 |
(US_TIMER_MODE << MXC_F_TMR_CTRL_MODE_POS) |
(US_TIMER_PRESCALE << MXC_F_TMR_CTRL_PRESCALE_POS);
}
//******************************************************************************
void us_ticker_fire_interrupt(void)
{
TMR32_SetCompare(US_TIMER, 1);
NVIC_SetPendingIRQ(US_TIMER_IRQn);
}
//******************************************************************************
void us_ticker_disable_interrupt(void)
{
// There are no more events, set timer overflow to the max
TMR32_SetCompare(US_TIMER, 0xFFFFFFFF);
US_TIMER->inten = 0;
}
//******************************************************************************
void us_ticker_clear_interrupt(void)
{
// cleared in the local handler
US_TIMER->intfl = MXC_F_TMR_INTFL_TIMER0;
}
//******************************************************************************
void us_ticker_set(timestamp_t timestamp)
const ticker_info_t* us_ticker_get_info(void)
{
TMR32_Stop(US_TIMER);
current_cnt = (uint64_t)timestamp * ticks_per_us;
TMR32_SetCount(US_TIMER, 0);
TMR32_SetCompare(US_TIMER, 0xFFFFFFFF);
TMR32_Start(US_TIMER);
static const ticker_info_t info = {
RO_FREQ >> US_TIMER_PRESCALE,
US_TIMER_WIDTH
};
if (((uint64_t)timestamp * ticks_per_us) >= event_cnt) {
// The next timestamp has elapsed. Trigger the interrupt to handle it.
NVIC_SetPendingIRQ(US_TIMER_IRQn);
}
return &info;
}