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
This commit is contained in:
parent
e41137c745
commit
360475e266
|
@ -39,6 +39,16 @@
|
|||
|
||||
#define AUDIO_DMA_CHANNEL_COUNT NUM_DMA_CHANNELS
|
||||
|
||||
void audio_dma_reset(void) {
|
||||
for (size_t channel = 0; channel < AUDIO_DMA_CHANNEL_COUNT; channel++) {
|
||||
if (MP_STATE_PORT(playing_audio)[channel] == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
audio_dma_stop(MP_STATE_PORT(playing_audio)[channel]);
|
||||
}
|
||||
}
|
||||
|
||||
void audio_dma_convert_signed(audio_dma_t* dma, uint8_t* buffer, uint32_t buffer_length,
|
||||
uint8_t** output_buffer, uint32_t* output_buffer_length) {
|
||||
if (dma->first_buffer_free) {
|
||||
|
@ -292,9 +302,16 @@ void audio_dma_stop(audio_dma_t* dma) {
|
|||
for (size_t i = 0; i < 2; i++) {
|
||||
size_t channel = dma->channel[i];
|
||||
|
||||
dma_channel_config c = dma_channel_get_default_config(dma->channel[i]);
|
||||
channel_config_set_enable(&c, false);
|
||||
dma_channel_set_config(channel, &c, false /* trigger */);
|
||||
|
||||
if (dma_channel_is_busy(channel)) {
|
||||
dma_channel_abort(channel);
|
||||
}
|
||||
dma_channel_set_read_addr(channel, NULL, false /* trigger */);
|
||||
dma_channel_set_write_addr(channel, NULL, false /* trigger */);
|
||||
dma_channel_set_trans_count(channel, 0, false /* trigger */);
|
||||
dma_channel_unclaim(channel);
|
||||
MP_STATE_PORT(playing_audio)[channel] = NULL;
|
||||
dma->channel[i] = NUM_DMA_CHANNELS;
|
||||
|
|
|
@ -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.)"""
|
||||
// // | ...
|
||||
// // |
|
||||
//| 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, 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);
|
||||
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);
|
||||
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;
|
||||
// }
|
||||
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);
|
||||
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"));
|
||||
}
|
||||
|
||||
// //| 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)``"""
|
||||
// //| ...
|
||||
// //|
|
||||
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);
|
||||
|
||||
// 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);
|
||||
//| 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)``"""
|
||||
//| ...
|
||||
//|
|
||||
|
||||
// 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);
|
||||
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_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);
|
||||
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);
|
||||
|
||||
// if (out_length != in_length) {
|
||||
// mp_raise_ValueError(translate("buffer slices must be of equal 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) {
|
||||
// return mp_const_none;
|
||||
// }
|
||||
if (out_length == 0 && in_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);
|
||||
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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
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);
|
||||
|
||||
// Writes out the given data.
|
||||
extern bool common_hal_rp2pio_statemachine_write(rp2pio_statemachine_obj_t *self, const uint8_t *data, size_t len);
|
||||
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);
|
||||
|
||||
// // 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);
|
||||
|
||||
// // 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);
|
||||
|
||||
// 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
|
||||
|
|
|
@ -0,0 +1,224 @@
|
|||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2021 Scott Shawcroft for Adafruit Industries
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "mpconfigport.h"
|
||||
|
||||
#include "py/gc.h"
|
||||
#include "py/mperrno.h"
|
||||
#include "py/runtime.h"
|
||||
#include "common-hal/audiobusio/I2SOut.h"
|
||||
#include "shared-bindings/audiobusio/I2SOut.h"
|
||||
#include "shared-bindings/microcontroller/Pin.h"
|
||||
#include "shared-module/audiocore/__init__.h"
|
||||
#include "bindings/rp2pio/StateMachine.h"
|
||||
#include "supervisor/shared/translate.h"
|
||||
|
||||
const uint16_t i2s_program[] = {
|
||||
// ; Load the next set of samples
|
||||
// ; /--- LRCLK
|
||||
// ; |/-- BCLK
|
||||
// ; ||
|
||||
// pull noblock side 0b01 ; Loads OSR with the next FIFO value or X
|
||||
0x8880,
|
||||
// mov x osr side 0b01 ; Save the new value in case we need it again
|
||||
0xa827,
|
||||
// set y 14 side 0b01
|
||||
0xe84e,
|
||||
// bitloop1:
|
||||
// out pins 1 side 0b00 [2]
|
||||
0x6201,
|
||||
// jmp y-- bitloop1 side 0b01 [2]
|
||||
0x0a83,
|
||||
// out pins 1 side 0b10 [2]
|
||||
0x7201,
|
||||
// set y 14 side 0b11 [2]
|
||||
0xfa4e,
|
||||
// bitloop0:
|
||||
// out pins 1 side 0b10 [2]
|
||||
0x7201,
|
||||
// jmp y-- bitloop0 side 0b11 [2]
|
||||
0x1a87,
|
||||
// out pins 1 side 0b00 [2]
|
||||
0x6201
|
||||
};
|
||||
|
||||
const uint16_t i2s_program_left_justified[] = {
|
||||
// ; Load the next set of samples
|
||||
// ; /--- LRCLK
|
||||
// ; |/-- BCLK
|
||||
// ; ||
|
||||
// pull noblock side 0b11 ; Loads OSR with the next FIFO value or X
|
||||
0x9880,
|
||||
// mov x osr side 0b11 ; Save the new value in case we need it again
|
||||
0xb827,
|
||||
// set y 14 side 0b11
|
||||
0xf84e,
|
||||
// bitloop1:
|
||||
// out pins 1 side 0b00 [2]
|
||||
0x6201,
|
||||
// jmp y-- bitloop1 side 0b01 [2]
|
||||
0x0a83,
|
||||
// out pins 1 side 0b10 [2]
|
||||
0x6201,
|
||||
// set y 14 side 0b01 [2]
|
||||
0xea4e,
|
||||
// bitloop0:
|
||||
// out pins 1 side 0b10 [2]
|
||||
0x7201,
|
||||
// jmp y-- bitloop0 side 0b11 [2]
|
||||
0x1a87,
|
||||
// out pins 1 side 0b10 [2]
|
||||
0x7201
|
||||
};
|
||||
|
||||
void i2sout_reset(void) {
|
||||
}
|
||||
|
||||
// Caller validates that pins are free.
|
||||
void common_hal_audiobusio_i2sout_construct(audiobusio_i2sout_obj_t* self,
|
||||
const mcu_pin_obj_t* bit_clock, const mcu_pin_obj_t* word_select,
|
||||
const mcu_pin_obj_t* data, bool left_justified) {
|
||||
if (bit_clock->number != word_select->number - 1) {
|
||||
mp_raise_ValueError(translate("Bit clock and word select must be sequential pins"));
|
||||
}
|
||||
|
||||
const uint16_t* program = i2s_program;
|
||||
size_t program_len = sizeof(i2s_program) / sizeof(i2s_program[0]);
|
||||
if (left_justified) {
|
||||
program = i2s_program_left_justified;
|
||||
program_len = sizeof(i2s_program_left_justified) / sizeof(i2s_program_left_justified[0]);;
|
||||
}
|
||||
|
||||
// Use the state machine to manage pins.
|
||||
common_hal_rp2pio_statemachine_construct(&self->state_machine,
|
||||
program, program_len,
|
||||
44100 * 32 * 6, // Clock at 44.1 khz to warm the DAC up.
|
||||
NULL, 0,
|
||||
data, 1, 0, 0xffffffff, // out pin
|
||||
NULL, 0, // in pins
|
||||
NULL, 0, 0, 0x1f, // set pins
|
||||
bit_clock, 2, 0, 0x1f, // sideset pins
|
||||
true, // exclusive pin use
|
||||
false, 32, false, // shift out left to start with MSB
|
||||
false, // Wait for txstall
|
||||
false, 32, false); // in settings
|
||||
|
||||
self->playing = false;
|
||||
audio_dma_init(&self->dma);
|
||||
}
|
||||
|
||||
bool common_hal_audiobusio_i2sout_deinited(audiobusio_i2sout_obj_t* self) {
|
||||
return common_hal_rp2pio_statemachine_deinited(&self->state_machine);
|
||||
}
|
||||
|
||||
void common_hal_audiobusio_i2sout_deinit(audiobusio_i2sout_obj_t* self) {
|
||||
if (common_hal_audiobusio_i2sout_deinited(self)) {
|
||||
return;
|
||||
}
|
||||
|
||||
common_hal_rp2pio_statemachine_deinit(&self->state_machine);
|
||||
}
|
||||
|
||||
void common_hal_audiobusio_i2sout_play(audiobusio_i2sout_obj_t* self,
|
||||
mp_obj_t sample, bool loop) {
|
||||
if (common_hal_audiobusio_i2sout_get_playing(self)) {
|
||||
common_hal_audiobusio_i2sout_stop(self);
|
||||
}
|
||||
uint8_t bits_per_sample = audiosample_bits_per_sample(sample);
|
||||
// Make sure we transmit a minimum of 16 bits.
|
||||
// TODO: Maybe we need an intermediate object to upsample instead. This is
|
||||
// only needed for some I2S devices that expect at least 8.
|
||||
if (bits_per_sample < 16) {
|
||||
bits_per_sample = 16;
|
||||
}
|
||||
// We always output stereo so output twice as many bits.
|
||||
uint16_t bits_per_sample_output = bits_per_sample * 2;
|
||||
size_t clocks_per_bit = 6;
|
||||
uint32_t frequency = bits_per_sample_output * audiosample_sample_rate(sample);
|
||||
common_hal_rp2pio_statemachine_set_frequency(&self->state_machine, clocks_per_bit * frequency);
|
||||
common_hal_rp2pio_statemachine_restart(&self->state_machine);
|
||||
|
||||
uint8_t channel_count = audiosample_channel_count(sample);
|
||||
if (channel_count > 2) {
|
||||
mp_raise_ValueError(translate("Too many channels in sample."));
|
||||
}
|
||||
|
||||
audio_dma_result result = audio_dma_setup_playback(
|
||||
&self->dma,
|
||||
sample,
|
||||
loop,
|
||||
false, // single channel
|
||||
0, // audio channel
|
||||
true, // output signed
|
||||
bits_per_sample,
|
||||
(uint32_t) &self->state_machine.pio->txf[self->state_machine.state_machine], // output register
|
||||
self->state_machine.tx_dreq); // data request line
|
||||
|
||||
if (result == AUDIO_DMA_DMA_BUSY) {
|
||||
common_hal_audiobusio_i2sout_stop(self);
|
||||
mp_raise_RuntimeError(translate("No DMA channel found"));
|
||||
} else if (result == AUDIO_DMA_MEMORY_ERROR) {
|
||||
common_hal_audiobusio_i2sout_stop(self);
|
||||
mp_raise_RuntimeError(translate("Unable to allocate buffers for signed conversion"));
|
||||
}
|
||||
|
||||
// Turn on the state machine's clock.
|
||||
|
||||
self->playing = true;
|
||||
}
|
||||
|
||||
void common_hal_audiobusio_i2sout_pause(audiobusio_i2sout_obj_t* self) {
|
||||
audio_dma_pause(&self->dma);
|
||||
}
|
||||
|
||||
void common_hal_audiobusio_i2sout_resume(audiobusio_i2sout_obj_t* self) {
|
||||
// Maybe: Clear any overrun/underrun errors
|
||||
|
||||
audio_dma_resume(&self->dma);
|
||||
}
|
||||
|
||||
bool common_hal_audiobusio_i2sout_get_paused(audiobusio_i2sout_obj_t* self) {
|
||||
return audio_dma_get_paused(&self->dma);
|
||||
}
|
||||
|
||||
void common_hal_audiobusio_i2sout_stop(audiobusio_i2sout_obj_t* self) {
|
||||
audio_dma_stop(&self->dma);
|
||||
|
||||
common_hal_rp2pio_statemachine_stop(&self->state_machine);
|
||||
|
||||
self->playing = false;
|
||||
}
|
||||
|
||||
bool common_hal_audiobusio_i2sout_get_playing(audiobusio_i2sout_obj_t* self) {
|
||||
bool playing = audio_dma_get_playing(&self->dma);
|
||||
if (!playing && self->playing) {
|
||||
common_hal_audiobusio_i2sout_stop(self);
|
||||
}
|
||||
return playing;
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2021 Scott Shawcroft for Adafruit Industries
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef MICROPY_INCLUDED_RASPBERRYPI_COMMON_HAL_AUDIOBUSIO_I2SOUT_H
|
||||
#define MICROPY_INCLUDED_RASPBERRYPI_COMMON_HAL_AUDIOBUSIO_I2SOUT_H
|
||||
|
||||
#include "common-hal/microcontroller/Pin.h"
|
||||
#include "common-hal/rp2pio/StateMachine.h"
|
||||
|
||||
#include "audio_dma.h"
|
||||
#include "py/obj.h"
|
||||
|
||||
// We don't bit pack because we'll only have two at most. Its better to save code size instead.
|
||||
typedef struct {
|
||||
mp_obj_base_t base;
|
||||
bool left_justified;
|
||||
rp2pio_statemachine_obj_t state_machine;
|
||||
bool playing;
|
||||
audio_dma_t dma;
|
||||
} audiobusio_i2sout_obj_t;
|
||||
|
||||
void i2sout_reset(void);
|
||||
|
||||
#endif // MICROPY_INCLUDED_RASPBERRYPI_COMMON_HAL_AUDIOBUSIO_I2SOUT_H
|
|
@ -0,0 +1,172 @@
|
|||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2021 Scott Shawcroft for Adafruit Industries
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "py/mperrno.h"
|
||||
#include "py/runtime.h"
|
||||
#include "shared-bindings/audiobusio/PDMIn.h"
|
||||
#include "shared-bindings/microcontroller/Pin.h"
|
||||
#include "supervisor/shared/translate.h"
|
||||
|
||||
#include "audio_dma.h"
|
||||
|
||||
#define OVERSAMPLING 64
|
||||
#define SAMPLES_PER_BUFFER 32
|
||||
|
||||
// MEMS microphones must be clocked at at least 1MHz.
|
||||
#define MIN_MIC_CLOCK 1000000
|
||||
|
||||
const uint16_t pdmin[] = {
|
||||
// in pins 1 side 0b1
|
||||
0x5001,
|
||||
// push iffull side 0b0
|
||||
0x8040
|
||||
};
|
||||
|
||||
// Caller validates that pins are free.
|
||||
void common_hal_audiobusio_pdmin_construct(audiobusio_pdmin_obj_t* self,
|
||||
const mcu_pin_obj_t* clock_pin,
|
||||
const mcu_pin_obj_t* data_pin,
|
||||
uint32_t sample_rate,
|
||||
uint8_t bit_depth,
|
||||
bool mono,
|
||||
uint8_t oversample) {
|
||||
if (!(bit_depth == 16 || bit_depth == 8) || !mono || oversample != OVERSAMPLING) {
|
||||
mp_raise_NotImplementedError(translate("Only 8 or 16 bit mono with " MP_STRINGIFY(OVERSAMPLING) "x oversampling is supported."));
|
||||
}
|
||||
|
||||
// Use the state machine to manage pins.
|
||||
common_hal_rp2pio_statemachine_construct(&self->state_machine,
|
||||
pdmin, sizeof(pdmin) / sizeof(pdmin[0]),
|
||||
44100 * 32 * 2, // Clock at 44.1 khz to warm the DAC up.
|
||||
NULL, 0,
|
||||
NULL, 1, 0, 0xffffffff, // out pin
|
||||
data_pin, 1, // in pins
|
||||
NULL, 0, 0, 0x1f, // set pins
|
||||
clock_pin, 1, 0, 0x1f, // sideset pins
|
||||
true, // exclusive pin use
|
||||
false, 32, false, // out settings
|
||||
false, // Wait for txstall
|
||||
false, 32, true); // in settings
|
||||
|
||||
uint32_t actual_frequency = common_hal_rp2pio_statemachine_get_frequency(&self->state_machine);
|
||||
if (actual_frequency < MIN_MIC_CLOCK) {
|
||||
mp_raise_ValueError(translate("sampling rate out of range"));
|
||||
}
|
||||
|
||||
self->sample_rate = actual_frequency / oversample;
|
||||
self->bit_depth = bit_depth;
|
||||
}
|
||||
|
||||
bool common_hal_audiobusio_pdmin_deinited(audiobusio_pdmin_obj_t* self) {
|
||||
return common_hal_rp2pio_statemachine_deinited(&self->state_machine);
|
||||
}
|
||||
|
||||
void common_hal_audiobusio_pdmin_deinit(audiobusio_pdmin_obj_t* self) {
|
||||
if (common_hal_audiobusio_pdmin_deinited(self)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t common_hal_audiobusio_pdmin_get_bit_depth(audiobusio_pdmin_obj_t* self) {
|
||||
return self->bit_depth;
|
||||
}
|
||||
|
||||
uint32_t common_hal_audiobusio_pdmin_get_sample_rate(audiobusio_pdmin_obj_t* self) {
|
||||
return self->sample_rate;
|
||||
}
|
||||
|
||||
// a windowed sinc filter for 44 khz, 64 samples
|
||||
//
|
||||
// This filter is good enough to use for lower sample rates as
|
||||
// well. It does not increase the noise enough to be a problem.
|
||||
//
|
||||
// In the long run we could use a fast filter like this to do the
|
||||
// decimation and initial filtering in real time, filtering to a
|
||||
// higher sample rate than specified. Then after the audio is
|
||||
// recorded, a more expensive filter non-real-time filter could be
|
||||
// used to down-sample and low-pass.
|
||||
const uint16_t sinc_filter [OVERSAMPLING] = {
|
||||
0, 2, 9, 21, 39, 63, 94, 132,
|
||||
179, 236, 302, 379, 467, 565, 674, 792,
|
||||
920, 1055, 1196, 1341, 1487, 1633, 1776, 1913,
|
||||
2042, 2159, 2263, 2352, 2422, 2474, 2506, 2516,
|
||||
2506, 2474, 2422, 2352, 2263, 2159, 2042, 1913,
|
||||
1776, 1633, 1487, 1341, 1196, 1055, 920, 792,
|
||||
674, 565, 467, 379, 302, 236, 179, 132,
|
||||
94, 63, 39, 21, 9, 2, 0, 0
|
||||
};
|
||||
|
||||
#define REPEAT_32_TIMES(X) do { X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X } while(0)
|
||||
|
||||
static uint16_t filter_sample(uint32_t pdm_samples[2]) {
|
||||
uint16_t running_sum = 0;
|
||||
const uint16_t *filter_ptr = sinc_filter;
|
||||
for (uint8_t i = 0; i < 2; i++) {
|
||||
uint32_t pdm_sample = pdm_samples[i];
|
||||
REPEAT_32_TIMES( {
|
||||
if (pdm_sample & 0x1) {
|
||||
running_sum += *filter_ptr;
|
||||
}
|
||||
filter_ptr++;
|
||||
pdm_sample >>= 1;
|
||||
}
|
||||
);
|
||||
}
|
||||
return running_sum;
|
||||
}
|
||||
|
||||
// output_buffer may be a byte buffer or a halfword buffer.
|
||||
// output_buffer_length is the number of slots, not the number of bytes.
|
||||
uint32_t common_hal_audiobusio_pdmin_record_to_buffer(audiobusio_pdmin_obj_t* self,
|
||||
uint16_t* output_buffer, uint32_t output_buffer_length) {
|
||||
uint32_t samples[2];
|
||||
size_t output_count = 0;
|
||||
common_hal_rp2pio_statemachine_clear_rxfifo(&self->state_machine);
|
||||
// Do one read to get the mic going and throw it away.
|
||||
common_hal_rp2pio_statemachine_readinto(&self->state_machine, (uint8_t*) samples, 2 * sizeof(uint32_t), sizeof(uint32_t));
|
||||
while (output_count < output_buffer_length && !common_hal_rp2pio_statemachine_get_rxstall(&self->state_machine)) {
|
||||
common_hal_rp2pio_statemachine_readinto(&self->state_machine, (uint8_t*) samples, 2 * sizeof(uint32_t), sizeof(uint32_t));
|
||||
// Call filter_sample just one place so it can be inlined.
|
||||
uint16_t value = filter_sample(samples);
|
||||
if (self->bit_depth == 8) {
|
||||
// Truncate to 8 bits.
|
||||
((uint8_t*) output_buffer)[output_count] = value >> 8;
|
||||
} else {
|
||||
output_buffer[output_count] = value;
|
||||
}
|
||||
output_count++;
|
||||
}
|
||||
|
||||
return output_count;
|
||||
}
|
||||
|
||||
void common_hal_audiobusio_pdmin_record_to_file(audiobusio_pdmin_obj_t* self, uint8_t* buffer, uint32_t length) {
|
||||
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2021 Scott Shawcroft for Adafruit Industries
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef MICROPY_INCLUDED_RASPBERRYPI_COMMON_HAL_AUDIOBUSIO_AUDIOOUT_H
|
||||
#define MICROPY_INCLUDED_RASPBERRYPI_COMMON_HAL_AUDIOBUSIO_AUDIOOUT_H
|
||||
|
||||
#include "common-hal/microcontroller/Pin.h"
|
||||
#include "bindings/rp2pio/StateMachine.h"
|
||||
|
||||
#include "extmod/vfs_fat.h"
|
||||
#include "py/obj.h"
|
||||
|
||||
typedef struct {
|
||||
mp_obj_base_t base;
|
||||
uint32_t sample_rate;
|
||||
uint8_t serializer;
|
||||
uint8_t clock_unit;
|
||||
uint8_t bytes_per_sample;
|
||||
uint8_t bit_depth;
|
||||
rp2pio_statemachine_obj_t state_machine;
|
||||
} audiobusio_pdmin_obj_t;
|
||||
|
||||
void pdmin_reset(void);
|
||||
|
||||
void pdmin_background(void);
|
||||
|
||||
#endif // MICROPY_INCLUDED_RASPBERRYPI_COMMON_HAL_AUDIOBUSIO_AUDIOOUT_H
|
|
@ -0,0 +1 @@
|
|||
// No audiobusio module functions.
|
|
@ -54,27 +54,25 @@ const uint16_t neopixel_program[] = {
|
|||
0xa142
|
||||
};
|
||||
|
||||
const uint16_t init_program[] = {
|
||||
0xe081
|
||||
};
|
||||
|
||||
void common_hal_neopixel_write(const digitalio_digitalinout_obj_t* digitalinout, uint8_t *pixels, uint32_t num_bytes) {
|
||||
// Set everything up.
|
||||
rp2pio_statemachine_obj_t state_machine;
|
||||
|
||||
// TODO: Cache the state machine after we create it once. We'll need a way to
|
||||
// change the pins then though.
|
||||
uint8_t pin_number = digitalinout->pin->number;
|
||||
uint32_t pins_we_use = 1 << digitalinout->pin->number;
|
||||
bool ok = rp2pio_statemachine_construct(&state_machine,
|
||||
neopixel_program, sizeof(neopixel_program) / sizeof(neopixel_program[0]),
|
||||
800000 * 6, // 800 khz * 6 cycles per bit
|
||||
init_program, 1,
|
||||
NULL, 1,
|
||||
NULL, 1,
|
||||
digitalinout->pin, 1,
|
||||
digitalinout->pin, 1,
|
||||
1 << pin_number, true, false,
|
||||
NULL, 0, // init program
|
||||
NULL, 1, // out
|
||||
NULL, 1, // in
|
||||
NULL, 1, // set
|
||||
digitalinout->pin, 1, // sideset
|
||||
0, pins_we_use, // initial pin state
|
||||
pins_we_use, true, false,
|
||||
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
|
||||
if (!ok) {
|
||||
|
@ -86,7 +84,7 @@ void common_hal_neopixel_write(const digitalio_digitalinout_obj_t* digitalinout,
|
|||
// two.
|
||||
while (port_get_raw_ticks(NULL) < next_start_raw_ticks) {}
|
||||
|
||||
common_hal_rp2pio_statemachine_write(&state_machine, pixels, num_bytes);
|
||||
common_hal_rp2pio_statemachine_write(&state_machine, pixels, num_bytes, 1 /* stride in bytes */);
|
||||
|
||||
// Use a private deinit of the state machine that doesn't reset the pin.
|
||||
rp2pio_statemachine_deinit(&state_machine, true);
|
||||
|
|
|
@ -54,7 +54,6 @@ STATIC PIO pio_instances[2] = {pio0, pio1};
|
|||
|
||||
void _reset_statemachine(PIO pio, uint8_t sm, bool leave_pins) {
|
||||
uint8_t pio_index = pio_get_index(pio);
|
||||
pio_sm_unclaim(pio, sm);
|
||||
uint32_t program_id = _current_program_id[pio_index][sm];
|
||||
if (program_id == 0) {
|
||||
return;
|
||||
|
@ -89,6 +88,7 @@ void _reset_statemachine(PIO pio, uint8_t sm, bool leave_pins) {
|
|||
}
|
||||
}
|
||||
_current_sm_pins[pio_index][sm] = 0;
|
||||
pio_sm_unclaim(pio, sm);
|
||||
}
|
||||
|
||||
void reset_rp2pio_statemachine(void) {
|
||||
|
@ -130,8 +130,10 @@ bool rp2pio_statemachine_construct(rp2pio_statemachine_obj_t *self,
|
|||
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,
|
||||
uint32_t initial_pin_state, uint32_t initial_pin_direction,
|
||||
uint32_t pins_we_use, bool tx_fifo, bool rx_fifo,
|
||||
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) {
|
||||
// Create a program id that isn't the pointer so we can store it without storing the original object.
|
||||
|
@ -199,6 +201,11 @@ bool rp2pio_statemachine_construct(rp2pio_statemachine_obj_t *self,
|
|||
_current_sm_pins[pio_index][state_machine] = pins_we_use;
|
||||
_current_pins[pio_index] |= pins_we_use;
|
||||
|
||||
pio_sm_set_pins_with_mask(self->pio, state_machine, initial_pin_state, pins_we_use);
|
||||
pio_sm_set_pindirs_with_mask(self->pio, state_machine, initial_pin_direction, pins_we_use);
|
||||
self->initial_pin_state = initial_pin_state;
|
||||
self->initial_pin_direction = initial_pin_direction;
|
||||
|
||||
for (size_t pin_number = 0; pin_number < TOTAL_GPIO_COUNT; pin_number++) {
|
||||
if ((pins_we_use & (1 << pin_number)) == 0) {
|
||||
continue;
|
||||
|
@ -260,27 +267,38 @@ bool rp2pio_statemachine_construct(rp2pio_statemachine_obj_t *self,
|
|||
self->out = tx_fifo;
|
||||
self->out_shift_right = out_shift_right;
|
||||
self->in_shift_right = in_shift_right;
|
||||
self->wait_for_txstall = wait_for_txstall;
|
||||
|
||||
self->init = init;
|
||||
self->init_len = init_len;
|
||||
|
||||
sm_config_set_fifo_join(&c, join);
|
||||
|
||||
pio_sm_init(self->pio, self->state_machine, program_offset, &c);
|
||||
common_hal_rp2pio_statemachine_run(self, init, init_len);
|
||||
|
||||
common_hal_rp2pio_statemachine_set_frequency(self, frequency);
|
||||
pio_sm_set_enabled(self->pio, self->state_machine, true);
|
||||
for (size_t i = 0; i < init_len; i++) {
|
||||
pio_sm_exec(self->pio, self->state_machine, init[i]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static uint32_t mask_and_rotate(const mcu_pin_obj_t* first_pin, uint32_t bit_count, uint32_t value) {
|
||||
value = value & ((1 << bit_count) - 1);
|
||||
uint32_t shift = first_pin->number;
|
||||
return value << shift | value >> (32 - shift);
|
||||
}
|
||||
|
||||
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) {
|
||||
|
||||
// First, check that all pins are free OR already in use by any PIO if exclusive_pin_use is false.
|
||||
|
@ -397,9 +415,26 @@ void common_hal_rp2pio_statemachine_construct(rp2pio_statemachine_obj_t *self,
|
|||
mp_raise_ValueError_varg(translate("Program does OUT without loading OSR"));
|
||||
}
|
||||
|
||||
if (in_pin_count > 8 || out_pin_count > 8) {
|
||||
mp_raise_NotImplementedError(translate("Only IN/OUT of up to 8 supported"));
|
||||
uint32_t initial_pin_state = mask_and_rotate(first_out_pin, out_pin_count, initial_out_pin_state);
|
||||
uint32_t initial_pin_direction = mask_and_rotate(first_out_pin, out_pin_count, initial_out_pin_direction);
|
||||
initial_set_pin_state = mask_and_rotate(first_set_pin, set_pin_count, initial_set_pin_state);
|
||||
initial_set_pin_direction = mask_and_rotate(first_set_pin, set_pin_count, initial_set_pin_direction);
|
||||
uint32_t set_out_overlap = mask_and_rotate(first_out_pin, out_pin_count, 0xffffffff) &
|
||||
mask_and_rotate(first_set_pin, set_pin_count, 0xffffffff);
|
||||
// Check that OUT and SET settings agree because we don't have a way of picking one over the other.
|
||||
if ((initial_pin_state & set_out_overlap) != (initial_set_pin_state & set_out_overlap)) {
|
||||
mp_raise_ValueError(translate("Initial set pin state conflicts with initial out pin state"));
|
||||
}
|
||||
if ((initial_pin_direction & set_out_overlap) != (initial_set_pin_direction & set_out_overlap)) {
|
||||
mp_raise_ValueError(translate("Initial set pin direcion conflicts with initial out pin direction"));
|
||||
}
|
||||
initial_pin_state |= initial_set_pin_state;
|
||||
initial_pin_direction |= initial_set_pin_direction;
|
||||
|
||||
// Sideset overrides OUT or SET so we always use its values.
|
||||
uint32_t sideset_mask = mask_and_rotate(first_sideset_pin, sideset_pin_count, 0x1f);
|
||||
initial_pin_state = (initial_pin_state & ~sideset_mask) | mask_and_rotate(first_sideset_pin, sideset_pin_count, initial_sideset_pin_state);
|
||||
initial_pin_direction = (initial_pin_direction & ~sideset_mask) | mask_and_rotate(first_sideset_pin, sideset_pin_count, initial_sideset_pin_direction);
|
||||
|
||||
bool ok = rp2pio_statemachine_construct(self,
|
||||
program, program_len,
|
||||
|
@ -409,8 +444,10 @@ void common_hal_rp2pio_statemachine_construct(rp2pio_statemachine_obj_t *self,
|
|||
first_in_pin, in_pin_count,
|
||||
first_set_pin, set_pin_count,
|
||||
first_sideset_pin, sideset_pin_count,
|
||||
initial_pin_state, initial_pin_direction,
|
||||
pins_we_use, tx_fifo, rx_fifo,
|
||||
auto_pull, pull_threshold, out_shift_right,
|
||||
wait_for_txstall,
|
||||
auto_push, push_threshold, in_shift_right,
|
||||
true /* claim pins */);
|
||||
if (!ok) {
|
||||
|
@ -418,10 +455,47 @@ void common_hal_rp2pio_statemachine_construct(rp2pio_statemachine_obj_t *self,
|
|||
}
|
||||
}
|
||||
|
||||
void common_hal_rp2pio_statemachine_restart(rp2pio_statemachine_obj_t *self) {
|
||||
pio_sm_restart(self->pio, self->state_machine);
|
||||
|
||||
uint8_t pio_index = pio_get_index(self->pio);
|
||||
uint32_t pins_we_use = _current_sm_pins[pio_index][self->state_machine];
|
||||
pio_sm_set_pins_with_mask(self->pio, self->state_machine, self->initial_pin_state, pins_we_use);
|
||||
pio_sm_set_pindirs_with_mask(self->pio, self->state_machine, self->initial_pin_direction, pins_we_use);
|
||||
common_hal_rp2pio_statemachine_run(self, self->init, self->init_len);
|
||||
pio_sm_set_enabled(self->pio, self->state_machine, true);
|
||||
}
|
||||
|
||||
void common_hal_rp2pio_statemachine_stop(rp2pio_statemachine_obj_t *self) {
|
||||
pio_sm_set_enabled(self->pio, self->state_machine, false);
|
||||
}
|
||||
|
||||
void common_hal_rp2pio_statemachine_run(rp2pio_statemachine_obj_t *self, const uint16_t *instructions, size_t len) {
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
pio_sm_exec(self->pio, self->state_machine, instructions[i]);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t common_hal_rp2pio_statemachine_get_frequency(rp2pio_statemachine_obj_t* self) {
|
||||
return self->actual_frequency;
|
||||
}
|
||||
|
||||
void common_hal_rp2pio_statemachine_set_frequency(rp2pio_statemachine_obj_t* self, uint32_t frequency) {
|
||||
if (frequency == 0) {
|
||||
frequency = clock_get_hz(clk_sys);
|
||||
}
|
||||
uint64_t frequency256 = ((uint64_t) clock_get_hz(clk_sys)) * 256;
|
||||
uint64_t div256 = frequency256 / frequency;
|
||||
if (frequency256 % div256 > 0) {
|
||||
div256 += 1;
|
||||
}
|
||||
self->actual_frequency = frequency256 / div256;
|
||||
|
||||
pio_sm_set_clkdiv_int_frac(self->pio, self->state_machine, div256 / 256, div256 % 256);
|
||||
// Reset the clkdiv counter in case our new TOP is lower.
|
||||
pio_sm_clkdiv_restart(self->pio, self->state_machine);
|
||||
}
|
||||
|
||||
void rp2pio_statemachine_deinit(rp2pio_statemachine_obj_t *self, bool leave_pins) {
|
||||
uint8_t sm = self->state_machine;
|
||||
uint8_t pio_index = pio_get_index(self->pio);
|
||||
|
@ -445,9 +519,21 @@ bool common_hal_rp2pio_statemachine_deinited(rp2pio_statemachine_obj_t *self) {
|
|||
return self->state_machine == NUM_PIO_STATE_MACHINES;
|
||||
}
|
||||
|
||||
enum dma_channel_transfer_size _stride_to_dma_size(uint8_t stride) {
|
||||
switch (stride) {
|
||||
case 4:
|
||||
return DMA_SIZE_32;
|
||||
case 2:
|
||||
return DMA_SIZE_16;
|
||||
case 1:
|
||||
default:
|
||||
return DMA_SIZE_8;
|
||||
}
|
||||
}
|
||||
|
||||
static bool _transfer(rp2pio_statemachine_obj_t *self,
|
||||
const uint8_t *data_out, size_t out_len,
|
||||
uint8_t *data_in, size_t in_len) {
|
||||
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) {
|
||||
// This implementation is based on SPI but varies because the tx and rx buffers
|
||||
// may be different lengths and occur at different times or speeds.
|
||||
|
||||
|
@ -472,42 +558,43 @@ static bool _transfer(rp2pio_statemachine_obj_t *self,
|
|||
if (tx) {
|
||||
tx_destination = (volatile uint8_t*) &self->pio->txf[self->state_machine];
|
||||
if (!self->out_shift_right) {
|
||||
tx_destination += 3;
|
||||
tx_destination += 4 - out_stride_in_bytes;
|
||||
}
|
||||
}
|
||||
if (rx) {
|
||||
rx_source = (const volatile uint8_t*) &self->pio->rxf[self->state_machine];
|
||||
if (!self->in_shift_right) {
|
||||
rx_source += 3;
|
||||
if (self->in_shift_right) {
|
||||
rx_source += 4 - in_stride_in_bytes;
|
||||
}
|
||||
}
|
||||
uint32_t stall_mask = 1 << (PIO_FDEBUG_TXSTALL_LSB + self->state_machine);
|
||||
bool use_dma = (!rx || chan_rx >= 0) && (!tx || chan_tx >= 0);
|
||||
if (use_dma) {
|
||||
dma_channel_config c;
|
||||
uint32_t channel_mask = 0;
|
||||
if (tx) {
|
||||
c = dma_channel_get_default_config(chan_tx);
|
||||
channel_config_set_transfer_data_size(&c, DMA_SIZE_8);
|
||||
channel_config_set_transfer_data_size(&c, _stride_to_dma_size(out_stride_in_bytes));
|
||||
channel_config_set_dreq(&c, self->tx_dreq);
|
||||
channel_config_set_read_increment(&c, true);
|
||||
channel_config_set_write_increment(&c, false);
|
||||
dma_channel_configure(chan_tx, &c,
|
||||
tx_destination,
|
||||
data_out,
|
||||
len,
|
||||
out_len / out_stride_in_bytes,
|
||||
false);
|
||||
channel_mask |= 1u << chan_tx;
|
||||
}
|
||||
if (rx) {
|
||||
c = dma_channel_get_default_config(chan_rx);
|
||||
channel_config_set_transfer_data_size(&c, DMA_SIZE_8);
|
||||
channel_config_set_transfer_data_size(&c, _stride_to_dma_size(in_stride_in_bytes));
|
||||
channel_config_set_dreq(&c, self->rx_dreq);
|
||||
channel_config_set_read_increment(&c, false);
|
||||
channel_config_set_write_increment(&c, true);
|
||||
dma_channel_configure(chan_rx, &c,
|
||||
data_in,
|
||||
rx_source,
|
||||
len,
|
||||
in_len / in_stride_in_bytes,
|
||||
false);
|
||||
channel_mask |= 1u << chan_rx;
|
||||
}
|
||||
|
@ -528,7 +615,7 @@ static bool _transfer(rp2pio_statemachine_obj_t *self,
|
|||
}
|
||||
}
|
||||
// Clear the stall bit so we can detect when the state machine is done transmitting.
|
||||
self->pio->fdebug = PIO_FDEBUG_TXSTALL_BITS;
|
||||
self->pio->fdebug = stall_mask;
|
||||
}
|
||||
|
||||
// If we have claimed only one channel successfully, we should release immediately. This also
|
||||
|
@ -542,27 +629,31 @@ static bool _transfer(rp2pio_statemachine_obj_t *self,
|
|||
|
||||
if (!use_dma && !mp_hal_is_interrupted()) {
|
||||
// Use software for small transfers, or if couldn't claim two DMA channels
|
||||
size_t rx_remaining = in_len;
|
||||
size_t tx_remaining = out_len;
|
||||
size_t rx_remaining = in_len / in_stride_in_bytes;
|
||||
size_t tx_remaining = out_len / out_stride_in_bytes;
|
||||
|
||||
while (rx_remaining || tx_remaining) {
|
||||
for (int i=0; i<32; i++) {
|
||||
bool did_transfer = false;
|
||||
if (tx_remaining && !pio_sm_is_tx_fifo_full(self->pio, self->state_machine)) {
|
||||
while (tx_remaining && !pio_sm_is_tx_fifo_full(self->pio, self->state_machine)) {
|
||||
if (out_stride_in_bytes == 1) {
|
||||
*tx_destination = *data_out;
|
||||
data_out++;
|
||||
--tx_remaining;
|
||||
did_transfer = true;
|
||||
} else if (in_stride_in_bytes == 2) {
|
||||
*((uint16_t*) tx_destination) = *((uint16_t*) data_out);
|
||||
} else if (in_stride_in_bytes == 4) {
|
||||
*((uint32_t*) tx_destination) = *((uint32_t*) data_out);
|
||||
}
|
||||
if (rx_remaining && !pio_sm_is_rx_fifo_empty(self->pio, self->state_machine)) {
|
||||
data_out += out_stride_in_bytes;
|
||||
--tx_remaining;
|
||||
}
|
||||
while (rx_remaining && !pio_sm_is_rx_fifo_empty(self->pio, self->state_machine)) {
|
||||
if (in_stride_in_bytes == 1) {
|
||||
*data_in = (uint8_t) *rx_source;
|
||||
data_in++;
|
||||
--rx_remaining;
|
||||
did_transfer = true;
|
||||
}
|
||||
if (!did_transfer) {
|
||||
break;
|
||||
} else if (in_stride_in_bytes == 2) {
|
||||
*((uint16_t*) data_in) = *((uint16_t*) rx_source);
|
||||
} else if (in_stride_in_bytes == 4) {
|
||||
*((uint32_t*) data_in) = *((uint32_t*) rx_source);
|
||||
}
|
||||
data_in += in_stride_in_bytes;
|
||||
--rx_remaining;
|
||||
}
|
||||
RUN_BACKGROUND_TASKS;
|
||||
if (mp_hal_is_interrupted()) {
|
||||
|
@ -570,24 +661,55 @@ static bool _transfer(rp2pio_statemachine_obj_t *self,
|
|||
}
|
||||
}
|
||||
// Clear the stall bit so we can detect when the state machine is done transmitting.
|
||||
self->pio->fdebug = PIO_FDEBUG_TXSTALL_BITS;
|
||||
self->pio->fdebug = stall_mask;
|
||||
}
|
||||
// Wait for the state machine to finish transmitting the data we've queued
|
||||
// up.
|
||||
if (tx) {
|
||||
while (!pio_sm_is_tx_fifo_empty(self->pio, self->state_machine) ||
|
||||
(self->pio->fdebug & PIO_FDEBUG_TXSTALL_BITS) == 0) {
|
||||
(self->wait_for_txstall && (self->pio->fdebug & stall_mask) == 0)) {
|
||||
RUN_BACKGROUND_TASKS;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Writes out the given data.
|
||||
bool common_hal_rp2pio_statemachine_write(rp2pio_statemachine_obj_t *self,
|
||||
const uint8_t *data, size_t len) {
|
||||
// TODO: Provide a way around these checks in case someone wants to use the FIFO
|
||||
// with manually run code.
|
||||
|
||||
bool common_hal_rp2pio_statemachine_write(rp2pio_statemachine_obj_t *self, const uint8_t *data, size_t len, uint8_t stride_in_bytes) {
|
||||
if (!self->out) {
|
||||
mp_raise_RuntimeError(translate("No out in program"));
|
||||
}
|
||||
return _transfer(self, data, len, NULL, 0);
|
||||
return _transfer(self, data, len, stride_in_bytes, NULL, 0, 0);
|
||||
}
|
||||
|
||||
bool common_hal_rp2pio_statemachine_readinto(rp2pio_statemachine_obj_t *self, uint8_t *data, size_t len, uint8_t stride_in_bytes) {
|
||||
if (!self->in) {
|
||||
mp_raise_RuntimeError(translate("No in in program"));
|
||||
}
|
||||
return _transfer(self, NULL, 0, 0, data, len, 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) {
|
||||
if (!self->in || !self->out) {
|
||||
mp_raise_RuntimeError(translate("No in or out in program"));
|
||||
}
|
||||
return _transfer(self, data_out, out_len, out_stride_in_bytes, data_in, in_len, in_stride_in_bytes);
|
||||
}
|
||||
|
||||
bool common_hal_rp2pio_statemachine_get_rxstall(rp2pio_statemachine_obj_t* self) {
|
||||
uint32_t stall_mask = 1 << (PIO_FDEBUG_RXSTALL_LSB + self->state_machine);
|
||||
return (self->pio->fdebug & stall_mask) != 0;
|
||||
}
|
||||
|
||||
void common_hal_rp2pio_statemachine_clear_rxfifo(rp2pio_statemachine_obj_t *self) {
|
||||
uint8_t level = pio_sm_get_rx_fifo_level(self->pio, self->state_machine);
|
||||
uint32_t stall_mask = 1 << (PIO_FDEBUG_RXSTALL_LSB + self->state_machine);
|
||||
for (size_t i = 0; i < level; i++) {
|
||||
(void) self->pio->rxf[self->state_machine];
|
||||
}
|
||||
self->pio->fdebug = stall_mask;
|
||||
}
|
||||
|
|
|
@ -36,8 +36,13 @@ typedef struct {
|
|||
uint32_t pins; // Bitmask of what pins this state machine uses.
|
||||
int state_machine;
|
||||
PIO pio;
|
||||
const uint16_t* init;
|
||||
size_t init_len;
|
||||
uint32_t initial_pin_state;
|
||||
uint32_t initial_pin_direction;
|
||||
bool in;
|
||||
bool out;
|
||||
bool wait_for_txstall;
|
||||
uint tx_dreq;
|
||||
uint rx_dreq;
|
||||
bool out_shift_right;
|
||||
|
@ -56,8 +61,10 @@ bool rp2pio_statemachine_construct(rp2pio_statemachine_obj_t *self,
|
|||
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,
|
||||
uint32_t initial_pin_state, uint32_t initial_pin_direction,
|
||||
uint32_t pins_we_use, bool tx_fifo, bool rx_fifo,
|
||||
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);
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@ CIRCUITPY_WATCHDOG = 1
|
|||
|
||||
# Audio via PWM
|
||||
CIRCUITPY_AUDIOIO = 0
|
||||
CIRCUITPY_AUDIOBUSIO ?= 0 # add this later
|
||||
CIRCUITPY_AUDIOBUSIO ?= 1
|
||||
CIRCUITPY_AUDIOCORE ?= 1
|
||||
CIRCUITPY_AUDIOPWMIO ?= 1
|
||||
|
||||
|
|
|
@ -112,6 +112,9 @@ void reset_port(void) {
|
|||
#if CIRCUITPY_AUDIOPWMIO
|
||||
audiopwmout_reset();
|
||||
#endif
|
||||
#if CIRCUITPY_AUDIOCORE
|
||||
audio_dma_reset();
|
||||
#endif
|
||||
|
||||
reset_all_pins();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
import audiocore
|
||||
import audiobusio
|
||||
import board
|
||||
import digitalio
|
||||
import array
|
||||
import time
|
||||
import math
|
||||
import rp2pio
|
||||
import adafruit_pioasm
|
||||
|
||||
time.sleep(10)
|
||||
|
||||
trigger = digitalio.DigitalInOut(board.D4)
|
||||
trigger.switch_to_output(True)
|
||||
|
||||
# Generate one period of sine wav.
|
||||
length = 8000 // 440
|
||||
|
||||
# signed 16 bit
|
||||
s16 = array.array("h", [0] * length)
|
||||
for i in range(length):
|
||||
s16[i] = int(math.sin(math.pi * 2 * i / length) * (2 ** 15))
|
||||
print(s16[i])
|
||||
|
||||
sample = audiocore.RawSample(s16, sample_rate=8000)
|
||||
|
||||
dac = audiobusio.I2SOut(bit_clock=board.D10,
|
||||
word_select=board.D11, data=board.D12)
|
||||
|
||||
trigger.value = False
|
||||
dac.play(sample, loop=True)
|
||||
time.sleep(1)
|
||||
dac.stop()
|
||||
trigger.value = True
|
||||
|
||||
print("done")
|
|
@ -0,0 +1,44 @@
|
|||
import audiobusio
|
||||
import board
|
||||
import digitalio
|
||||
import array
|
||||
import time
|
||||
import math
|
||||
|
||||
trigger = digitalio.DigitalInOut(board.D4)
|
||||
trigger.switch_to_output(True)
|
||||
|
||||
def mean(values):
|
||||
return sum(values) / len(values)
|
||||
|
||||
|
||||
def normalized_rms(values):
|
||||
minbuf = int(mean(values))
|
||||
samples_sum = sum(
|
||||
float(sample - minbuf) * (sample - minbuf)
|
||||
for sample in values
|
||||
)
|
||||
|
||||
return math.sqrt(samples_sum / len(values))
|
||||
|
||||
# signed 16 bit
|
||||
s16 = array.array("H", [0] * 10000)
|
||||
|
||||
pdm = audiobusio.PDMIn(clock_pin=board.D11, data_pin=board.D12, sample_rate=24000, bit_depth=16)
|
||||
|
||||
print("starting read")
|
||||
trigger.value = False
|
||||
count = pdm.record(s16, len(s16))
|
||||
trigger.value = True
|
||||
print("read done")
|
||||
print("recorded {} samples".format(count))
|
||||
for v in s16[:count]:
|
||||
print(v)
|
||||
|
||||
|
||||
magnitude = normalized_rms(s16)
|
||||
print("magnitude", magnitude)
|
||||
|
||||
print("count", count)
|
||||
|
||||
print("done")
|
Loading…
Reference in New Issue