Revamp background writing
Now a 'once' and a 'loop' buffer can be specified. 'once' is useful for things like writing a neopixel strip in the background, if you can guarantee the buffer contents are stable until the write is complete. 'loop' is useful for periodic things, like pwm & servos. both together are useful for some special cases of pwm/servo, where a transitional waveform needs to be played for one repetition and then a new waveform needs to be played after that. The API is renamed to reflect that it's a more generic 'background' operation.
This commit is contained in:
parent
457aba79f4
commit
989fb828d4
@ -1530,6 +1530,11 @@ msgstr ""
|
|||||||
msgid "Microphone startup delay must be in range 0.0 to 1.0"
|
msgid "Microphone startup delay must be in range 0.0 to 1.0"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: ports/raspberrypi/bindings/rp2pio/StateMachine.c
|
||||||
|
#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c
|
||||||
|
msgid "Mismatched data size"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: ports/mimxrt10xx/common-hal/busio/SPI.c ports/stm/common-hal/busio/SPI.c
|
#: ports/mimxrt10xx/common-hal/busio/SPI.c ports/stm/common-hal/busio/SPI.c
|
||||||
msgid "Missing MISO or MOSI Pin"
|
msgid "Missing MISO or MOSI Pin"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -3019,7 +3024,7 @@ msgstr ""
|
|||||||
msgid "complex values not supported"
|
msgid "complex values not supported"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: extmod/moduzlib.c shared-module/zlib/DecompIO.c
|
#: extmod/moduzlib.c
|
||||||
msgid "compression header"
|
msgid "compression header"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -459,8 +459,8 @@ void isr_dma_0(void) {
|
|||||||
dma->channels_to_load_mask |= mask;
|
dma->channels_to_load_mask |= mask;
|
||||||
background_callback_add(&dma->callback, dma_callback_fun, (void *)dma);
|
background_callback_add(&dma->callback, dma_callback_fun, (void *)dma);
|
||||||
}
|
}
|
||||||
if (MP_STATE_PORT(continuous_pio)[i] != NULL) {
|
if (MP_STATE_PORT(background_pio)[i] != NULL) {
|
||||||
rp2pio_statemachine_obj_t *pio = MP_STATE_PORT(continuous_pio)[i];
|
rp2pio_statemachine_obj_t *pio = MP_STATE_PORT(background_pio)[i];
|
||||||
rp2pio_statemachine_dma_complete(pio, i);
|
rp2pio_statemachine_dma_complete(pio, i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -426,63 +426,79 @@ STATIC mp_obj_t rp2pio_statemachine_write(size_t n_args, const mp_obj_t *pos_arg
|
|||||||
}
|
}
|
||||||
MP_DEFINE_CONST_FUN_OBJ_KW(rp2pio_statemachine_write_obj, 2, rp2pio_statemachine_write);
|
MP_DEFINE_CONST_FUN_OBJ_KW(rp2pio_statemachine_write_obj, 2, rp2pio_statemachine_write);
|
||||||
|
|
||||||
//| def start_continuous_write(self, buffer: Optional[ReadableBuffer], *, start: int = 0, end: Optional[int] = None) -> None:
|
//| def background_write(self, once: Optional[ReadableBuffer]=None, *, loop=Optional[ReadableBuffer]=None) -> None:
|
||||||
//| """Write the data contained in ``buffer`` to the state machine repeatedly until stopped. If the buffer is empty or None, an existing continuous_write is canceled.
|
//| """Write data to the TX fifo in the background, with optional looping.
|
||||||
|
//|
|
||||||
|
//| First, if any previous ``once`` or ``loop`` buffer has not been started, this function blocks until they have.
|
||||||
|
//| This means that any ``once`` or ``loop`` buffer will be written at least once.
|
||||||
|
//| Then the ``once`` and/or ``loop`` buffers are queued. and the function returns.
|
||||||
|
//| The ``once`` buffer (if specified) will be written just once.
|
||||||
|
//| Finally, the ``loop`` buffer (if specified) will continue being looped indefinitely.
|
||||||
//|
|
//|
|
||||||
//| Writes to the FIFO will match the input buffer's element size. For example, bytearray elements
|
//| Writes to the FIFO will match the input buffer's element size. For example, bytearray elements
|
||||||
//| will perform 8 bit writes to the PIO FIFO. The RP2040's memory bus will duplicate the value into
|
//| will perform 8 bit writes to the PIO FIFO. The RP2040's memory bus will duplicate the value into
|
||||||
//| the other byte positions. So, pulling more data in the PIO assembly will read the duplicated values.
|
//| the other byte positions. So, pulling more data in the PIO assembly will read the duplicated values.
|
||||||
//|
|
//|
|
||||||
//| To perform 16 or 32 bits writes into the FIFO use an `array.array` with a type code of the desired
|
//| To perform 16 or 32 bits writes into the FIFO use an `array.array` with a type code of the desired
|
||||||
//| size, or use `memoryview.cast` to change the interpretation of an existing buffer.
|
//| size, or use `memoryview.cast` to change the interpretation of an
|
||||||
|
//| existing buffer. To send just part of a larger buffer, slice a `memoryview`
|
||||||
|
//| of it.
|
||||||
//|
|
//|
|
||||||
//| To atomically change from one buffer to another, simply call
|
//| If a buffer is modified while it is being written out, the updated
|
||||||
//| `StateMachine.start_continuous_write` again with a different buffer with the same element size.
|
|
||||||
//| The call will only return once DMA has started putting the previous
|
|
||||||
//| buffer's data into the PIO FIFO.
|
|
||||||
//|
|
|
||||||
//| If the buffer is modified while it is being written out, the updated
|
|
||||||
//| values will be used. However, because of interactions between CPU
|
//| values will be used. However, because of interactions between CPU
|
||||||
//| writes, DMA and the PIO FIFO are complex, it is difficult to predict
|
//| writes, DMA and the PIO FIFO are complex, it is difficult to predict
|
||||||
//| the result of modifying multiple values. Instead, alternate between
|
//| the result of modifying multiple values. Instead, alternate between
|
||||||
//| a pair of buffers.
|
//| a pair of buffers.
|
||||||
//|
|
//|
|
||||||
//| :param ~circuitpython_typing.ReadableBuffer buffer: Write out the data in this buffer
|
//| Having both a ``once`` and a ``loop`` parameter is to support a special case in PWM generation
|
||||||
//| :param int start: Start of the slice of ``buffer`` to write out: ``buffer[start:end]``
|
//| where a change in duty cycle requires a special transitional buffer to be used exactly once. Most
|
||||||
//| :param int end: End of the slice; this index is not included. Defaults to ``len(buffer)``"""
|
//| use cases will probably only use one of ``once`` or ``loop``.
|
||||||
|
//|
|
||||||
|
//| :param ~Optional[circuitpython_typing.ReadableBuffer] once: Data to be written once
|
||||||
|
//| :param ~Optional[circuitpython_typing.ReadableBuffer] loop: Data to be written repeatedly
|
||||||
|
//| """
|
||||||
//| ...
|
//| ...
|
||||||
//|
|
//|
|
||||||
STATIC mp_obj_t rp2pio_statemachine_start_continuous_write(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
|
||||||
enum { ARG_buffer, ARG_start, ARG_end };
|
STATIC void fill_buf_info(sm_buf_info *info, mp_obj_t obj, size_t *stride_in_bytes) {
|
||||||
|
if (obj != mp_const_none) {
|
||||||
|
info->obj = obj;
|
||||||
|
mp_get_buffer_raise(obj, &info->info, MP_BUFFER_READ);
|
||||||
|
size_t stride = mp_binary_get_size('@', info->info.typecode, NULL);
|
||||||
|
if (stride > 4) {
|
||||||
|
mp_raise_ValueError(translate("Buffer elements must be 4 bytes long or less"));
|
||||||
|
}
|
||||||
|
if (*stride_in_bytes && stride != *stride_in_bytes) {
|
||||||
|
mp_raise_ValueError(translate("Mismatched data size"));
|
||||||
|
}
|
||||||
|
*stride_in_bytes = stride;
|
||||||
|
} else {
|
||||||
|
memset(info, 0, sizeof(*info));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
STATIC mp_obj_t rp2pio_statemachine_background_write(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||||
|
enum { ARG_once, ARG_loop };
|
||||||
static const mp_arg_t allowed_args[] = {
|
static const mp_arg_t allowed_args[] = {
|
||||||
{ MP_QSTR_buffer, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
|
{ MP_QSTR_once, MP_ARG_OBJ, {.u_obj = mp_const_none} },
|
||||||
{ MP_QSTR_start, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
|
{ MP_QSTR_loop, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = mp_const_none} },
|
||||||
{ MP_QSTR_end, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = INT_MAX} },
|
|
||||||
};
|
};
|
||||||
rp2pio_statemachine_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]);
|
rp2pio_statemachine_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]);
|
||||||
check_for_deinit(self);
|
check_for_deinit(self);
|
||||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||||
mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||||
|
|
||||||
mp_buffer_info_t bufinfo = {};
|
sm_buf_info once_info;
|
||||||
if (args[ARG_buffer].u_obj != mp_const_none) {
|
sm_buf_info loop_info;
|
||||||
mp_get_buffer_raise(args[ARG_buffer].u_obj, &bufinfo, MP_BUFFER_READ);
|
size_t stride_in_bytes = 0;
|
||||||
}
|
fill_buf_info(&once_info, args[ARG_once].u_obj, &stride_in_bytes);
|
||||||
int32_t start = args[ARG_start].u_int;
|
fill_buf_info(&loop_info, args[ARG_loop].u_obj, &stride_in_bytes);
|
||||||
size_t length = bufinfo.len;
|
if (!stride_in_bytes) {
|
||||||
normalize_buffer_bounds(&start, args[ARG_end].u_int, &length);
|
return mp_const_none;
|
||||||
bool ok = true;
|
|
||||||
if (length == 0) {
|
|
||||||
ok = common_hal_rp2pio_statemachine_end_continuous_write(self);
|
|
||||||
} else {
|
|
||||||
uint8_t *original_pointer = bufinfo.buf;
|
|
||||||
int stride_in_bytes = mp_binary_get_size('@', bufinfo.typecode, NULL);
|
|
||||||
if (stride_in_bytes > 4) {
|
|
||||||
mp_raise_ValueError(translate("Buffer elements must be 4 bytes long or less"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ok = common_hal_rp2pio_statemachine_start_continuous_write(self, args[ARG_buffer].u_obj, ((uint8_t *)bufinfo.buf) + start, length, stride_in_bytes);
|
bool ok = common_hal_rp2pio_statemachine_background_write(self, &once_info, &loop_info, stride_in_bytes);
|
||||||
}
|
|
||||||
if (mp_hal_is_interrupted()) {
|
if (mp_hal_is_interrupted()) {
|
||||||
return mp_const_none;
|
return mp_const_none;
|
||||||
}
|
}
|
||||||
@ -491,14 +507,14 @@ STATIC mp_obj_t rp2pio_statemachine_start_continuous_write(size_t n_args, const
|
|||||||
}
|
}
|
||||||
return mp_const_none;
|
return mp_const_none;
|
||||||
}
|
}
|
||||||
MP_DEFINE_CONST_FUN_OBJ_KW(rp2pio_statemachine_start_continuous_write_obj, 2, rp2pio_statemachine_start_continuous_write);
|
MP_DEFINE_CONST_FUN_OBJ_KW(rp2pio_statemachine_background_write_obj, 1, rp2pio_statemachine_background_write);
|
||||||
|
|
||||||
//| def end_continuous_write(self) -> None:
|
//| def stop_background_write(self) -> None:
|
||||||
//| """Stop a continuous write, if one is in progress."""
|
//| """Immediately stop a background write, if one is in progress. Items already in the TX FIFO are not affected."""
|
||||||
//|
|
//|
|
||||||
STATIC mp_obj_t rp2pio_statemachine_obj_end_continuous_write(mp_obj_t self_in) {
|
STATIC mp_obj_t rp2pio_statemachine_obj_stop_background_write(mp_obj_t self_in) {
|
||||||
rp2pio_statemachine_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
rp2pio_statemachine_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||||
bool ok = common_hal_rp2pio_statemachine_end_continuous_write(self);
|
bool ok = common_hal_rp2pio_statemachine_stop_background_write(self);
|
||||||
if (mp_hal_is_interrupted()) {
|
if (mp_hal_is_interrupted()) {
|
||||||
return mp_const_none;
|
return mp_const_none;
|
||||||
}
|
}
|
||||||
@ -507,8 +523,45 @@ STATIC mp_obj_t rp2pio_statemachine_obj_end_continuous_write(mp_obj_t self_in) {
|
|||||||
}
|
}
|
||||||
return mp_const_none;
|
return mp_const_none;
|
||||||
}
|
}
|
||||||
|
MP_DEFINE_CONST_FUN_OBJ_1(rp2pio_statemachine_stop_background_write_obj, rp2pio_statemachine_obj_stop_background_write);
|
||||||
|
|
||||||
|
//| @property
|
||||||
|
//| def writing(self) -> bool:
|
||||||
|
//| """Returns True if a background write is in progress"""
|
||||||
|
//|
|
||||||
|
STATIC mp_obj_t rp2pio_statemachine_obj_get_writing(mp_obj_t self_in) {
|
||||||
|
rp2pio_statemachine_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||||
|
return mp_obj_new_bool(common_hal_rp2pio_statemachine_get_writing(self));
|
||||||
|
}
|
||||||
|
MP_DEFINE_CONST_FUN_OBJ_1(rp2pio_statemachine_get_writing_obj, rp2pio_statemachine_obj_get_writing);
|
||||||
|
|
||||||
|
const mp_obj_property_t rp2pio_statemachine_writing_obj = {
|
||||||
|
.base.type = &mp_type_property,
|
||||||
|
.proxy = {(mp_obj_t)&rp2pio_statemachine_get_writing_obj,
|
||||||
|
MP_ROM_NONE,
|
||||||
|
MP_ROM_NONE},
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//| @property
|
||||||
|
//| def pending(self) -> int:
|
||||||
|
//| """Returns the number of pending buffers for background writing.
|
||||||
|
//|
|
||||||
|
//| If the number is 0, then a `StateMachine.background_write` call will not block."""
|
||||||
|
//|
|
||||||
|
STATIC mp_obj_t rp2pio_statemachine_obj_get_pending(mp_obj_t self_in) {
|
||||||
|
rp2pio_statemachine_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||||
|
return mp_obj_new_int(common_hal_rp2pio_statemachine_get_pending(self));
|
||||||
|
}
|
||||||
|
MP_DEFINE_CONST_FUN_OBJ_1(rp2pio_statemachine_get_pending_obj, rp2pio_statemachine_obj_get_pending);
|
||||||
|
|
||||||
|
const mp_obj_property_t rp2pio_statemachine_pending_obj = {
|
||||||
|
.base.type = &mp_type_property,
|
||||||
|
.proxy = {(mp_obj_t)&rp2pio_statemachine_get_pending_obj,
|
||||||
|
MP_ROM_NONE,
|
||||||
|
MP_ROM_NONE},
|
||||||
|
};
|
||||||
|
|
||||||
MP_DEFINE_CONST_FUN_OBJ_1(rp2pio_statemachine_end_continuous_write_obj, rp2pio_statemachine_obj_end_continuous_write);
|
|
||||||
//| def readinto(self, buffer: WriteableBuffer, *, start: int = 0, end: Optional[int] = None) -> None:
|
//| def readinto(self, buffer: WriteableBuffer, *, start: int = 0, end: Optional[int] = None) -> None:
|
||||||
//| """Read into ``buffer``. If the number of bytes to read is 0, nothing happens. The buffer
|
//| """Read into ``buffer``. If the number of bytes to read is 0, nothing happens. The buffer
|
||||||
//| includes any data added to the fifo even if it was added before this was called.
|
//| includes any data added to the fifo even if it was added before this was called.
|
||||||
@ -728,8 +781,10 @@ STATIC const mp_rom_map_elem_t rp2pio_statemachine_locals_dict_table[] = {
|
|||||||
{ MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&rp2pio_statemachine_readinto_obj) },
|
{ MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&rp2pio_statemachine_readinto_obj) },
|
||||||
{ MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&rp2pio_statemachine_write_obj) },
|
{ MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&rp2pio_statemachine_write_obj) },
|
||||||
{ MP_ROM_QSTR(MP_QSTR_write_readinto), MP_ROM_PTR(&rp2pio_statemachine_write_readinto_obj) },
|
{ MP_ROM_QSTR(MP_QSTR_write_readinto), MP_ROM_PTR(&rp2pio_statemachine_write_readinto_obj) },
|
||||||
{ MP_ROM_QSTR(MP_QSTR_start_continuous_write), MP_ROM_PTR(&rp2pio_statemachine_start_continuous_write_obj) },
|
{ MP_ROM_QSTR(MP_QSTR_background_write), MP_ROM_PTR(&rp2pio_statemachine_background_write_obj) },
|
||||||
{ MP_ROM_QSTR(MP_QSTR_end_continuous_write), MP_ROM_PTR(&rp2pio_statemachine_end_continuous_write_obj) },
|
{ MP_ROM_QSTR(MP_QSTR_stop_background_write), MP_ROM_PTR(&rp2pio_statemachine_stop_background_write_obj) },
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_writing), MP_ROM_PTR(&rp2pio_statemachine_writing_obj) },
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_pending), MP_ROM_PTR(&rp2pio_statemachine_pending_obj) },
|
||||||
|
|
||||||
{ MP_ROM_QSTR(MP_QSTR_frequency), MP_ROM_PTR(&rp2pio_statemachine_frequency_obj) },
|
{ MP_ROM_QSTR(MP_QSTR_frequency), MP_ROM_PTR(&rp2pio_statemachine_frequency_obj) },
|
||||||
{ MP_ROM_QSTR(MP_QSTR_rxstall), MP_ROM_PTR(&rp2pio_statemachine_rxstall_obj) },
|
{ MP_ROM_QSTR(MP_QSTR_rxstall), MP_ROM_PTR(&rp2pio_statemachine_rxstall_obj) },
|
||||||
|
@ -65,8 +65,10 @@ void common_hal_rp2pio_statemachine_run(rp2pio_statemachine_obj_t *self, const u
|
|||||||
|
|
||||||
// Writes out the given data.
|
// Writes out the given data.
|
||||||
bool common_hal_rp2pio_statemachine_write(rp2pio_statemachine_obj_t *self, const uint8_t *data, size_t len, uint8_t stride_in_bytes);
|
bool common_hal_rp2pio_statemachine_write(rp2pio_statemachine_obj_t *self, const uint8_t *data, size_t len, uint8_t stride_in_bytes);
|
||||||
bool common_hal_rp2pio_statemachine_start_continuous_write(rp2pio_statemachine_obj_t *self, mp_obj_t buf_obj, const uint8_t *data, size_t len, uint8_t stride_in_bytes);
|
bool common_hal_rp2pio_statemachine_background_write(rp2pio_statemachine_obj_t *self, const sm_buf_info *once_obj, const sm_buf_info *loop_obj, uint8_t stride_in_bytes);
|
||||||
bool common_hal_rp2pio_statemachine_end_continuous_write(rp2pio_statemachine_obj_t *self);
|
bool common_hal_rp2pio_statemachine_stop_background_write(rp2pio_statemachine_obj_t *self);
|
||||||
|
mp_int_t common_hal_rp2pio_statemachine_get_pending(rp2pio_statemachine_obj_t *self);
|
||||||
|
bool common_hal_rp2pio_statemachine_get_writing(rp2pio_statemachine_obj_t *self);
|
||||||
bool common_hal_rp2pio_statemachine_readinto(rp2pio_statemachine_obj_t *self, uint8_t *data, size_t len, uint8_t stride_in_bytes);
|
bool common_hal_rp2pio_statemachine_readinto(rp2pio_statemachine_obj_t *self, uint8_t *data, size_t len, uint8_t stride_in_bytes);
|
||||||
bool common_hal_rp2pio_statemachine_write_readinto(rp2pio_statemachine_obj_t *self,
|
bool common_hal_rp2pio_statemachine_write_readinto(rp2pio_statemachine_obj_t *self,
|
||||||
const uint8_t *data_out, size_t out_len, uint8_t out_stride_in_bytes,
|
const uint8_t *data_out, size_t out_len, uint8_t out_stride_in_bytes,
|
||||||
|
@ -24,6 +24,8 @@
|
|||||||
* THE SOFTWARE.
|
* THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
#include "bindings/rp2pio/StateMachine.h"
|
#include "bindings/rp2pio/StateMachine.h"
|
||||||
|
|
||||||
#include "common-hal/microcontroller/__init__.h"
|
#include "common-hal/microcontroller/__init__.h"
|
||||||
@ -86,7 +88,7 @@ STATIC void rp2pio_statemachine_clear_dma(int pio_index, int sm) {
|
|||||||
if (!dma_hw->inte0) {
|
if (!dma_hw->inte0) {
|
||||||
irq_set_mask_enabled(1 << DMA_IRQ_0, false);
|
irq_set_mask_enabled(1 << DMA_IRQ_0, false);
|
||||||
}
|
}
|
||||||
MP_STATE_PORT(continuous_pio)[channel] = NULL;
|
MP_STATE_PORT(background_pio)[channel] = NULL;
|
||||||
dma_channel_abort(channel);
|
dma_channel_abort(channel);
|
||||||
dma_channel_unclaim(channel);
|
dma_channel_unclaim(channel);
|
||||||
}
|
}
|
||||||
@ -608,7 +610,7 @@ void common_hal_rp2pio_statemachine_set_frequency(rp2pio_statemachine_obj_t *sel
|
|||||||
|
|
||||||
void rp2pio_statemachine_deinit(rp2pio_statemachine_obj_t *self, bool leave_pins) {
|
void rp2pio_statemachine_deinit(rp2pio_statemachine_obj_t *self, bool leave_pins) {
|
||||||
common_hal_rp2pio_statemachine_stop(self);
|
common_hal_rp2pio_statemachine_stop(self);
|
||||||
(void)common_hal_rp2pio_statemachine_end_continuous_write(self);
|
(void)common_hal_rp2pio_statemachine_stop_background_write(self);
|
||||||
|
|
||||||
uint8_t sm = self->state_machine;
|
uint8_t sm = self->state_machine;
|
||||||
uint8_t pio_index = pio_get_index(self->pio);
|
uint8_t pio_index = pio_get_index(self->pio);
|
||||||
@ -877,31 +879,42 @@ uint8_t rp2pio_statemachine_program_offset(rp2pio_statemachine_obj_t *self) {
|
|||||||
return _current_program_offset[pio_index][sm];
|
return _current_program_offset[pio_index][sm];
|
||||||
}
|
}
|
||||||
|
|
||||||
bool common_hal_rp2pio_statemachine_start_continuous_write(rp2pio_statemachine_obj_t *self, mp_obj_t buf_obj, const uint8_t *data, size_t len, uint8_t stride_in_bytes) {
|
bool common_hal_rp2pio_statemachine_background_write(rp2pio_statemachine_obj_t *self, const sm_buf_info *once, const sm_buf_info *loop, uint8_t stride_in_bytes) {
|
||||||
uint8_t pio_index = pio_get_index(self->pio);
|
uint8_t pio_index = pio_get_index(self->pio);
|
||||||
uint8_t sm = self->state_machine;
|
uint8_t sm = self->state_machine;
|
||||||
|
|
||||||
if (SM_DMA_ALLOCATED(pio_index, sm) && stride_in_bytes == self->continuous_stride_in_bytes) {
|
int pending_buffers = (once->info.buf != NULL) + (loop->info.buf != NULL);
|
||||||
while (self->pending_set_data) {
|
if (!once->info.buf) {
|
||||||
|
once = loop;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (SM_DMA_ALLOCATED(pio_index, sm)) {
|
||||||
|
if (stride_in_bytes != self->background_stride_in_bytes) {
|
||||||
|
mp_raise_ValueError(translate("Mismatched data size"));
|
||||||
|
}
|
||||||
|
|
||||||
|
while (self->pending_buffers) {
|
||||||
RUN_BACKGROUND_TASKS;
|
RUN_BACKGROUND_TASKS;
|
||||||
if (self->user_interruptible && mp_hal_is_interrupted()) {
|
if (self->user_interruptible && mp_hal_is_interrupted()) {
|
||||||
(void)common_hal_rp2pio_statemachine_end_continuous_write(self);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
common_hal_mcu_disable_interrupts();
|
common_hal_mcu_disable_interrupts();
|
||||||
self->next_buffer = data;
|
self->once = *once;
|
||||||
self->next_size = len / stride_in_bytes;
|
self->loop = *loop;
|
||||||
self->pending_set_data = true;
|
self->pending_buffers = pending_buffers;
|
||||||
common_hal_mcu_enable_interrupts();
|
|
||||||
|
|
||||||
// need to keep a reference alive to the buffer, lest the GC collect it while its lone remaining pointer is in the DMA peripheral register
|
if (self->dma_completed) {
|
||||||
self->buf_objs[++self->buf_obj_idx % 2] = buf_obj;
|
rp2pio_statemachine_dma_complete(self, SM_DMA_GET_CHANNEL(pio_index, sm));
|
||||||
return true;
|
self->dma_completed = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
common_hal_rp2pio_statemachine_end_continuous_write(self);
|
common_hal_mcu_enable_interrupts();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
int channel = dma_claim_unused_channel(false);
|
int channel = dma_claim_unused_channel(false);
|
||||||
if (channel == -1) {
|
if (channel == -1) {
|
||||||
@ -916,13 +929,12 @@ bool common_hal_rp2pio_statemachine_start_continuous_write(rp2pio_statemachine_o
|
|||||||
|
|
||||||
dma_channel_config c;
|
dma_channel_config c;
|
||||||
|
|
||||||
self->pending_set_data = false;
|
self->current = *once;
|
||||||
self->continuous_stride_in_bytes = stride_in_bytes;
|
self->once = *loop;
|
||||||
self->buf_objs[0] = buf_obj;
|
self->loop = *loop;
|
||||||
self->buf_objs[1] = NULL;
|
self->pending_buffers = pending_buffers;
|
||||||
|
self->dma_completed = false;
|
||||||
self->next_buffer = data;
|
self->background_stride_in_bytes = stride_in_bytes;
|
||||||
self->next_size = len / stride_in_bytes;
|
|
||||||
|
|
||||||
c = dma_channel_get_default_config(channel);
|
c = dma_channel_get_default_config(channel);
|
||||||
channel_config_set_transfer_data_size(&c, _stride_to_dma_size(stride_in_bytes));
|
channel_config_set_transfer_data_size(&c, _stride_to_dma_size(stride_in_bytes));
|
||||||
@ -931,12 +943,12 @@ bool common_hal_rp2pio_statemachine_start_continuous_write(rp2pio_statemachine_o
|
|||||||
channel_config_set_write_increment(&c, false);
|
channel_config_set_write_increment(&c, false);
|
||||||
dma_channel_configure(channel, &c,
|
dma_channel_configure(channel, &c,
|
||||||
tx_destination,
|
tx_destination,
|
||||||
data,
|
once->info.buf,
|
||||||
len / stride_in_bytes,
|
once->info.len / stride_in_bytes,
|
||||||
false);
|
false);
|
||||||
|
|
||||||
common_hal_mcu_disable_interrupts();
|
common_hal_mcu_disable_interrupts();
|
||||||
MP_STATE_PORT(continuous_pio)[channel] = self;
|
MP_STATE_PORT(background_pio)[channel] = self;
|
||||||
dma_hw->inte0 |= 1u << channel;
|
dma_hw->inte0 |= 1u << channel;
|
||||||
irq_set_mask_enabled(1 << DMA_IRQ_0, true);
|
irq_set_mask_enabled(1 << DMA_IRQ_0, true);
|
||||||
dma_start_channel_mask(1u << channel);
|
dma_start_channel_mask(1u << channel);
|
||||||
@ -946,15 +958,36 @@ bool common_hal_rp2pio_statemachine_start_continuous_write(rp2pio_statemachine_o
|
|||||||
}
|
}
|
||||||
|
|
||||||
void rp2pio_statemachine_dma_complete(rp2pio_statemachine_obj_t *self, int channel) {
|
void rp2pio_statemachine_dma_complete(rp2pio_statemachine_obj_t *self, int channel) {
|
||||||
dma_channel_set_read_addr(channel, self->next_buffer, false);
|
self->current = self->once;
|
||||||
dma_channel_set_trans_count(channel, self->next_size, true);
|
self->once = self->loop;
|
||||||
|
|
||||||
self->pending_set_data = false;
|
if (self->current.info.buf) {
|
||||||
|
if (self->pending_buffers > 0) {
|
||||||
|
self->pending_buffers--;
|
||||||
|
}
|
||||||
|
dma_channel_set_read_addr(channel, self->current.info.buf, false);
|
||||||
|
dma_channel_set_trans_count(channel, self->current.info.len / self->background_stride_in_bytes, true);
|
||||||
|
} else {
|
||||||
|
self->dma_completed = true;
|
||||||
|
self->pending_buffers = 0; // should be a no-op
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool common_hal_rp2pio_statemachine_end_continuous_write(rp2pio_statemachine_obj_t *self) {
|
bool common_hal_rp2pio_statemachine_stop_background_write(rp2pio_statemachine_obj_t *self) {
|
||||||
uint8_t pio_index = pio_get_index(self->pio);
|
uint8_t pio_index = pio_get_index(self->pio);
|
||||||
uint8_t sm = self->state_machine;
|
uint8_t sm = self->state_machine;
|
||||||
rp2pio_statemachine_clear_dma(pio_index, sm);
|
rp2pio_statemachine_clear_dma(pio_index, sm);
|
||||||
|
memset(&self->current, 0, sizeof(self->current));
|
||||||
|
memset(&self->once, 0, sizeof(self->once));
|
||||||
|
memset(&self->loop, 0, sizeof(self->loop));
|
||||||
|
self->pending_buffers = 0;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool common_hal_rp2pio_statemachine_get_writing(rp2pio_statemachine_obj_t *self) {
|
||||||
|
return !self->dma_completed;
|
||||||
|
}
|
||||||
|
|
||||||
|
int common_hal_rp2pio_statemachine_get_pending(rp2pio_statemachine_obj_t *self) {
|
||||||
|
return self->pending_buffers;
|
||||||
|
}
|
||||||
|
@ -32,6 +32,11 @@
|
|||||||
#include "common-hal/microcontroller/Pin.h"
|
#include "common-hal/microcontroller/Pin.h"
|
||||||
#include "src/rp2_common/hardware_pio/include/hardware/pio.h"
|
#include "src/rp2_common/hardware_pio/include/hardware/pio.h"
|
||||||
|
|
||||||
|
typedef struct sm_buf_info {
|
||||||
|
mp_obj_t obj;
|
||||||
|
mp_buffer_info_t info;
|
||||||
|
} sm_buf_info;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
mp_obj_base_t base;
|
mp_obj_base_t base;
|
||||||
uint32_t pins; // Bitmask of what pins this state machine uses.
|
uint32_t pins; // Bitmask of what pins this state machine uses.
|
||||||
@ -56,12 +61,10 @@ typedef struct {
|
|||||||
uint8_t offset;
|
uint8_t offset;
|
||||||
|
|
||||||
// dma-related items
|
// dma-related items
|
||||||
uint8_t buf_obj_idx;
|
volatile int pending_buffers;
|
||||||
const uint8_t *next_buffer;
|
sm_buf_info current, once, loop;
|
||||||
size_t next_size;
|
int background_stride_in_bytes;
|
||||||
mp_obj_t buf_objs[2];
|
bool dma_completed;
|
||||||
int continuous_stride_in_bytes;
|
|
||||||
volatile int pending_set_data;
|
|
||||||
} rp2pio_statemachine_obj_t;
|
} rp2pio_statemachine_obj_t;
|
||||||
|
|
||||||
void reset_rp2pio_statemachine(void);
|
void reset_rp2pio_statemachine(void);
|
||||||
|
@ -46,7 +46,7 @@
|
|||||||
#define MICROPY_PORT_ROOT_POINTERS \
|
#define MICROPY_PORT_ROOT_POINTERS \
|
||||||
mp_obj_t counting[NUM_PWM_SLICES]; \
|
mp_obj_t counting[NUM_PWM_SLICES]; \
|
||||||
mp_obj_t playing_audio[NUM_DMA_CHANNELS]; \
|
mp_obj_t playing_audio[NUM_DMA_CHANNELS]; \
|
||||||
mp_obj_t continuous_pio[NUM_DMA_CHANNELS]; \
|
mp_obj_t background_pio[NUM_DMA_CHANNELS]; \
|
||||||
CIRCUITPY_COMMON_ROOT_POINTERS;
|
CIRCUITPY_COMMON_ROOT_POINTERS;
|
||||||
|
|
||||||
#endif // __INCLUDED_MPCONFIGPORT_H
|
#endif // __INCLUDED_MPCONFIGPORT_H
|
||||||
|
Loading…
x
Reference in New Issue
Block a user