Prevent freezing USB during high frequency PulseIn.
We now track the last time the background task ran and bail on the PulseIn if it starves the background work. In practice, this happens after the numbers from pulsein are no longer accurate. This also adjusts interrupt priorities so most are the lowest level except for the tick and USB interrupts. Fixes #516 and #876
This commit is contained in:
parent
c01ce175bb
commit
2fbab8067a
@ -26,11 +26,19 @@
|
||||
#include "background.h"
|
||||
|
||||
#include "audio_dma.h"
|
||||
#include "tick.h"
|
||||
#include "usb.h"
|
||||
#include "usb_mass_storage.h"
|
||||
|
||||
volatile uint64_t last_finished_tick = 0;
|
||||
|
||||
void run_background_tasks(void) {
|
||||
audio_dma_background();
|
||||
usb_msc_background();
|
||||
usb_cdc_background();
|
||||
last_finished_tick = ticks_ms;
|
||||
}
|
||||
|
||||
bool background_tasks_ok(void) {
|
||||
return ticks_ms - last_finished_tick < 1000;
|
||||
}
|
||||
|
@ -27,6 +27,9 @@
|
||||
#ifndef MICROPY_INCLUDED_ATMEL_SAMD_BACKGROUND_H
|
||||
#define MICROPY_INCLUDED_ATMEL_SAMD_BACKGROUND_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
void run_background_tasks(void);
|
||||
bool background_tasks_ok(void);
|
||||
|
||||
#endif // MICROPY_INCLUDED_ATMEL_SAMD_BACKGROUND_H
|
||||
|
@ -40,18 +40,10 @@ void common_hal_mcu_delay_us(uint32_t delay) {
|
||||
mp_hal_delay_us(delay);
|
||||
}
|
||||
|
||||
// Interrupt flags that will be saved and restored during disable/Enable
|
||||
// interrupt functions below.
|
||||
|
||||
// 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) {
|
||||
if (nesting_count == 0) {
|
||||
interrupt_flags = __get_PRIMASK();
|
||||
__disable_irq();
|
||||
__DMB();
|
||||
}
|
||||
__disable_irq();
|
||||
__DMB();
|
||||
nesting_count++;
|
||||
}
|
||||
|
||||
@ -66,7 +58,7 @@ void common_hal_mcu_enable_interrupts(void) {
|
||||
return;
|
||||
}
|
||||
__DMB();
|
||||
__set_PRIMASK(interrupt_flags);
|
||||
__enable_irq();
|
||||
}
|
||||
|
||||
extern uint32_t _ezero;
|
||||
|
@ -31,6 +31,7 @@
|
||||
#include "atmel_start_pins.h"
|
||||
#include "hal/include/hal_gpio.h"
|
||||
|
||||
#include "background.h"
|
||||
#include "mpconfigport.h"
|
||||
#include "py/gc.h"
|
||||
#include "py/runtime.h"
|
||||
@ -60,10 +61,16 @@ void pulsein_interrupt_handler(uint8_t channel) {
|
||||
uint32_t current_us;
|
||||
uint64_t current_ms;
|
||||
current_tick(¤t_ms, ¤t_us);
|
||||
|
||||
// current_tick gives us the remaining us until the next tick but we want the number since the
|
||||
// last ms.
|
||||
current_us = 1000 - current_us;
|
||||
pulseio_pulsein_obj_t* self = get_eic_channel_data(channel);
|
||||
if (!background_tasks_ok() || self->errored_too_fast) {
|
||||
self->errored_too_fast = true;
|
||||
common_hal_pulseio_pulsein_pause(self);
|
||||
return;
|
||||
}
|
||||
if (self->first_edge) {
|
||||
self->first_edge = false;
|
||||
pulsein_set_config(self, false);
|
||||
@ -118,6 +125,7 @@ void common_hal_pulseio_pulsein_construct(pulseio_pulsein_obj_t* self,
|
||||
self->first_edge = true;
|
||||
self->last_us = 0;
|
||||
self->last_ms = 0;
|
||||
self->errored_too_fast = 0;
|
||||
|
||||
set_eic_channel_data(pin->extint_channel, (void*) self);
|
||||
|
||||
@ -157,6 +165,9 @@ void common_hal_pulseio_pulsein_resume(pulseio_pulsein_obj_t* self,
|
||||
// Make sure we're paused.
|
||||
common_hal_pulseio_pulsein_pause(self);
|
||||
|
||||
// Reset erroring
|
||||
self->errored_too_fast = false;
|
||||
|
||||
// Send the trigger pulse.
|
||||
if (trigger_duration > 0) {
|
||||
gpio_set_pin_pull_mode(self->pin, GPIO_PULL_OFF);
|
||||
@ -207,6 +218,11 @@ uint16_t common_hal_pulseio_pulsein_get_len(pulseio_pulsein_obj_t* self) {
|
||||
return self->len;
|
||||
}
|
||||
|
||||
bool common_hal_pulseio_pulsein_get_paused(pulseio_pulsein_obj_t* self) {
|
||||
uint32_t mask = 1 << self->channel;
|
||||
return (EIC->INTENSET.reg & (mask << EIC_INTENSET_EXTINT_Pos)) == 0;
|
||||
}
|
||||
|
||||
uint16_t common_hal_pulseio_pulsein_get_item(pulseio_pulsein_obj_t* self,
|
||||
int16_t index) {
|
||||
common_hal_mcu_disable_interrupts();
|
||||
|
@ -43,6 +43,7 @@ typedef struct {
|
||||
volatile bool first_edge;
|
||||
volatile uint64_t last_ms;
|
||||
volatile uint16_t last_us;
|
||||
volatile bool errored_too_fast;
|
||||
} pulseio_pulsein_obj_t;
|
||||
|
||||
void pulsein_reset(void);
|
||||
|
@ -60,6 +60,13 @@ void tick_init() {
|
||||
uint32_t ticks_per_ms = common_hal_mcu_processor_get_frequency() / 1000;
|
||||
SysTick_Config(ticks_per_ms-1);
|
||||
NVIC_EnableIRQ(SysTick_IRQn);
|
||||
// Set all peripheral interrupt priorities to the lowest priority by default.
|
||||
for (uint16_t i = 0; i < PERIPH_COUNT_IRQn; i++) {
|
||||
NVIC_SetPriority(i, (1UL << __NVIC_PRIO_BITS) - 1UL);
|
||||
}
|
||||
// Bump up the systick interrupt.
|
||||
NVIC_SetPriority(SysTick_IRQn, 1);
|
||||
NVIC_SetPriority(USB_IRQn, 1);
|
||||
}
|
||||
|
||||
void tick_delay(uint32_t us) {
|
||||
|
@ -219,6 +219,26 @@ const mp_obj_property_t pulseio_pulsein_maxlen_obj = {
|
||||
(mp_obj_t)&mp_const_none_obj},
|
||||
};
|
||||
|
||||
//| .. attribute:: paused
|
||||
//|
|
||||
//| True when pulse capture is paused as a result of :py:func:`pause` or an error during capture
|
||||
//| such as a signal that is too fast.
|
||||
//|
|
||||
STATIC mp_obj_t pulseio_pulsein_obj_get_paused(mp_obj_t self_in) {
|
||||
pulseio_pulsein_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
raise_error_if_deinited(common_hal_pulseio_pulsein_deinited(self));
|
||||
|
||||
return mp_obj_new_bool(common_hal_pulseio_pulsein_get_paused(self));
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(pulseio_pulsein_get_paused_obj, pulseio_pulsein_obj_get_paused);
|
||||
|
||||
const mp_obj_property_t pulseio_pulsein_paused_obj = {
|
||||
.base.type = &mp_type_property,
|
||||
.proxy = {(mp_obj_t)&pulseio_pulsein_get_paused_obj,
|
||||
(mp_obj_t)&mp_const_none_obj,
|
||||
(mp_obj_t)&mp_const_none_obj},
|
||||
};
|
||||
|
||||
//| .. method:: __len__()
|
||||
//|
|
||||
//| Returns the current pulse length
|
||||
@ -285,7 +305,10 @@ STATIC const mp_rom_map_elem_t pulseio_pulsein_locals_dict_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR_resume), MP_ROM_PTR(&pulseio_pulsein_resume_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_clear), MP_ROM_PTR(&pulseio_pulsein_clear_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_popleft), MP_ROM_PTR(&pulseio_pulsein_popleft_obj) },
|
||||
|
||||
// Properties
|
||||
{ MP_ROM_QSTR(MP_QSTR_maxlen), MP_ROM_PTR(&pulseio_pulsein_maxlen_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_paused), MP_ROM_PTR(&pulseio_pulsein_paused_obj) },
|
||||
};
|
||||
STATIC MP_DEFINE_CONST_DICT(pulseio_pulsein_locals_dict, pulseio_pulsein_locals_dict_table);
|
||||
|
||||
|
@ -41,6 +41,7 @@ extern void common_hal_pulseio_pulsein_resume(pulseio_pulsein_obj_t* self, uint1
|
||||
extern void common_hal_pulseio_pulsein_clear(pulseio_pulsein_obj_t* self);
|
||||
extern uint16_t common_hal_pulseio_pulsein_popleft(pulseio_pulsein_obj_t* self);
|
||||
extern uint16_t common_hal_pulseio_pulsein_get_maxlen(pulseio_pulsein_obj_t* self);
|
||||
extern bool common_hal_pulseio_pulsein_get_paused(pulseio_pulsein_obj_t* self);
|
||||
extern uint16_t common_hal_pulseio_pulsein_get_len(pulseio_pulsein_obj_t* self);
|
||||
extern uint16_t common_hal_pulseio_pulsein_get_item(pulseio_pulsein_obj_t* self, int16_t index);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user