Fix SAMD
RTC needed to wait for sync. NeoPixel on SAMD doesn't need disabled caches. It just needed timing adjustment for 120mhz clock speed.
This commit is contained in:
parent
1a0b4193b7
commit
5c33c9d597
3
main.c
3
main.c
|
@ -485,11 +485,10 @@ STATIC bool run_code_py(safe_mode_t safe_mode) {
|
||||||
time_to_next_change = MIN(time_to_next_change, time_to_epaper_refresh);
|
time_to_next_change = MIN(time_to_next_change, time_to_epaper_refresh);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
if (time_to_next_change > 0) {
|
|
||||||
// time_to_next_change is in ms and ticks are slightly shorter so
|
// time_to_next_change is in ms and ticks are slightly shorter so
|
||||||
// we'll undersleep just a little. It shouldn't matter.
|
// we'll undersleep just a little. It shouldn't matter.
|
||||||
port_interrupt_after_ticks(time_to_next_change);
|
port_interrupt_after_ticks(time_to_next_change);
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
port_idle_until_interrupt();
|
port_idle_until_interrupt();
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
// QSPI Data pins
|
// QSPI Data pins
|
||||||
#define MICROPY_PORT_A (PORT_PA08 | PORT_PA09 | PORT_PA10 | PORT_PA11)
|
#define MICROPY_PORT_A (PORT_PA08 | PORT_PA09 | PORT_PA10 | PORT_PA11)
|
||||||
// QSPI CS, QSPI SCK and NeoPixel pin
|
// QSPI CS, QSPI SCK and NeoPixel pin
|
||||||
#define MICROPY_PORT_B (PORT_PB03 | PORT_PB10 | PORT_PB11)
|
#define MICROPY_PORT_B (PORT_PB10 | PORT_PB11)
|
||||||
#define MICROPY_PORT_C (0)
|
#define MICROPY_PORT_C (0)
|
||||||
#define MICROPY_PORT_D (0)
|
#define MICROPY_PORT_D (0)
|
||||||
|
|
||||||
|
|
|
@ -60,7 +60,7 @@ static void neopixel_send_buffer_core(volatile uint32_t *clraddr, uint32_t pinMa
|
||||||
" movs r6, #3; d2: sub r6, #1; bne d2;" // delay 3
|
" movs r6, #3; d2: sub r6, #1; bne d2;" // delay 3
|
||||||
#endif
|
#endif
|
||||||
#ifdef SAM_D5X_E5X
|
#ifdef SAM_D5X_E5X
|
||||||
" movs r6, #3; d2: subs r6, #1; bne d2;" // delay 3
|
" movs r6, #16; d2: subs r6, #1; bne d2;" // delay 3
|
||||||
#endif
|
#endif
|
||||||
" tst r4, r5;" // mask&r5
|
" tst r4, r5;" // mask&r5
|
||||||
" bne skipclr;"
|
" bne skipclr;"
|
||||||
|
@ -70,7 +70,7 @@ static void neopixel_send_buffer_core(volatile uint32_t *clraddr, uint32_t pinMa
|
||||||
" movs r6, #6; d0: sub r6, #1; bne d0;" // delay 6
|
" movs r6, #6; d0: sub r6, #1; bne d0;" // delay 6
|
||||||
#endif
|
#endif
|
||||||
#ifdef SAM_D5X_E5X
|
#ifdef SAM_D5X_E5X
|
||||||
" movs r6, #6; d0: subs r6, #1; bne d0;" // delay 6
|
" movs r6, #16; d0: subs r6, #1; bne d0;" // delay 6
|
||||||
#endif
|
#endif
|
||||||
" str r1, [r0, #0];" // clr (possibly again, doesn't matter)
|
" str r1, [r0, #0];" // clr (possibly again, doesn't matter)
|
||||||
#ifdef SAMD21
|
#ifdef SAMD21
|
||||||
|
@ -85,10 +85,13 @@ static void neopixel_send_buffer_core(volatile uint32_t *clraddr, uint32_t pinMa
|
||||||
" movs r6, #2; d1: sub r6, #1; bne d1;" // delay 2
|
" movs r6, #2; d1: sub r6, #1; bne d1;" // delay 2
|
||||||
#endif
|
#endif
|
||||||
#ifdef SAM_D5X_E5X
|
#ifdef SAM_D5X_E5X
|
||||||
" movs r6, #2; d1: subs r6, #1; bne d1;" // delay 2
|
" movs r6, #15; d1: subs r6, #1; bne d1;" // delay 2
|
||||||
#endif
|
#endif
|
||||||
" b loopBit;"
|
" b loopBit;"
|
||||||
"nextbyte:"
|
"nextbyte:"
|
||||||
|
#ifdef SAM_D5X_E5X
|
||||||
|
" movs r6, #12; d3: subs r6, #1; bne d3;" // delay 2
|
||||||
|
#endif
|
||||||
" cmp r2, r3;"
|
" cmp r2, r3;"
|
||||||
" bcs neopixel_stop;"
|
" bcs neopixel_stop;"
|
||||||
" b loopLoad;"
|
" b loopLoad;"
|
||||||
|
@ -114,42 +117,12 @@ void common_hal_neopixel_write(const digitalio_digitalinout_obj_t *digitalinout,
|
||||||
// Turn off interrupts of any kind during timing-sensitive code.
|
// Turn off interrupts of any kind during timing-sensitive code.
|
||||||
mp_hal_disable_all_interrupts();
|
mp_hal_disable_all_interrupts();
|
||||||
|
|
||||||
|
|
||||||
#ifdef SAM_D5X_E5X
|
|
||||||
// When this routine is positioned at certain addresses, the timing logic
|
|
||||||
// below can be too fast by about 2.5x. This is some kind of (un)fortunate code
|
|
||||||
// positioning with respect to a cache line.
|
|
||||||
// Theoretically we should turn on off the CMCC caches and the
|
|
||||||
// NVM caches to ensure consistent timing. Testing shows the the NVMCTRL
|
|
||||||
// cache disabling seems to make the difference. But turn both off to make sure.
|
|
||||||
// It's difficult to test because additions to the code before the timing loop
|
|
||||||
// below change instruction placement. (though this should be less true now that
|
|
||||||
// the main code is in the cache-aligned function neopixel_send_buffer_core)
|
|
||||||
// Testing was done by adding cache changes below the loop (so only the
|
|
||||||
// first time through is wrong).
|
|
||||||
//
|
|
||||||
// Turn off instruction, data, and NVM caches to force consistent timing.
|
|
||||||
// Invalidate existing cache entries.
|
|
||||||
hri_cmcc_set_CFG_reg(CMCC, CMCC_CFG_DCDIS | CMCC_CFG_ICDIS);
|
|
||||||
hri_cmcc_write_MAINT0_reg(CMCC, CMCC_MAINT0_INVALL);
|
|
||||||
hri_nvmctrl_set_CTRLA_CACHEDIS0_bit(NVMCTRL);
|
|
||||||
hri_nvmctrl_set_CTRLA_CACHEDIS1_bit(NVMCTRL);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
uint32_t pin = digitalinout->pin->number;
|
uint32_t pin = digitalinout->pin->number;
|
||||||
port = &PORT->Group[GPIO_PORT(pin)]; // Convert GPIO # to port register
|
port = &PORT->Group[GPIO_PORT(pin)]; // Convert GPIO # to port register
|
||||||
pinMask = (1UL << (pin % 32)); // From port_pin_set_output_level ASF code.
|
pinMask = (1UL << (pin % 32)); // From port_pin_set_output_level ASF code.
|
||||||
volatile uint32_t *clr = &(port->OUTCLR.reg);
|
volatile uint32_t *clr = &(port->OUTCLR.reg);
|
||||||
neopixel_send_buffer_core(clr, pinMask, pixels, numBytes);
|
neopixel_send_buffer_core(clr, pinMask, pixels, numBytes);
|
||||||
|
|
||||||
#ifdef SAM_D5X_E5X
|
|
||||||
// Turn instruction, data, and NVM caches back on.
|
|
||||||
hri_cmcc_clear_CFG_reg(CMCC, CMCC_CFG_DCDIS | CMCC_CFG_ICDIS);
|
|
||||||
hri_nvmctrl_clear_CTRLA_CACHEDIS0_bit(NVMCTRL);
|
|
||||||
hri_nvmctrl_clear_CTRLA_CACHEDIS1_bit(NVMCTRL);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Update the next start.
|
// Update the next start.
|
||||||
next_start_raw_ticks = port_get_raw_ticks(NULL) + 4;
|
next_start_raw_ticks = port_get_raw_ticks(NULL) + 4;
|
||||||
|
|
||||||
|
|
|
@ -462,6 +462,8 @@ static uint32_t _get_count(uint64_t *overflow_count) {
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
volatile bool _woken_up;
|
||||||
|
|
||||||
static void _port_interrupt_after_ticks(uint32_t ticks) {
|
static void _port_interrupt_after_ticks(uint32_t ticks) {
|
||||||
uint32_t current_ticks = _get_count(NULL);
|
uint32_t current_ticks = _get_count(NULL);
|
||||||
if (ticks > 1 << 28) {
|
if (ticks > 1 << 28) {
|
||||||
|
@ -473,9 +475,16 @@ static void _port_interrupt_after_ticks(uint32_t ticks) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
RTC->MODE0.COMP[0].reg = current_ticks + (ticks << 4);
|
uint32_t target = current_ticks + (ticks << 4);
|
||||||
|
RTC->MODE0.COMP[0].reg = target;
|
||||||
|
#ifdef SAM_D5X_E5X
|
||||||
|
while ((RTC->MODE0.SYNCBUSY.reg & (RTC_MODE0_SYNCBUSY_COMP0)) != 0) {
|
||||||
|
}
|
||||||
|
#endif
|
||||||
RTC->MODE0.INTFLAG.reg = RTC_MODE0_INTFLAG_CMP0;
|
RTC->MODE0.INTFLAG.reg = RTC_MODE0_INTFLAG_CMP0;
|
||||||
RTC->MODE0.INTENSET.reg = RTC_MODE0_INTENSET_CMP0;
|
RTC->MODE0.INTENSET.reg = RTC_MODE0_INTENSET_CMP0;
|
||||||
|
current_ticks = _get_count(NULL);
|
||||||
|
_woken_up = current_ticks >= target;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RTC_Handler(void) {
|
void RTC_Handler(void) {
|
||||||
|
@ -485,15 +494,18 @@ void RTC_Handler(void) {
|
||||||
// Our RTC is 32 bits and we're clocking it at 16.384khz which is 16 (2 ** 4) subticks per
|
// Our RTC is 32 bits and we're clocking it at 16.384khz which is 16 (2 ** 4) subticks per
|
||||||
// tick.
|
// tick.
|
||||||
overflowed_ticks += (1L << (32 - 4));
|
overflowed_ticks += (1L << (32 - 4));
|
||||||
|
}
|
||||||
#ifdef SAM_D5X_E5X
|
#ifdef SAM_D5X_E5X
|
||||||
} else if (intflag & RTC_MODE0_INTFLAG_PER2) {
|
if (intflag & RTC_MODE0_INTFLAG_PER2) {
|
||||||
RTC->MODE0.INTFLAG.reg = RTC_MODE0_INTFLAG_PER2;
|
RTC->MODE0.INTFLAG.reg = RTC_MODE0_INTFLAG_PER2;
|
||||||
// Do things common to all ports when the tick occurs
|
// Do things common to all ports when the tick occurs
|
||||||
supervisor_tick();
|
supervisor_tick();
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
} else if (intflag & RTC_MODE0_INTFLAG_CMP0) {
|
if (intflag & RTC_MODE0_INTFLAG_CMP0) {
|
||||||
// Clear the interrupt because we may have hit a sleep and _ticks_enabled
|
// Clear the interrupt because we may have hit a sleep and _ticks_enabled
|
||||||
RTC->MODE0.INTFLAG.reg = RTC_MODE0_INTFLAG_CMP0;
|
RTC->MODE0.INTFLAG.reg = RTC_MODE0_INTFLAG_CMP0;
|
||||||
|
_woken_up = true;
|
||||||
#ifdef SAMD21
|
#ifdef SAMD21
|
||||||
if (_ticks_enabled) {
|
if (_ticks_enabled) {
|
||||||
// Do things common to all ports when the tick occurs.
|
// Do things common to all ports when the tick occurs.
|
||||||
|
@ -565,7 +577,7 @@ void port_idle_until_interrupt(void) {
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
common_hal_mcu_disable_interrupts();
|
common_hal_mcu_disable_interrupts();
|
||||||
if (!tud_task_event_ready() && !hold_interrupt) {
|
if (!tud_task_event_ready() && !hold_interrupt && !_woken_up) {
|
||||||
__DSB();
|
__DSB();
|
||||||
__WFI();
|
__WFI();
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,7 @@
|
||||||
#define MICROPY_HW_BOARD_NAME "Adafruit Feather nRF52840 Express"
|
#define MICROPY_HW_BOARD_NAME "Adafruit Feather nRF52840 Express"
|
||||||
#define MICROPY_HW_MCU_NAME "nRF52840"
|
#define MICROPY_HW_MCU_NAME "nRF52840"
|
||||||
|
|
||||||
// #define MICROPY_HW_NEOPIXEL (&pin_P0_16)
|
#define MICROPY_HW_NEOPIXEL (&pin_P0_16)
|
||||||
|
|
||||||
#define MICROPY_HW_LED_STATUS (&pin_P1_15)
|
#define MICROPY_HW_LED_STATUS (&pin_P1_15)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue