summaryrefslogtreecommitdiff
path: root/ports/raspberrypi/common-hal
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/common-hal
parent4246cc3f6d718e2f28fbeb73fc8c3c39dfb9d7f0 (diff)
parentefc2667b5f087480b1b3233ae00ab1529a442f9f (diff)
Merge branch 'main' into rp2040_countio
Diffstat (limited to 'ports/raspberrypi/common-hal')
-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
19 files changed, 1233 insertions, 408 deletions
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);