Skip to content

Commit 2fbab80

Browse files
committed
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
1 parent c01ce17 commit 2fbab80

File tree

8 files changed

+62
-11
lines changed

8 files changed

+62
-11
lines changed

ports/atmel-samd/background.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,19 @@
2626
#include "background.h"
2727

2828
#include "audio_dma.h"
29+
#include "tick.h"
2930
#include "usb.h"
3031
#include "usb_mass_storage.h"
3132

33+
volatile uint64_t last_finished_tick = 0;
34+
3235
void run_background_tasks(void) {
3336
audio_dma_background();
3437
usb_msc_background();
3538
usb_cdc_background();
39+
last_finished_tick = ticks_ms;
40+
}
41+
42+
bool background_tasks_ok(void) {
43+
return ticks_ms - last_finished_tick < 1000;
3644
}

ports/atmel-samd/background.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@
2727
#ifndef MICROPY_INCLUDED_ATMEL_SAMD_BACKGROUND_H
2828
#define MICROPY_INCLUDED_ATMEL_SAMD_BACKGROUND_H
2929

30+
#include <stdbool.h>
31+
3032
void run_background_tasks(void);
33+
bool background_tasks_ok(void);
3134

3235
#endif // MICROPY_INCLUDED_ATMEL_SAMD_BACKGROUND_H

ports/atmel-samd/common-hal/microcontroller/__init__.c

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -40,18 +40,10 @@ void common_hal_mcu_delay_us(uint32_t delay) {
4040
mp_hal_delay_us(delay);
4141
}
4242

43-
// Interrupt flags that will be saved and restored during disable/Enable
44-
// interrupt functions below.
45-
46-
// ASF4's interrupt disable doesn't handle duplicate calls
47-
volatile uint32_t interrupt_flags;
4843
volatile uint32_t nesting_count = 0;
4944
void common_hal_mcu_disable_interrupts(void) {
50-
if (nesting_count == 0) {
51-
interrupt_flags = __get_PRIMASK();
52-
__disable_irq();
53-
__DMB();
54-
}
45+
__disable_irq();
46+
__DMB();
5547
nesting_count++;
5648
}
5749

@@ -66,7 +58,7 @@ void common_hal_mcu_enable_interrupts(void) {
6658
return;
6759
}
6860
__DMB();
69-
__set_PRIMASK(interrupt_flags);
61+
__enable_irq();
7062
}
7163

7264
extern uint32_t _ezero;

ports/atmel-samd/common-hal/pulseio/PulseIn.c

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include "atmel_start_pins.h"
3232
#include "hal/include/hal_gpio.h"
3333

