458 lines
11 KiB
C
458 lines
11 KiB
C
/* SPDX-License-Identifier: BSD-3-Clause */
|
|
/*
|
|
* Copyright (c) 2020-2021 Rockchip Electronics Co., Ltd.
|
|
*/
|
|
|
|
#include "hal_base.h"
|
|
|
|
/** @addtogroup RK_HAL_Driver
|
|
* @{
|
|
*/
|
|
|
|
/** @addtogroup HAL_BASE
|
|
* @{
|
|
*/
|
|
|
|
/** @defgroup HAL_BASE_How_To_Use How To Use
|
|
* @{
|
|
|
|
HAL system support is including delay system, HAL tick system and global system clock,
|
|
|
|
HAL system tick setting:
|
|
|
|
- Attach HAL_IncTick() to system tick interrupt handler;
|
|
- Notify the HAL system the system's tick frequency by calling HAL_SetTickFreq() unless
|
|
it is the same as default value HAL_TICK_FREQ_1KHZ;
|
|
- If you need a more accurate delay system, specify SYS_TIMER in hal_conf.h.
|
|
|
|
Init HAL system:
|
|
|
|
- Initialize the HAL system by calling HAL_Init():
|
|
|
|
Reset when SOC system is changed:
|
|
|
|
- Update system with new core clock and new SysTick clock source by calling HAL_SystemCoreClockUpdate();
|
|
|
|
APIs:
|
|
|
|
- Get system time by calling HAL_GetTick().
|
|
- Delay for a certain length of time, HAL_DelayMs(), HAL_DelayUs(), and HAL_CPUDelayUs().
|
|
- Blocking for a certain period of time to continuously query HW status, use HAL_GetTick()
|
|
to do timeout, this will be more accurate.
|
|
- Get current cpu usage by calling HAL_GetCPUUsage().
|
|
|
|
@} */
|
|
|
|
/** @defgroup HAL_BASE_Private_Definition Private Definition
|
|
* @{
|
|
*/
|
|
/********************* Private MACRO Definition ******************************/
|
|
|
|
#define HAL_TICK_FREQ_DEFAULT HAL_TICK_FREQ_1KHZ
|
|
|
|
/********************* Private Structure Definition **************************/
|
|
|
|
/********************* Private Variable Definition ***************************/
|
|
|
|
static __IO uint32_t uwTick;
|
|
static eHAL_tickFreq uwTickFreq = HAL_TICK_FREQ_DEFAULT;
|
|
|
|
/********************* Private Function Definition ***************************/
|
|
#if defined(__CORTEX_A) || defined(__CORTEX_M)
|
|
#if __CORTEX_M == 0U || !defined(__GNUC__)
|
|
static void CPUCycleLoop(uint32_t cycles)
|
|
{
|
|
uint32_t count;
|
|
|
|
if (cycles < 100U) {
|
|
return;
|
|
}
|
|
|
|
count = cycles / 3;
|
|
while (count-- > 0) {
|
|
__asm volatile ("nop");
|
|
}
|
|
}
|
|
#else
|
|
static void CPUCycleLoop(uint32_t cycles)
|
|
{
|
|
__ASM volatile (
|
|
"mov r0, %0\n\t"
|
|
"adds r0, r0, #2\n\t" // 1 2 Round to the nearest multiple of 4.
|
|
"lsrs r0, r0, #2\n\t" // 1 2 Divide by 4 and set flags.
|
|
"beq 2f\n\t" // 2 2 Skip if 0.
|
|
".align 4\n\t"
|
|
"1:\n\t"
|
|
"adds r0, r0, #1\n\t" // 1 2 Increment the counter.
|
|
"subs r0, r0, #2\n\t" // 1 2 Decrement the counter by 2.
|
|
"bne 1b\n\t" // (1)2 2 2 CPU cycles (if branch is taken).
|
|
"nop\n\t" // 1 2 Loop alignment padding.
|
|
"2:"
|
|
: : "r" (cycles)
|
|
);
|
|
}
|
|
#endif
|
|
#elif defined(__RISC_V)
|
|
static void CPUCycleLoop(uint32_t cycles)
|
|
{
|
|
asm volatile (
|
|
"mv a0, %0\n\t"
|
|
"addi a0, a0, 2\n\t" // 1 2 Round to the nearest multiple of 4.
|
|
"li a1, 4\n\t"
|
|
"div a0, a0, a1\n\t" // 1 2 Divide by 4 and set flags.
|
|
"li a1, 2\n\t"
|
|
"bnez a0, 1f\n\t" // 2 2 Skip if 0.
|
|
"j 2f\n\t"
|
|
".align 6\n\t"
|
|
"1:\n\t"
|
|
"addi a0, a0, 1\n\t" // 1 2 Increment the counter.
|
|
"sub a0, a0, a1\n\t" // 1 2 Decrement the counter by 2.
|
|
"bnez a0, 1b\n\t" // (1)2 2 2 CPU cycles (if branch is taken).
|
|
"nop\n\t" // 1 2 Loop alignment padding.
|
|
"2:"
|
|
: : "r" (cycles)
|
|
);
|
|
}
|
|
#endif
|
|
|
|
#if defined(SYS_TIMER) && defined(HAL_TIMER_MODULE_ENABLED)
|
|
__STATIC_FORCEINLINE HAL_Status TimerDelayUs(uint32_t us)
|
|
{
|
|
uint64_t count, from, now, pass;
|
|
|
|
from = HAL_TIMER_GetCount(SYS_TIMER);
|
|
count = PLL_INPUT_OSC_RATE / 1000000 * us;
|
|
|
|
do {
|
|
now = HAL_TIMER_GetCount(SYS_TIMER);
|
|
pass = now > from ? now - from : from - now;
|
|
} while (pass < count);
|
|
|
|
return HAL_OK;
|
|
}
|
|
#endif
|
|
|
|
/** @} */
|
|
/********************* Public Function Definition ***************************/
|
|
/** @defgroup HAL_BASE_Exported_Functions_Group4 Init and DeInit Functions
|
|
|
|
This section provides functions allowing to init and deinit the module:
|
|
|
|
* @{
|
|
*/
|
|
|
|
/**
|
|
* @brief Init HAL driver basic code.
|
|
* @return HAL_OK.
|
|
*/
|
|
HAL_Status HAL_Init(void)
|
|
{
|
|
#ifdef __CORTEX_M
|
|
#ifdef HAL_NVIC_MODULE_ENABLED
|
|
/* Set Interrupt Group Priority */
|
|
HAL_NVIC_Init();
|
|
|
|
/* Set Interrupt Group Priority */
|
|
HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_DEFAULT);
|
|
#endif
|
|
#endif
|
|
|
|
#if defined(SYS_TIMER) && defined(HAL_TIMER_MODULE_ENABLED)
|
|
HAL_TIMER_SysTimerInit(SYS_TIMER);
|
|
#endif
|
|
|
|
#ifdef HAL_PINCTRL_MODULE_ENABLED
|
|
HAL_PINCTRL_Init();
|
|
#endif
|
|
|
|
return HAL_OK;
|
|
}
|
|
|
|
/**
|
|
* @brief HAL system update with new core clock and systick clock source.
|
|
* @param hz: new core clock.
|
|
* @param clkSource: new systick clock source.
|
|
* @return HAL_OK.
|
|
*/
|
|
HAL_Status HAL_SystemCoreClockUpdate(uint32_t hz, eHAL_systickClkSource clkSource)
|
|
{
|
|
uint32_t rate = hz;
|
|
HAL_Status ret = HAL_OK;
|
|
|
|
#if defined(__CORTEX_M) && defined(HAL_SYSTICK_MODULE_ENABLED)
|
|
ret = HAL_SYSTICK_CLKSourceConfig(clkSource);
|
|
if (ret == HAL_OK && clkSource == HAL_SYSTICK_CLKSRC_EXT) {
|
|
rate = PLL_INPUT_OSC_RATE;
|
|
}
|
|
HAL_SYSTICK_Config(rate / (1000 / HAL_GetTickFreq()));
|
|
ret = HAL_OK;
|
|
#endif
|
|
|
|
if (ret == HAL_OK) {
|
|
SystemCoreClock = rate; /* Update global SystemCoreClock */
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief HAL deinit.
|
|
* @return HAL_Status: HAL_OK.
|
|
*/
|
|
HAL_Status HAL_DeInit(void)
|
|
{
|
|
/* TO-DO */
|
|
|
|
return HAL_OK;
|
|
}
|
|
|
|
/** @} */
|
|
|
|
/** @defgroup HAL_BASE_Exported_Functions_Group5 Other Functions
|
|
* @{
|
|
*/
|
|
|
|
/**
|
|
* @brief Count plus tickFreq when interrupt occurs.
|
|
* @return HAL_Status: HAL_OK.
|
|
*/
|
|
HAL_Status HAL_IncTick(void)
|
|
{
|
|
uwTick += uwTickFreq;
|
|
|
|
return HAL_OK;
|
|
}
|
|
|
|
/**
|
|
* @brief Provides tick value in millisecond.
|
|
* @return uint32_t: tick value in millisecond.
|
|
* @attention this API allow direct use in the HAL layer.
|
|
*/
|
|
uint32_t HAL_GetTick(void)
|
|
{
|
|
#if defined(SYS_TIMER) && defined(HAL_TIMER_MODULE_ENABLED)
|
|
uint64_t tick = HAL_TIMER_GetCount(SYS_TIMER);
|
|
uint32_t base = PLL_INPUT_OSC_RATE / 1000;
|
|
|
|
if (tick >> 62) {
|
|
tick = ~tick;
|
|
}
|
|
|
|
return (uint32_t)HAL_DivU64(tick, base);
|
|
#else
|
|
|
|
return uwTick;
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* @brief Provides system timer count.
|
|
* @return uint64_t: timer count.
|
|
* @attention this API allow direct use in the HAL layer.
|
|
*/
|
|
uint64_t HAL_GetSysTimerCount(void)
|
|
{
|
|
#if defined(SYS_TIMER) && defined(HAL_TIMER_MODULE_ENABLED)
|
|
uint64_t count = HAL_TIMER_GetCount(SYS_TIMER);
|
|
if (count >> 62) {
|
|
count = ~count;
|
|
}
|
|
|
|
return count;
|
|
#else
|
|
|
|
return 0LLU;
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* @brief Set new tick frequency.
|
|
* @return HAL_Status.
|
|
*/
|
|
HAL_Status HAL_SetTickFreq(eHAL_tickFreq freq)
|
|
{
|
|
HAL_ASSERT(IS_TICKFREQ(freq));
|
|
|
|
uwTickFreq = freq;
|
|
|
|
return HAL_OK;
|
|
}
|
|
|
|
/**
|
|
* @brief Return tick frequency.
|
|
* @return uint32_t: tick period in Hz.
|
|
* @attention this API allow direct use in the HAL layer.
|
|
*/
|
|
eHAL_tickFreq HAL_GetTickFreq(void)
|
|
{
|
|
return uwTickFreq;
|
|
}
|
|
|
|
/**
|
|
* @brief SysTick mdelay.
|
|
* @param ms: mdelay count.
|
|
* @return HAL_Status: HAL_OK.
|
|
* @attention this API allow direct use in the HAL layer. Blocking for a
|
|
* certain period of time to continuously query HW status, use HAL_GetTick
|
|
* to do timeout, that will be more accurate.
|
|
*/
|
|
__WEAK HAL_Status HAL_DelayMs(uint32_t ms)
|
|
{
|
|
for (uint32_t i = 0; i < ms; i++) {
|
|
HAL_DelayUs(1000);
|
|
}
|
|
|
|
return HAL_OK;
|
|
}
|
|
|
|
/**
|
|
* @brief SysTick udelay.
|
|
* @param us: udelay count.
|
|
* @return HAL_Status: HAL_OK.
|
|
* @attention this API allow direct use in the HAL layer. The longer the delay,
|
|
* the more accurate. Actual delay is greater than the parameter.
|
|
*/
|
|
HAL_Status HAL_DelayUs(uint32_t us)
|
|
{
|
|
#if defined(SYS_TIMER) && defined(HAL_TIMER_MODULE_ENABLED)
|
|
|
|
return TimerDelayUs(us);
|
|
#else
|
|
|
|
return HAL_CPUDelayUs(us);
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* @brief CPU loop udelay.
|
|
* @param us: udelay count.
|
|
* @return HAL_Status: HAL_OK.
|
|
* @attention this API allow direct use in the HAL layer. The longer the delay,
|
|
* the more accurate. Actual delay is greater than the parameter.
|
|
* During delay, CPU rate change result in delay imprecise, so call it in
|
|
* following case:
|
|
* 1.IRQ disable
|
|
* 2.CRU code
|
|
*/
|
|
HAL_Status HAL_CPUDelayUs(uint32_t us)
|
|
{
|
|
volatile uint32_t cycles;
|
|
|
|
#if (__CORTEX_M == 0)
|
|
cycles = (uint32_t)HAL_DivU64((uint64_t)SystemCoreClock, 1000000) * us; /* Add few cycles penalty */
|
|
#else
|
|
cycles = SystemCoreClock / 1000000 * us; /* Add few cycles penalty */
|
|
#endif
|
|
|
|
CPUCycleLoop(cycles);
|
|
|
|
return HAL_OK;
|
|
}
|
|
|
|
#if defined(HAL_CPU_USAGE_ENABLED)
|
|
static uint64_t g_last_enter_idle_time = 0; /* Last time current CPU entered the idle state. */
|
|
static uint64_t g_total_idle_time = 0; /* Total time for current CPU to enter idle state. */
|
|
static uint64_t g_last_elapsed_time = 0; /* Last elapsed time for current CPU. */
|
|
|
|
/**
|
|
* @brief Get current CPU usage.
|
|
* @return 0-100
|
|
* @attention The cpu usage function depends on HAL_CPUEnterIdle function.
|
|
*/
|
|
uint32_t HAL_GetCPUUsage(void)
|
|
{
|
|
uint64_t elapsed_time, active_time, current_time;
|
|
uint32_t usage;
|
|
|
|
current_time = HAL_GetSysTimerCount();
|
|
elapsed_time = current_time - g_last_elapsed_time;
|
|
|
|
/* Prevent the risk of dividing by 0 caused by repeated calls for a short time. */
|
|
if (!elapsed_time) {
|
|
return 0;
|
|
}
|
|
|
|
HAL_ASSERT(elapsed_time > g_total_idle_time);
|
|
active_time = elapsed_time - g_total_idle_time;
|
|
usage = (active_time * 100) / elapsed_time;
|
|
g_total_idle_time = 0;
|
|
g_last_elapsed_time = current_time;
|
|
|
|
return usage;
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
* @brief CPU enter idle.
|
|
*/
|
|
void HAL_CPU_EnterIdle(void)
|
|
{
|
|
#if defined(HAL_CPU_USAGE_ENABLED)
|
|
uint64_t idle_time;
|
|
|
|
__disable_irq();
|
|
g_last_enter_idle_time = HAL_GetSysTimerCount();
|
|
#endif
|
|
|
|
// __asm__ volatile ("wfi");
|
|
|
|
#if defined(HAL_CPU_USAGE_ENABLED)
|
|
idle_time = HAL_GetSysTimerCount() - g_last_enter_idle_time;
|
|
g_total_idle_time += idle_time;
|
|
__enable_irq();
|
|
#endif
|
|
}
|
|
|
|
/** @} */
|
|
|
|
/** @} */
|
|
|
|
/** @} */
|
|
/********************* Public Function Definition ***************************/
|
|
/** @defgroup HAL_BASE_EX_Exported_Functions_Group5 Other Functions
|
|
* @{
|
|
*/
|
|
|
|
/**
|
|
* @brief uint64_t numerator / uint32_t denominator with remainder
|
|
* @param numerator
|
|
* @param denominator
|
|
* @param pRemainder [out] pointer to unsigned 32bit remainder
|
|
* @return uint64_t result. sets *pRemainder if pRemainder is not null
|
|
*/
|
|
uint64_t HAL_DivU64Rem(uint64_t numerator, uint32_t denominator, uint32_t *pRemainder)
|
|
{
|
|
uint64_t remainder = numerator;
|
|
uint64_t b = denominator;
|
|
uint64_t result;
|
|
uint64_t d = 1;
|
|
uint32_t high = numerator >> 32;
|
|
|
|
result = 0;
|
|
if (high >= denominator) {
|
|
high /= denominator;
|
|
result = (uint64_t)high << 32;
|
|
remainder -= (uint64_t)(high * denominator) << 32;
|
|
}
|
|
|
|
while ((int64_t)b > 0 && b < remainder) {
|
|
b = b + b;
|
|
d = d + d;
|
|
}
|
|
|
|
do {
|
|
if (remainder >= b) {
|
|
remainder -= b;
|
|
result += d;
|
|
}
|
|
b >>= 1;
|
|
d >>= 1;
|
|
} while (d);
|
|
|
|
if (pRemainder) {
|
|
*pRemainder = remainder;
|
|
}
|
|
|
|
return result;
|
|
}
|