blob: ac5d77c5ec3517fb50029b073b161ce3db4ab474 [file] [log] [blame]
/**
******************************************************************************
*
* @file timer.h
*
* @brief Atmosic Timer Driver
*
* Copyright (C) Atmosic 2019-2022
*
******************************************************************************
*/
#ifndef __TIMER_H__
#define __TIMER_H__
#include "at_wrpr.h"
/**
* @defgroup TIMER HW Timer APIs
* @ingroup DRIVERS
* @brief User driver for application to use HW timers
* @{
*/
#ifdef __cplusplus
extern "C" {
#endif
/// Number of centisecond per second
#define CS_PER_SEC 100
/// Number of millisecond per second
#define MS_PER_SEC 1000
/// Number of millisecond per centisecond
#define MS_PER_CS 10
/// Number of microsecond per centisecond
#define US_PER_CS 10000
/// Number of microsecond per millisecond
#define US_PER_MS 1000
/// Number of second per minute
#define SEC_PER_MIN 60
/// Number of minute per hour
#define MIN_PER_HOUR 60
typedef enum {
ATM_TIMER0 = 0,
ATM_TIMER1 = 1,
ATM_DUALTIMER1 = 2,
ATM_DUALTIMER2 = 3,
ATM_SYSTICK = 4,
ATM_SLWTIMER = 5, // Runs off 32Khz clock
ATM_TIMERS_MAX = 6
} atm_timer_id_t;
typedef enum {
ATM_TIMER_MODE_SINGLE_SHOT = 0,
ATM_TIMER_MODE_PERIODIC = 1,
ATM_TIMER_MODE_MAX = 2
} atm_timer_mode_t;
typedef enum {
ATM_TIMER_SUCCESS = 0,
ATM_TIMER_INVALID_PARAM = 1,
ATM_TIMER_RESOURCE_BUSY = 2,
ATM_TIMER_GENERIC_ERROR = 3
} atm_timer_error_t;
typedef void (*atm_timer_callback_t) (void *app_context);
/**
* @brief Setup timer.
* @param[in] id Timer ID
* @param[in] mode SingleShot vs Periodic
* @param[in] timeout_handler Application layer callback handler
* (called from interrupt context)
* @return Success or Error status
*/
atm_timer_error_t atm_timer_setup(atm_timer_id_t id, atm_timer_mode_t mode,
atm_timer_callback_t timeout_handler);
/**
* @brief Start timer.
* @param[in] id Timer ID
* @param[in] timeout_us Timer value in micro seconds
* @param[in] app_context Application specific context
* @return Success or Error status
*/
atm_timer_error_t atm_timer_start(atm_timer_id_t id, uint32_t timeout_us,
void *app_context);
/**
* @brief Start dual timer with more accurate unit.(Only for single shot mode)
* @param[in] id Dual timer ID
* @param[in] timeout_cycles Timer value in cycles
*/
void atm_dual_timer_single_shot_quick_start(atm_timer_id_t id,
uint32_t timeout_cycles);
/**
* @brief Stop timer.
* @param[in] id Timer ID
* @return Success or Error status
*/
atm_timer_error_t atm_timer_stop(atm_timer_id_t id);
/**
* @brief Busy wait using CMSDK_PSEQ->CURRENT_REAL_TIME.
* @param[in] msec Delay value in milliseconds
* @return Success or Error status
*/
atm_timer_error_t atm_timer_mdelay(uint32_t msec);
/**
* @brief Busy wait using the Cortex-M0 SysTick.
* @param[in] usec Delay value in microseconds
* @return Success or Error status
*/
atm_timer_error_t atm_timer_udelay(uint32_t usec);
/**
* @brief Sleep in WFI using a timer.
* @param[in] id Timer ID
* @param[in] usec Timer value in microseconds
* @return Success or Error status
*/
atm_timer_error_t atm_timer_usleep(atm_timer_id_t id, uint32_t usec);
// Permit customization for restrictive calling contexts
// or to analyze rt{,1,2,3} locals
#ifndef TIMER_ASSERT_ERR
#define TIMER_ASSERT_ERR ASSERT_ERR
#endif
/**
* @brief Get the current system time based on the 32kHz clock
*/
__INLINE uint32_t atm_get_sys_time(void)
{
// The high clock domain latch low clock domain signal directly
// would probably get bad value due to metastability. Considering
// the combinations, it could have 3 conditions:
// 1. bad -> good -> good
// 2. good -> good
// 3. good -> bad -> good -> good
uint32_t rt;
WRPR_CTRL_PUSH(CMSDK_PSEQ, WRPR_CTRL__CLK_ENABLE) {
GLOBAL_INT_DISABLE();
uint32_t rt1 = CMSDK_PSEQ->CURRENT_REAL_TIME;
rt = CMSDK_PSEQ->CURRENT_REAL_TIME;
if (rt != rt1) {
uint32_t rt3 = CMSDK_PSEQ->CURRENT_REAL_TIME;
if (rt != rt3) {
__UNUSED uint32_t rt2 = rt;
rt = CMSDK_PSEQ->CURRENT_REAL_TIME;
TIMER_ASSERT_ERR(rt == rt3);
}
}
GLOBAL_INT_RESTORE();
} WRPR_CTRL_POP();
return rt;
}
#ifdef LPC_RCOS
/**
* @brief Fetch LP clock frequency
*
* @return Value in Hz, or 0 when using a xtal
*/
uint32_t lpc_rcos_hz(void);
#define TIMER_GET_LPC_FREQ ({ \
uint32_t lp_hz = lpc_rcos_hz(); \
lp_hz ? lp_hz : 32768; \
})
#else
#define TIMER_GET_LPC_FREQ (32768)
#endif
/**
* @brief Busy wait using CMSDK_PSEQ->CURRENT_REAL_TIME.
* @param[in] ticks Delay value in counts of CMSDK_PSEQ->CURRENT_REAL_TIME.
* @return Success or Error status
*/
__INLINE void atm_timer_lpc_delay(uint32_t ticks)
{
uint32_t then = atm_get_sys_time();
while (atm_get_sys_time() - then < ticks) {
YIELD();
}
}
/**
* @brief Translate centiseconds to ticks
* @param[in] cs Number of centiseconds
* @return LPC duration
*/
static inline uint32_t atm_cs_to_lpc(uint32_t cs)
{
return ((uint64_t)cs * TIMER_GET_LPC_FREQ) / CS_PER_SEC;
}
/**
* @brief Translate milliseconds to ticks
* @param[in] ms Number of milliseconds
* @return LPC duration
*/
static inline uint32_t atm_ms_to_lpc(uint32_t ms)
{
return ((uint64_t)ms * TIMER_GET_LPC_FREQ) / MS_PER_SEC;
}
/**
* @brief Translate microseconds to ticks
* @param[in] us Number of microseconds
* @return LPC duration
*/
static inline uint32_t atm_us_to_lpc(uint64_t us)
{
return (us * TIMER_GET_LPC_FREQ) / (US_PER_MS * MS_PER_SEC);
}
/**
* @brief Translate ticks to centiseconds
* @param[in] lpc LPC duration
* @return Number of centiseconds
*/
static inline uint32_t atm_lpc_to_cs(uint32_t lpc)
{
return ((uint64_t)lpc * CS_PER_SEC) / TIMER_GET_LPC_FREQ;
}
/**
* @brief Translate ticks to milliseconds
* @param[in] lpc LPC duration
* @return Number of milliseconds
*/
static inline uint32_t atm_lpc_to_ms(uint32_t lpc)
{
return ((uint64_t)lpc * MS_PER_SEC) / TIMER_GET_LPC_FREQ;
}
/**
* @brief Translate ticks to microseconds
* @param[in] lpc LPC duration
* @return Number of microseconds
*/
static inline uint64_t atm_lpc_to_us(uint32_t lpc)
{
return ((uint64_t)lpc * US_PER_MS * MS_PER_SEC) / TIMER_GET_LPC_FREQ;
}
#undef TIMER_GET_LPC_FREQ
#ifdef __cplusplus
}
#endif
/// @} TIMER
#endif // __TIMER_H__