Skip to content
Snippets Groups Projects
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
No related branches found
No related tags found
1 merge request!475change(pycardium): Switch systick to 32 kHz clock source
Pipeline #5255 passed
......@@ -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);
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment