diff --git a/ports/raspberrypi/bindings/rp2pio/StateMachine.c b/ports/raspberrypi/bindings/rp2pio/StateMachine.c index 8d96c51dfe..3dffc39363 100644 --- a/ports/raspberrypi/bindings/rp2pio/StateMachine.c +++ b/ports/raspberrypi/bindings/rp2pio/StateMachine.c @@ -80,12 +80,13 @@ //| initial_sideset_pin_direction: int = 0x1f, //| exclusive_pin_use: bool = True, //| auto_pull: bool = False, -//| pull_threshold : int = 32, -//| out_shift_right : bool = True, +//| pull_threshold: int = 32, +//| out_shift_right: bool = True, //| wait_for_txstall: bool = True, //| auto_push: bool = False, -//| push_threshold : int = 32, -//| in_shift_right : bool = True) -> None: +//| push_threshold: int = 32, +//| in_shift_right: bool = True, +//| user_interruptible: bool = True) -> None: //| //| """Construct a StateMachine object on the given pins with the given program. //| @@ -126,7 +127,14 @@ //| :param int push_threshold: Number of bits to shift before saving the ISR value to the RX FIFO //| :param bool in_shift_right: When True, data is shifted into the right side (LSB) of the //| ISR. It is shifted into the left (MSB) otherwise. NOTE! This impacts data alignment -//| when the number of bytes is not a power of two (1, 2 or 4 bytes).""" +//| when the number of bytes is not a power of two (1, 2 or 4 bytes). +//| :param bool user_interruptible: When True (the default), +//| `write()`, `readinto()`, and `write_readinto()` can be interrupted by a ctrl-C. +//| This is useful when developing a PIO program: if there is an error in the program +//| that causes an infinite loop, you will be able to interrupt the loop. +//| However, if you are writing to a device that can get into a bad state if a read or write +//| is interrupted, you may want to set this to False after your program has been vetted. +//| """ //| ... //| @@ -143,7 +151,8 @@ STATIC mp_obj_t rp2pio_statemachine_make_new(const mp_obj_type_t *type, size_t n ARG_exclusive_pin_use, ARG_auto_pull, ARG_pull_threshold, ARG_out_shift_right, ARG_wait_for_txstall, - ARG_auto_push, ARG_push_threshold, ARG_in_shift_right}; + ARG_auto_push, ARG_push_threshold, ARG_in_shift_right, + ARG_user_interruptible,}; static const mp_arg_t allowed_args[] = { { MP_QSTR_program, MP_ARG_REQUIRED | MP_ARG_OBJ }, { MP_QSTR_frequency, MP_ARG_REQUIRED | MP_ARG_INT }, @@ -179,6 +188,7 @@ STATIC mp_obj_t rp2pio_statemachine_make_new(const mp_obj_type_t *type, size_t n { MP_QSTR_auto_push, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, { MP_QSTR_push_threshold, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 32} }, { MP_QSTR_in_shift_right, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = true} }, + { MP_QSTR_user_interruptible, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = true} }, }; mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); @@ -252,7 +262,8 @@ STATIC mp_obj_t rp2pio_statemachine_make_new(const mp_obj_type_t *type, size_t n args[ARG_exclusive_pin_use].u_bool, args[ARG_auto_pull].u_bool, pull_threshold, args[ARG_out_shift_right].u_bool, args[ARG_wait_for_txstall].u_bool, - args[ARG_auto_push].u_bool, push_threshold, args[ARG_in_shift_right].u_bool); + args[ARG_auto_push].u_bool, push_threshold, args[ARG_in_shift_right].u_bool, + args[ARG_user_interruptible].u_bool); return MP_OBJ_FROM_PTR(self); } diff --git a/ports/raspberrypi/bindings/rp2pio/StateMachine.h b/ports/raspberrypi/bindings/rp2pio/StateMachine.h index 295a414cd9..effa9c9ce3 100644 --- a/ports/raspberrypi/bindings/rp2pio/StateMachine.h +++ b/ports/raspberrypi/bindings/rp2pio/StateMachine.h @@ -49,7 +49,8 @@ void common_hal_rp2pio_statemachine_construct(rp2pio_statemachine_obj_t *self, bool exclusive_pin_use, bool auto_pull, uint8_t pull_threshold, bool out_shift_right, bool wait_for_txstall, - bool auto_push, uint8_t push_threshold, bool in_shift_right); + bool auto_push, uint8_t push_threshold, bool in_shift_right, + bool user_interruptible); void common_hal_rp2pio_statemachine_deinit(rp2pio_statemachine_obj_t *self); bool common_hal_rp2pio_statemachine_deinited(rp2pio_statemachine_obj_t *self); diff --git a/ports/raspberrypi/common-hal/audiobusio/I2SOut.c b/ports/raspberrypi/common-hal/audiobusio/I2SOut.c index 425b65e601..0ac11c690f 100644 --- a/ports/raspberrypi/common-hal/audiobusio/I2SOut.c +++ b/ports/raspberrypi/common-hal/audiobusio/I2SOut.c @@ -132,7 +132,8 @@ void common_hal_audiobusio_i2sout_construct(audiobusio_i2sout_obj_t *self, true, // exclusive pin use false, 32, false, // shift out left to start with MSB false, // Wait for txstall - false, 32, false); // in settings + false, 32, false, // in settings + false); // Not user-interruptible. self->playing = false; audio_dma_init(&self->dma); diff --git a/ports/raspberrypi/common-hal/audiobusio/PDMIn.c b/ports/raspberrypi/common-hal/audiobusio/PDMIn.c index 4dc14eaeda..323a3dfbc8 100644 --- a/ports/raspberrypi/common-hal/audiobusio/PDMIn.c +++ b/ports/raspberrypi/common-hal/audiobusio/PDMIn.c @@ -76,7 +76,8 @@ void common_hal_audiobusio_pdmin_construct(audiobusio_pdmin_obj_t *self, true, // exclusive pin use false, 32, false, // out settings false, // Wait for txstall - false, 32, true); // in settings + false, 32, true, // in settings + false); // Not user-interruptible uint32_t actual_frequency = common_hal_rp2pio_statemachine_get_frequency(&self->state_machine); if (actual_frequency < MIN_MIC_CLOCK) { diff --git a/ports/raspberrypi/common-hal/displayio/ParallelBus.c b/ports/raspberrypi/common-hal/displayio/ParallelBus.c index 5b97c79521..93477e2453 100644 --- a/ports/raspberrypi/common-hal/displayio/ParallelBus.c +++ b/ports/raspberrypi/common-hal/displayio/ParallelBus.c @@ -104,8 +104,8 @@ void common_hal_displayio_parallelbus_construct(displayio_parallelbus_obj_t *sel true, // exclusive pin usage true, 8, true, // TX, auto pull every 8 bits. shift left to output msb first false, // wait for TX stall - false, 32, true // RX setting we don't use - ); + false, 32, true, // RX setting we don't use + false); // Not user-interruptible. common_hal_rp2pio_statemachine_never_reset(&self->state_machine); } diff --git a/ports/raspberrypi/common-hal/imagecapture/ParallelImageCapture.c b/ports/raspberrypi/common-hal/imagecapture/ParallelImageCapture.c index 0b388f4b3e..1d0355d1c0 100644 --- a/ports/raspberrypi/common-hal/imagecapture/ParallelImageCapture.c +++ b/ports/raspberrypi/common-hal/imagecapture/ParallelImageCapture.c @@ -116,7 +116,9 @@ void common_hal_imagecapture_parallelimagecapture_construct(imagecapture_paralle true, // exclusive pin use false, 32, false, // out settings false, // wait for txstall - true, 32, true); // in settings + true, 32, true, // in settings + false); // Not user-interruptible. + PIO pio = self->state_machine.pio; uint8_t pio_index = pio_get_index(pio); diff --git a/ports/raspberrypi/common-hal/neopixel_write/__init__.c b/ports/raspberrypi/common-hal/neopixel_write/__init__.c index 3f923c8e0b..b3d704453c 100644 --- a/ports/raspberrypi/common-hal/neopixel_write/__init__.c +++ b/ports/raspberrypi/common-hal/neopixel_write/__init__.c @@ -76,7 +76,8 @@ void common_hal_neopixel_write(const digitalio_digitalinout_obj_t *digitalinout, true, 8, false, // TX, auto pull every 8 bits. shift left to output msb first true, // Wait for txstall. If we don't, then we'll deinit too quickly. false, 32, true, // RX setting we don't use - false); // claim pins + false, // claim pins + false); // Not user-interruptible. if (!ok) { // Do nothing. Maybe bitbang? return; diff --git a/ports/raspberrypi/common-hal/pulseio/PulseIn.c b/ports/raspberrypi/common-hal/pulseio/PulseIn.c index b9210af5f0..4e9b56a1a1 100644 --- a/ports/raspberrypi/common-hal/pulseio/PulseIn.c +++ b/ports/raspberrypi/common-hal/pulseio/PulseIn.c @@ -72,7 +72,8 @@ void common_hal_pulseio_pulsein_construct(pulseio_pulsein_obj_t *self, false, 8, false, // TX, unused false, true, 32, true, // RX auto-push every 32 bits - false); // claim pins + false, // claim pins + false); // Not user-interruptible. if (!ok) { mp_raise_RuntimeError(translate("All state machines in use")); diff --git a/ports/raspberrypi/common-hal/rotaryio/IncrementalEncoder.c b/ports/raspberrypi/common-hal/rotaryio/IncrementalEncoder.c index d5251f6598..54bbd71f6d 100644 --- a/ports/raspberrypi/common-hal/rotaryio/IncrementalEncoder.c +++ b/ports/raspberrypi/common-hal/rotaryio/IncrementalEncoder.c @@ -89,7 +89,8 @@ void common_hal_rotaryio_incrementalencoder_construct(rotaryio_incrementalencode true, // exclusive pin use false, 32, false, // out settings false, // Wait for txstall - false, 32, false); // in settings + false, 32, false, // in settings + false); // Not user-interruptible. common_hal_rp2pio_statemachine_run(&self->state_machine, encoder_init, MP_ARRAY_SIZE(encoder_init)); diff --git a/ports/raspberrypi/common-hal/rp2pio/StateMachine.c b/ports/raspberrypi/common-hal/rp2pio/StateMachine.c index 6594dd9c6f..2962ddd9fc 100644 --- a/ports/raspberrypi/common-hal/rp2pio/StateMachine.c +++ b/ports/raspberrypi/common-hal/rp2pio/StateMachine.c @@ -163,7 +163,9 @@ bool rp2pio_statemachine_construct(rp2pio_statemachine_obj_t *self, bool auto_pull, uint8_t pull_threshold, bool out_shift_right, bool wait_for_txstall, bool auto_push, uint8_t push_threshold, bool in_shift_right, - bool claim_pins) { + bool claim_pins, + bool user_interruptible + ) { // Create a program id that isn't the pointer so we can store it without storing the original object. uint32_t program_id = ~((uint32_t)program); @@ -303,6 +305,7 @@ bool rp2pio_statemachine_construct(rp2pio_statemachine_obj_t *self, self->out_shift_right = out_shift_right; self->in_shift_right = in_shift_right; self->wait_for_txstall = wait_for_txstall; + self->user_interruptible = user_interruptible; self->init = init; self->init_len = init_len; @@ -338,7 +341,8 @@ void common_hal_rp2pio_statemachine_construct(rp2pio_statemachine_obj_t *self, bool exclusive_pin_use, bool auto_pull, uint8_t pull_threshold, bool out_shift_right, bool wait_for_txstall, - bool auto_push, uint8_t push_threshold, bool in_shift_right) { + bool auto_push, uint8_t push_threshold, bool in_shift_right, + bool user_interruptible) { // First, check that all pins are free OR already in use by any PIO if exclusive_pin_use is false. uint32_t pins_we_use = wait_gpio_mask; @@ -482,7 +486,8 @@ void common_hal_rp2pio_statemachine_construct(rp2pio_statemachine_obj_t *self, if (initial_pin_direction & (pull_up | pull_down)) { mp_raise_ValueError(translate("pull masks conflict with direction masks")); } - bool ok = rp2pio_statemachine_construct(self, + bool ok = rp2pio_statemachine_construct( + self, program, program_len, frequency, init, init_len, @@ -497,7 +502,8 @@ void common_hal_rp2pio_statemachine_construct(rp2pio_statemachine_obj_t *self, auto_pull, pull_threshold, out_shift_right, wait_for_txstall, auto_push, push_threshold, in_shift_right, - true /* claim pins */); + true /* claim pins */, + user_interruptible); if (!ok) { mp_raise_RuntimeError(translate("All state machines in use")); } @@ -664,6 +670,16 @@ static bool _transfer(rp2pio_statemachine_obj_t *self, (tx && dma_channel_is_busy(chan_tx))) { // TODO: We should idle here until we get a DMA interrupt or something else. RUN_BACKGROUND_TASKS; + if (self->user_interruptible && mp_hal_is_interrupted()) { + if (rx && dma_channel_is_busy(chan_rx)) { + dma_channel_abort(chan_rx); + } + if (tx && dma_channel_is_busy(chan_tx)) { + dma_channel_abort(chan_tx); + } + break; + } + } // Clear the stall bit so we can detect when the state machine is done transmitting. self->pio->fdebug = stall_mask; @@ -678,7 +694,7 @@ static bool _transfer(rp2pio_statemachine_obj_t *self, dma_channel_unclaim(chan_tx); } - if (!use_dma) { + if (!use_dma && !(self->user_interruptible && mp_hal_is_interrupted())) { // Use software for small transfers, or if couldn't claim two DMA channels size_t rx_remaining = in_len / in_stride_in_bytes; size_t tx_remaining = out_len / out_stride_in_bytes; @@ -707,6 +723,9 @@ static bool _transfer(rp2pio_statemachine_obj_t *self, --rx_remaining; } RUN_BACKGROUND_TASKS; + if (self->user_interruptible && mp_hal_is_interrupted()) { + break; + } } // Clear the stall bit so we can detect when the state machine is done transmitting. self->pio->fdebug = stall_mask; @@ -717,6 +736,9 @@ static bool _transfer(rp2pio_statemachine_obj_t *self, while (!pio_sm_is_tx_fifo_empty(self->pio, self->state_machine) || (self->wait_for_txstall && (self->pio->fdebug & stall_mask) == 0)) { RUN_BACKGROUND_TASKS; + if (self->user_interruptible && mp_hal_is_interrupted()) { + break; + } } } return true; diff --git a/ports/raspberrypi/common-hal/rp2pio/StateMachine.h b/ports/raspberrypi/common-hal/rp2pio/StateMachine.h index b04d9c5049..f877bdb152 100644 --- a/ports/raspberrypi/common-hal/rp2pio/StateMachine.h +++ b/ports/raspberrypi/common-hal/rp2pio/StateMachine.h @@ -43,15 +43,16 @@ typedef struct { uint32_t initial_pin_direction; uint32_t pull_pin_up; uint32_t pull_pin_down; + uint tx_dreq; + uint rx_dreq; + uint32_t actual_frequency; + pio_sm_config sm_config; bool in; bool out; bool wait_for_txstall; - uint tx_dreq; - uint rx_dreq; bool out_shift_right; bool in_shift_right; - uint32_t actual_frequency; - pio_sm_config sm_config; + bool user_interruptible; uint8_t offset; } rp2pio_statemachine_obj_t; @@ -73,7 +74,8 @@ bool rp2pio_statemachine_construct(rp2pio_statemachine_obj_t *self, bool auto_pull, uint8_t pull_threshold, bool out_shift_right, bool wait_for_txstall, bool auto_push, uint8_t push_threshold, bool in_shift_right, - bool claim_pins); + bool claim_pins, + bool interruptible); uint8_t rp2pio_statemachine_program_offset(rp2pio_statemachine_obj_t *self); void rp2pio_statemachine_set_wrap(rp2pio_statemachine_obj_t *self, uint wrap_target, uint wrap);