summaryrefslogtreecommitdiff
path: root/ports/raspberrypi
diff options
context:
space:
mode:
authorMark <56205165+gamblor21@users.noreply.github.com>2021-03-02 19:47:53 -0600
committerGitHub <noreply@github.com>2021-03-02 19:47:53 -0600
commit37ee18349f99ffafd7b60db8a107cd6f1e9e3f92 (patch)
tree5db36911199729499d91d0c317957cfff0b71013 /ports/raspberrypi
parent4246cc3f6d718e2f28fbeb73fc8c3c39dfb9d7f0 (diff)
parentefc2667b5f087480b1b3233ae00ab1529a442f9f (diff)
Merge branch 'main' into rp2040_countio
Diffstat (limited to 'ports/raspberrypi')
-rw-r--r--ports/raspberrypi/Makefile21
-rw-r--r--ports/raspberrypi/audio_dma.c17
-rw-r--r--ports/raspberrypi/bindings/rp2pio/StateMachine.c377
-rw-r--r--ports/raspberrypi/bindings/rp2pio/StateMachine.h38
-rw-r--r--ports/raspberrypi/boards/adafruit_feather_rp2040/board.c4
-rw-r--r--ports/raspberrypi/boards/adafruit_feather_rp2040/mpconfigboard.h4
-rw-r--r--ports/raspberrypi/boards/adafruit_feather_rp2040/pins.c7
-rw-r--r--ports/raspberrypi/boards/qtpy_rp2040/mpconfigboard.h4
-rw-r--r--ports/raspberrypi/boards/raspberry_pi_pico/mpconfigboard.mk2
-rw-r--r--ports/raspberrypi/bs2_default_padded_checksummed.S23
-rw-r--r--ports/raspberrypi/common-hal/audiobusio/I2SOut.c224
-rw-r--r--ports/raspberrypi/common-hal/audiobusio/I2SOut.h47
-rw-r--r--ports/raspberrypi/common-hal/audiobusio/PDMIn.c172
-rw-r--r--ports/raspberrypi/common-hal/audiobusio/PDMIn.h50
-rw-r--r--ports/raspberrypi/common-hal/audiobusio/__init__.c1
-rw-r--r--ports/raspberrypi/common-hal/busio/UART.c527
-rw-r--r--ports/raspberrypi/common-hal/busio/UART.h23
-rw-r--r--ports/raspberrypi/common-hal/digitalio/DigitalInOut.c28
-rw-r--r--ports/raspberrypi/common-hal/neopixel_write/__init__.c22
-rw-r--r--ports/raspberrypi/common-hal/nvm/ByteArray.c102
-rw-r--r--ports/raspberrypi/common-hal/nvm/ByteArray.h38
-rw-r--r--ports/raspberrypi/common-hal/nvm/__init__.c1
-rw-r--r--ports/raspberrypi/common-hal/pwmio/PWMOut.c78
-rw-r--r--ports/raspberrypi/common-hal/pwmio/PWMOut.h5
-rw-r--r--ports/raspberrypi/common-hal/rgbmatrix/RGBMatrix.c70
-rw-r--r--ports/raspberrypi/common-hal/rgbmatrix/RGBMatrix.h37
-rw-r--r--ports/raspberrypi/common-hal/rgbmatrix/__init__.c0
-rw-r--r--ports/raspberrypi/common-hal/rp2pio/StateMachine.c209
-rw-r--r--ports/raspberrypi/common-hal/rp2pio/StateMachine.h7
-rw-r--r--ports/raspberrypi/link.ld2
-rw-r--r--ports/raspberrypi/mpconfigport.h9
-rw-r--r--ports/raspberrypi/mpconfigport.mk6
-rw-r--r--ports/raspberrypi/supervisor/port.c10
33 files changed, 1573 insertions, 592 deletions
diff --git a/ports/raspberrypi/Makefile b/ports/raspberrypi/Makefile
index f5e65f256..9d66338c7 100644
--- a/ports/raspberrypi/Makefile
+++ b/ports/raspberrypi/Makefile
@@ -231,8 +231,9 @@ SRC_SHARED_MODULE_EXPANDED = $(addprefix shared-bindings/, $(SRC_SHARED_MODULE))
SRC_COMMON_HAL_SHARED_MODULE_EXPANDED = $(sort $(SRC_COMMON_HAL_EXPANDED) $(SRC_SHARED_MODULE_EXPANDED))
SRC_S = supervisor/$(CHIP_FAMILY)_cpu.s
-SRC_S_UPPER = bs2_default_padded_checksummed.S \
- sdk/src/rp2_common/hardware_divider/divider.S \
+BOOT2_S_UPPER ?= sdk/src/rp2_common/boot_stage2/boot2_generic_03h.S
+BOOT2_S_CFLAGS ?= -DPICO_FLASH_SPI_CLKDIV=2
+SRC_S_UPPER = sdk/src/rp2_common/hardware_divider/divider.S \
sdk/src/rp2_common/hardware_irq/irq_handler_chain.S \
sdk/src/rp2_common/pico_bit_ops/bit_ops_aeabi.S \
sdk/src/rp2_common/pico_double/double_aeabi.S \
@@ -253,7 +254,23 @@ endif
OBJ += $(addprefix $(BUILD)/, $(SRC_S:.s=.o))
OBJ += $(addprefix $(BUILD)/, $(SRC_S_UPPER:.S=.o))
OBJ += $(addprefix $(BUILD)/, $(SRC_MOD:.c=.o))
+OBJ += $(BUILD)/boot2_padded_checksummed.o
+$(BUILD)/boot2_padded_checksummed.o: $(BUILD)/boot2_padded_checksummed.S
+ $(STEPECHO) "CC $<"
+ $(Q)$(CC) $(CFLAGS) -c -o $@ $<
+
+$(BUILD)/boot2_padded_checksummed.S: $(BUILD)/boot2.bin
+ $(STEPECHO) "PAD_CHECKSUM $<"
+ $(Q)$(PYTHON3) sdk/src/rp2_common/boot_stage2/pad_checksum -s 0xffffffff $< $@
+
+$(BUILD)/boot2.bin: $(BUILD)/boot2.elf
+ $(STEPECHO) "OBJCOPY $<"
+ $(Q)$(OBJCOPY) -O binary $< $@
+
+$(BUILD)/boot2.elf: $(BOOT2_S_UPPER) | $(BUILD)/
+ $(STEPECHO) "BOOT $<"
+ $(Q)$(CC) $(CFLAGS) $(BOOT2_S_CFLAGS) -Isdk/src/rp2_common/boot_stage2/asminclude --specs=nosys.specs -nostartfiles -Wl,-T,sdk/src/rp2_common/boot_stage2/boot_stage2.ld -o $@ $<
SRC_QSTR += $(SRC_C) $(SRC_SUPERVISOR) $(SRC_COMMON_HAL_EXPANDED) $(SRC_SHARED_MODULE_EXPANDED)
diff --git a/ports/raspberrypi/audio_dma.c b/ports/raspberrypi/audio_dma.c
index 96317984d..ccf08b1b4 100644
--- a/ports/raspberrypi/audio_dma.c
+++ b/ports/raspberrypi/audio_dma.c
@@ -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;
diff --git a/ports/raspberrypi/bindings/rp2pio/StateMachine.c b/ports/raspberrypi/bindings/rp2pio/StateMachine.c
index 34633949e..444e00ace 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"));
+ }
+
+ 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"));
+ }
+
+ 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
diff --git a/ports/raspberrypi/boards/adafruit_feather_rp2040/board.c b/ports/raspberrypi/boards/adafruit_feather_rp2040/board.c
index 8686f6d0c..c4021a83e 100644
--- a/ports/raspberrypi/boards/adafruit_feather_rp2040/board.c
+++ b/ports/raspberrypi/boards/adafruit_feather_rp2040/board.c
@@ -30,10 +30,6 @@
#include "src/rp2_common/hardware_gpio/include/hardware/gpio.h"
void board_init(void) {
- common_hal_never_reset_pin(&pin_GPIO17);
- gpio_init(17);
- gpio_set_dir(17, GPIO_OUT);
- gpio_put(17, true);
}
bool board_requests_safe_mode(void) {
diff --git a/ports/raspberrypi/boards/adafruit_feather_rp2040/mpconfigboard.h b/ports/raspberrypi/boards/adafruit_feather_rp2040/mpconfigboard.h
index f491a7769..a3124c7e5 100644
--- a/ports/raspberrypi/boards/adafruit_feather_rp2040/mpconfigboard.h
+++ b/ports/raspberrypi/boards/adafruit_feather_rp2040/mpconfigboard.h
@@ -13,5 +13,5 @@
// #define DEFAULT_UART_BUS_RX (&pin_PA11)
// #define DEFAULT_UART_BUS_TX (&pin_PA10)
-// Flash chip is GD25Q32 connected over QSPI
-#define TOTAL_FLASH_SIZE (4 * 1024 * 1024)
+// Flash chip is GD25Q64 connected over QSPI
+#define TOTAL_FLASH_SIZE (8 * 1024 * 1024)
diff --git a/ports/raspberrypi/boards/adafruit_feather_rp2040/pins.c b/ports/raspberrypi/boards/adafruit_feather_rp2040/pins.c
index 1617ac865..a558d7fe6 100644
--- a/ports/raspberrypi/boards/adafruit_feather_rp2040/pins.c
+++ b/ports/raspberrypi/boards/adafruit_feather_rp2040/pins.c
@@ -14,12 +14,12 @@ STATIC const mp_rom_map_elem_t board_global_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_RX), MP_ROM_PTR(&pin_GPIO1) },
{ MP_ROM_QSTR(MP_QSTR_D1), MP_ROM_PTR(&pin_GPIO0) },
{ MP_ROM_QSTR(MP_QSTR_TX), MP_ROM_PTR(&pin_GPIO0) },
- { MP_ROM_QSTR(MP_QSTR_D4), MP_ROM_PTR(&pin_GPIO4) },
+ { MP_ROM_QSTR(MP_QSTR_D4), MP_ROM_PTR(&pin_GPIO6) },
{ MP_ROM_QSTR(MP_QSTR_SDA), MP_ROM_PTR(&pin_GPIO2) },
{ MP_ROM_QSTR(MP_QSTR_SCL), MP_ROM_PTR(&pin_GPIO3) },
- { MP_ROM_QSTR(MP_QSTR_D5), MP_ROM_PTR(&pin_GPIO5) },
- { MP_ROM_QSTR(MP_QSTR_D6), MP_ROM_PTR(&pin_GPIO6) },
+ { MP_ROM_QSTR(MP_QSTR_D5), MP_ROM_PTR(&pin_GPIO7) },
+ { MP_ROM_QSTR(MP_QSTR_D6), MP_ROM_PTR(&pin_GPIO8) },
{ MP_ROM_QSTR(MP_QSTR_D9), MP_ROM_PTR(&pin_GPIO9) },
{ MP_ROM_QSTR(MP_QSTR_D10), MP_ROM_PTR(&pin_GPIO10) },
{ MP_ROM_QSTR(MP_QSTR_D11), MP_ROM_PTR(&pin_GPIO11) },
@@ -27,7 +27,6 @@ STATIC const mp_rom_map_elem_t board_global_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_D13), MP_ROM_PTR(&pin_GPIO13) },
{ MP_ROM_QSTR(MP_QSTR_NEOPIXEL), MP_ROM_PTR(&pin_GPIO16) },
- { MP_ROM_QSTR(MP_QSTR_NEOPIXEL_POWER), MP_ROM_PTR(&pin_GPIO17) },
{ MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&board_i2c_obj) },
{ MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&board_spi_obj) },
diff --git a/ports/raspberrypi/boards/qtpy_rp2040/mpconfigboard.h b/ports/raspberrypi/boards/qtpy_rp2040/mpconfigboard.h
index 3f4778457..f5734b513 100644
--- a/ports/raspberrypi/boards/qtpy_rp2040/mpconfigboard.h
+++ b/ports/raspberrypi/boards/qtpy_rp2040/mpconfigboard.h
@@ -13,5 +13,5 @@
// #define DEFAULT_UART_BUS_RX (&pin_PA11)
// #define DEFAULT_UART_BUS_TX (&pin_PA10)
-// Flash chip is GD25Q32 connected over QSPI
-#define TOTAL_FLASH_SIZE (4 * 1024 * 1024)
+// Flash chip is GD25Q64 connected over QSPI
+#define TOTAL_FLASH_SIZE (8 * 1024 * 1024)
diff --git a/ports/raspberrypi/boards/raspberry_pi_pico/mpconfigboard.mk b/ports/raspberrypi/boards/raspberry_pi_pico/mpconfigboard.mk
index 54c130660..8d6ca5330 100644
--- a/ports/raspberrypi/boards/raspberry_pi_pico/mpconfigboard.mk
+++ b/ports/raspberrypi/boards/raspberry_pi_pico/mpconfigboard.mk
@@ -9,3 +9,5 @@ CHIP_FAMILY = rp2
INTERNAL_FLASH_FILESYSTEM = 1
CIRCUITPY__EVE = 1
+
+BOOT2_S_UPPER = sdk/src/rp2_common/boot_stage2/boot2_w25q080.S
diff --git a/ports/raspberrypi/bs2_default_padded_checksummed.S b/ports/raspberrypi/bs2_default_padded_checksummed.S
deleted file mode 100644
index 6b7074e3b..000000000
--- a/ports/raspberrypi/bs2_default_padded_checksummed.S
+++ /dev/null
@@ -1,23 +0,0 @@
-// Padded and checksummed version of: /home/pi/pico/pico-examples/build/pico-sdk/src/rp2_common/boot_stage2/bs2_default.bin
-
-.cpu cortex-m0plus
-.thumb
-
-.section .boot2, "ax"
-
-.byte 0x00, 0xb5, 0x32, 0x4b, 0x21, 0x20, 0x58, 0x60, 0x98, 0x68, 0x02, 0x21, 0x88, 0x43, 0x98, 0x60
-.byte 0xd8, 0x60, 0x18, 0x61, 0x58, 0x61, 0x2e, 0x4b, 0x00, 0x21, 0x99, 0x60, 0x02, 0x21, 0x59, 0x61
-.byte 0x01, 0x21, 0xf0, 0x22, 0x99, 0x50, 0x2b, 0x49, 0x19, 0x60, 0x01, 0x21, 0x99, 0x60, 0x35, 0x20
-.byte 0x00, 0xf0, 0x44, 0xf8, 0x02, 0x22, 0x90, 0x42, 0x14, 0xd0, 0x06, 0x21, 0x19, 0x66, 0x00, 0xf0
-.byte 0x34, 0xf8, 0x19, 0x6e, 0x01, 0x21, 0x19, 0x66, 0x00, 0x20, 0x18, 0x66, 0x1a, 0x66, 0x00, 0xf0
-.byte 0x2c, 0xf8, 0x19, 0x6e, 0x19, 0x6e, 0x19, 0x6e, 0x05, 0x20, 0x00, 0xf0, 0x2f, 0xf8, 0x01, 0x21
-.byte 0x08, 0x42, 0xf9, 0xd1, 0x00, 0x21, 0x99, 0x60, 0x1b, 0x49, 0x19, 0x60, 0x00, 0x21, 0x59, 0x60
-.byte 0x1a, 0x49, 0x1b, 0x48, 0x01, 0x60, 0x01, 0x21, 0x99, 0x60, 0xeb, 0x21, 0x19, 0x66, 0xa0, 0x21
-.byte 0x19, 0x66, 0x00, 0xf0, 0x12, 0xf8, 0x00, 0x21, 0x99, 0x60, 0x16, 0x49, 0x14, 0x48, 0x01, 0x60
-.byte 0x01, 0x21, 0x99, 0x60, 0x01, 0xbc, 0x00, 0x28, 0x00, 0xd0, 0x00, 0x47, 0x12, 0x48, 0x13, 0x49
-.byte 0x08, 0x60, 0x03, 0xc8, 0x80, 0xf3, 0x08, 0x88, 0x08, 0x47, 0x03, 0xb5, 0x99, 0x6a, 0x04, 0x20
-.byte 0x01, 0x42, 0xfb, 0xd0, 0x01, 0x20, 0x01, 0x42, 0xf8, 0xd1, 0x03, 0xbd, 0x02, 0xb5, 0x18, 0x66
-.byte 0x18, 0x66, 0xff, 0xf7, 0xf2, 0xff, 0x18, 0x6e, 0x18, 0x6e, 0x02, 0xbd, 0x00, 0x00, 0x02, 0x40
-.byte 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x07, 0x00, 0x00, 0x03, 0x5f, 0x00, 0x21, 0x22, 0x00, 0x00
-.byte 0xf4, 0x00, 0x00, 0x18, 0x22, 0x20, 0x00, 0xa0, 0x00, 0x01, 0x00, 0x10, 0x08, 0xed, 0x00, 0xe0
-.byte 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x74, 0xb2, 0x4e, 0x7a
diff --git a/ports/raspberrypi/common-hal/audiobusio/I2SOut.c b/ports/raspberrypi/common-hal/audiobusio/I2SOut.c
new file mode 100644
index 000000000..83a443834
--- /dev/null
+++ b/ports/raspberrypi/common-hal/audiobusio/I2SOut.c
@@ -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;
+}
diff --git a/ports/raspberrypi/common-hal/audiobusio/I2SOut.h b/ports/raspberrypi/common-hal/audiobusio/I2SOut.h
new file mode 100644
index 000000000..851e86c8a
--- /dev/null
+++ b/ports/raspberrypi/common-hal/audiobusio/I2SOut.h
@@ -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
diff --git a/ports/raspberrypi/common-hal/audiobusio/PDMIn.c b/ports/raspberrypi/common-hal/audiobusio/PDMIn.c
new file mode 100644
index 000000000..ffe09326f
--- /dev/null
+++ b/ports/raspberrypi/common-hal/audiobusio/PDMIn.c
@@ -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) {
+
+}
diff --git a/ports/raspberrypi/common-hal/audiobusio/PDMIn.h b/ports/raspberrypi/common-hal/audiobusio/PDMIn.h
new file mode 100644
index 000000000..995ffb5d7
--- /dev/null
+++ b/ports/raspberrypi/common-hal/audiobusio/PDMIn.h
@@ -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
diff --git a/ports/raspberrypi/common-hal/audiobusio/__init__.c b/ports/raspberrypi/common-hal/audiobusio/__init__.c
new file mode 100644
index 000000000..87db40496
--- /dev/null
+++ b/ports/raspberrypi/common-hal/audiobusio/__init__.c
@@ -0,0 +1 @@
+// No audiobusio module functions.
diff --git a/ports/raspberrypi/common-hal/busio/UART.c b/ports/raspberrypi/common-hal/busio/UART.c
index f9a75b499..1bc121177 100644
--- a/ports/raspberrypi/common-hal/busio/UART.c
+++ b/ports/raspberrypi/common-hal/busio/UART.c
@@ -3,7 +3,7 @@
*
* The MIT License (MIT)
*
- * Copyright (c) 2021 Scott Shawcroft for Adafruit Industries
+ * Copyright (c) 2021 microDev
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -24,238 +24,184 @@
* THE SOFTWARE.
*/
-#include "shared-bindings/microcontroller/__init__.h"
#include "shared-bindings/busio/UART.h"
-#include "mpconfigport.h"
-#include "lib/utils/interrupt_char.h"
-#include "py/gc.h"
+#include "py/stream.h"
#include "py/mperrno.h"
#include "py/runtime.h"
-#include "py/stream.h"
-#include "supervisor/shared/translate.h"
#include "supervisor/shared/tick.h"
+#include "lib/utils/interrupt_char.h"
+#include "common-hal/microcontroller/Pin.h"
-#define UART_DEBUG(...) (void)0
-// #define UART_DEBUG(...) mp_printf(&mp_plat_print __VA_OPT__(,) __VA_ARGS__)
-
-// Do-nothing callback needed so that usart_async code will enable rx interrupts.
-// See comment below re usart_async_register_callback()
-// static void usart_async_rxc_callback(const struct usart_async_descriptor *const descr) {
-// // Nothing needs to be done by us.
-// }
+#include "src/rp2_common/hardware_irq/include/hardware/irq.h"
+#include "src/rp2_common/hardware_gpio/include/hardware/gpio.h"
#define NO_PIN 0xff
+#define UART_INST(uart) (((uart) ? uart1 : uart0))
+
+typedef enum {
+ STATUS_FREE = 0,
+ STATUS_BUSY,
+ STATUS_NEVER_RESET
+} uart_status_t;
+
+static uart_status_t uart_status[NUM_UARTS];
+
+void reset_uart(void) {
+ for (uint8_t num = 0; num < NUM_UARTS; num++) {
+ if (uart_status[num] == STATUS_BUSY) {
+ uart_status[num] = STATUS_FREE;
+ uart_deinit(UART_INST(num));
+ }
+ }
+}
+
+void never_reset_uart(uint8_t num) {
+ uart_status[num] = STATUS_NEVER_RESET;
+}
+
+static uint8_t pin_init(const uint8_t uart, const mcu_pin_obj_t * pin, const uint8_t pin_type) {
+ if (pin == NULL) {
+ return NO_PIN;
+ }
+ if (!(((pin->number % 4) == pin_type) && ((((pin->number + 4) / 8) % NUM_UARTS) == uart))) {
+ mp_raise_ValueError(translate("Invalid pins"));
+ }
+ claim_pin(pin);
+ gpio_set_function(pin->number, GPIO_FUNC_UART);
+ return pin->number;
+}
+
+static busio_uart_obj_t* active_uarts[NUM_UARTS];
+
+static void _copy_into_ringbuf(ringbuf_t* r, uart_inst_t* uart) {
+ while (uart_is_readable(uart) && ringbuf_num_empty(r) > 0) {
+ ringbuf_put(r, (uint8_t) uart_get_hw(uart)->dr);
+ }
+}
+
+static void shared_callback(busio_uart_obj_t *self) {
+ _copy_into_ringbuf(&self->ringbuf, self->uart);
+ // We always clear the interrupt so it doesn't continue to fire because we
+ // may not have read everything available.
+ uart_get_hw(self->uart)->icr = UART_UARTICR_RXIC_BITS;
+}
+
+static void uart0_callback(void) {
+ shared_callback(active_uarts[0]);
+}
+
+static void uart1_callback(void) {
+ shared_callback(active_uarts[1]);
+}
+
void common_hal_busio_uart_construct(busio_uart_obj_t *self,
- const mcu_pin_obj_t * tx, const mcu_pin_obj_t * rx,
- const mcu_pin_obj_t * rts, const mcu_pin_obj_t * cts,
- const mcu_pin_obj_t * rs485_dir, bool rs485_invert,
- uint32_t baudrate, uint8_t bits, busio_uart_parity_t parity, uint8_t stop,
- mp_float_t timeout, uint16_t receiver_buffer_size, byte* receiver_buffer,
- bool sigint_enabled) {
- mp_raise_NotImplementedError(translate("UART not yet supported"));
-
-// Sercom* sercom = NULL;
-// uint8_t sercom_index = 255; // Unset index
-// uint32_t rx_pinmux = 0;
-// uint8_t rx_pad = 255; // Unset pad
-// uint32_t tx_pinmux = 0;
-// uint8_t tx_pad = 255; // Unset pad
-
-// if ((rts != NULL) || (cts != NULL) || (rs485_dir != NULL) || (rs485_invert)) {
-// mp_raise_ValueError(translate("RTS/CTS/RS485 Not yet supported on this device"));
-// }
-
-// if (bits > 8) {
-// mp_raise_NotImplementedError(translate("bytes > 8 bits not supported"));
-// }
-
-// bool have_tx = tx != NULL;
-// bool have_rx = rx != NULL;
-// if (!have_tx && !have_rx) {
-// mp_raise_ValueError(translate("tx and rx cannot both be None"));
-// }
-
-// self->baudrate = baudrate;
-// self->character_bits = bits;
-// self->timeout_ms = timeout * 1000;
-
-// // This assignment is only here because the usart_async routines take a *const argument.
-// struct usart_async_descriptor * const usart_desc_p = (struct usart_async_descriptor * const) &self->usart_desc;
-
-// for (int i = 0; i < NUM_SERCOMS_PER_PIN; i++) {
-// Sercom* potential_sercom = NULL;
-// if (have_tx) {
-// sercom_index = tx->sercom[i].index;
-// if (sercom_index >= SERCOM_INST_NUM) {
-// continue;
-// }
-// potential_sercom = sercom_insts[sercom_index];
-// #ifdef SAMD21
-// if (potential_sercom->USART.CTRLA.bit.ENABLE != 0 ||
-// !(tx->sercom[i].pad == 0 ||
-// tx->sercom[i].pad == 2)) {
-// continue;
-// }
-// #endif
-// #ifdef SAM_D5X_E5X
-// if (potential_sercom->USART.CTRLA.bit.ENABLE != 0 ||
-// !(tx->sercom[i].pad == 0)) {
-// continue;
-// }
-// #endif
-// tx_pinmux = PINMUX(tx->number, (i == 0) ? MUX_C : MUX_D);
-// tx_pad = tx->sercom[i].pad;
-// if (rx == NULL) {
-// sercom = potential_sercom;
-// break;
-// }
-// }
-// for (int j = 0; j < NUM_SERCOMS_PER_PIN; j++) {
-// if (((!have_tx && rx->sercom[j].index < SERCOM_INST_NUM &&
-// sercom_insts[rx->sercom[j].index]->USART.CTRLA.bit.ENABLE == 0) ||
-// sercom_index == rx->sercom[j].index) &&
-// rx->sercom[j].pad != tx_pad) {
-// rx_pinmux = PINMUX(rx->number, (j == 0) ? MUX_C : MUX_D);
-// rx_pad = rx->sercom[j].pad;
-// sercom = sercom_insts[rx->sercom[j].index];
-// sercom_index = rx->sercom[j].index;
-// break;
-// }
-// }
-// if (sercom != NULL) {
-// break;
-// }
-// }
-// if (sercom == NULL) {
-// mp_raise_ValueError(translate("Invalid pins"));
-// }
-// if (!have_tx) {
-// tx_pad = 0;
-// if (rx_pad == 0) {
-// tx_pad = 2;
-// }
-// }
-// if (!have_rx) {
-// rx_pad = (tx_pad + 1) % 4;
-// }
-
-// // Set up clocks on SERCOM.
-// samd_peripherals_sercom_clock_init(sercom, sercom_index);
-
-// if (rx && receiver_buffer_size > 0) {
-// self->buffer_length = receiver_buffer_size;
-// // Initially allocate the UART's buffer in the long-lived part of the
-// // heap. UARTs are generally long-lived objects, but the "make long-
-// // lived" machinery is incapable of moving internal pointers like
-// // self->buffer, so do it manually. (However, as long as internal
-// // pointers like this are NOT moved, allocating the buffer
-// // in the long-lived pool is not strictly necessary)
-// self->buffer = (uint8_t *) gc_alloc(self->buffer_length * sizeof(uint8_t), false, true);
-// if (self->buffer == NULL) {
-// common_hal_busio_uart_deinit(self);
-// mp_raise_msg_varg(&mp_type_MemoryError, translate("Failed to allocate RX buffer of %d bytes"), self->buffer_length * sizeof(uint8_t));
-// }
-// } else {
-// self->buffer_length = 0;
-// self->buffer = NULL;
-// }
-
-// if (usart_async_init(usart_desc_p, sercom, self->buffer, self->buffer_length, NULL) != ERR_NONE) {
-// mp_raise_ValueError(translate("Could not initialize UART"));
-// }
-
-// // usart_async_init() sets a number of defaults based on a prototypical SERCOM
-// // which don't necessarily match what we need. After calling it, set the values
-// // specific to this instantiation of UART.
-
-// // Set pads computed for this SERCOM.
-// // TXPO:
-// // 0x0: TX pad 0; no RTS/CTS
-// // 0x1: TX pad 2; no RTS/CTS
-// // 0x2: TX pad 0; RTS: pad 2, CTS: pad 3 (not used by us right now)
-// // So divide by 2 to map pad to value.
-// // RXPO:
-// // 0x0: RX pad 0
-// // 0x1: RX pad 1
-// // 0x2: RX pad 2
-// // 0x3: RX pad 3
-
-// // Doing a group mask and set of the registers saves 60 bytes over setting the bitfields individually.
-
-// sercom->USART.CTRLA.reg &= ~(SERCOM_USART_CTRLA_TXPO_Msk |
-// SERCOM_USART_CTRLA_RXPO_Msk |
-// SERCOM_USART_CTRLA_FORM_Msk);
-// sercom->USART.CTRLA.reg |= SERCOM_USART_CTRLA_TXPO(tx_pad / 2) |
-// SERCOM_USART_CTRLA_RXPO(rx_pad) |
-// (parity == BUSIO_UART_PARITY_NONE ? 0 : SERCOM_USART_CTRLA_FORM(1));
-
-// // Enable tx and/or rx based on whether the pins were specified.
-// // CHSIZE is 0 for 8 bits, 5, 6, 7 for 5, 6, 7 bits. 1 for 9 bits, but we don't support that.
-// sercom->USART.CTRLB.reg &= ~(SERCOM_USART_CTRLB_TXEN |
-// SERCOM_USART_CTRLB_RXEN |
-// SERCOM_USART_CTRLB_PMODE |
-// SERCOM_USART_CTRLB_SBMODE |
-// SERCOM_USART_CTRLB_CHSIZE_Msk);
-// sercom->USART.CTRLB.reg |= (have_tx ? SERCOM_USART_CTRLB_TXEN : 0) |
-// (have_rx ? SERCOM_USART_CTRLB_RXEN : 0) |
-// (parity == BUSIO_UART_PARITY_ODD ? SERCOM_USART_CTRLB_PMODE : 0) |
-// (stop > 1 ? SERCOM_USART_CTRLB_SBMODE : 0) |
-// SERCOM_USART_CTRLB_CHSIZE(bits % 8);
-
-// // Set baud rate
-// common_hal_busio_uart_set_baudrate(self, baudrate);
-
-// // Turn on rx interrupt handling. The UART async driver has its own set of internal callbacks,
-// // which are set up by uart_async_init(). These in turn can call user-specified callbacks.
-// // In fact, the actual interrupts are not enabled unless we set up a user-specified callback.
-// // This is confusing. It's explained in the Atmel START User Guide -> Implementation Description ->
-// // Different read function behavior in some asynchronous drivers. As of this writing:
-// // http://start.atmel.com/static/help/index.html?GUID-79201A5A-226F-4FBB-B0B8-AB0BE0554836
-// // Look at the ASFv4 code example for async USART.
-// usart_async_register_callback(usart_desc_p, USART_ASYNC_RXC_CB, usart_async_rxc_callback);
-
-
-// if (have_tx) {
-// gpio_set_pin_direction(tx->number, GPIO_DIRECTION_OUT);
-// gpio_set_pin_pull_mode(tx->number, GPIO_PULL_OFF);
-// gpio_set_pin_function(tx->number, tx_pinmux);
-// self->tx_pin = tx->number;
-// claim_pin(tx);
-// } else {
-// self->tx_pin = NO_PIN;
-// }
-
-// if (have_rx) {
-// gpio_set_pin_direction(rx->number, GPIO_DIRECTION_IN);
-// gpio_set_pin_pull_mode(rx->number, GPIO_PULL_OFF);
-// gpio_set_pin_function(rx->number, rx_pinmux);
-// self->rx_pin = rx->number;
-// claim_pin(rx);
-// } else {
-// self->rx_pin = NO_PIN;
-// }
-
-// usart_async_enable(usart_desc_p);
+ const mcu_pin_obj_t * tx, const mcu_pin_obj_t * rx,
+ const mcu_pin_obj_t * rts, const mcu_pin_obj_t * cts,
+ const mcu_pin_obj_t * rs485_dir, bool rs485_invert,
+ uint32_t baudrate, uint8_t bits, busio_uart_parity_t parity, uint8_t stop,
+ mp_float_t timeout, uint16_t receiver_buffer_size, byte* receiver_buffer,
+ bool sigint_enabled) {
+
+ if (bits > 8) {
+ mp_raise_ValueError(translate("Invalid word/bit length"));
+ }
+
+ if (receiver_buffer_size == 0) {
+ mp_raise_ValueError(translate("Invalid buffer size"));
+ }
+
+ if ((rs485_dir != NULL) || (rs485_invert)) {
+ mp_raise_NotImplementedError(translate("RS485 Not yet supported on this device"));
+ }
+
+ uint8_t uart_id = ((((tx != NULL) ? tx->number : rx->number) + 4) / 8) % NUM_UARTS;
+
+ if (uart_status[uart_id] != STATUS_FREE) {
+ mp_raise_RuntimeError(translate("All UART peripherals are in use"));
+ } else {
+ uart_status[uart_id] = STATUS_BUSY;
+ }
+
+ self->tx_pin = pin_init(uart_id, tx, 0);
+ self->rx_pin = pin_init(uart_id, rx, 1);
+ self->cts_pin = pin_init(uart_id, cts, 2);
+ self->rts_pin = pin_init(uart_id, rts, 3);
+
+ self->uart = UART_INST(uart_id);
+ self->uart_id = uart_id;
+ self->baudrate = baudrate;
+ self->timeout_ms = timeout * 1000;
+
+ uart_init(self->uart, self->baudrate);
+ uart_set_fifo_enabled(self->uart, true);
+ uart_set_format(self->uart, bits, stop, parity);
+ uart_set_hw_flow(self->uart, (cts != NULL), (rts != NULL));
+
+ if (rx != NULL) {
+ // Initially allocate the UART's buffer in the long-lived part of the
+ // heap. UARTs are generally long-lived objects, but the "make long-
+ // lived" machinery is incapable of moving internal pointers like
+ // self->buffer, so do it manually. (However, as long as internal
+ // pointers like this are NOT moved, allocating the buffer
+ // in the long-lived pool is not strictly necessary)
+ // (This is a macro.)
+ if (!ringbuf_alloc(&self->ringbuf, receiver_buffer_size, true)) {
+ mp_raise_msg(&mp_type_MemoryError, translate("Failed to allocate RX buffer"));
+ }
+ active_uarts[uart_id] = self;
+ if (uart_id == 1) {
+ self->uart_irq_id = UART1_IRQ;
+ irq_set_exclusive_handler(self->uart_irq_id, uart1_callback);
+ } else {
+ self->uart_irq_id = UART0_IRQ;
+ irq_set_exclusive_handler(self->uart_irq_id, uart0_callback);
+ }
+ irq_set_enabled(self->uart_irq_id, true);
+ uart_set_irq_enables(self->uart, true /* rx has data */, false /* tx needs data */);
+ }
}
bool common_hal_busio_uart_deinited(busio_uart_obj_t *self) {
- return self->rx_pin == NO_PIN && self->tx_pin == NO_PIN;
+ return self->tx_pin == NO_PIN && self->rx_pin == NO_PIN;
}
void common_hal_busio_uart_deinit(busio_uart_obj_t *self) {
if (common_hal_busio_uart_deinited(self)) {
return;
}
- // This assignment is only here because the usart_async routines take a *const argument.
- // struct usart_async_descriptor * const usart_desc_p = (struct usart_async_descriptor * const) &self->usart_desc;
- // usart_async_disable(usart_desc_p);
- // usart_async_deinit(usart_desc_p);
- reset_pin_number(self->rx_pin);
+ uart_deinit(self->uart);
+ ringbuf_free(&self->ringbuf);
+ active_uarts[self->uart_id] = NULL;
+ uart_status[self->uart_id] = STATUS_FREE;
reset_pin_number(self->tx_pin);
- self->rx_pin = NO_PIN;
+ reset_pin_number(self->rx_pin);
+ reset_pin_number(self->cts_pin);
+ reset_pin_number(self->rts_pin);
self->tx_pin = NO_PIN;
+ self->rx_pin = NO_PIN;
+ self->cts_pin = NO_PIN;
+ self->rts_pin = NO_PIN;
+}
+
+// Write characters.
+size_t common_hal_busio_uart_write(busio_uart_obj_t *self, const uint8_t *data, size_t len, int *errcode) {
+ if (self->tx_pin == NO_PIN) {
+ mp_raise_ValueError(translate("No TX pin"));
+ }
+
+ while (len > 0) {
+ while (uart_is_writable(self->uart) && len > 0) {
+ // Write and advance.
+ uart_get_hw(self->uart)->dr = *data++;
+ // Decrease how many chars left to write.
+ len--;
+ }
+ RUN_BACKGROUND_TASKS;
+ }
+
+ return len;
}
// Read characters.
@@ -264,87 +210,56 @@ size_t common_hal_busio_uart_read(busio_uart_obj_t *self, uint8_t *data, size_t
mp_raise_ValueError(translate("No RX pin"));
}
- // This assignment is only here because the usart_async routines take a *const argument.
- // struct usart_async_descriptor * const usart_desc_p = (struct usart_async_descriptor * const) &self->usart_desc;
-
if (len == 0) {
// Nothing to read.
return 0;
}
- // struct io_descriptor *io;
- // usart_async_get_io_descriptor(usart_desc_p, &io);
-
- size_t total_read = 0;
- // uint64_t start_ticks = supervisor_ticks_ms64();
-
- // // Busy-wait until timeout or until we've read enough chars.
- // while (supervisor_ticks_ms64() - start_ticks <= self->timeout_ms) {
- // // Read as many chars as we can right now, up to len.
- // size_t num_read = io_read(io, data, len);
-
- // // Advance pointer in data buffer, and decrease how many chars left to read.
- // data += num_read;
- // len -= num_read;
- // total_read += num_read;
- // if (len == 0) {
- // // Don't need to read any more: data buf is full.
- // break;
- // }
- // if (num_read > 0) {
- // // Reset the timeout on every character read.
- // start_ticks = supervisor_ticks_ms64();
- // }
- // RUN_BACKGROUND_TASKS;
- // // Allow user to break out of a timeout with a KeyboardInterrupt.
- // if (mp_hal_is_interrupted()) {
- // break;
- // }
- // // If we are zero timeout, make sure we don't loop again (in the event
- // // we read in under 1ms)
- // if (self->timeout_ms == 0) {
- // break;
- // }
- // }
-
- // if (total_read == 0) {
- // *errcode = EAGAIN;
- // return MP_STREAM_ERROR;
- // }
+ // Prevent conflict with uart irq.
+ irq_set_enabled(self->uart_irq_id, false);
+
+ // Copy as much received data as available, up to len bytes.
+ size_t total_read = ringbuf_get_n(&self->ringbuf, data, len);
+
+ // Check if we still need to read more data.
+ if (len > total_read) {
+ len-=total_read;
+ uint64_t start_ticks = supervisor_ticks_ms64();
+ // Busy-wait until timeout or until we've read enough chars.
+ while (len > 0 && (supervisor_ticks_ms64() - start_ticks < self->timeout_ms)) {
+ if (uart_is_readable(self->uart)) {
+ // Read and advance.
+ data[total_read] = uart_get_hw(self->uart)->dr;
+
+ // Adjust the counters.
+ len--;
+ total_read++;
+
+ // Reset the timeout on every character read.
+ start_ticks = supervisor_ticks_ms64();
+ }
+ RUN_BACKGROUND_TASKS;
+ // Allow user to break out of a timeout with a KeyboardInterrupt.
+ if (mp_hal_is_interrupted()) {
+ break;
+ }
+ }
+ }
- return total_read;
-}
+ // Now that we've emptied the ringbuf some, fill it up with anything in the
+ // FIFO. This ensures that we'll empty the FIFO as much as possible and
+ // reset the interrupt when we catch up.
+ _copy_into_ringbuf(&self->ringbuf, self->uart);
-// Write characters.
-size_t common_hal_busio_uart_write(busio_uart_obj_t *self, const uint8_t *data, size_t len, int *errcode) {
- if (self->tx_pin == NO_PIN) {
- mp_raise_ValueError(translate("No TX pin"));
- }
+ // Re-enable irq.
+ irq_set_enabled(self->uart_irq_id, true);
- // This assignment is only here because the usart_async routines take a *const argument.
- // struct usart_async_descriptor * const usart_desc_p = (struct usart_async_descriptor * const) &self->usart_desc;
-
- // struct io_descriptor *io;
- // usart_async_get_io_descriptor(usart_desc_p, &io);
-
- // // Start writing characters. This is non-blocking and will
- // // return immediately after setting up the write.
- // if (io_write(io, data, len) < 0) {
- // *errcode = MP_EAGAIN;
- // return MP_STREAM_ERROR;
- // }
-
- // // Busy-wait until all characters transmitted.
- // struct usart_async_status async_status;
- // while (true) {
- // usart_async_get_status(usart_desc_p, &async_status);
- // if (async_status.txcnt >= len) {
- // break;
- // }
- // RUN_BACKGROUND_TASKS;
- // }
+ if (total_read == 0) {
+ *errcode = EAGAIN;
+ return MP_STREAM_ERROR;
+ }
- return len;
+ return total_read;
}
uint32_t common_hal_busio_uart_get_baudrate(busio_uart_obj_t *self) {
@@ -352,18 +267,8 @@ uint32_t common_hal_busio_uart_get_baudrate(busio_uart_obj_t *self) {
}
void common_hal_busio_uart_set_baudrate(busio_uart_obj_t *self, uint32_t baudrate) {
- // This assignment is only here because the usart_async routines take a *const argument.
- // struct usart_async_descriptor * const usart_desc_p = (struct usart_async_descriptor * const) &self->usart_desc;
- // usart_async_set_baud_rate(usart_desc_p,
- // // Samples and ARITHMETIC vs FRACTIONAL must correspond to USART_SAMPR in
- // // hpl_sercom_config.h.
- // _usart_async_calculate_baud_rate(baudrate, // e.g. 9600 baud
- // PROTOTYPE_SERCOM_USART_ASYNC_CLOCK_FREQUENCY,
- // 16, // samples
- // USART_BAUDRATE_ASYNCH_ARITHMETIC,
- // 0 // fraction - not used for ARITHMETIC
- // ));
self->baudrate = baudrate;
+ uart_set_baudrate(self->uart, baudrate);
}
mp_float_t common_hal_busio_uart_get_timeout(busio_uart_obj_t *self) {
@@ -375,30 +280,30 @@ void common_hal_busio_uart_set_timeout(busio_uart_obj_t *self, mp_float_t timeou
}
uint32_t common_hal_busio_uart_rx_characters_available(busio_uart_obj_t *self) {
- // This assignment is only here because the usart_async routines take a *const argument.
- // struct usart_async_descriptor * const usart_desc_p = (struct usart_async_descriptor * const) &self->usart_desc;
- // struct usart_async_status async_status;
- // usart_async_get_status(usart_desc_p, &async_status);
- // return async_status.rxcnt;
- return 0;
+ // Prevent conflict with uart irq.
+ irq_set_enabled(self->uart_irq_id, false);
+ // The UART only interrupts after a threshold so make sure to copy anything
+ // out of its FIFO before measuring how many bytes we've received.
+ _copy_into_ringbuf(&self->ringbuf, self->uart);
+ irq_set_enabled(self->uart_irq_id, false);
+ return ringbuf_num_filled(&self->ringbuf);
}
void common_hal_busio_uart_clear_rx_buffer(busio_uart_obj_t *self) {
- // This assignment is only here because the usart_async routines take a *const argument.
- // struct usart_async_descriptor * const usart_desc_p = (struct usart_async_descriptor * const) &self->usart_desc;
- // usart_async_flush_rx_buffer(usart_desc_p);
+ // Prevent conflict with uart irq.
+ irq_set_enabled(self->uart_irq_id, false);
+ ringbuf_clear(&self->ringbuf);
+ // Throw away the FIFO contents too.
+ while (uart_is_readable(self->uart)) {
+ (void) uart_get_hw(self->uart)->dr;
+ }
+ irq_set_enabled(self->uart_irq_id, true);
}
-// True if there are no characters still to be written.
bool common_hal_busio_uart_ready_to_tx(busio_uart_obj_t *self) {
if (self->tx_pin == NO_PIN) {
return false;
}
- return false;
- // // This assignment is only here because the usart_async routines take a *const argument.
- // struct usart_async_descriptor * const usart_desc_p = (struct usart_async_descriptor * const) &self->usart_desc;
- // struct usart_async_status async_status;
- // usart_async_get_status(usart_desc_p, &async_status);
- // return !(async_status.flags & USART_ASYNC_STATUS_BUSY);
+ return uart_is_writable(self->uart);
}
diff --git a/ports/raspberrypi/common-hal/busio/UART.h b/ports/raspberrypi/common-hal/busio/UART.h
index 43ed9bee0..4c07de240 100644
--- a/ports/raspberrypi/common-hal/busio/UART.h
+++ b/ports/raspberrypi/common-hal/busio/UART.h
@@ -3,7 +3,7 @@
*
* The MIT License (MIT)
*
- * Copyright (c) 2021 Scott Shawcroft for Adafruit Industries
+ * Copyright (c) 2021 microDev
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -27,21 +27,26 @@
#ifndef MICROPY_INCLUDED_RASPBERRYPI_COMMON_HAL_BUSIO_UART_H
#define MICROPY_INCLUDED_RASPBERRYPI_COMMON_HAL_BUSIO_UART_H
-#include "common-hal/microcontroller/Pin.h"
-
#include "py/obj.h"
+#include "py/ringbuf.h"
+
+#include "src/rp2_common/hardware_uart/include/hardware/uart.h"
typedef struct {
mp_obj_base_t base;
- // struct usart_async_descriptor usart_desc;
- uint8_t rx_pin;
uint8_t tx_pin;
- uint8_t character_bits;
- bool rx_error;
+ uint8_t rx_pin;
+ uint8_t cts_pin;
+ uint8_t rts_pin;
+ uint8_t uart_id;
+ uint8_t uart_irq_id;
uint32_t baudrate;
uint32_t timeout_ms;
- uint32_t buffer_length;
- uint8_t* buffer;
+ uart_inst_t * uart;
+ ringbuf_t ringbuf;
} busio_uart_obj_t;
+extern void reset_uart(void);
+extern void never_reset_uart(uint8_t num);
+
#endif // MICROPY_INCLUDED_RASPBERRYPI_COMMON_HAL_BUSIO_UART_H
diff --git a/ports/raspberrypi/common-hal/digitalio/DigitalInOut.c b/ports/raspberrypi/common-hal/digitalio/DigitalInOut.c
index b0bc1b96e..a1a23b9ab 100644
--- a/ports/raspberrypi/common-hal/digitalio/DigitalInOut.c
+++ b/ports/raspberrypi/common-hal/digitalio/DigitalInOut.c
@@ -43,6 +43,7 @@ digitalinout_result_t common_hal_digitalio_digitalinout_construct(
self->output = false;
self->open_drain = false;
+ // Set to input. No output value.
gpio_init(pin->number);
return DIGITALINOUT_OK;
}
@@ -75,11 +76,17 @@ digitalinout_result_t common_hal_digitalio_digitalinout_switch_to_output(
digitalio_digitalinout_obj_t* self, bool value,
digitalio_drive_mode_t drive_mode) {
const uint8_t pin = self->pin->number;
- gpio_set_dir(pin, GPIO_OUT);
- // TODO: Turn on "strong" pin driving (more current available).
+ gpio_disable_pulls(pin);
+
+ // Turn on "strong" pin driving (more current available).
+ hw_write_masked(&padsbank0_hw->io[pin],
+ PADS_BANK0_GPIO0_DRIVE_VALUE_12MA << PADS_BANK0_GPIO0_DRIVE_LSB,
+ PADS_BANK0_GPIO0_DRIVE_BITS);
self->output = true;
- common_hal_digitalio_digitalinout_set_drive_mode(self, drive_mode);
+ self->open_drain = drive_mode == DRIVE_MODE_OPEN_DRAIN;
+
+ // Pin direction is ultimately set in set_value. We don't need to do it here.
common_hal_digitalio_digitalinout_set_value(self, value);
return DIGITALINOUT_OK;
}
@@ -92,10 +99,18 @@ digitalio_direction_t common_hal_digitalio_digitalinout_get_direction(
void common_hal_digitalio_digitalinout_set_value(
digitalio_digitalinout_obj_t* self, bool value) {
const uint8_t pin = self->pin->number;
- if (self->open_drain) {
- gpio_set_dir(pin, value ? GPIO_IN : GPIO_OUT);
+ if (self->open_drain && value) {
+ // If true and open-drain, set the direction -before- setting
+ // the pin value, to to avoid a high glitch on the pin before
+ // switching from output to input for open-drain.
+ gpio_set_dir(pin, GPIO_IN);
+ gpio_put(pin, value);
} else {
+ // Otherwise set the direction -after- setting the pin value,
+ // to avoid a glitch which might occur if the old value was
+ // different and the pin was previously set to input.
gpio_put(pin, value);
+ gpio_set_dir(pin, GPIO_OUT);
}
}
@@ -110,9 +125,6 @@ digitalinout_result_t common_hal_digitalio_digitalinout_set_drive_mode(
const uint8_t pin = self->pin->number;
bool value = common_hal_digitalio_digitalinout_get_value(self);
self->open_drain = drive_mode == DRIVE_MODE_OPEN_DRAIN;
- if (self->open_drain) {
- gpio_put(pin, false);
- }
// True is implemented differently between modes so reset the value to make
// sure it's correct for the new mode.
if (value) {
diff --git a/ports/raspberrypi/common-hal/neopixel_write/__init__.c b/ports/raspberrypi/common-hal/neopixel_write/__init__.c
index 561d438e2..1f0b71ca0 100644
--- a/ports/raspberrypi/common-hal/neopixel_write/__init__.c
+++ b/ports/raspberrypi/common-hal/neopixel_write/__init__.c
@@ -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);
diff --git a/ports/raspberrypi/common-hal/nvm/ByteArray.c b/ports/raspberrypi/common-hal/nvm/ByteArray.c
new file mode 100644
index 000000000..fdc2c6080
--- /dev/null
+++ b/ports/raspberrypi/common-hal/nvm/ByteArray.c
@@ -0,0 +1,102 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 microDev
+ *
+ * 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 "common-hal/nvm/ByteArray.h"
+
+#include <string.h>
+
+#include "py/runtime.h"
+#include "src/rp2_common/hardware_flash/include/hardware/flash.h"
+
+extern uint32_t __flash_binary_start;
+static const uint32_t flash_binary_start = (uint32_t) &__flash_binary_start;
+
+#define RMV_OFFSET(addr) addr - flash_binary_start
+
+uint32_t common_hal_nvm_bytearray_get_length(nvm_bytearray_obj_t* self) {
+ return self->len;
+}
+
+static void write_page(uint32_t page_addr, uint32_t offset, uint32_t len, uint8_t* bytes) {
+ // Write a whole page to flash, buffering it first and then erasing and rewriting it
+ // since we can only write a whole page at a time.
+ if (offset == 0 && len == FLASH_PAGE_SIZE) {
+ flash_range_program(RMV_OFFSET(page_addr), bytes, FLASH_PAGE_SIZE);
+ } else {
+ uint8_t buffer[FLASH_PAGE_SIZE];
+ memcpy(buffer, (uint8_t*) page_addr, FLASH_PAGE_SIZE);
+ memcpy(buffer + offset, bytes, len);
+ flash_range_program(RMV_OFFSET(page_addr), buffer, FLASH_PAGE_SIZE);
+ }
+}
+
+static void erase_and_write_sector(uint32_t address, uint32_t len, uint8_t* bytes) {
+ // Write a whole sector to flash, buffering it first and then erasing and rewriting it
+ // since we can only erase a whole sector at a time.
+ uint8_t buffer[FLASH_SECTOR_SIZE];
+ memcpy(buffer, (uint8_t*) CIRCUITPY_INTERNAL_NVM_START_ADDR, FLASH_SECTOR_SIZE);
+ memcpy(buffer + address, bytes, len);
+ flash_range_erase(RMV_OFFSET(CIRCUITPY_INTERNAL_NVM_START_ADDR), FLASH_SECTOR_SIZE);
+ flash_range_program(RMV_OFFSET(CIRCUITPY_INTERNAL_NVM_START_ADDR), buffer, FLASH_SECTOR_SIZE);
+}
+
+void common_hal_nvm_bytearray_get_bytes(nvm_bytearray_obj_t* self,
+ uint32_t start_index, uint32_t len, uint8_t* values) {
+ memcpy(values, self->start_address + start_index, len);
+}
+
+bool common_hal_nvm_bytearray_set_bytes(nvm_bytearray_obj_t* self,
+ uint32_t start_index, uint8_t* values, uint32_t len) {
+ uint8_t values_in[len];
+ common_hal_nvm_bytearray_get_bytes(self, start_index, len, values_in);
+
+ bool all_ones = true;
+ for (uint32_t i = 0; i < len; i++) {
+ if (values_in[i] != UINT8_MAX) {
+ all_ones = false;
+ break;
+ }
+ }
+
+ if (all_ones) {
+ uint32_t address = (uint32_t) self->start_address + start_index;
+ uint32_t offset = address % FLASH_PAGE_SIZE;
+ uint32_t page_addr = address - offset;
+
+ while (len) {
+ uint32_t write_len = MIN(len, FLASH_PAGE_SIZE - offset);
+ write_page(page_addr, offset, write_len, values);
+ len -= write_len;
+ values += write_len;
+ page_addr += FLASH_PAGE_SIZE;
+ offset = 0;
+ }
+ } else {
+ erase_and_write_sector(start_index, len, values);
+ }
+
+ return true;
+}
diff --git a/ports/raspberrypi/common-hal/nvm/ByteArray.h b/ports/raspberrypi/common-hal/nvm/ByteArray.h
new file mode 100644
index 000000000..a2a904fea
--- /dev/null
+++ b/ports/raspberrypi/common-hal/nvm/ByteArray.h
@@ -0,0 +1,38 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 microDev
+ *
+ * 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_NVM_BYTEARRAY_H
+#define MICROPY_INCLUDED_RASPBERRYPI_COMMON_HAL_NVM_BYTEARRAY_H
+
+#include "py/obj.h"
+
+typedef struct {
+ mp_obj_base_t base;
+ uint8_t * start_address;
+ uint32_t len;
+} nvm_bytearray_obj_t;
+
+#endif // MICROPY_INCLUDED_RASPBERRYPI_COMMON_HAL_NVM_BYTEARRAY_H
diff --git a/ports/raspberrypi/common-hal/nvm/__init__.c b/ports/raspberrypi/common-hal/nvm/__init__.c
new file mode 100644
index 000000000..1b702a158
--- /dev/null
+++ b/ports/raspberrypi/common-hal/nvm/__init__.c
@@ -0,0 +1 @@
+// No nvm module functions.
diff --git a/ports/raspberrypi/common-hal/pwmio/PWMOut.c b/ports/raspberrypi/common-hal/pwmio/PWMOut.c
index 348d99ebe..4343b9e5c 100644
--- a/ports/raspberrypi/common-hal/pwmio/PWMOut.c
+++ b/ports/raspberrypi/common-hal/pwmio/PWMOut.c
@@ -82,16 +82,23 @@ void pwmio_release_slice_channels(uint8_t slice) {
channel_use &= ~channel_mask;
channel_mask = _mask(slice, 1);
channel_use &= ~channel_mask;
+
+void pwmout_never_reset(uint8_t slice, uint8_t channel) {
+ never_reset_channel |= _mask(slice, channel);
+}
+
+void pwmout_reset_ok(uint8_t slice, uint8_t channel) {
+ never_reset_channel &= ~_mask(slice, channel);
}
void common_hal_pwmio_pwmout_never_reset(pwmio_pwmout_obj_t *self) {
- never_reset_channel |= _mask(self->slice, self->channel);
+ pwmout_never_reset(self->slice, self->channel);
never_reset_pin_number(self->pin->number);
}
void common_hal_pwmio_pwmout_reset_ok(pwmio_pwmout_obj_t *self) {
- never_reset_channel &= ~_mask(self->slice, self->channel);
+ pwmout_reset_ok(self->slice, self->channel);
}
void pwmout_reset(void) {
@@ -115,21 +122,7 @@ void pwmout_reset(void) {
}
}
-pwmout_result_t common_hal_pwmio_pwmout_construct(pwmio_pwmout_obj_t* self,
- const mcu_pin_obj_t* pin,
- uint16_t duty,
- uint32_t frequency,
- bool variable_frequency) {
- self->pin = pin;
- self->variable_frequency = variable_frequency;
- self->duty_cycle = duty;
-
- if (frequency == 0 || frequency > (common_hal_mcu_processor_get_frequency() / 2)) {
- return PWMOUT_INVALID_FREQUENCY;
- }
-
- uint8_t slice = pwm_gpio_to_slice_num(pin->number);
- uint8_t channel = pwm_gpio_to_channel(pin->number);
+pwmout_result_t pwmout_allocate(uint8_t slice, uint8_t channel, bool variable_frequency, uint32_t frequency) {
uint32_t channel_use_mask = _mask(slice, channel);
// Check the channel first.
@@ -151,14 +144,39 @@ pwmout_result_t common_hal_pwmio_pwmout_construct(pwmio_pwmout_obj_t* self,
return PWMOUT_ALL_TIMERS_ON_PIN_IN_USE;
}
}
- self->slice = slice;
- self->channel = channel;
channel_use |= channel_use_mask;
if (variable_frequency) {
slice_variable_frequency |= 1 << slice;
}
+ return PWMOUT_OK;
+}
+
+pwmout_result_t common_hal_pwmio_pwmout_construct(pwmio_pwmout_obj_t* self,
+ const mcu_pin_obj_t* pin,
+ uint16_t duty,
+ uint32_t frequency,
+ bool variable_frequency) {
+ self->pin = pin;
+ self->variable_frequency = variable_frequency;
+ self->duty_cycle = duty;
+
+ if (frequency == 0 || frequency > (common_hal_mcu_processor_get_frequency() / 2)) {
+ return PWMOUT_INVALID_FREQUENCY;
+ }
+
+ uint8_t slice = pwm_gpio_to_slice_num(pin->number);
+ uint8_t channel = pwm_gpio_to_channel(pin->number);
+
+ int r = pwmout_allocate(slice, channel, variable_frequency, frequency);
+ if (r != PWMOUT_OK) {
+ return r;
+ }
+
+ self->slice = slice;
+ self->channel = channel;
+
if (target_slice_frequencies[slice] != frequency) {
// Reset the counter and compare values.
pwm_hw->slice[slice].ctr = PWM_CH0_CTR_RESET;
@@ -166,6 +184,7 @@ pwmout_result_t common_hal_pwmio_pwmout_construct(pwmio_pwmout_obj_t* self,
common_hal_pwmio_pwmout_set_frequency(self, frequency);
pwm_set_enabled(slice, true);
} else {
+ common_hal_pwmio_pwmout_set_frequency(self, frequency);
common_hal_pwmio_pwmout_set_duty_cycle(self, duty);
}
@@ -179,20 +198,23 @@ bool common_hal_pwmio_pwmout_deinited(pwmio_pwmout_obj_t* self) {
return self->pin == NULL;
}
-void common_hal_pwmio_pwmout_deinit(pwmio_pwmout_obj_t* self) {
- if (common_hal_pwmio_pwmout_deinited(self)) {
- return;
- }
- uint32_t channel_mask = _mask(self->slice, self->channel);
+void pwmout_free(uint8_t slice, uint8_t channel) {
+ uint32_t channel_mask = _mask(slice, channel);
channel_use &= ~channel_mask;
never_reset_channel &= ~channel_mask;
- uint32_t slice_mask = ((1 << CHANNELS_PER_SLICE) - 1) << (self->slice * CHANNELS_PER_SLICE + self->channel);
+ uint32_t slice_mask = ((1 << CHANNELS_PER_SLICE) - 1) << (slice * CHANNELS_PER_SLICE);
if ((channel_use & slice_mask) == 0) {
- target_slice_frequencies[self->slice] = 0;
- slice_variable_frequency &= ~(1 << self->slice);
- pwm_set_enabled(self->slice, false);
+ target_slice_frequencies[slice] = 0;
+ slice_variable_frequency &= ~(1 << slice);
+ pwm_set_enabled(slice, false);
}
+}
+void common_hal_pwmio_pwmout_deinit(pwmio_pwmout_obj_t* self) {
+ if (common_hal_pwmio_pwmout_deinited(self)) {
+ return;
+ }
+ pwmout_free(self->slice, self->channel);
reset_pin_number(self->pin->number);
self->pin = NULL;
}
diff --git a/ports/raspberrypi/common-hal/pwmio/PWMOut.h b/ports/raspberrypi/common-hal/pwmio/PWMOut.h
index 6c0dda2db..41da81010 100644
--- a/ports/raspberrypi/common-hal/pwmio/PWMOut.h
+++ b/ports/raspberrypi/common-hal/pwmio/PWMOut.h
@@ -45,6 +45,11 @@ typedef struct {
void pwmout_reset(void);
// Private API for AudioPWMOut.
void pwmio_pwmout_set_top(pwmio_pwmout_obj_t* self, uint16_t top);
+// Private APIs for RGBMatrix
+enum pwmout_result_t pwmout_allocate(uint8_t slice, uint8_t channel, bool variable_frequency, uint32_t frequency);
+void pwmout_free(uint8_t slice, uint8_t channel);
+void pwmout_never_reset(uint8_t slice, uint8_t channel);
+void pwmout_reset_ok(uint8_t slice, uint8_t channel);
// Private API for countio to claim both channels on a slice
bool pwmio_claim_slice_channels(uint8_t slice);
diff --git a/ports/raspberrypi/common-hal/rgbmatrix/RGBMatrix.c b/ports/raspberrypi/common-hal/rgbmatrix/RGBMatrix.c
new file mode 100644
index 000000000..657c7f0a6
--- /dev/null
+++ b/ports/raspberrypi/common-hal/rgbmatrix/RGBMatrix.c
@@ -0,0 +1,70 @@
+/*
+ * This file is part of the Micro Python project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Jeff Epler 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 <stddef.h>
+
+#include "py/mphal.h"
+
+#include "common-hal/rgbmatrix/RGBMatrix.h"
+#include "shared-bindings/pwmio/PWMOut.h"
+#include "shared-module/rgbmatrix/RGBMatrix.h"
+
+#include "src/rp2_common/hardware_pwm/include/hardware/pwm.h"
+
+void *common_hal_rgbmatrix_timer_allocate(rgbmatrix_rgbmatrix_obj_t *self) {
+ // Choose a PWM channel based on the first RGB pin
+ uint8_t slice = pwm_gpio_to_slice_num(self->rgb_pins[0]);
+ uint8_t channel = pwm_gpio_to_channel(self->rgb_pins[0]);
+ int result = pwmout_allocate(slice, channel, true, 125000000/3);
+ if (result == PWMOUT_OK) {
+ // return value must be nonzero (but slice and channel can both be
+ // zero), so set bit 16...
+ pwmout_never_reset(slice, channel);
+ return (void*)(intptr_t)(slice | (channel << 8) | 0x10000);
+ }
+ return NULL;
+}
+
+void common_hal_rgbmatrix_timer_enable(void* ptr) {
+ int8_t slice = ((intptr_t)ptr) & 0xff;
+ pwm_set_enabled(slice, false);
+ pwm_clear_irq(slice);
+ pwm_set_enabled(slice, true);
+}
+
+void common_hal_rgbmatrix_timer_disable(void* ptr) {
+ int8_t slice = ((intptr_t)ptr) & 0xff;
+ pwm_set_enabled(slice, false);
+}
+
+void common_hal_rgbmatrix_timer_free(void* ptr) {
+ intptr_t value = (intptr_t)ptr;
+ uint8_t slice = value & 0xff;
+ uint8_t channel = value >> 8;
+ pwm_set_enabled(slice, false);
+ pwmout_free(slice, channel);
+ return;
+}
diff --git a/ports/raspberrypi/common-hal/rgbmatrix/RGBMatrix.h b/ports/raspberrypi/common-hal/rgbmatrix/RGBMatrix.h
new file mode 100644
index 000000000..2378cf173
--- /dev/null
+++ b/ports/raspberrypi/common-hal/rgbmatrix/RGBMatrix.h
@@ -0,0 +1,37 @@
+/*
+ * This file is part of the Micro Python project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Jeff Epler 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_ATMEL_SAMD_COMMON_HAL_RGBMATRIX_RGBMATRIX_H
+#define MICROPY_INCLUDED_ATMEL_SAMD_COMMON_HAL_RGBMATRIX_RGBMATRIX_H
+
+#include "shared-module/rgbmatrix/RGBMatrix.h"
+
+void *common_hal_rgbmatrix_timer_allocate(rgbmatrix_rgbmatrix_obj_t *self);
+void common_hal_rgbmatrix_timer_enable(void*);
+void common_hal_rgbmatrix_timer_disable(void*);
+void common_hal_rgbmatrix_timer_free(void*);
+
+#endif
diff --git a/ports/raspberrypi/common-hal/rgbmatrix/__init__.c b/ports/raspberrypi/common-hal/rgbmatrix/__init__.c
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/ports/raspberrypi/common-hal/rgbmatrix/__init__.c
diff --git a/ports/raspberrypi/common-hal/rp2pio/StateMachine.c b/ports/raspberrypi/common-hal/rp2pio/StateMachine.c
index 90c48130e..54c5220e7 100644
--- a/ports/raspberrypi/common-hal/rp2pio/StateMachine.c
+++ b/ports/raspberrypi/common-hal/rp2pio/StateMachine.c
@@ -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 direction 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,51 @@ 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;
+ }
+ // 0 is interpreted as 0x10000 so it's valid.
+ if (div256 / 256 > 0x10000 || frequency > clock_get_hz(clk_sys)) {
+ mp_raise_ValueError_varg(translate("%q out of range"), MP_QSTR_frequency);
+ }
+ 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 +523,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 +562,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 +619,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 +633,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 (out_stride_in_bytes == 2) {
+ *((uint16_t*) tx_destination) = *((uint16_t*) data_out);
+ } else if (out_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 +665,58 @@ 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;
+ if (mp_hal_is_interrupted()) {
+ break;
+ }
}
}
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;
}
diff --git a/ports/raspberrypi/common-hal/rp2pio/StateMachine.h b/ports/raspberrypi/common-hal/rp2pio/StateMachine.h
index 6b70b6b5b..f084e0986 100644
--- a/ports/raspberrypi/common-hal/rp2pio/StateMachine.h
+++ b/ports/raspberrypi/common-hal/rp2pio/StateMachine.h
@@ -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);
diff --git a/ports/raspberrypi/link.ld b/ports/raspberrypi/link.ld
index b83299dee..653408d4e 100644
--- a/ports/raspberrypi/link.ld
+++ b/ports/raspberrypi/link.ld
@@ -23,7 +23,7 @@
MEMORY
{
- FLASH_FIRMWARE (rx) : ORIGIN = 0x10000000, LENGTH = 1024k
+ FLASH_FIRMWARE (rx) : ORIGIN = 0x10000000, LENGTH = 1020k
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 256k
SCRATCH_X (rwx) : ORIGIN = 0x20040000, LENGTH = 4k
SCRATCH_Y (rwx) : ORIGIN = 0x20041000, LENGTH = 4k
diff --git a/ports/raspberrypi/mpconfigport.h b/ports/raspberrypi/mpconfigport.h
index ccc8e8b03..dba008a94 100644
--- a/ports/raspberrypi/mpconfigport.h
+++ b/ports/raspberrypi/mpconfigport.h
@@ -29,15 +29,16 @@
#include "src/rp2040/hardware_regs/include/hardware/platform_defs.h"
-#define MICROPY_PY_SYS_PLATFORM "RP2040"
+#define MICROPY_PY_SYS_PLATFORM "RP2040"
-#define CIRCUITPY_INTERNAL_NVM_SIZE 0
+#define CIRCUITPY_INTERNAL_NVM_SIZE (4*1024)
+#define CIRCUITPY_INTERNAL_NVM_START_ADDR (0x100FF000)
-#define CIRCUITPY_DEFAULT_STACK_SIZE (24*1024)
+#define CIRCUITPY_DEFAULT_STACK_SIZE (24*1024)
#define MICROPY_USE_INTERNAL_PRINTF (1)
-#define CIRCUITPY_PROCESSOR_COUNT (2)
+#define CIRCUITPY_PROCESSOR_COUNT (2)
// This also includes mpconfigboard.h.
#include "py/circuitpy_mpconfig.h"
diff --git a/ports/raspberrypi/mpconfigport.mk b/ports/raspberrypi/mpconfigport.mk
index dc0c85b68..4c149cb2b 100644
--- a/ports/raspberrypi/mpconfigport.mk
+++ b/ports/raspberrypi/mpconfigport.mk
@@ -23,21 +23,23 @@ else
CIRCUITPY_NEOPIXEL_WRITE = 0
endif
+CIRCUITPY_FRAMEBUFFERIO = 1
CIRCUITPY_FULL_BUILD = 1
CIRCUITPY_BITOPS = 1
CIRCUITPY_PWMIO = 1
+CIRCUITPY_RGBMATRIX = 1
# Things that need to be implemented.
CIRCUITPY_FREQUENCYIO = 0 # Use PWM interally
CIRCUITPY_I2CPERIPHERAL = 0
-CIRCUITPY_NVM = 0
+CIRCUITPY_NVM = 1
CIRCUITPY_PULSEIO = 0 # Use PIO interally
CIRCUITPY_ROTARYIO = 0 # Use PIO interally
CIRCUITPY_WATCHDOG = 1
# Audio via PWM
CIRCUITPY_AUDIOIO = 0
-CIRCUITPY_AUDIOBUSIO ?= 0 # add this later
+CIRCUITPY_AUDIOBUSIO ?= 1
CIRCUITPY_AUDIOCORE ?= 1
CIRCUITPY_AUDIOPWMIO ?= 1
diff --git a/ports/raspberrypi/supervisor/port.c b/ports/raspberrypi/supervisor/port.c
index 36d2e8275..501a0c5ce 100644
--- a/ports/raspberrypi/supervisor/port.c
+++ b/ports/raspberrypi/supervisor/port.c
@@ -39,6 +39,9 @@
#include "shared-bindings/rtc/__init__.h"
#include "shared-bindings/pwmio/PWMOut.h"
+#include "common-hal/rtc/RTC.h"
+#include "common-hal/busio/UART.h"
+
#include "supervisor/shared/safe_mode.h"
#include "supervisor/shared/stack.h"
#include "supervisor/shared/tick.h"
@@ -78,6 +81,9 @@ safe_mode_t port_init(void) {
// Reset everything into a known state before board_init.
reset_port();
+ // Initialize RTC
+ common_hal_rtc_init();
+
// For the tick.
hardware_alarm_claim(0);
hardware_alarm_set_callback(0, _tick_callback);
@@ -95,6 +101,7 @@ void reset_port(void) {
#if CIRCUITPY_BUSIO
reset_i2c();
reset_spi();
+ reset_uart();
#endif
#if CIRCUITPY_PWMIO
@@ -112,6 +119,9 @@ void reset_port(void) {
#if CIRCUITPY_AUDIOPWMIO
audiopwmout_reset();
#endif
+ #if CIRCUITPY_AUDIOCORE
+ audio_dma_reset();
+ #endif
reset_all_pins();
}