34+
#include "background.h"
3435
#include "mpconfigport.h"
3536
#include "py/gc.h"
3637
#include "py/runtime.h"
@@ -60,10 +61,16 @@ void pulsein_interrupt_handler(uint8_t channel) {
6061
uint32_t current_us;
6162
uint64_t current_ms;
6263
current_tick(&current_ms, &current_us);
64+
6365
// current_tick gives us the remaining us until the next tick but we want the number since the
6466
// last ms.
6567
current_us = 1000 - current_us;
6668
pulseio_pulsein_obj_t* self = get_eic_channel_data(channel);
69+
if (!background_tasks_ok() || self->errored_too_fast) {
70+
self->errored_too_fast = true;
71+
common_hal_pulseio_pulsein_pause(self);
72+
return;
73+
}
6774
if (self->first_edge) {
6875
self->first_edge = false;
6976
pulsein_set_config(self, false);
@@ -118,6 +125,7 @@ void common_hal_pulseio_pulsein_construct(pulseio_pulsein_obj_t* self,
118125
self->first_edge = true;
119126
self->last_us = 0;
120127
self->last_ms = 0;
128+
self->errored_too_fast = 0;
121129

122130
set_eic_channel_data(pin->extint_channel, (void*) self);
123131

@@ -157,6 +165,9 @@ void common_hal_pulseio_pulsein_resume(pulseio_pulsein_obj_t* self,
157165
// Make sure we're paused.
158166
common_hal_pulseio_pulsein_pause(self);
159167

168+
// Reset erroring
169+
self->errored_too_fast = false;
170+
160171
// Send the trigger pulse.
161172
if (trigger_duration > 0) {
162173
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) {
207218
return self->len;
208219
}
209220

221+
bool common_hal_pulseio_pulsein_get_paused(pulseio_pulsein_obj_t* self) {
222+
uint32_t mask = 1 << self->channel;
223+
return (EIC->INTENSET.reg & (mask << EIC_INTENSET_EXTINT_Pos)) == 0;
224+
}
225+
210226
uint16_t common_hal_pulseio_pulsein_get_item(pulseio_pulsein_obj_t* self,
211227
int16_t index) {
212228
common_hal_mcu_disable_interrupts();

ports/atmel-samd/common-hal/pulseio/PulseIn.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ typedef struct {
4343
volatile bool first_edge;
4444
volatile uint64_t last_ms;
4545
volatile uint16_t last_us;
46+
volatile bool errored_too_fast;
4647
} pulseio_pulsein_obj_t;
4748

4849
void pulsein_reset(void);

ports/atmel-samd/tick.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,13 @@ void tick_init() {
6060
uint32_t ticks_per_ms = common_hal_mcu_processor_get_frequency() / 1000;
6161
SysTick_Config(ticks_per_ms-1);
6262
NVIC_EnableIRQ(SysTick_IRQn);
63+
// Set all peripheral interrupt priorities to the lowest priority by default.
64+
for (uint16_t i = 0; i < PERIPH_COUNT_IRQn; i++) {
65+
NVIC_SetPriority(i, (1UL << __NVIC_PRIO_BITS) - 1UL);
66+
}
67+
// Bump up the systick interrupt.
68+
NVIC_SetPriority(SysTick_IRQn, 1);
69+
NVIC_SetPriority(USB_IRQn, 1);
6370
}
6471

6572
void tick_delay(uint32_t us) {

shared-bindings/pulseio/PulseIn.c

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,26 @@ const mp_obj_property_t pulseio_pulsein_maxlen_obj = {
219219
(mp_obj_t)&mp_const_none_obj},
220220
};
221221

222+
//| .. attribute:: paused
223+
//|
224+
//| True when pulse capture is paused as a result of :py:func:`pause` or an error during capture
225+
//| such as a signal that is too fast.
226+
//|
227+
STATIC mp_obj_t pulseio_pulsein_obj_get_paused(mp_obj_t self_in) {
228+
pulseio_pulsein_obj_t *self = MP_OBJ_TO_PTR(self_in);
229+
raise_error_if_deinited(common_hal_pulseio_pulsein_deinited(self));
230+
231+
return mp_obj_new_bool(common_hal_pulseio_pulsein_get_paused(self));
232+
}
233+
MP_DEFINE_CONST_FUN_OBJ_1(pulseio_pulsein_get_paused_obj, pulseio_pulsein_obj_get_paused);
234+
235+
const mp_obj_property_t pulseio_pulsein_paused_obj = {
236+
.base.type = &mp_type_property,
237+
.proxy = {(mp_obj_t)&pulseio_pulsein_get_paused_obj,
238+
(mp_obj_t)&mp_const_none_obj,
239+
(mp_obj_t)&mp_const_none_obj},
240+
};
241+
222242
//| .. method:: __len__()
223243
//|
224244
//| Returns the current pulse length
@@ -285,7 +305,10 @@ STATIC const mp_rom_map_elem_t pulseio_pulsein_locals_dict_table[] = {
285305
{ MP_ROM_QSTR(MP_QSTR_resume), MP_ROM_PTR(&pulseio_pulsein_resume_obj) },
286306
{ MP_ROM_QSTR(MP_QSTR_clear), MP_ROM_PTR(&pulseio_pulsein_clear_obj) },
287307
{ MP_ROM_QSTR(MP_QSTR_popleft), MP_ROM_PTR(&pulseio_pulsein_popleft_obj) },
308+
309+
// Properties
288310
{ MP_ROM_QSTR(MP_QSTR_maxlen), MP_ROM_PTR(&pulseio_pulsein_maxlen_obj) },
311+
{ MP_ROM_QSTR(MP_QSTR_paused), MP_ROM_PTR(&pulseio_pulsein_paused_obj) },
289312
};
290313
STATIC MP_DEFINE_CONST_DICT(pulseio_pulsein_locals_dict, pulseio_pulsein_locals_dict_table);
291314

shared-bindings/pulseio/PulseIn.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ extern void common_hal_pulseio_pulsein_resume(pulseio_pulsein_obj_t* self, uint1
4141
extern void common_hal_pulseio_pulsein_clear(pulseio_pulsein_obj_t* self);
4242
extern uint16_t common_hal_pulseio_pulsein_popleft(pulseio_pulsein_obj_t* self);
4343
extern uint16_t common_hal_pulseio_pulsein_get_maxlen(pulseio_pulsein_obj_t* self);
44+
extern bool common_hal_pulseio_pulsein_get_paused(pulseio_pulsein_obj_t* self);
4445
extern uint16_t common_hal_pulseio_pulsein_get_len(pulseio_pulsein_obj_t* self);
4546
extern uint16_t common_hal_pulseio_pulsein_get_item(pulseio_pulsein_obj_t* self, int16_t index);
4647

0 commit comments

Comments
 (0)