diff options
| author | Scott Shawcroft <scott@tannewt.org> | 2021-02-23 15:50:00 -0800 |
|---|---|---|
| committer | Scott Shawcroft <scott@tannewt.org> | 2021-02-23 15:50:00 -0800 |
| commit | 360475e26618cba98522d5dd86d0051d3a296fb3 (patch) | |
| tree | 2300e783be8602cf156fc1b174f8edb517478ab1 /ports/raspberrypi/bindings | |
| parent | e41137c745af65b6342c7e2d3b09d8930eefedb0 (diff) | |
Implement audiobusio and enhance PIO for it
This adds I2SOut and PDMIn support via PIO.
StateMachines can now:
* read and read while writing
* transfer in 1, 2 or 4 byte increments
* init pins based on expected defaults automatically
* be stopped and restarted
* rxfifo can be cleared and rxstalls detected (good for tracking when
the reading code isn't keeping up)
Fixes #4162
Diffstat (limited to 'ports/raspberrypi/bindings')
| -rw-r--r-- | ports/raspberrypi/bindings/rp2pio/StateMachine.c | 377 | ||||
| -rw-r--r-- | ports/raspberrypi/bindings/rp2pio/StateMachine.h | 38 |
2 files changed, 275 insertions, 140 deletions
diff --git a/ports/raspberrypi/bindings/rp2pio/StateMachine.c b/ports/raspberrypi/bindings/rp2pio/StateMachine.c index 34633949e..77f61acd0 100644 --- a/ports/raspberrypi/bindings/rp2pio/StateMachine.c +++ b/ports/raspberrypi/bindings/rp2pio/StateMachine.c @@ -36,6 +36,7 @@ #include "lib/utils/buffer_helper.h" #include "lib/utils/context_manager_helpers.h" #include "lib/utils/interrupt_char.h" +#include "py/binary.h" #include "py/mperrno.h" #include "py/objproperty.h" #include "py/runtime.h" @@ -64,16 +65,23 @@ //| init: Optional[ReadableBuffer] = None, //| first_out_pin: Optional[microcontroller.Pin] = None, //| out_pin_count: int = 1, +//| initial_out_pin_state: int = 0, +//| initial_out_pin_direction: int = 0xffffffff, //| first_in_pin: Optional[microcontroller.Pin] = None, //| in_pin_count: int = 1, //| first_set_pin: Optional[microcontroller.Pin] = None, //| set_pin_count: int = 1, +//| initial_set_pin_state: int = 0, +//| initial_set_pin_direction: int = 0x1f, //| first_sideset_pin: Optional[microcontroller.Pin] = None, //| sideset_pin_count: int = 1, +//| initial_sideset_pin_state: int = 0, +//| initial_sideset_pin_direction: int = 0x1f, //| exclusive_pin_use: bool = True, //| auto_pull: bool = False, //| 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: @@ -87,12 +95,18 @@ //| is started so instructions may be intermingled //| :param ~microcontroller.Pin first_out_pin: the first pin to use with the OUT instruction //| :param int out_pin_count: the count of consecutive pins to use with OUT starting at first_out_pin +//| :param int initial_out_pin_state: the initial output value for out pins starting at first_out_pin +//| :param int initial_out_pin_direction: the initial output direction for out pins starting at first_out_pin //| :param ~microcontroller.Pin first_in_pin: the first pin to use with the IN instruction //| :param int in_pin_count: the count of consecutive pins to use with IN starting at first_in_pin //| :param ~microcontroller.Pin first_set_pin: the first pin to use with the SET instruction //| :param int set_pin_count: the count of consecutive pins to use with SET starting at first_set_pin +//| :param int initial_set_pin_state: the initial output value for set pins starting at first_set_pin +//| :param int initial_set_pin_direction: the initial output direction for set pins starting at first_set_pin //| :param ~microcontroller.Pin first_sideset_pin: the first pin to use with a side set //| :param int sideset_pin_count: the count of consecutive pins to use with a side set starting at first_sideset_pin +//| :param int initial_sideset_pin_state: the initial output value for sideset pins starting at first_sideset_pin +//| :param int initial_sideset_pin_direction: the initial output direction for sideset pins starting at first_sideset_pin //| :param bool exclusive_pin_use: When True, do not share any pins with other state machines. Pins are never shared with other peripherals //| :param bool auto_pull: When True, automatically load data from the tx FIFO into the //| output shift register (OSR) when an OUT instruction shifts more than pull_threshold bits @@ -100,6 +114,10 @@ //| :param bool out_shift_right: When True, data is shifted out the right side (LSB) of the //| OSR. It is shifted out 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). +//| :param bool wait_for_txstall: When True, writing data out will block until the TX FIFO and OSR are empty +//| and an instruction is stalled waiting for more data. When False, data writes won't +//| wait for the OSR to empty (only the TX FIFO) so make sure you give enough time before +//| deiniting or stopping the state machine. //| :param bool auto_push: When True, automatically save data from input shift register //| (ISR) into the rx FIFO when an IN instruction shifts more than push_threshold bits //| :param int push_threshold: Number of bits to shift before saving the ISR value to the RX FIFO @@ -113,29 +131,42 @@ STATIC mp_obj_t rp2pio_statemachine_make_new(const mp_obj_type_t *type, size_t n rp2pio_statemachine_obj_t *self = m_new_obj(rp2pio_statemachine_obj_t); self->base.type = &rp2pio_statemachine_type; enum { ARG_program, ARG_frequency, ARG_init, - ARG_first_out_pin, ARG_out_pin_count, + ARG_first_out_pin, ARG_out_pin_count, ARG_initial_out_pin_state, ARG_initial_out_pin_direction, ARG_first_in_pin, ARG_in_pin_count, - ARG_first_set_pin, ARG_set_pin_count, - ARG_first_sideset_pin, ARG_sideset_pin_count, + ARG_first_set_pin, ARG_set_pin_count, ARG_initial_set_pin_state, ARG_initial_set_pin_direction, + ARG_first_sideset_pin, ARG_sideset_pin_count, ARG_initial_sideset_pin_state, ARG_initial_sideset_pin_direction, 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}; 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 }, { MP_QSTR_init, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_first_out_pin, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, { MP_QSTR_out_pin_count, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 1} }, + { MP_QSTR_initial_out_pin_state, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_initial_out_pin_direction, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0xffffffff} }, + { MP_QSTR_first_in_pin, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, { MP_QSTR_in_pin_count, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 1} }, + { MP_QSTR_first_set_pin, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, { MP_QSTR_set_pin_count, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 1} }, + { MP_QSTR_initial_set_pin_state, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_initial_set_pin_direction, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0x1f} }, + { MP_QSTR_first_sideset_pin, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, { MP_QSTR_sideset_pin_count, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 1} }, + { MP_QSTR_initial_sideset_pin_state, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_initial_sideset_pin_direction, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0x1f} }, + { MP_QSTR_exclusive_pin_use, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = true} }, { MP_QSTR_auto_pull, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, { MP_QSTR_pull_threshold, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 32} }, { MP_QSTR_out_shift_right, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = true} }, + { MP_QSTR_wait_for_txstall, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = true} }, { 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} }, @@ -201,12 +232,13 @@ STATIC mp_obj_t rp2pio_statemachine_make_new(const mp_obj_type_t *type, size_t n bufinfo.buf, bufinfo.len / 2, args[ARG_frequency].u_int, init_bufinfo.buf, init_bufinfo.len / 2, - first_out_pin, args[ARG_out_pin_count].u_int, + first_out_pin, args[ARG_out_pin_count].u_int, args[ARG_initial_out_pin_state].u_int, args[ARG_initial_out_pin_direction].u_int, first_in_pin, args[ARG_in_pin_count].u_int, - first_set_pin, args[ARG_set_pin_count].u_int, - first_sideset_pin, args[ARG_sideset_pin_count].u_int, + first_set_pin, args[ARG_set_pin_count].u_int, args[ARG_initial_set_pin_state].u_int, args[ARG_initial_set_pin_direction].u_int, + first_sideset_pin, args[ARG_sideset_pin_count].u_int, args[ARG_initial_sideset_pin_state].u_int, args[ARG_initial_sideset_pin_direction].u_int, 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); return MP_OBJ_FROM_PTR(self); } @@ -247,11 +279,53 @@ STATIC void check_for_deinit(rp2pio_statemachine_obj_t *self) { } } -// // | def restart(self, *other_state_machines) -> None: -// // | """Restarts this state machine and any others given. They must share -// // | an underlying PIO. An exception will be raised otherwise.""" -// // | ... -// // | +//| def restart(self) -> None: +//| """Resets this state machine, runs any init and enables the clock.""" +// TODO: "and any others given. They must share an underlying PIO. An exception will be raised otherwise."" +//| ... +//| +STATIC mp_obj_t rp2pio_statemachine_restart(mp_obj_t self_obj) { + rp2pio_statemachine_obj_t *self = MP_OBJ_TO_PTR(self_obj); + check_for_deinit(self); + + common_hal_rp2pio_statemachine_restart(self); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(rp2pio_statemachine_restart_obj, rp2pio_statemachine_restart); + + +//| def run(self, instructions: ReadableBuffer) -> None: +//| """Runs all given instructions. They will likely be interleaved with +//| in-memory instructions. Make sure this doesn't wait for input! +//| +//| This can be used to output internal state to the RX FIFO and then +//| read with `readinto`.""" +//| ... +//| +STATIC mp_obj_t rp2pio_statemachine_run(mp_obj_t self_obj, mp_obj_t instruction_obj) { + rp2pio_statemachine_obj_t *self = MP_OBJ_TO_PTR(self_obj); + check_for_deinit(self); + + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(instruction_obj, &bufinfo, MP_BUFFER_READ); + + common_hal_rp2pio_statemachine_run(self, bufinfo.buf, bufinfo.len); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(rp2pio_statemachine_run_obj, rp2pio_statemachine_run); + +//| def stop(self) -> None: +//| """Stops the state machine clock. Use `restart` to enable it.""" +//| ... +//| +STATIC mp_obj_t rp2pio_statemachine_stop(mp_obj_t self_obj) { + rp2pio_statemachine_obj_t *self = MP_OBJ_TO_PTR(self_obj); + check_for_deinit(self); + + common_hal_rp2pio_statemachine_stop(self); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(rp2pio_statemachine_stop_obj, rp2pio_statemachine_stop); //| def write(self, buffer: ReadableBuffer, *, start: int = 0, end: Optional[int] = None) -> None: //| """Write the data contained in ``buffer`` to the state machine. If the buffer is empty, nothing happens. @@ -261,7 +335,6 @@ STATIC void check_for_deinit(rp2pio_statemachine_obj_t *self) { //| :param int end: End of the slice; this index is not included. Defaults to ``len(buffer)``""" //| ... //| - STATIC mp_obj_t rp2pio_statemachine_write(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { enum { ARG_buffer, ARG_start, ARG_end }; static const mp_arg_t allowed_args[] = { @@ -279,12 +352,17 @@ STATIC mp_obj_t rp2pio_statemachine_write(size_t n_args, const mp_obj_t *pos_arg int32_t start = args[ARG_start].u_int; size_t length = bufinfo.len; normalize_buffer_bounds(&start, args[ARG_end].u_int, &length); - if (length == 0) { return mp_const_none; } - bool ok = common_hal_rp2pio_statemachine_write(self, ((uint8_t*)bufinfo.buf) + start, length); + 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")); + } + + bool ok = common_hal_rp2pio_statemachine_write(self, ((uint8_t*)bufinfo.buf) + start, length, stride_in_bytes); if (mp_hal_is_interrupted()) { return mp_const_none; } @@ -296,110 +374,132 @@ 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); -// // | 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. -// // | -// // | :param ~_typing.WriteableBuffer buffer: Read data into this buffer -// // | :param int start: Start of the slice of ``buffer`` to read into: ``buffer[start:end]`` -// // | :param int end: End of the slice; this index is not included. Defaults to ``len(buffer)`` -// // | :param int write_value: Value to write while reading. (Usually ignored.)""" -// // | ... -// // | - -// STATIC mp_obj_t rp2pio_statemachine_readinto(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { -// enum { ARG_buffer, ARG_start, ARG_end, ARG_write_value }; -// static const mp_arg_t allowed_args[] = { -// { MP_QSTR_buffer, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, -// { MP_QSTR_start, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, -// { MP_QSTR_end, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = INT_MAX} }, -// { MP_QSTR_write_value,MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, -// }; -// rp2pio_statemachine_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); -// check_for_deinit(self); -// check_lock(self); -// 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_buffer_info_t bufinfo; -// mp_get_buffer_raise(args[ARG_buffer].u_obj, &bufinfo, MP_BUFFER_WRITE); -// int32_t start = args[ARG_start].u_int; -// size_t length = bufinfo.len; -// normalize_buffer_bounds(&start, args[ARG_end].u_int, &length); - -// if (length == 0) { -// return mp_const_none; -// } - -// bool ok = common_hal_rp2pio_statemachine_read(self, ((uint8_t*)bufinfo.buf) + start, length, args[ARG_write_value].u_int); -// if (!ok) { -// mp_raise_OSError(MP_EIO); -// } -// return mp_const_none; -// } -// MP_DEFINE_CONST_FUN_OBJ_KW(rp2pio_statemachine_readinto_obj, 2, rp2pio_statemachine_readinto); - -// //| def write_readinto(self, buffer_out: ReadableBuffer, buffer_in: WriteableBuffer, *, out_start: int = 0, out_end: Optional[int] = None, in_start: int = 0, in_end: Optional[int] = None) -> None: -// //| """Write out the data in ``buffer_out`` while simultaneously reading data into ``buffer_in``. -// //| The SPI object must be locked. -// //| The lengths of the slices defined by ``buffer_out[out_start:out_end]`` and ``buffer_in[in_start:in_end]`` -// //| must be equal. -// //| If buffer slice lengths are both 0, nothing happens. -// //| -// //| :param ~_typing.ReadableBuffer buffer_out: Write out the data in this buffer -// //| :param ~_typing.WriteableBuffer buffer_in: Read data into this buffer -// //| :param int out_start: Start of the slice of buffer_out to write out: ``buffer_out[out_start:out_end]`` -// //| :param int out_end: End of the slice; this index is not included. Defaults to ``len(buffer_out)`` -// //| :param int in_start: Start of the slice of ``buffer_in`` to read into: ``buffer_in[in_start:in_end]`` -// //| :param int in_end: End of the slice; this index is not included. Defaults to ``len(buffer_in)``""" -// //| ... -// //| - -// STATIC mp_obj_t rp2pio_statemachine_write_readinto(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { -// enum { ARG_buffer_out, ARG_buffer_in, ARG_out_start, ARG_out_end, ARG_in_start, ARG_in_end }; -// static const mp_arg_t allowed_args[] = { -// { MP_QSTR_buffer_out, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, -// { MP_QSTR_buffer_in, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, -// { MP_QSTR_out_start, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, -// { MP_QSTR_out_end, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = INT_MAX} }, -// { MP_QSTR_in_start, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, -// { MP_QSTR_in_end, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = INT_MAX} }, -// }; -// rp2pio_statemachine_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); -// check_for_deinit(self); -// check_lock(self); -// 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_buffer_info_t buf_out_info; -// mp_get_buffer_raise(args[ARG_buffer_out].u_obj, &buf_out_info, MP_BUFFER_READ); -// int32_t out_start = args[ARG_out_start].u_int; -// size_t out_length = buf_out_info.len; -// normalize_buffer_bounds(&out_start, args[ARG_out_end].u_int, &out_length); - -// mp_buffer_info_t buf_in_info; -// mp_get_buffer_raise(args[ARG_buffer_in].u_obj, &buf_in_info, MP_BUFFER_WRITE); -// int32_t in_start = args[ARG_in_start].u_int; -// size_t in_length = buf_in_info.len; -// normalize_buffer_bounds(&in_start, args[ARG_in_end].u_int, &in_length); - -// if (out_length != in_length) { -// mp_raise_ValueError(translate("buffer slices must be of equal length")); -// } - -// if (out_length == 0) { -// return mp_const_none; -// } - -// bool ok = common_hal_rp2pio_statemachine_transfer(self, -// ((uint8_t*)buf_out_info.buf) + out_start, -// ((uint8_t*)buf_in_info.buf) + in_start, -// out_length); -// if (!ok) { -// mp_raise_OSError(MP_EIO); -// } -// return mp_const_none; -// } -// MP_DEFINE_CONST_FUN_OBJ_KW(rp2pio_statemachine_write_readinto_obj, 2, rp2pio_statemachine_write_readinto); +//| 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 +//| include any data added to the fifo even if it was added before this was called. +//| +//| :param ~_typing.WriteableBuffer buffer: Read data into this buffer +//| :param int start: Start of the slice of ``buffer`` to read into: ``buffer[start:end]`` +//| :param int end: End of the slice; this index is not included. Defaults to ``len(buffer)``""" +//| ... +//| + +STATIC mp_obj_t rp2pio_statemachine_readinto(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_buffer, ARG_start, ARG_end }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_buffer, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_start, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + { 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]); + check_for_deinit(self); + 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_buffer_info_t bufinfo; + mp_get_buffer_raise(args[ARG_buffer].u_obj, &bufinfo, MP_BUFFER_WRITE); + int32_t start = args[ARG_start].u_int; + size_t length = bufinfo.len; + normalize_buffer_bounds(&start, args[ARG_end].u_int, &length); + + if (length == 0) { + return mp_const_none; + } + + 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")); + } + + bool ok = common_hal_rp2pio_statemachine_readinto(self, ((uint8_t*)bufinfo.buf) + start, length, stride_in_bytes); + if (!ok) { + mp_raise_OSError(MP_EIO); + } + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(rp2pio_statemachine_readinto_obj, 2, rp2pio_statemachine_readinto); + +//| def write_readinto(self, buffer_out: ReadableBuffer, buffer_in: WriteableBuffer, *, out_start: int = 0, out_end: Optional[int] = None, in_start: int = 0, in_end: Optional[int] = None) -> None: +//| """Write out the data in ``buffer_out`` while simultaneously reading data into ``buffer_in``. +//| The lengths of the slices defined by ``buffer_out[out_start:out_end]`` and ``buffer_in[in_start:in_end]`` +//| may be different. The function will return once both are filled. +//| If buffer slice lengths are both 0, nothing happens. +//| +//| :param ~_typing.ReadableBuffer buffer_out: Write out the data in this buffer +//| :param ~_typing.WriteableBuffer buffer_in: Read data into this buffer +//| :param int out_start: Start of the slice of buffer_out to write out: ``buffer_out[out_start:out_end]`` +//| :param int out_end: End of the slice; this index is not included. Defaults to ``len(buffer_out)`` +//| :param int in_start: Start of the slice of ``buffer_in`` to read into: ``buffer_in[in_start:in_end]`` +//| :param int in_end: End of the slice; this index is not included. Defaults to ``len(buffer_in)``""" +//| ... +//| + +STATIC mp_obj_t rp2pio_statemachine_write_readinto(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_buffer_out, ARG_buffer_in, ARG_out_start, ARG_out_end, ARG_in_start, ARG_in_end }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_buffer_out, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_buffer_in, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_out_start, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_out_end, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = INT_MAX} }, + { MP_QSTR_in_start, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_in_end, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = INT_MAX} }, + }; + rp2pio_statemachine_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + check_for_deinit(self); + 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_buffer_info_t buf_out_info; + mp_get_buffer_raise(args[ARG_buffer_out].u_obj, &buf_out_info, MP_BUFFER_READ); + int32_t out_start = args[ARG_out_start].u_int; + size_t out_length = buf_out_info.len; + normalize_buffer_bounds(&out_start, args[ARG_out_end].u_int, &out_length); + + mp_buffer_info_t buf_in_info; + mp_get_buffer_raise(args[ARG_buffer_in].u_obj, &buf_in_info, MP_BUFFER_WRITE); + int32_t in_start = args[ARG_in_start].u_int; + size_t in_length = buf_in_info.len; + normalize_buffer_bounds(&in_start, args[ARG_in_end].u_int, &in_length); + + if (out_length == 0 && in_length == 0) { + return mp_const_none; + } + + int in_stride_in_bytes = mp_binary_get_size('@', buf_in_info.typecode, NULL); + if (in_stride_in_bytes > 4) { + mp_raise_ValueError(translate("In buffer elements must be 4 bytes long or less")); + } + + int out_stride_in_bytes = mp_binary_get_size('@', buf_out_info.typecode, NULL); + if (out_stride_in_bytes > 4) { + mp_raise_ValueError(translate("Out buffer elements must be 4 bytes long or less")); + } + + bool ok = common_hal_rp2pio_statemachine_write_readinto(self, + ((uint8_t*)buf_out_info.buf) + out_start, + out_length, + out_stride_in_bytes, + ((uint8_t*)buf_in_info.buf) + in_start, + in_length, + in_stride_in_bytes); + if (!ok) { + mp_raise_OSError(MP_EIO); + } + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(rp2pio_statemachine_write_readinto_obj, 2, rp2pio_statemachine_write_readinto); + +//| def clear_rxfifo(self) -> None: +//| """Clears any unread bytes in the rxfifo.""" +//| ... +//| +STATIC mp_obj_t rp2pio_statemachine_obj_clear_rxfifo(mp_obj_t self_in) { + rp2pio_statemachine_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_rp2pio_statemachine_clear_rxfifo(self); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(rp2pio_statemachine_clear_rxfifo_obj, rp2pio_statemachine_obj_clear_rxfifo); //| frequency: int //| """The actual state machine frequency. This may not match the frequency requested @@ -413,9 +513,37 @@ STATIC mp_obj_t rp2pio_statemachine_obj_get_frequency(mp_obj_t self_in) { } MP_DEFINE_CONST_FUN_OBJ_1(rp2pio_statemachine_get_frequency_obj, rp2pio_statemachine_obj_get_frequency); +STATIC mp_obj_t rp2pio_statemachine_obj_set_frequency(mp_obj_t self_in, mp_obj_t frequency) { + rp2pio_statemachine_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + + common_hal_rp2pio_statemachine_set_frequency(self, mp_obj_get_int(frequency)); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(rp2pio_statemachine_set_frequency_obj, rp2pio_statemachine_obj_set_frequency); + const mp_obj_property_t rp2pio_statemachine_frequency_obj = { .base.type = &mp_type_property, .proxy = {(mp_obj_t)&rp2pio_statemachine_get_frequency_obj, + (mp_obj_t)&rp2pio_statemachine_set_frequency_obj, + (mp_obj_t)&mp_const_none_obj}, +}; + +//| rxstall: bool +//| """True when the state machine has stalled due to a full RX FIFO since the last +//| `clear_rxfifo` call.""" +//| + +STATIC mp_obj_t rp2pio_statemachine_obj_get_rxstall(mp_obj_t self_in) { + rp2pio_statemachine_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + return MP_OBJ_NEW_SMALL_INT(common_hal_rp2pio_statemachine_get_rxstall(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(rp2pio_statemachine_get_rxstall_obj, rp2pio_statemachine_obj_get_rxstall); + +const mp_obj_property_t rp2pio_statemachine_rxstall_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&rp2pio_statemachine_get_rxstall_obj, (mp_obj_t)&mp_const_none_obj, (mp_obj_t)&mp_const_none_obj}, }; @@ -425,12 +553,17 @@ STATIC const mp_rom_map_elem_t rp2pio_statemachine_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) }, { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&rp2pio_statemachine_obj___exit___obj) }, -// { MP_ROM_QSTR(MP_QSTR_restart), MP_ROM_PTR(&rp2pio_statemachine_configure_obj) }, + { MP_ROM_QSTR(MP_QSTR_stop), MP_ROM_PTR(&rp2pio_statemachine_stop_obj) }, + { MP_ROM_QSTR(MP_QSTR_restart), MP_ROM_PTR(&rp2pio_statemachine_restart_obj) }, + { MP_ROM_QSTR(MP_QSTR_run), MP_ROM_PTR(&rp2pio_statemachine_run_obj) }, + { MP_ROM_QSTR(MP_QSTR_clear_rxfifo), MP_ROM_PTR(&rp2pio_statemachine_clear_rxfifo_obj) }, -// { 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_readinto), MP_ROM_PTR(&rp2pio_statemachine_write_readinto_obj) }, - { MP_ROM_QSTR(MP_QSTR_frequency), MP_ROM_PTR(&rp2pio_statemachine_frequency_obj) } + { MP_ROM_QSTR(MP_QSTR_write_readinto), MP_ROM_PTR(&rp2pio_statemachine_write_readinto_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) } }; STATIC MP_DEFINE_CONST_DICT(rp2pio_statemachine_locals_dict, rp2pio_statemachine_locals_dict_table); diff --git a/ports/raspberrypi/bindings/rp2pio/StateMachine.h b/ports/raspberrypi/bindings/rp2pio/StateMachine.h index 5ff20a75b..36e44f191 100644 --- a/ports/raspberrypi/bindings/rp2pio/StateMachine.h +++ b/ports/raspberrypi/bindings/rp2pio/StateMachine.h @@ -36,36 +36,38 @@ extern const mp_obj_type_t rp2pio_statemachine_type; // Construct an underlying SPI object. -extern void common_hal_rp2pio_statemachine_construct(rp2pio_statemachine_obj_t *self, +void common_hal_rp2pio_statemachine_construct(rp2pio_statemachine_obj_t *self, const uint16_t* program, size_t program_len, size_t frequency, const uint16_t* init, size_t init_len, - const mcu_pin_obj_t * first_out_pin, uint8_t out_pin_count, + const mcu_pin_obj_t * first_out_pin, uint8_t out_pin_count, uint32_t initial_out_pin_state, uint32_t initial_out_pin_direction, const mcu_pin_obj_t * first_in_pin, uint8_t in_pin_count, - const mcu_pin_obj_t * first_set_pin, uint8_t set_pin_count, - const mcu_pin_obj_t * first_sideset_pin, uint8_t sideset_pin_count, + const mcu_pin_obj_t * first_set_pin, uint8_t set_pin_count, uint32_t initial_set_pin_state, uint32_t initial_set_pin_direction, + const mcu_pin_obj_t * first_sideset_pin, uint8_t sideset_pin_count, uint32_t initial_sideset_pin_state, uint32_t initial_sideset_pin_direction, 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); -extern void common_hal_rp2pio_statemachine_deinit(rp2pio_statemachine_obj_t *self); -extern bool common_hal_rp2pio_statemachine_deinited(rp2pio_statemachine_obj_t *self); +void common_hal_rp2pio_statemachine_deinit(rp2pio_statemachine_obj_t *self); +bool common_hal_rp2pio_statemachine_deinited(rp2pio_statemachine_obj_t *self); -// Writes out the given data. -extern bool common_hal_rp2pio_statemachine_write(rp2pio_statemachine_obj_t *self, const uint8_t *data, size_t len); - -// // Reads in len bytes while outputting zeroes. -// extern bool common_hal_rp2pio_statemachine_read(rp2pio_statemachine_obj_t *self, uint8_t *data, size_t len, uint8_t write_value); +void common_hal_rp2pio_statemachine_restart(rp2pio_statemachine_obj_t *self); +void common_hal_rp2pio_statemachine_stop(rp2pio_statemachine_obj_t *self); +void common_hal_rp2pio_statemachine_run(rp2pio_statemachine_obj_t *self, const uint16_t *instructions, size_t len); -// // Reads and write len bytes simultaneously. -// extern bool common_hal_rp2pio_statemachine_transfer(rp2pio_statemachine_obj_t *self, -// const uint8_t *data_out, size_t out_len, -// uint8_t *data_in, size_t in_len); +// 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_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, + const uint8_t *data_out, size_t out_len, uint8_t out_stride_in_bytes, + uint8_t *data_in, size_t in_len, uint8_t in_stride_in_bytes); -// Return actual SPI bus frequency. +// Return actual state machine frequency. uint32_t common_hal_rp2pio_statemachine_get_frequency(rp2pio_statemachine_obj_t* self); +void common_hal_rp2pio_statemachine_set_frequency(rp2pio_statemachine_obj_t* self, uint32_t frequency); -// This is used by the supervisor to claim SPI devices indefinitely. -// extern void common_hal_rp2pio_statemachine_never_reset(rp2pio_statemachine_obj_t *self); +bool common_hal_rp2pio_statemachine_get_rxstall(rp2pio_statemachine_obj_t* self); +void common_hal_rp2pio_statemachine_clear_rxfifo(rp2pio_statemachine_obj_t *self); #endif // MICROPY_INCLUDED_RASPBERRYPI_BINDINGS_RP2PIO_STATEMACHINE_H |
