diff --git a/ports/mimxrt/Makefile b/ports/mimxrt/Makefile index 85e77b978e..3a001182a0 100644 --- a/ports/mimxrt/Makefile +++ b/ports/mimxrt/Makefile @@ -82,6 +82,7 @@ SRC_TINYUSB_IMX_C += \ $(MCU_DIR)/project_template/clock_config.c \ $(MCU_DIR)/drivers/fsl_clock.c \ $(MCU_DIR)/drivers/fsl_gpio.c \ + $(MCU_DIR)/drivers/fsl_gpt.c \ $(MCU_DIR)/drivers/fsl_common.c \ $(MCU_DIR)/drivers/fsl_lpuart.c \ $(MCU_DIR)/drivers/fsl_flexram.c \ @@ -90,6 +91,7 @@ SRC_C = \ main.c \ led.c \ pin.c \ + ticks.c \ tusb_port.c \ board_init.c \ $(BOARD_DIR)/flash_config.c \ diff --git a/ports/mimxrt/board_init.c b/ports/mimxrt/board_init.c index cd7dc9a7dd..9095c88048 100644 --- a/ports/mimxrt/board_init.c +++ b/ports/mimxrt/board_init.c @@ -53,9 +53,6 @@ void board_init(void) { // Enable IOCON clock CLOCK_EnableClock(kCLOCK_Iomuxc); - // 1ms tick timer - SysTick_Config(SystemCoreClock / 1000); - // ------------- USB0 ------------- // // Clock @@ -85,10 +82,6 @@ void board_init(void) { // CLOCK_EnableUsbhs1Clock(kCLOCK_Usb480M, 480000000U); } -void SysTick_Handler(void) { - systick_ms++; -} - void USB_OTG1_IRQHandler(void) { tud_int_handler(0); tud_task(); diff --git a/ports/mimxrt/main.c b/ports/mimxrt/main.c index e06283a8ab..7832afffcb 100644 --- a/ports/mimxrt/main.c +++ b/ports/mimxrt/main.c @@ -32,6 +32,7 @@ #include "py/stackctrl.h" #include "lib/utils/gchelper.h" #include "lib/utils/pyexec.h" +#include "ticks.h" #include "tusb.h" #include "led.h" @@ -41,6 +42,7 @@ void board_init(void); int main(void) { board_init(); + ticks_init(); tusb_init(); led_init(); diff --git a/ports/mimxrt/mphalport.c b/ports/mimxrt/mphalport.c index 9fee3e85d0..02a61a4f2a 100644 --- a/ports/mimxrt/mphalport.c +++ b/ports/mimxrt/mphalport.c @@ -27,6 +27,7 @@ #include "py/runtime.h" #include "py/mphal.h" +#include "ticks.h" #include "tusb.h" #include CPU_HEADER_H @@ -46,22 +47,6 @@ void mp_hal_set_interrupt_char(int c) { #endif -void mp_hal_delay_ms(mp_uint_t ms) { - ms += 1; - uint32_t t0 = systick_ms; - while (systick_ms - t0 < ms) { - MICROPY_EVENT_POLL_HOOK - } -} - -void mp_hal_delay_us(mp_uint_t us) { - uint32_t ms = us / 1000 + 1; - uint32_t t0 = systick_ms; - while (systick_ms - t0 < ms) { - __WFI(); - } -} - int mp_hal_stdin_rx_chr(void) { for (;;) { // TODO diff --git a/ports/mimxrt/mphalport.h b/ports/mimxrt/mphalport.h index 2b9e49432c..993e151566 100644 --- a/ports/mimxrt/mphalport.h +++ b/ports/mimxrt/mphalport.h @@ -28,21 +28,29 @@ #define MICROPY_INCLUDED_MIMXRT_MPHALPORT_H #include +#include "ticks.h" #define mp_hal_pin_high(p) (GPIO_PinWrite(p->gpio, p->pin, 1U)) #define mp_hal_pin_low(p) (GPIO_PinWrite(p->gpio, p->pin, 0U)) #define mp_hal_pin_toggle(p) (GPIO_PortToggle(p->gpio, (1 << p->pin))) -extern volatile uint32_t systick_ms; - void mp_hal_set_interrupt_char(int c); static inline mp_uint_t mp_hal_ticks_ms(void) { - return systick_ms; + return ticks_ms32(); } static inline mp_uint_t mp_hal_ticks_us(void) { - return systick_ms * 1000; + return ticks_us32(); +} + +static inline void mp_hal_delay_ms(mp_uint_t ms) { + uint64_t us = (uint64_t)ms * 1000; + ticks_delay_us64(us); +} + +static inline void mp_hal_delay_us(mp_uint_t us) { + ticks_delay_us64(us); } static inline mp_uint_t mp_hal_ticks_cpu(void) { diff --git a/ports/mimxrt/ticks.c b/ports/mimxrt/ticks.c new file mode 100644 index 0000000000..676f81b30e --- /dev/null +++ b/ports/mimxrt/ticks.c @@ -0,0 +1,137 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "py/mphal.h" +#include "ticks.h" +#include "fsl_gpt.h" + +// General purpose timer for keeping microsecond and millisecond tick values. +#define GPTx GPT2 +#define GPTx_IRQn GPT2_IRQn +#define GPTx_IRQHandler GPT2_IRQHandler + +static uint32_t ticks_us64_upper; +static uint32_t ticks_ms_upper; + +void ticks_init(void) { + ticks_us64_upper = 0; + ticks_ms_upper = 0; + + gpt_config_t config; + config.clockSource = kGPT_ClockSource_Osc; + config.divider = 24; // XTAL is 24MHz + config.enableFreeRun = true; + config.enableRunInWait = true; + config.enableRunInStop = true; + config.enableRunInDoze = true; + config.enableRunInDbg = false; + config.enableMode = true; + GPT_Init(GPTx, &config); + + GPT_EnableInterrupts(GPTx, kGPT_RollOverFlagInterruptEnable); + NVIC_SetPriority(GPTx_IRQn, 0); // highest priority + NVIC_EnableIRQ(GPTx_IRQn); + + GPT_StartTimer(GPTx); +} + +void GPTx_IRQHandler(void) { + if (GPT_GetStatusFlags(GPTx, kGPT_OutputCompare1Flag)) { + GPT_ClearStatusFlags(GPTx, kGPT_OutputCompare1Flag); + GPT_DisableInterrupts(GPTx, kGPT_OutputCompare1InterruptEnable); + __SEV(); + } + if (GPT_GetStatusFlags(GPTx, kGPT_RollOverFlag)) { + GPT_ClearStatusFlags(GPTx, kGPT_RollOverFlag); + ++ticks_us64_upper; + if (++ticks_ms_upper >= 1000) { + // Wrap upper counter at a multiple of 1000 so that when mp_hal_ticks_ms() + // wraps due to overflow it wraps smoothly. + ticks_ms_upper = 0; + } + } +} + +static void ticks_wake_after_us32(uint32_t us) { + if (us < 2) { + // Delay too short to guarantee that we won't miss it when setting the OCR below. + __SEV(); + } else { + // Disable IRQs so setting the OCR is done without any interruption. + uint32_t irq_state = DisableGlobalIRQ(); + GPT_EnableInterrupts(GPTx, kGPT_OutputCompare1InterruptEnable); + uint32_t oc = GPT_GetCurrentTimerCount(GPTx) + us; + GPT_SetOutputCompareValue(GPTx, kGPT_OutputCompare_Channel1, oc); + EnableGlobalIRQ(irq_state); + } +} + +static uint64_t ticks_us64_with(uint32_t *upper_ptr) { + uint32_t irq_state = DisableGlobalIRQ(); + uint32_t lower = GPT_GetCurrentTimerCount(GPTx); + uint32_t upper = *upper_ptr; + uint32_t overflow = GPT_GetStatusFlags(GPTx, kGPT_RollOverFlag); + EnableGlobalIRQ(irq_state); + if (overflow && lower < 0x80000000) { + // The timer counter overflowed before reading it but the IRQ handler + // has not yet been called, so perform the IRQ arithmetic now. + ++upper; + } + return (uint64_t)upper << 32 | (uint64_t)lower; +} + +uint32_t ticks_us32(void) { + return GPT_GetCurrentTimerCount(GPTx); +} + +uint64_t ticks_us64(void) { + return ticks_us64_with(&ticks_us64_upper); +} + +uint32_t ticks_ms32(void) { + // This will return a value that only has the lower 32-bits valid. + return ticks_us64_with(&ticks_ms_upper) / 1000; +} + +void ticks_delay_us64(uint64_t us) { + uint64_t t0 = ticks_us64(); + for (;;) { + uint64_t dt = ticks_us64() - t0; + if (dt >= us) { + return; + } + dt = us - dt; + if (dt > 0xffffffff) { + dt = 0xffffffff; + } + ticks_wake_after_us32((uint32_t)dt); + if (dt < 50) { + __WFE(); + } else { + MICROPY_EVENT_POLL_HOOK + } + } +} diff --git a/ports/mimxrt/ticks.h b/ports/mimxrt/ticks.h new file mode 100644 index 0000000000..11127c29cc --- /dev/null +++ b/ports/mimxrt/ticks.h @@ -0,0 +1,35 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef MICROPY_INCLUDED_MIMXRT_TICKS_H +#define MICROPY_INCLUDED_MIMXRT_TICKS_H + +void ticks_init(void); +uint32_t ticks_us32(void); +uint64_t ticks_us64(void); +uint32_t ticks_ms32(void); +void ticks_delay_us64(uint64_t us); + +#endif // MICROPY_INCLUDED_MIMXRT_TICKS_H