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:
Scott Shawcroft 2018-06-05 12:38:31 -07:00
parent c01ce175bb
commit 2fbab8067a
8 changed files with 62 additions and 11 deletions

View File

@ -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;
}

View File

@ -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

View File

@ -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;

View File

@ -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(&current_ms, &current_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();

View File

@ -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);

View File

@ -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) {

View File

@ -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);

View File

@ -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);