atmel-samd: Make ticks more atomic.

Always use current_tick when sub millisecond precision is required.
Otherwise getting the ms/us to correspond is tricky.
This commit is contained in:
Scott Shawcroft 2018-05-31 16:47:18 -07:00
parent d0fb6e7a2f
commit 9920f0a5de
2 changed files with 45 additions and 7 deletions

View File

@ -42,13 +42,31 @@ void common_hal_mcu_delay_us(uint32_t delay) {
// Interrupt flags that will be saved and restored during disable/Enable
// interrupt functions below.
volatile hal_atomic_t flags;
// ASF4's interrupt disable doesn't handle duplicate calls
volatile uint32_t interrupt_flags;
volatile uint32_t nesting_count = 0;
void common_hal_mcu_disable_interrupts(void) {
atomic_enter_critical(&flags);
if (nesting_count == 0) {
interrupt_flags = __get_PRIMASK();
__disable_irq();
__DMB();
}
nesting_count++;
}
void common_hal_mcu_enable_interrupts(void) {
atomic_leave_critical(&flags);
if (nesting_count == 0) {
// This is very very bad because it means there was mismatched disable/enables so we
// "HardFault".
HardFault_Handler();
}
nesting_count--;
if (nesting_count > 0) {
return;
}
__DMB();
__set_PRIMASK(interrupt_flags);
}
extern uint32_t _ezero;

View File

@ -30,6 +30,7 @@
#include "supervisor/shared/autoreload.h"
#include "shared-module/gamepad/__init__.h"
#include "shared-bindings/microcontroller/__init__.h"
#include "shared-bindings/microcontroller/Processor.h"
// Global millisecond tick count
@ -38,8 +39,13 @@ volatile uint64_t ticks_ms = 0;
void SysTick_Handler(void) {
// SysTick interrupt handler called when the SysTick timer reaches zero
// (every millisecond).
common_hal_mcu_disable_interrupts();
ticks_ms += 1;
// Read the control register to reset the COUNTFLAG.
(void) SysTick->CTRL;
common_hal_mcu_enable_interrupts();
#ifdef CIRCUITPY_AUTORELOAD_DELAY_MS
autoreload_tick();
#endif
@ -61,7 +67,7 @@ void tick_delay(uint32_t us) {
uint32_t us_until_next_tick = SysTick->VAL / ticks_per_us;
uint32_t start_tick;
while (us >= us_until_next_tick) {
start_tick=SysTick->VAL; // wait for SysTick->VAL to RESET
start_tick = SysTick->VAL; // wait for SysTick->VAL to RESET
while (SysTick->VAL < start_tick) {}
us -= us_until_next_tick;
us_until_next_tick = 1000;
@ -72,11 +78,25 @@ void tick_delay(uint32_t us) {
// us counts down!
void current_tick(uint64_t* ms, uint32_t* us_until_ms) {
uint32_t ticks_per_us = common_hal_mcu_processor_get_frequency() / 1000 / 1000;
*ms = ticks_ms;
*us_until_ms = SysTick->VAL / ticks_per_us;
// We disable interrupts to prevent ticks_ms from changing while we grab it.
common_hal_mcu_disable_interrupts();
uint32_t tick_status = SysTick->CTRL;
uint32_t current_us = SysTick->VAL;
uint32_t tick_status2 = SysTick->CTRL;
uint64_t current_ms = ticks_ms;
// The second clause ensures our value actually rolled over. Its possible it hit zero between
// the VAL read and CTRL read.
if ((tick_status & SysTick_CTRL_COUNTFLAG_Msk) != 0 ||
((tick_status2 & SysTick_CTRL_COUNTFLAG_Msk) != 0 && current_us > ticks_per_us)) {
current_ms++;
}
common_hal_mcu_enable_interrupts();
*ms = current_ms;
*us_until_ms = current_us / ticks_per_us;
}
void wait_until(uint64_t ms, uint32_t us_until_ms) {
uint32_t ticks_per_us = common_hal_mcu_processor_get_frequency() / 1000 / 1000;
while(ticks_ms <= ms && SysTick->VAL / ticks_per_us >= us_until_ms) {}
while (ticks_ms <= ms && SysTick->VAL / ticks_per_us >= us_until_ms) {}
}