Commit 77bd73ba authored by Rahix's avatar Rahix
Browse files

Merge 'Switch systick to 32 kHz clock source'

See merge request !475
parents e99885b5 e524d839
Pipeline #5255 passed with stages
in 1 minute and 47 seconds
......@@ -20,6 +20,30 @@
#include <stdio.h>
#include <string.h>
// Smallest interval which can be reached exactly
#define SYSTICK_INTERVAL_US 15625ULL
#define SYSTICK_FREQ_HZ (1000000 / SYSTICK_INTERVAL_US)
/*
* Copied from core_cm4.h and modified to select the
* 32768 Hz RTC crystal as the clock source */
static uint32_t systick_config(uint32_t ticks)
{
if ((ticks - 1) > SysTick_LOAD_RELOAD_Msk)
return (1); /* Reload value impossible */
SysTick->LOAD = ticks - 1; /* set reload register */
NVIC_SetPriority(
SysTick_IRQn,
(1 << __NVIC_PRIO_BITS) -
1); /* set Priority for Systick Interrupt */
SysTick->VAL = 0; /* Load the SysTick Counter Value */
SysTick->CTRL =
SysTick_CTRL_TICKINT_Msk |
SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */
return (0); /* Function successful */
}
/* Initialize everything for MicroPython */
void pycardium_hal_init(void)
{
......@@ -33,9 +57,9 @@ void pycardium_hal_init(void)
epic_interrupt_enable(EPIC_INT_UART_RX);
/*
* Configure SysTick timer for 1ms period.
* Configure SysTick timer for SYSTICK_INTERVAL_US period.
*/
SysTick_Config(SystemCoreClock / 1000);
systick_config(32768 / SYSTICK_FREQ_HZ);
}
/******************************************************************************
......@@ -141,14 +165,21 @@ void SysTick_Handler(void)
*/
static uint64_t systick_get_us()
{
uint32_t val, count;
uint32_t irqsaved = __get_PRIMASK();
__set_PRIMASK(0);
uint64_t counts_per_us = SystemCoreClock / 1000000;
uint64_t us = systick_count * 1000 +
(SysTick->LOAD - SysTick->VAL) / counts_per_us;
/* The asynchronous/slow clocking of the systick means that
* its value can jump to 0 before the interrupt is triggered.
* Simply wait until it is not 0 and then read the count. */
do {
__set_PRIMASK(0);
val = SysTick->VAL;
count = systick_count;
__set_PRIMASK(irqsaved);
} while (val == 0);
__set_PRIMASK(irqsaved);
uint64_t us = count * SYSTICK_INTERVAL_US +
(SysTick->LOAD - val) * 1000000ULL / 32768;
return us;
}
......@@ -160,14 +191,12 @@ static void systick_delay_precise(uint32_t us)
* instruction, read the current timer value to ensure as little skew as
* possible.
*
* Subtract 0.3us (constant_offset) to account for the duration of the
* calculations.
* Accuracy is about 30 us (due to the 32 kHz systick)
*/
uint32_t count_to_overflow = SysTick->VAL;
uint32_t count_reload = SysTick->LOAD;
uint32_t clocks_per_us = SystemCoreClock / 1000000;
uint32_t constant_offset = clocks_per_us * 3 / 10;
uint32_t delay_count = us * clocks_per_us - constant_offset;
uint32_t delay_count = us * 32768 / 1000000;
/*
* Calculate the final count for both paths. Marked as volatile so the
......@@ -203,14 +232,14 @@ static void systick_delay_sleep(uint32_t us)
while (1) {
uint64_t now = systick_get_us();
if (now >= final_time) {
if ((now + SYSTICK_INTERVAL_US) > final_time) {
break;
}
/*
* Sleep with WFI if more than 1ms of delay is remaining. The
* SysTick interrupt is guaranteed to happen within any timespan
* of 1ms.
* Sleep with WFI if more than SYSTICK_INTERVAL_US of delay
* is remaining. The SysTick interrupt is guaranteed to
* happen within any timespan of SYSTICK_INTERVAL_US.
*
* Use a critical section encompassing both the check and the
* WFI to prevent a race-condition where the interrupt happens
......@@ -218,7 +247,7 @@ static void systick_delay_sleep(uint32_t us)
*/
uint32_t irqsaved = __get_PRIMASK();
__set_PRIMASK(0);
if ((now + 1000) < final_time) {
if ((now + SYSTICK_INTERVAL_US) < final_time) {
__WFI();
}
__set_PRIMASK(irqsaved);
......@@ -234,6 +263,11 @@ static void systick_delay_sleep(uint32_t us)
*/
mp_handle_pending(true);
}
uint64_t now = systick_get_us();
if (now < final_time) {
systick_delay_precise(final_time - now);
}
}
static void systick_delay(uint32_t us)
......@@ -243,12 +277,12 @@ static void systick_delay(uint32_t us)
/*
* For very short delays, use the systick_delay_precise() function which
* delays with a microsecond accuracy. For anything >1ms, use
* delays with a microsecond accuracy. For anything >SYSTICK_INTERVAL_US, use
* systick_delay_sleep() which puts the CPU to sleep when nothing is
* happening and also checks for MicroPython interrupts every now and
* then.
*/
if (us < 1000) {
if (us < SYSTICK_INTERVAL_US) {
systick_delay_precise(us);
} else {
systick_delay_sleep(us);
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment