Merge pull request #7880 from tannewt/feather_dvi

Add PicoDVI support
This commit is contained in:
Dan Halbert 2023-04-20 14:50:10 -04:00 committed by GitHub
commit 7d02bff6b8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
36 changed files with 1134 additions and 401 deletions

4
.gitmodules vendored
View File

@ -331,3 +331,7 @@
[submodule "ports/silabs/tools/slc_cli_linux"]
path = ports/silabs/tools/slc_cli_linux
url = https://github.com/SiliconLabs/circuitpython_slc_cli_linux
[submodule "ports/raspberrypi/lib/PicoDVI"]
path = ports/raspberrypi/lib/PicoDVI
url = https://github.com/circuitpython/PicoDVI.git
branch = circuitpython

View File

@ -126,6 +126,7 @@ msgstr ""
#: ports/espressif/common-hal/espulp/ULP.c
#: ports/mimxrt10xx/common-hal/audiobusio/__init__.c
#: ports/raspberrypi/common-hal/picodvi/Framebuffer.c
#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c
#: shared-bindings/digitalio/DigitalInOut.c
#: shared-bindings/microcontroller/Pin.c
@ -164,11 +165,11 @@ msgstr ""
msgid "%q length must be >= %d"
msgstr ""
#: py/argcheck.c
#: ports/raspberrypi/common-hal/picodvi/Framebuffer.c py/argcheck.c
msgid "%q must be %d"
msgstr ""
#: py/argcheck.c
#: py/argcheck.c shared-bindings/displayio/Bitmap.c
msgid "%q must be %d-%d"
msgstr ""
@ -468,6 +469,7 @@ msgstr ""
msgid "All event channels in use"
msgstr ""
#: ports/raspberrypi/common-hal/picodvi/Framebuffer.c
#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c
msgid "All state machines in use"
msgstr ""
@ -476,6 +478,7 @@ msgstr ""
msgid "All sync event channels in use"
msgstr ""
#: ports/raspberrypi/common-hal/picodvi/Framebuffer.c
#: shared-bindings/pwmio/PWMOut.c
msgid "All timers for this pin are in use"
msgstr ""
@ -1222,7 +1225,9 @@ msgid "Interrupt error."
msgstr ""
#: ports/mimxrt10xx/common-hal/audiobusio/__init__.c
#: ports/mimxrt10xx/common-hal/pwmio/PWMOut.c py/argcheck.c
#: ports/mimxrt10xx/common-hal/pwmio/PWMOut.c
#: ports/raspberrypi/bindings/picodvi/Framebuffer.c
#: ports/raspberrypi/common-hal/picodvi/Framebuffer.c py/argcheck.c
#: shared-bindings/digitalio/DigitalInOut.c
#: shared-bindings/displayio/EPaperDisplay.c
msgid "Invalid %q"

10
main.c
View File

@ -153,11 +153,15 @@ STATIC void start_mp(supervisor_allocation *heap, supervisor_allocation *pystack
supervisor_workflow_reset();
// Stack limit should be less than real stack size, so we have a chance
// to recover from limit hit. (Limit is measured in bytes.)
// to recover from limit hit. (Limit is measured in bytes.) The top of the
// stack is set to our current state. Not the actual top.
mp_stack_ctrl_init();
if (stack_get_bottom() != NULL) {
mp_stack_set_limit(stack_get_length() - 1024);
uint32_t *stack_bottom = stack_get_bottom();
if (stack_bottom != NULL) {
size_t stack_length = stack_get_length();
mp_stack_set_top(stack_bottom + (stack_length / sizeof(uint32_t)));
mp_stack_set_limit(stack_length - 1024);
}

View File

@ -42,6 +42,7 @@ void common_hal_videocore_framebuffer_deinit(videocore_framebuffer_obj_t *self)
if (vcmailbox_release_framebuffer()) {
self->framebuffer = NULL;
}
self->base.type = &mp_type_NoneType;
}
bool common_hal_videocore_framebuffer_deinited(videocore_framebuffer_obj_t *self) {

View File

@ -33,11 +33,11 @@ INC_CYW43 := \
-isystem lib/cyw43-driver/firmware \
-isystem lib/cyw43-driver/src \
-isystem lib/lwip/src/include \
-isystem sdk/src/rp2_common/pico_async_context/include/ \
-isystem sdk/src/rp2_common/pico_async_context/include/ \
-isystem sdk/src/rp2_common/pico_cyw43_arch/include/ \
-isystem sdk/src/rp2_common/pico_cyw43_driver/include/ \
-isystem sdk/src/rp2_common/pico_lwip/include/ \
-isystem sdk/src/rp2_common/pico_rand/include/ \
-isystem sdk/src/rp2_common/pico_rand/include/ \
CFLAGS_CYW43 := -DCYW43_LWIP=1 -DPICO_CYW43_ARCH_THREADSAFE_BACKGROUND=1 -DCYW43_USE_SPI -DIGNORE_GPIO25 -DIGNORE_GPIO23 -DIGNORE_GPIO24 -DCYW43_LOGIC_DEBUG=0 -DCYW43_NETUTILS=1
SRC_SDK_CYW43 := \
@ -105,6 +105,7 @@ INC += \
-isystem sdk/src/common/pico_util/include/ \
-isystem sdk/src/rp2040/hardware_regs/include/ \
-isystem sdk/src/rp2040/hardware_structs/include/ \
-isystem sdk/src/rp2_common/cmsis/ \
-isystem sdk/src/rp2_common/hardware_adc/include/ \
-isystem sdk/src/rp2_common/hardware_base/include/ \
-isystem sdk/src/rp2_common/hardware_claim/include/ \
@ -113,16 +114,19 @@ INC += \
-isystem sdk/src/rp2_common/hardware_dma/include/ \
-isystem sdk/src/rp2_common/hardware_flash/include/ \
-isystem sdk/src/rp2_common/hardware_gpio/include/ \
-isystem sdk/src/rp2_common/hardware_interp/include/ \
-isystem sdk/src/rp2_common/hardware_irq/include/ \
-isystem sdk/src/rp2_common/hardware_i2c/include/ \
-isystem sdk/src/rp2_common/hardware_pio/include/ \
-isystem sdk/src/rp2_common/hardware_pll/include/ \
-isystem sdk/src/rp2_common/hardware_pwm/include/ \
-isystem sdk/src/rp2_common/hardware_resets/include/ \
-isystem sdk/src/rp2_common/hardware_rtc/include/ \
-isystem sdk/src/rp2_common/hardware_spi/include/ \
-isystem sdk/src/rp2_common/hardware_sync/include/ \
-isystem sdk/src/rp2_common/hardware_timer/include/ \
-isystem sdk/src/rp2_common/hardware_uart/include/ \
-isystem sdk/src/rp2_common/hardware_vreg/include/ \
-isystem sdk/src/rp2_common/hardware_watchdog/include/ \
-isystem sdk/src/rp2_common/hardware_xosc/include/ \
-isystem sdk/src/rp2_common/pico_multicore/include/ \
@ -131,7 +135,7 @@ INC += \
-isystem sdk/src/rp2_common/pico_printf/include/ \
-isystem sdk/src/rp2_common/pico_float/include/ \
-isystem sdk/src/rp2_common/pico_platform/include/ \
-isystem sdk/src/rp2_common/pico_runtime/printf/include/ \
-isystem sdk/src/rp2_common/pico_runtime/include/ \
-isystem sdk/src/rp2_common/pico_bootrom/include/ \
-isystem sdk/src/rp2_common/pico_unique_id/include/ \
$(INC_CYW43) \
@ -141,7 +145,7 @@ INC += \
-I$(BUILD)
# Pico specific configuration
CFLAGS += -DRASPBERRYPI -DPICO_ON_DEVICE=1 -DPICO_NO_BINARY_INFO=0 -DPICO_TIME_DEFAULT_ALARM_POOL_DISABLED=0 -DPICO_DIVIDER_CALL_IDIV0=0 -DPICO_DIVIDER_CALL_LDIV0=0 -DPICO_DIVIDER_HARDWARE=1 -DPICO_DOUBLE_ROM=1 -DPICO_FLOAT_ROM=1 -DPICO_MULTICORE=1 -DPICO_BITS_IN_RAM=0 -DPICO_DIVIDER_IN_RAM=0 -DPICO_DOUBLE_PROPAGATE_NANS=0 -DPICO_DOUBLE_IN_RAM=0 -DPICO_MEM_IN_RAM=0 -DPICO_FLOAT_IN_RAM=0 -DPICO_FLOAT_PROPAGATE_NANS=1 -DPICO_NO_FLASH=0 -DPICO_COPY_TO_RAM=0 -DPICO_DISABLE_SHARED_IRQ_HANDLERS=0 -DPICO_NO_BI_BOOTSEL_VIA_DOUBLE_RESET=0
CFLAGS += -DRASPBERRYPI -DPICO_ON_DEVICE=1 -DPICO_NO_BINARY_INFO=0 -DPICO_TIME_DEFAULT_ALARM_POOL_DISABLED=0 -DPICO_DIVIDER_CALL_IDIV0=0 -DPICO_DIVIDER_CALL_LDIV0=0 -DPICO_DIVIDER_HARDWARE=1 -DPICO_DOUBLE_ROM=1 -DPICO_FLOAT_ROM=1 -DPICO_MULTICORE=1 -DPICO_BITS_IN_RAM=0 -DPICO_DIVIDER_IN_RAM=0 -DPICO_DOUBLE_PROPAGATE_NANS=0 -DPICO_DOUBLE_IN_RAM=0 -DPICO_MEM_IN_RAM=0 -DPICO_FLOAT_IN_RAM=0 -DPICO_FLOAT_PROPAGATE_NANS=1 -DPICO_NO_FLASH=0 -DPICO_COPY_TO_RAM=0 -DPICO_DISABLE_SHARED_IRQ_HANDLERS=0 -DPICO_NO_BI_BOOTSEL_VIA_DOUBLE_RESET=0 -DDVI_1BPP_BIT_REVERSE=0
OPTIMIZATION_FLAGS ?= -O3
# TinyUSB defines
CFLAGS += -DTUD_OPT_RP2040_USB_DEVICE_ENUMERATION_FIX=1 -DCFG_TUSB_MCU=OPT_MCU_RP2040 -DCFG_TUD_MIDI_RX_BUFSIZE=128 -DCFG_TUD_CDC_RX_BUFSIZE=256 -DCFG_TUD_MIDI_TX_BUFSIZE=128 -DCFG_TUD_CDC_TX_BUFSIZE=256 -DCFG_TUD_MSC_BUFSIZE=1024
@ -172,8 +176,8 @@ DISABLE_WARNINGS = -Wno-stringop-overflow -Wno-cast-align
CFLAGS += $(INC) -Wall -Werror -std=gnu11 -nostdlib -fshort-enums $(BASE_CFLAGS) $(CFLAGS_MOD) $(COPT) $(DISABLE_WARNINGS) -Werror=missing-prototypes
CFLAGS += \
-march=armv6-m \
-mthumb \
-march=armv6-m \
-mthumb \
-mabi=aapcs-linux \
-mcpu=cortex-m0plus \
-msoft-float \
@ -195,6 +199,7 @@ SRC_SDK := \
src/common/pico_time/time.c \
src/common/pico_time/timeout_helper.c \
src/common/pico_util/pheap.c \
src/common/pico_util/queue.c \
src/rp2_common/hardware_adc/adc.c \
src/rp2_common/hardware_claim/claim.c \
src/rp2_common/hardware_clocks/clocks.c \
@ -202,6 +207,7 @@ SRC_SDK := \
src/rp2_common/hardware_flash/flash.c \
src/rp2_common/hardware_gpio/gpio.c \
src/rp2_common/hardware_i2c/i2c.c \
src/rp2_common/hardware_interp/interp.c \
src/rp2_common/hardware_irq/irq.c \
src/rp2_common/hardware_pio/pio.c \
src/rp2_common/hardware_pll/pll.c \
@ -210,6 +216,7 @@ SRC_SDK := \
src/rp2_common/hardware_sync/sync.c \
src/rp2_common/hardware_timer/timer.c \
src/rp2_common/hardware_uart/uart.c \
src/rp2_common/hardware_vreg/vreg.c \
src/rp2_common/hardware_watchdog/watchdog.c \
src/rp2_common/hardware_xosc/xosc.c \
src/rp2_common/pico_bootrom/bootrom.c \
@ -223,6 +230,7 @@ SRC_SDK := \
src/rp2_common/pico_printf/printf.c \
src/rp2_common/pico_runtime/runtime.c \
src/rp2_common/pico_stdio/stdio.c \
src/rp2_common/pico_stdlib/stdlib.c \
src/rp2_common/pico_unique_id/unique_id.c \
$(SRC_SDK_CYW43) \
@ -240,12 +248,24 @@ SRC_C += \
background.c \
peripherals/pins.c \
lib/crypto-algorithms/sha256.c \
lib/PicoDVI/software/libdvi/dvi.c \
lib/PicoDVI/software/libdvi/dvi_serialiser.c \
lib/PicoDVI/software/libdvi/dvi_timing.c \
lib/PicoDVI/software/libdvi/tmds_encode.c \
lib/tinyusb/src/portable/raspberrypi/rp2040/dcd_rp2040.c \
lib/tinyusb/src/portable/raspberrypi/rp2040/rp2040_usb.c \
mphalport.c \
$(SRC_CYW43) \
$(SRC_LWIP) \
ifeq ($(CIRCUITPY_PICODVI),1)
SRC_C += \
bindings/picodvi/__init__.c \
bindings/picodvi/Framebuffer.c \
common-hal/picodvi/Framebuffer.c \
endif
ifeq ($(CIRCUITPY_SSL),1)
CFLAGS += -isystem $(TOP)/mbedtls/include
SRC_MBEDTLS := $(addprefix lib/mbedtls/library/, \
@ -359,6 +379,7 @@ SRC_S_UPPER = sdk/src/rp2_common/hardware_divider/divider.S \
sdk/src/rp2_common/pico_int64_ops/pico_int64_ops_aeabi.S \
sdk/src/rp2_common/pico_mem_ops/mem_ops_aeabi.S \
sdk/src/rp2_common/pico_standard_link/crt0.S \
lib/PicoDVI/software/libdvi/tmds_encode_asm.S \
OBJ = $(PY_O) $(SUPERVISOR_O) $(addprefix $(BUILD)/, $(SRC_C:.c=.o))
OBJ += $(addprefix $(BUILD)/, $(SRC_SDK:.c=.o))
@ -405,13 +426,19 @@ SRC_QSTR += $(SRC_C) $(SRC_SUPERVISOR) $(SRC_COMMON_HAL_EXPANDED) $(SRC_SHARED_M
all: $(BUILD)/firmware.uf2
LINK_LD := $(firstword $(wildcard boards/$(BOARD)/link.ld link.ld))
$(BUILD)/firmware.elf: $(OBJ) $(LINK_LD)
BOARD_LD := $(wildcard boards/$(BOARD)/link.ld)
ifneq ($(BOARD_LD),)
LINKER_SCRIPTS = -Wl,-T,$(BOARD_LD)
endif
LINKER_SCRIPTS += -Wl,-T,link.ld
$(BUILD)/firmware.elf: $(OBJ) $(BOARD_LD) link.ld
$(STEPECHO) "LINK $@"
$(Q)echo $(OBJ) > $(BUILD)/firmware.objs
$(Q)echo $(PICO_LDFLAGS) > $(BUILD)/firmware.ldflags
$(Q)$(CC) -o $@ $(CFLAGS) @$(BUILD)/firmware.ldflags -Wl,-T,$(LINK_LD) -Wl,-Map=$@.map -Wl,-cref -Wl,--gc-sections @$(BUILD)/firmware.objs -Wl,-lc
$(Q)$(SIZE) $@ | $(PYTHON) $(TOP)/tools/build_memory_info.py $(LINK_LD) $(BUILD)
$(Q)$(CC) -o $@ $(CFLAGS) @$(BUILD)/firmware.ldflags $(LINKER_SCRIPTS) -Wl,--print-memory-usage -Wl,-Map=$@.map -Wl,-cref -Wl,--gc-sections @$(BUILD)/firmware.objs -Wl,-lc
$(BUILD)/firmware.bin: $(BUILD)/firmware.elf
$(STEPECHO) "Create $@"

View File

@ -37,7 +37,7 @@
#include "src/rp2_common/hardware_irq/include/hardware/irq.h"
#if CIRCUITPY_AUDIOPWMIO || CIRCUITPY_AUDIOBUSIO
#if CIRCUITPY_AUDIOCORE
void audio_dma_reset(void) {
for (size_t channel = 0; channel < NUM_DMA_CHANNELS; channel++) {

View File

@ -0,0 +1,264 @@
/*
* This file is part of the Micro Python project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2023 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 "py/obj.h"
#include "py/objproperty.h"
#include "py/runtime.h"
#include "py/objarray.h"
#include "bindings/picodvi/Framebuffer.h"
#include "shared-bindings/util.h"
#include "shared-module/displayio/__init__.h"
#include "shared-module/framebufferio/__init__.h"
#include "shared-module/framebufferio/FramebufferDisplay.h"
//| class Framebuffer:
//| """A PicoDVI managed frame buffer."""
//|
//| def __init__(
//| self,
//| width: int,
//| height: int,
//| *,
//| clk_dp: microcontroller.Pin,
//| clk_dn: microcontroller.Pin,
//| red_dp: microcontroller.Pin,
//| red_dn: microcontroller.Pin,
//| green_dp: microcontroller.Pin,
//| green_dn: microcontroller.Pin,
//| blue_dp: microcontroller.Pin,
//| blue_dn: microcontroller.Pin,
//| color_depth: int = 8,
//| ) -> None:
//| """Create a Framebuffer object with the given dimensions (640x480 or 800x480). Memory is
//| allocated outside of onto the heap and then moved outside on VM
//| end.
//|
//| This will change the system clock speed to match the DVI signal.
//| Make sure to initialize other objects after this one so they account
//| for the changed clock. This also allocates a very large framebuffer
//| and is most likely to succeed the earlier it is attempted.
//|
//| Each dp and dn pair of pins must be neighboring, such as 19 and 20.
//| They must also be ordered the same way. In other words, dp must be
//| less than dn for all pairs or dp must be greater than dn for all pairs.
//|
//| The framebuffer pixel format varies depending on color_depth:
//| * 1 - Each bit is a pixel. Either white (1) or black (0).
//| * 2 - Each 2 bits is a pixels. Grayscale between white (0x3) and black (0x0).
//| * 8 - Each byte is a pixels in RGB332 format.
//| * 16 - Each two bytes are a pixel in RGB565 format.
//|
//| Monochrome framebuffers (color_depth=1 or 2) will be full resolution.
//| Color framebuffers will be half resolution and pixels will be
//| duplicated to create a signal with the target dimensions.
//|
//| A Framebuffer is often used in conjunction with a
//| `framebufferio.FramebufferDisplay`.
//|
//| :param int width: the width of the target display signal. It will be halved when
//| color_depth >= 8 when creating the framebuffer. Only 640 or 800 is currently supported.
//| :param int height: the height of the target display signal. It will be halved when
//| color_depth >= 8 when creating the framebuffer. Only 480 is currently supported.
//| :param ~microcontroller.Pin clk_dp: the positive clock signal pin
//| :param ~microcontroller.Pin clk_dn: the negative clock signal pin
//| :param ~microcontroller.Pin red_dp: the positive red signal pin
//| :param ~microcontroller.Pin red_dn: the negative red signal pin
//| :param ~microcontroller.Pin green_dp: the positive green signal pin
//| :param ~microcontroller.Pin green_dn: the negative green signal pin
//| :param ~microcontroller.Pin blue_dp: the positive blue signal pin
//| :param ~microcontroller.Pin blue_dn: the negative blue signal pin
//| :param int color_depth: the color depth of the framebuffer in bits. 1, 2 for grayscale
//| and 8 or 16 for color
//| """
STATIC mp_obj_t picodvi_framebuffer_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
enum { ARG_width, ARG_height, ARG_clk_dp, ARG_clk_dn, ARG_red_dp, ARG_red_dn, ARG_green_dp,
ARG_green_dn, ARG_blue_dp, ARG_blue_dn, ARG_color_depth };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_width, MP_ARG_INT | MP_ARG_REQUIRED },
{ MP_QSTR_height, MP_ARG_INT | MP_ARG_REQUIRED },
{ MP_QSTR_clk_dp, MP_ARG_KW_ONLY | MP_ARG_OBJ | MP_ARG_REQUIRED },
{ MP_QSTR_clk_dn, MP_ARG_KW_ONLY | MP_ARG_OBJ | MP_ARG_REQUIRED },
{ MP_QSTR_red_dp, MP_ARG_KW_ONLY | MP_ARG_OBJ | MP_ARG_REQUIRED },
{ MP_QSTR_red_dn, MP_ARG_KW_ONLY | MP_ARG_OBJ | MP_ARG_REQUIRED },
{ MP_QSTR_green_dp, MP_ARG_KW_ONLY | MP_ARG_OBJ | MP_ARG_REQUIRED },
{ MP_QSTR_green_dn, MP_ARG_KW_ONLY | MP_ARG_OBJ | MP_ARG_REQUIRED },
{ MP_QSTR_blue_dp, MP_ARG_KW_ONLY | MP_ARG_OBJ | MP_ARG_REQUIRED },
{ MP_QSTR_blue_dn, MP_ARG_KW_ONLY | MP_ARG_OBJ | MP_ARG_REQUIRED },
{ MP_QSTR_color_depth, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 8} },
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
picodvi_framebuffer_obj_t *self = &allocate_display_bus_or_raise()->picodvi;
self->base.type = &picodvi_framebuffer_type;
mp_uint_t width = (mp_uint_t)mp_arg_validate_int_min(args[ARG_width].u_int, 0, MP_QSTR_width);
mp_uint_t height = (mp_uint_t)mp_arg_validate_int_min(args[ARG_height].u_int, 0, MP_QSTR_height);
mp_uint_t color_depth = args[ARG_color_depth].u_int;
if (color_depth != 1 && color_depth != 2 && color_depth != 8 && color_depth != 16) {
mp_raise_ValueError_varg(translate("Invalid %q"), MP_QSTR_color_depth);
}
common_hal_picodvi_framebuffer_construct(self,
width, height,
validate_obj_is_free_pin(args[ARG_clk_dp].u_obj, MP_QSTR_clk_dp),
validate_obj_is_free_pin(args[ARG_clk_dn].u_obj, MP_QSTR_clk_dn),
validate_obj_is_free_pin(args[ARG_red_dp].u_obj, MP_QSTR_red_dp),
validate_obj_is_free_pin(args[ARG_red_dn].u_obj, MP_QSTR_red_dn),
validate_obj_is_free_pin(args[ARG_green_dp].u_obj, MP_QSTR_green_dp),
validate_obj_is_free_pin(args[ARG_green_dn].u_obj, MP_QSTR_green_dn),
validate_obj_is_free_pin(args[ARG_blue_dp].u_obj, MP_QSTR_blue_dp),
validate_obj_is_free_pin(args[ARG_blue_dn].u_obj, MP_QSTR_blue_dn),
color_depth);
return MP_OBJ_FROM_PTR(self);
}
//| def deinit(self) -> None:
//| """Free the resources (pins, timers, etc.) associated with this
//| rgbmatrix instance. After deinitialization, no further operations
//| may be performed."""
//| ...
STATIC mp_obj_t picodvi_framebuffer_deinit(mp_obj_t self_in) {
picodvi_framebuffer_obj_t *self = (picodvi_framebuffer_obj_t *)self_in;
common_hal_picodvi_framebuffer_deinit(self);
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(picodvi_framebuffer_deinit_obj, picodvi_framebuffer_deinit);
static void check_for_deinit(picodvi_framebuffer_obj_t *self) {
if (common_hal_picodvi_framebuffer_deinited(self)) {
raise_deinited_error();
}
}
//| width: int
//| """The width of the framebuffer, in pixels. It may be doubled for output (and half of what
//| width was given to __init__.)"""
STATIC mp_obj_t picodvi_framebuffer_get_width(mp_obj_t self_in) {
picodvi_framebuffer_obj_t *self = (picodvi_framebuffer_obj_t *)self_in;
check_for_deinit(self);
return MP_OBJ_NEW_SMALL_INT(common_hal_picodvi_framebuffer_get_width(self));
}
MP_DEFINE_CONST_FUN_OBJ_1(picodvi_framebuffer_get_width_obj, picodvi_framebuffer_get_width);
MP_PROPERTY_GETTER(picodvi_framebuffer_width_obj,
(mp_obj_t)&picodvi_framebuffer_get_width_obj);
//| height: int
//| """The width of the framebuffer, in pixels. It may be doubled for output (and half of what
//| width was given to __init__.)"""
//|
STATIC mp_obj_t picodvi_framebuffer_get_height(mp_obj_t self_in) {
picodvi_framebuffer_obj_t *self = (picodvi_framebuffer_obj_t *)self_in;
check_for_deinit(self);
return MP_OBJ_NEW_SMALL_INT(common_hal_picodvi_framebuffer_get_height(self));
}
MP_DEFINE_CONST_FUN_OBJ_1(picodvi_framebuffer_get_height_obj, picodvi_framebuffer_get_height);
MP_PROPERTY_GETTER(picodvi_framebuffer_height_obj,
(mp_obj_t)&picodvi_framebuffer_get_height_obj);
STATIC const mp_rom_map_elem_t picodvi_framebuffer_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&picodvi_framebuffer_deinit_obj) },
{ MP_ROM_QSTR(MP_QSTR_width), MP_ROM_PTR(&picodvi_framebuffer_width_obj) },
{ MP_ROM_QSTR(MP_QSTR_height), MP_ROM_PTR(&picodvi_framebuffer_height_obj) },
};
STATIC MP_DEFINE_CONST_DICT(picodvi_framebuffer_locals_dict, picodvi_framebuffer_locals_dict_table);
STATIC void picodvi_framebuffer_get_bufinfo(mp_obj_t self_in, mp_buffer_info_t *bufinfo) {
common_hal_picodvi_framebuffer_get_buffer(self_in, bufinfo, 0);
}
// These versions exist so that the prototype matches the protocol,
// avoiding a type cast that can hide errors
STATIC void picodvi_framebuffer_swapbuffers(mp_obj_t self_in, uint8_t *dirty_row_bitmap) {
(void)dirty_row_bitmap;
common_hal_picodvi_framebuffer_refresh(self_in);
}
STATIC void picodvi_framebuffer_deinit_proto(mp_obj_t self_in) {
common_hal_picodvi_framebuffer_deinit(self_in);
}
STATIC int picodvi_framebuffer_get_width_proto(mp_obj_t self_in) {
return common_hal_picodvi_framebuffer_get_width(self_in);
}
STATIC int picodvi_framebuffer_get_height_proto(mp_obj_t self_in) {
return common_hal_picodvi_framebuffer_get_height(self_in);
}
STATIC int picodvi_framebuffer_get_color_depth_proto(mp_obj_t self_in) {
return common_hal_picodvi_framebuffer_get_color_depth(self_in);
;
}
STATIC int picodvi_framebuffer_get_bytes_per_cell_proto(mp_obj_t self_in) {
return 1;
}
STATIC int picodvi_framebuffer_get_native_frames_per_second_proto(mp_obj_t self_in) {
return 60;
}
STATIC bool picodvi_framebuffer_get_pixels_in_byte_share_row_proto(mp_obj_t self_in) {
return true;
}
STATIC int picodvi_framebuffer_get_row_stride_proto(mp_obj_t self_in) {
return common_hal_picodvi_framebuffer_get_row_stride(self_in);
}
STATIC const framebuffer_p_t picodvi_framebuffer_proto = {
MP_PROTO_IMPLEMENT(MP_QSTR_protocol_framebuffer)
.get_bufinfo = picodvi_framebuffer_get_bufinfo,
.get_width = picodvi_framebuffer_get_width_proto,
.get_height = picodvi_framebuffer_get_height_proto,
.get_color_depth = picodvi_framebuffer_get_color_depth_proto,
.get_row_stride = picodvi_framebuffer_get_row_stride_proto,
.get_bytes_per_cell = picodvi_framebuffer_get_bytes_per_cell_proto,
.get_native_frames_per_second = picodvi_framebuffer_get_native_frames_per_second_proto,
.get_pixels_in_byte_share_row = picodvi_framebuffer_get_pixels_in_byte_share_row_proto,
.swapbuffers = picodvi_framebuffer_swapbuffers,
.deinit = picodvi_framebuffer_deinit_proto,
};
const mp_obj_type_t picodvi_framebuffer_type = {
{ &mp_type_type },
.flags = MP_TYPE_FLAG_EXTENDED,
.name = MP_QSTR_Framebuffer,
.locals_dict = (mp_obj_dict_t *)&picodvi_framebuffer_locals_dict,
.make_new = picodvi_framebuffer_make_new,
MP_TYPE_EXTENDED_FIELDS(
.buffer_p = { .get_buffer = common_hal_picodvi_framebuffer_get_buffer, },
.protocol = &picodvi_framebuffer_proto,
),
};

View File

@ -0,0 +1,49 @@
/*
* This file is part of the Micro Python project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2023 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.
*/
#pragma once
#include "common-hal/picodvi/Framebuffer.h"
#include "shared-bindings/microcontroller/Pin.h"
extern const mp_obj_type_t picodvi_framebuffer_type;
void common_hal_picodvi_framebuffer_construct(picodvi_framebuffer_obj_t *self,
mp_uint_t width, mp_uint_t height,
const mcu_pin_obj_t *clk_dp, const mcu_pin_obj_t *clk_dn,
const mcu_pin_obj_t *red_dp, const mcu_pin_obj_t *red_dn,
const mcu_pin_obj_t *green_dp, const mcu_pin_obj_t *green_dn,
const mcu_pin_obj_t *blue_dp, const mcu_pin_obj_t *blue_dn,
mp_uint_t color_depth);
void common_hal_picodvi_framebuffer_deinit(picodvi_framebuffer_obj_t *self);
bool common_hal_picodvi_framebuffer_deinited(picodvi_framebuffer_obj_t *self);
void common_hal_picodvi_framebuffer_refresh(picodvi_framebuffer_obj_t *self);
int common_hal_picodvi_framebuffer_get_width(picodvi_framebuffer_obj_t *self);
int common_hal_picodvi_framebuffer_get_height(picodvi_framebuffer_obj_t *self);
int common_hal_picodvi_framebuffer_get_row_stride(picodvi_framebuffer_obj_t *self);
int common_hal_picodvi_framebuffer_get_color_depth(picodvi_framebuffer_obj_t *self);
mp_int_t common_hal_picodvi_framebuffer_get_buffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, mp_uint_t flags);

View File

@ -0,0 +1,48 @@
/*
* 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 "py/obj.h"
#include "py/runtime.h"
#include "bindings/picodvi/Framebuffer.h"
//| """Low-level routines for interacting with PicoDVI Output"""
STATIC const mp_rom_map_elem_t picodvi_module_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_picodvi) },
{ MP_ROM_QSTR(MP_QSTR_Framebuffer), MP_ROM_PTR(&picodvi_framebuffer_type) },
};
STATIC MP_DEFINE_CONST_DICT(picodvi_module_globals, picodvi_module_globals_table);
const mp_obj_module_t picodvi_module = {
.base = { &mp_type_module },
.globals = (mp_obj_dict_t *)&picodvi_module_globals,
};
MP_REGISTER_MODULE(MP_QSTR_picodvi, picodvi_module, CIRCUITPY_PICODVI);

View File

@ -26,4 +26,27 @@
#include "supervisor/board.h"
#include "bindings/picodvi/Framebuffer.h"
#include "shared-module/displayio/__init__.h"
#include "shared-bindings/framebufferio/FramebufferDisplay.h"
void board_init(void) {
picodvi_framebuffer_obj_t *fb = &allocate_display_bus()->picodvi;
fb->base.type = &picodvi_framebuffer_type;
common_hal_picodvi_framebuffer_construct(fb, 640, 480,
&pin_GPIO17, &pin_GPIO16,
&pin_GPIO19, &pin_GPIO18,
&pin_GPIO21, &pin_GPIO20,
&pin_GPIO23, &pin_GPIO22,
8);
framebufferio_framebufferdisplay_obj_t *display = &displays[0].framebuffer_display;
display->base.type = &framebufferio_framebufferdisplay_type;
common_hal_framebufferio_framebufferdisplay_construct(
display,
MP_OBJ_FROM_PTR(fb),
0,
true);
}
// Use the MP_WEAK supervisor/shared/board.c versions of routines not defined here.

View File

@ -7,3 +7,5 @@ CHIP_VARIANT = RP2040
CHIP_FAMILY = rp2
EXTERNAL_FLASH_DEVICES = "GD25Q64C,W25Q64JVxQ"
CIRCUITPY_PICODVI = 1

View File

@ -1,5 +1,7 @@
#include "shared-bindings/board/__init__.h"
#include "shared-module/displayio/__init__.h"
STATIC const mp_rom_map_elem_t board_module_globals_table[] = {
CIRCUITPYTHON_BOARD_DICT_STANDARD_ITEMS
@ -49,6 +51,8 @@ STATIC const mp_rom_map_elem_t board_module_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR_D2N), MP_ROM_PTR(&pin_GPIO22) },
{ MP_ROM_QSTR(MP_QSTR_D2P), MP_ROM_PTR(&pin_GPIO23) },
{ MP_ROM_QSTR(MP_QSTR_DISPLAY), MP_ROM_PTR(&displays[0].framebuffer_display)},
{ MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&board_i2c_obj) },
{ MP_ROM_QSTR(MP_QSTR_STEMMA_I2C), MP_ROM_PTR(&board_i2c_obj) },
{ MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&board_spi_obj) },

View File

@ -9,3 +9,4 @@ CHIP_FAMILY = rp2
EXTERNAL_FLASH_DEVICES = "W25Q16JVxQ"
CIRCUITPY__EVE = 1
CIRCUITPY_PICODVI = 1

View File

@ -1,294 +1 @@
/* Based on GCC ARM embedded samples.
Defines the following symbols for use by code:
__exidx_start
__exidx_end
__etext
__data_start__
__preinit_array_start
__preinit_array_end
__init_array_start
__init_array_end
__fini_array_start
__fini_array_end
__data_end__
__bss_start__
__bss_end__
__end__
end
__HeapLimit
__StackLimit
__StackTop
__stack (== StackTop)
*/
MEMORY
{
FLASH_FIRMWARE (rx) : ORIGIN = 0x10000000, LENGTH = 1532k
/* Followed by: 4kB of NVRAM and at least 512kB of CIRCUITPY */
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 256k
SCRATCH_X (rwx) : ORIGIN = 0x20040000, LENGTH = 4k
SCRATCH_Y (rwx) : ORIGIN = 0x20041000, LENGTH = 4k
}
ENTRY(_entry_point)
SECTIONS
{
/* Second stage bootloader is prepended to the image. It must be 256 bytes big
and checksummed. It is usually built by the boot_stage2 target
in the Pico SDK
*/
.flash_begin : {
__flash_binary_start = .;
} > FLASH_FIRMWARE
.boot2 : {
__boot2_start__ = .;
KEEP (*(.boot2))
__boot2_end__ = .;
} > FLASH_FIRMWARE
ASSERT(__boot2_end__ - __boot2_start__ == 256,
"ERROR: Pico second stage bootloader must be 256 bytes in size")
/* The second stage will always enter the image at the start of .text.
The debugger will use the ELF entry point, which is the _entry_point
symbol if present, otherwise defaults to start of .text.
This can be used to transfer control back to the bootrom on debugger
launches only, to perform proper flash setup.
*/
.text : {
__logical_binary_start = .;
KEEP (*(.vectors))
KEEP (*(.binary_info_header))
__binary_info_header_end = .;
KEEP (*(.reset))
/* TODO revisit this now memset/memcpy/float in ROM */
/* bit of a hack right now to exclude all floating point and time critical (e.g. memset, memcpy) code from
* FLASH ... we will include any thing excluded here in .data below by default */
*(.init)
__property_getter_start = .;
*(.property_getter)
__property_getter_end = .;
__property_getset_start = .;
*(.property_getset)
__property_getset_end = .;
*(EXCLUDE_FILE(*libgcc.a: *libc.a:*lib_a-mem*.o *libm.a:) .text*)
*(.fini)
/* Pull all c'tors into .text */
*crtbegin.o(.ctors)
*crtbegin?.o(.ctors)
*(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors)
*(SORT(.ctors.*))
*(.ctors)
/* Followed by destructors */
*crtbegin.o(.dtors)
*crtbegin?.o(.dtors)
*(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors)
*(SORT(.dtors.*))
*(.dtors)
*(.eh_frame*)
. = ALIGN(4);
} > FLASH_FIRMWARE
.rodata : {
*(EXCLUDE_FILE(*libgcc.a: *libc.a:*lib_a-mem*.o *libm.a:) .rodata*)
. = ALIGN(4);
*(SORT_BY_ALIGNMENT(SORT_BY_NAME(.flashdata*)))
. = ALIGN(4);
} > FLASH_FIRMWARE
.ARM.extab :
{
*(.ARM.extab* .gnu.linkonce.armextab.*)
} > FLASH_FIRMWARE
__exidx_start = .;
.ARM.exidx :
{
*(.ARM.exidx* .gnu.linkonce.armexidx.*)
} > FLASH_FIRMWARE
__exidx_end = .;
/* Machine inspectable binary information */
. = ALIGN(4);
__binary_info_start = .;
.binary_info :
{
KEEP(*(.binary_info.keep.*))
*(.binary_info.*)
} > FLASH_FIRMWARE
__binary_info_end = .;
. = ALIGN(4);
/* End of .text-like segments */
__etext = .;
.ram_vector_table (COPY): {
*(.ram_vector_table)
} > RAM
.data : {
__data_start__ = .;
*(vtable)
*(.time_critical*)
/* remaining .text and .rodata; i.e. stuff we exclude above because we want it in RAM */
*(.text*)
. = ALIGN(4);
*(.rodata*)
. = ALIGN(4);
*(.data*)
. = ALIGN(4);
*(.after_data.*)
. = ALIGN(4);
/* preinit data */
PROVIDE_HIDDEN (__mutex_array_start = .);
KEEP(*(SORT(.mutex_array.*)))
KEEP(*(.mutex_array))
PROVIDE_HIDDEN (__mutex_array_end = .);
. = ALIGN(4);
/* preinit data */
PROVIDE_HIDDEN (__preinit_array_start = .);
KEEP(*(SORT(.preinit_array.*)))
KEEP(*(.preinit_array))
PROVIDE_HIDDEN (__preinit_array_end = .);
. = ALIGN(4);
/* init data */
PROVIDE_HIDDEN (__init_array_start = .);
KEEP(*(SORT(.init_array.*)))
KEEP(*(.init_array))
PROVIDE_HIDDEN (__init_array_end = .);
. = ALIGN(4);
/* finit data */
PROVIDE_HIDDEN (__fini_array_start = .);
*(SORT(.fini_array.*))
*(.fini_array)
PROVIDE_HIDDEN (__fini_array_end = .);
*(.jcr)
. = ALIGN(4);
/* All data end */
__data_end__ = .;
} > RAM AT> FLASH_FIRMWARE
.itcm :
{
. = ALIGN(4);
*(.itcm.*)
. = ALIGN(4);
} > RAM AT> FLASH_FIRMWARE
_ld_itcm_destination = ADDR(.itcm);
_ld_itcm_flash_copy = LOADADDR(.itcm);
_ld_itcm_size = SIZEOF(.itcm);
.dtcm_data :
{
. = ALIGN(4);
*(.dtcm_data.*)
. = ALIGN(4);
} > RAM AT> FLASH_FIRMWARE
_ld_dtcm_data_destination = ADDR(.dtcm_data);
_ld_dtcm_data_flash_copy = LOADADDR(.dtcm_data);
_ld_dtcm_data_size = SIZEOF(.dtcm_data);
.dtcm_bss :
{
. = ALIGN(4);
*(.dtcm_bss.*)
. = ALIGN(4);
} > RAM AT> RAM
_ld_dtcm_bss_start = ADDR(.dtcm_bss);
_ld_dtcm_bss_size = SIZEOF(.dtcm_bss);
.uninitialized_data (COPY): {
. = ALIGN(4);
*(.uninitialized_data*)
} > RAM
/* Start and end symbols must be word-aligned */
.scratch_x : {
__scratch_x_start__ = .;
*(.scratch_x.*)
. = ALIGN(4);
__scratch_x_end__ = .;
} > SCRATCH_X AT > FLASH_FIRMWARE
__scratch_x_source__ = LOADADDR(.scratch_x);
.scratch_y : {
__scratch_y_start__ = .;
*(.scratch_y.*)
. = ALIGN(4);
__scratch_y_end__ = .;
} > SCRATCH_Y AT > FLASH_FIRMWARE
__scratch_y_source__ = LOADADDR(.scratch_y);
.bss : {
. = ALIGN(4);
__bss_start__ = .;
*(SORT_BY_ALIGNMENT(SORT_BY_NAME(.bss*)))
*(COMMON)
. = ALIGN(4);
__bss_end__ = .;
} > RAM
.heap (COPY):
{
__end__ = .;
end = __end__;
*(.heap*)
__HeapLimit = .;
} > RAM
/* .stack*_dummy section doesn't contains any symbols. It is only
* used for linker to calculate size of stack sections, and assign
* values to stack symbols later
*
* stack1 section may be empty/missing if platform_launch_core1 is not used */
/* by default we put core 0 stack at the end of scratch Y, so that if core 1
* stack is not used then all of SCRATCH_X is free.
*/
.stack1_dummy (COPY):
{
*(.stack1*)
} > SCRATCH_X
.stack_dummy (COPY):
{
*(.stack*)
} > SCRATCH_Y
.flash_end : {
__flash_binary_end = .;
} > FLASH_FIRMWARE
/* stack limit is poorly named, but historically is maximum heap ptr */
__StackLimit = ORIGIN(RAM) + LENGTH(RAM);
__StackOneTop = ORIGIN(SCRATCH_X) + LENGTH(SCRATCH_X);
__StackTop = ORIGIN(SCRATCH_Y) + LENGTH(SCRATCH_Y);
__StackOneBottom = __StackOneTop - SIZEOF(.stack1_dummy);
__StackBottom = __StackTop - SIZEOF(.stack_dummy);
PROVIDE(__stack = __StackTop);
/* Check if data + heap + stack exceeds RAM limit */
ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed")
ASSERT( __binary_info_header_end - __logical_binary_start <= 256, "Binary info must be in first 256 bytes of the binary")
/* todo assert on extra code */
}
firmware_size = 1532k;

View File

@ -19,6 +19,8 @@ CIRCUITPY_MDNS = 1
CIRCUITPY_SOCKETPOOL = 1
CIRCUITPY_WIFI = 1
CIRCUITPY_PICODVI = 1
CFLAGS += -DCYW43_PIN_WL_HOST_WAKE=24 -DCYW43_PIN_WL_REG_ON=23 -DCYW43_WL_GPIO_COUNT=3 -DCYW43_WL_GPIO_LED_PIN=0
# Must be accompanied by a linker script change
CFLAGS += -DCIRCUITPY_FIRMWARE_SIZE='(1536 * 1024)'

View File

@ -0,0 +1,419 @@
/*
* This file is part of the Micro Python project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2023 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 "bindings/picodvi/Framebuffer.h"
#include "py/gc.h"
#include "py/runtime.h"
#include "shared-bindings/time/__init__.h"
#include "common-hal/pwmio/PWMOut.h"
#include "common-hal/rp2pio/StateMachine.h"
#include "src/common/pico_stdlib/include/pico/stdlib.h"
#include "src/rp2040/hardware_structs/include/hardware/structs/mpu.h"
#include "src/rp2_common/cmsis/stub/CMSIS/Device/RaspberryPi/RP2040/Include/RP2040.h"
#include "src/rp2_common/hardware_pwm/include/hardware/pwm.h"
#include "src/rp2_common/hardware_vreg/include/hardware/vreg.h"
#include "src/rp2_common/pico_multicore/include/pico/multicore.h"
#include "lib/PicoDVI/software/libdvi/tmds_encode.h"
picodvi_framebuffer_obj_t *active_picodvi = NULL;
STATIC PIO pio_instances[2] = {pio0, pio1};
static void __not_in_flash_func(core1_main)(void) {
// The MPU is reset before this starts.
picodvi_framebuffer_obj_t *self = active_picodvi;
dvi_register_irqs_this_core(&self->dvi, DMA_IRQ_1);
while (queue_is_empty(&self->dvi.q_colour_valid)) {
__wfe();
}
dvi_start(&self->dvi);
// Turn off flash access. After this, it will hard fault. Better than messing
// up CIRCUITPY.
MPU->CTRL = MPU_CTRL_PRIVDEFENA_Msk | MPU_CTRL_ENABLE_Msk;
MPU->RNR = 6; // 7 is used by pico-sdk stack protection.
MPU->RBAR = XIP_MAIN_BASE | MPU_RBAR_VALID_Msk;
MPU->RASR = MPU_RASR_XN_Msk | // Set execute never and everything else is restricted.
MPU_RASR_ENABLE_Msk |
(0x1b << MPU_RASR_SIZE_Pos); // Size is 0x10000000 which masks up to SRAM region.
MPU->RNR = 7;
uint y = 0;
while (1) {
uint32_t *scanbuf;
queue_remove_blocking_u32(&self->dvi.q_colour_valid, &scanbuf);
uint32_t *tmdsbuf;
queue_remove_blocking_u32(&self->dvi.q_tmds_free, &tmdsbuf);
// Check to see if the tmds memory has moved and replace this tmdsbuf
// the corresponding on at a new location.
size_t old_fb = tmdsbuf[self->tmdsbuf_size - 1];
if (old_fb != (uint32_t)self->framebuffer) {
size_t index = ((uint32_t)(tmdsbuf - old_fb)) / self->tmdsbuf_size;
// Check our index and hang if it is out of range. Hang is ok since this is core 1.
// Better than writing the wrong memory that is shared with CP.
while (index >= DVI_N_TMDS_BUFFERS) {
}
tmdsbuf = self->framebuffer + self->framebuffer_len + (self->tmdsbuf_size * index);
tmdsbuf[self->tmdsbuf_size - 1] = (uint32_t)self->framebuffer;
}
uint pixwidth = self->dvi.timing->h_active_pixels;
uint words_per_channel = pixwidth / DVI_SYMBOLS_PER_WORD;
if (self->color_depth == 8) {
tmds_encode_data_channel_8bpp(scanbuf, tmdsbuf + 0 * words_per_channel, pixwidth / 2, DVI_8BPP_BLUE_MSB, DVI_8BPP_BLUE_LSB);
tmds_encode_data_channel_8bpp(scanbuf, tmdsbuf + 1 * words_per_channel, pixwidth / 2, DVI_8BPP_GREEN_MSB, DVI_8BPP_GREEN_LSB);
tmds_encode_data_channel_8bpp(scanbuf, tmdsbuf + 2 * words_per_channel, pixwidth / 2, DVI_8BPP_RED_MSB, DVI_8BPP_RED_LSB);
} else if (self->color_depth == 16) {
tmds_encode_data_channel_16bpp(scanbuf, tmdsbuf + 0 * words_per_channel, pixwidth / 2, DVI_16BPP_BLUE_MSB, DVI_16BPP_BLUE_LSB);
tmds_encode_data_channel_16bpp(scanbuf, tmdsbuf + 1 * words_per_channel, pixwidth / 2, DVI_16BPP_GREEN_MSB, DVI_16BPP_GREEN_LSB);
tmds_encode_data_channel_16bpp(scanbuf, tmdsbuf + 2 * words_per_channel, pixwidth / 2, DVI_16BPP_RED_MSB, DVI_16BPP_RED_LSB);
} else if (self->color_depth == 1) {
tmds_encode_1bpp(scanbuf, tmdsbuf, pixwidth);
} else if (self->color_depth == 2) {
tmds_encode_2bpp(scanbuf, tmdsbuf, pixwidth);
}
queue_add_blocking_u32(&self->dvi.q_tmds_valid, &tmdsbuf);
queue_add_blocking_u32(&self->dvi.q_colour_free, &scanbuf);
++y;
if (y == self->dvi.timing->v_active_lines) {
y = 0;
}
}
__builtin_unreachable();
}
static void __not_in_flash_func(core1_scanline_callback)(void) {
picodvi_framebuffer_obj_t *self = active_picodvi;
uint32_t *next_scanline_buf;
next_scanline_buf = self->framebuffer + (self->pitch * self->next_scanline);
queue_add_blocking_u32(&self->dvi.q_colour_valid, &next_scanline_buf);
// Remove any buffers that were sent back to us.
while (queue_try_remove_u32(&self->dvi.q_colour_free, &next_scanline_buf)) {
}
self->next_scanline += 1;
if (self->next_scanline >= self->height) {
self->next_scanline = 0;
// Update the framebuffer pointer in case it moved.
self->framebuffer = self->allocation->ptr;
}
}
extern uint8_t dvi_vertical_repeat;
extern bool dvi_monochrome_tmds;
void common_hal_picodvi_framebuffer_construct(picodvi_framebuffer_obj_t *self,
mp_uint_t width, mp_uint_t height,
const mcu_pin_obj_t *clk_dp, const mcu_pin_obj_t *clk_dn,
const mcu_pin_obj_t *red_dp, const mcu_pin_obj_t *red_dn,
const mcu_pin_obj_t *green_dp, const mcu_pin_obj_t *green_dn,
const mcu_pin_obj_t *blue_dp, const mcu_pin_obj_t *blue_dn,
mp_uint_t color_depth) {
const struct dvi_timing *timing = NULL;
if (width == 640 && height == 480) {
timing = &dvi_timing_640x480p_60hz;
} else if (width == 800 && height == 480) {
timing = &dvi_timing_800x480p_60hz;
} else {
if (height == 480) {
mp_raise_ValueError_varg(translate("%q must be %d"), MP_QSTR_width, 480);
}
mp_raise_ValueError_varg(translate("Invalid %q"), MP_QSTR_height);
}
if (active_picodvi != NULL) {
mp_raise_msg_varg(&mp_type_RuntimeError, translate("%q in use"), MP_QSTR_picodvi);
}
bool invert_diffpairs = clk_dn->number < clk_dp->number;
int8_t other_pins[4];
int8_t *a;
int8_t *b;
if (invert_diffpairs) {
a = other_pins;
b = self->pin_pair;
} else {
a = self->pin_pair;
b = other_pins;
}
a[0] = clk_dp->number;
a[1] = red_dp->number;
a[2] = green_dp->number;
a[3] = blue_dp->number;
b[0] = clk_dn->number;
b[1] = red_dn->number;
b[2] = green_dn->number;
b[3] = blue_dn->number;
qstr pin_names[4] = {MP_QSTR_clk_dp, MP_QSTR_red_dp, MP_QSTR_green_dp, MP_QSTR_blue_dp};
for (size_t i = 0; i < 4; i++) {
if (other_pins[i] - self->pin_pair[i] != 1) {
raise_ValueError_invalid_pin_name(pin_names[i]);
}
}
uint8_t slice = pwm_gpio_to_slice_num(self->pin_pair[0]);
pio_program_t program_struct = {
.instructions = NULL,
.length = 2,
.origin = -1
};
size_t pio_index = NUM_PIOS;
int free_state_machines[4]; // We may find all four free. We only use the first three.
for (size_t i = 0; i < NUM_PIOS; i++) {
PIO pio = pio_instances[i];
uint8_t free_count = 0;
for (size_t sm = 0; sm < NUM_PIO_STATE_MACHINES; sm++) {
if (!pio_sm_is_claimed(pio, sm)) {
free_state_machines[free_count] = sm;
free_count++;
}
}
if (free_count >= 3 && pio_can_add_program(pio, &program_struct)) {
pio_index = i;
break;
}
}
if (pio_index == NUM_PIOS) {
mp_raise_RuntimeError(translate("All state machines in use"));
}
self->width = width;
self->height = height;
size_t tmds_bufs_per_scanline;
if (color_depth >= 8) {
dvi_vertical_repeat = 2;
dvi_monochrome_tmds = false;
self->width /= 2;
self->height /= 2;
tmds_bufs_per_scanline = 3;
} else {
dvi_vertical_repeat = 1;
dvi_monochrome_tmds = true;
// One tmds buffer is used for all three color outputs.
tmds_bufs_per_scanline = 1;
}
self->pitch = (self->width * color_depth) / 8;
// Align each row to words.
if (self->pitch % sizeof(uint32_t) != 0) {
self->pitch += sizeof(uint32_t) - (self->pitch % sizeof(uint32_t));
}
self->pitch /= sizeof(uint32_t);
size_t framebuffer_size = self->pitch * self->height;
// use width here because it hasn't been downsized for the frame buffer
self->tmdsbuf_size = tmds_bufs_per_scanline * width / DVI_SYMBOLS_PER_WORD + 1;
size_t total_allocation_size = sizeof(uint32_t) * (framebuffer_size + DVI_N_TMDS_BUFFERS * self->tmdsbuf_size);
self->allocation = allocate_memory(total_allocation_size, false, true);
if (self->allocation == NULL) {
m_malloc_fail(total_allocation_size);
return;
}
// Do the pwmio check last because it claims the pwm slice.
if (!pwmio_claim_slice_ab_channels(slice)) {
mp_raise_ValueError(translate("All timers for this pin are in use"));
}
self->pwm_slice = slice;
pwmout_never_reset(self->pwm_slice, 0);
pwmout_never_reset(self->pwm_slice, 1);
for (size_t i = 0; i < 4; i++) {
never_reset_pin_number(self->pin_pair[i]);
never_reset_pin_number(self->pin_pair[i] + 1);
}
for (size_t i = 0; i < 3; i++) {
rp2pio_statemachine_never_reset(pio_instances[pio_index], free_state_machines[i]);
}
// For the output.
user_irq_claim(DMA_IRQ_1);
self->framebuffer_len = framebuffer_size;
self->framebuffer = self->allocation->ptr;
self->color_depth = color_depth;
self->dvi.timing = timing;
self->dvi.ser_cfg.pio = pio_instances[pio_index];
self->dvi.ser_cfg.sm_tmds[0] = free_state_machines[0];
self->dvi.ser_cfg.sm_tmds[1] = free_state_machines[1];
self->dvi.ser_cfg.sm_tmds[2] = free_state_machines[2];
self->dvi.ser_cfg.pins_clk = self->pin_pair[0];
self->dvi.ser_cfg.pins_tmds[0] = self->pin_pair[1];
self->dvi.ser_cfg.pins_tmds[1] = self->pin_pair[2];
self->dvi.ser_cfg.pins_tmds[2] = self->pin_pair[3];
self->dvi.ser_cfg.invert_diffpairs = invert_diffpairs;
self->dvi.scanline_callback = core1_scanline_callback;
vreg_set_voltage(VREG_VOLTAGE_1_20);
common_hal_time_delay_ms(10);
set_sys_clock_khz(timing->bit_clk_khz, true); // Run at TMDS bit clock
self->tmds_lock = next_striped_spin_lock_num();
self->colour_lock = next_striped_spin_lock_num();
dvi_init(&self->dvi, self->tmds_lock, self->colour_lock);
// Load up the TMDS buffers.
for (int i = 0; i < DVI_N_TMDS_BUFFERS; ++i) {
uint32_t *tmdsbuf = self->framebuffer + (self->framebuffer_len + self->tmdsbuf_size * i);
// Use the last word in the buffer to track its original root. That way
// we can detect when framebuffer is moved.
tmdsbuf[self->tmdsbuf_size - 1] = (uint32_t)self->framebuffer;
queue_add_blocking_u32(&self->dvi.q_tmds_free, &tmdsbuf);
}
active_picodvi = self;
// Core 1 will wait until it sees the first colour buffer, then start up the
// DVI signalling.
multicore_launch_core1(core1_main);
self->next_scanline = 0;
uint32_t *next_scanline_buf = self->framebuffer + (self->pitch * self->next_scanline);
queue_add_blocking_u32(&self->dvi.q_colour_valid, &next_scanline_buf);
self->next_scanline += 1;
next_scanline_buf = self->framebuffer + (self->pitch * self->next_scanline);
queue_add_blocking_u32(&self->dvi.q_colour_valid, &next_scanline_buf);
self->next_scanline += 1;
// Wait for the second core to run dvi_start because it is in flash. Once it is done,
// it'll pull from this queue. Not waiting may lead to us reading flash when this core
// doesn't want us to.
while (queue_get_level(&self->dvi.q_colour_valid) == 2) {
}
}
STATIC void _turn_off_dma(uint8_t channel) {
dma_channel_config c = dma_channel_get_default_config(channel);
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_irq1_enabled(channel, false);
dma_channel_unclaim(channel);
}
void common_hal_picodvi_framebuffer_deinit(picodvi_framebuffer_obj_t *self) {
if (common_hal_picodvi_framebuffer_deinited(self)) {
return;
}
// Stop the other core and free resources.
// Grab the locks before shutting down the other core so we don't leave the
// locks locked.
spin_lock_t *tmds_lock = spin_lock_instance(self->tmds_lock);
spin_lock_t *colour_lock = spin_lock_instance(self->colour_lock);
uint32_t tmds_save = spin_lock_blocking(tmds_lock);
uint32_t colour_save = spin_lock_blocking(colour_lock);
multicore_reset_core1();
spin_unlock(colour_lock, colour_save);
spin_unlock(tmds_lock, tmds_save);
for (size_t i = 0; i < 4; i++) {
reset_pin_number(self->pin_pair[i]);
reset_pin_number(self->pin_pair[i] + 1);
}
for (int i = 0; i < N_TMDS_LANES; ++i) {
// Turn off data first because it chains to the ctrl DMA.
_turn_off_dma(self->dvi.dma_cfg[i].chan_data);
_turn_off_dma(self->dvi.dma_cfg[i].chan_ctrl);
}
pwm_set_enabled(self->pwm_slice, false);
pwmout_free(self->pwm_slice, 0);
pwmout_free(self->pwm_slice, 1);
pio_program_t program_struct = {
.length = 2
};
PIO pio = self->dvi.ser_cfg.pio;
for (size_t i = 0; i < 3; i++) {
int sm = self->dvi.ser_cfg.sm_tmds[i];
pio_sm_set_enabled(pio, sm, false);
pio_sm_unclaim(pio, sm);
rp2pio_statemachine_reset_ok(pio, sm);
}
pio_remove_program(pio, &program_struct, self->dvi.ser_cfg.prog_offs);
if (user_irq_is_claimed(DMA_IRQ_1)) {
user_irq_unclaim(DMA_IRQ_1);
}
active_picodvi = NULL;
free_memory(self->allocation);
self->framebuffer = NULL;
self->base.type = &mp_type_NoneType;
}
bool common_hal_picodvi_framebuffer_deinited(picodvi_framebuffer_obj_t *self) {
return self->framebuffer == NULL;
}
void common_hal_picodvi_framebuffer_refresh(picodvi_framebuffer_obj_t *self) {
}
int common_hal_picodvi_framebuffer_get_width(picodvi_framebuffer_obj_t *self) {
return self->width;
}
int common_hal_picodvi_framebuffer_get_height(picodvi_framebuffer_obj_t *self) {
return self->height;
}
int common_hal_picodvi_framebuffer_get_color_depth(picodvi_framebuffer_obj_t *self) {
return self->color_depth;
}
mp_int_t common_hal_picodvi_framebuffer_get_buffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, mp_uint_t flags) {
picodvi_framebuffer_obj_t *self = (picodvi_framebuffer_obj_t *)self_in;
bufinfo->buf = self->framebuffer;
char typecode = 'B';
if (self->color_depth == 16) {
typecode = 'H';
}
bufinfo->typecode = typecode;
bufinfo->len = self->framebuffer_len * sizeof(uint32_t);
return 0;
}
int common_hal_picodvi_framebuffer_get_row_stride(picodvi_framebuffer_obj_t *self) {
// Pitch is in words but row stride is expected as bytes.
return self->pitch * sizeof(uint32_t);
}

View File

@ -0,0 +1,51 @@
#pragma once
/*
* This file is part of the Micro Python project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2023 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 "py/obj.h"
#include "supervisor/memory.h"
#include "lib/PicoDVI/software/libdvi/dvi.h"
typedef struct {
mp_obj_base_t base;
supervisor_allocation *allocation;
uint32_t *framebuffer;
size_t framebuffer_len; // in words
size_t tmdsbuf_size; // in words
struct dvi_inst dvi;
mp_uint_t width;
mp_uint_t height;
uint tmds_lock;
uint colour_lock;
uint16_t next_scanline;
uint16_t pitch; // Number of words between rows. (May be more than a width's worth.)
uint8_t color_depth;
uint8_t pwm_slice;
int8_t pin_pair[4];
} picodvi_framebuffer_obj_t;

View File

@ -218,9 +218,7 @@ bool rp2pio_statemachine_construct(rp2pio_statemachine_obj_t *self,
_current_program_len[i][j] == program_len) {
program_offset = _current_program_offset[i][j];
}
int temp_claim = pio_claim_unused_sm(pio, false);
if (temp_claim >= 0) {
pio_sm_unclaim(pio, temp_claim);
if (!pio_sm_is_claimed(pio, j)) {
free_count++;
}
}
@ -634,6 +632,16 @@ void common_hal_rp2pio_statemachine_set_frequency(rp2pio_statemachine_obj_t *sel
pio_sm_clkdiv_restart(self->pio, self->state_machine);
}
void rp2pio_statemachine_reset_ok(PIO pio, int sm) {
uint8_t pio_index = pio_get_index(pio);
_never_reset[pio_index][sm] = false;
}
void rp2pio_statemachine_never_reset(PIO pio, int sm) {
uint8_t pio_index = pio_get_index(pio);
_never_reset[pio_index][sm] = true;
}
void rp2pio_statemachine_deinit(rp2pio_statemachine_obj_t *self, bool leave_pins) {
common_hal_rp2pio_statemachine_stop(self);
(void)common_hal_rp2pio_statemachine_stop_background_write(self);
@ -654,9 +662,7 @@ void common_hal_rp2pio_statemachine_deinit(rp2pio_statemachine_obj_t *self) {
}
void common_hal_rp2pio_statemachine_never_reset(rp2pio_statemachine_obj_t *self) {
uint8_t sm = self->state_machine;
uint8_t pio_index = pio_get_index(self->pio);
_never_reset[pio_index][sm] = true;
rp2pio_statemachine_never_reset(self->pio, self->state_machine);
// TODO: never reset all the pins
}

View File

@ -95,6 +95,9 @@ uint8_t rp2pio_statemachine_program_offset(rp2pio_statemachine_obj_t *self);
void rp2pio_statemachine_deinit(rp2pio_statemachine_obj_t *self, bool leave_pins);
void rp2pio_statemachine_dma_complete(rp2pio_statemachine_obj_t *self, int channel);
void rp2pio_statemachine_reset_ok(PIO pio, int sm);
void rp2pio_statemachine_never_reset(PIO pio, int sm);
extern const mp_obj_type_t rp2pio_statemachine_type;
#endif // MICROPY_INCLUDED_RASPBERRYPI_COMMON_HAL_RP2PIO_STATEMACHINE_H

@ -0,0 +1 @@
Subproject commit 23a3a3bf18820f2abd78e8a9c05b45c01b5a3810

View File

@ -21,13 +21,16 @@
__stack (== StackTop)
*/
firmware_size = DEFINED(firmware_size) ? firmware_size : 1020K ;
MEMORY
{
FLASH_FIRMWARE (rx) : ORIGIN = 0x10000000, LENGTH = 1020k
FLASH_FIRMWARE (rx) : ORIGIN = 0x10000000, LENGTH = firmware_size
/* Followed by: 4kB of NVRAM and at least 1024kB of CIRCUITPY */
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 256k
SCRATCH_X (rwx) : ORIGIN = 0x20040000, LENGTH = 4k
SCRATCH_Y (rwx) : ORIGIN = 0x20041000, LENGTH = 4k
SCRATCH_Y (rwx) : ORIGIN = 0x20040000, LENGTH = 4k
/* X is used by core 1 so we put it last. */
SCRATCH_X (rwx) : ORIGIN = 0x20041000, LENGTH = 4k
}
ENTRY(_entry_point)
@ -77,7 +80,7 @@ SECTIONS
*(.property_getset)
__property_getset_end = .;
*(EXCLUDE_FILE(*libgcc.a: *libc.a:*lib_a-mem*.o *libm.a:) .text*)
*(EXCLUDE_FILE(*libgcc.a: *libc.a:*lib_a-mem*.o *libm.a: *interp.o *divider.o) .text*)
*(.fini)
/* Pull all c'tors into .text */
*crtbegin.o(.ctors)
@ -137,7 +140,7 @@ SECTIONS
__data_start__ = .;
*(vtable)
*(.time_critical*)
*(EXCLUDE_FILE(*tmds_encode.o) .time_critical*)
/* remaining .text and .rodata; i.e. stuff we exclude above because we want it in RAM */
*(.text*)
@ -222,23 +225,6 @@ SECTIONS
*(.uninitialized_data*)
} > RAM
/* Start and end symbols must be word-aligned */
.scratch_x : {
__scratch_x_start__ = .;
*(.scratch_x.*)
. = ALIGN(4);
__scratch_x_end__ = .;
} > SCRATCH_X AT > FLASH_FIRMWARE
__scratch_x_source__ = LOADADDR(.scratch_x);
.scratch_y : {
__scratch_y_start__ = .;
*(.scratch_y.*)
. = ALIGN(4);
__scratch_y_end__ = .;
} > SCRATCH_Y AT > FLASH_FIRMWARE
__scratch_y_source__ = LOADADDR(.scratch_y);
.bss : {
. = ALIGN(4);
__bss_start__ = .;
@ -252,10 +238,30 @@ SECTIONS
{
__end__ = .;
end = __end__;
_ld_cp_dynamic_mem_start = .;
*(.heap*)
__HeapLimit = .;
} > RAM
/* Start and end symbols must be word-aligned */
.scratch_x : {
__scratch_x_start__ = .;
*(.scratch_x.*)
*tmds_encode.o (.time_critical*)
. = ALIGN(4);
__scratch_x_end__ = .;
} > SCRATCH_X AT > FLASH_FIRMWARE
__scratch_x_source__ = LOADADDR(.scratch_x);
.scratch_y : {
__scratch_y_start__ = .;
/* Don't put anything into scratch y because CircuitPython manages it and uses it for core 0 stack.
/* *(.scratch_y.*) */
. = ALIGN(4);
__scratch_y_end__ = .;
} > SCRATCH_Y AT > FLASH_FIRMWARE
__scratch_y_source__ = LOADADDR(.scratch_y);
/* .stack*_dummy section doesn't contains any symbols. It is only
* used for linker to calculate size of stack sections, and assign
* values to stack symbols later
@ -269,6 +275,7 @@ SECTIONS
{
*(.stack1*)
} > SCRATCH_X
.stack_dummy (COPY):
{
*(.stack*)
@ -282,6 +289,7 @@ SECTIONS
__StackLimit = ORIGIN(RAM) + LENGTH(RAM);
__StackOneTop = ORIGIN(SCRATCH_X) + LENGTH(SCRATCH_X);
__StackTop = ORIGIN(SCRATCH_Y) + LENGTH(SCRATCH_Y);
_ld_cp_dynamic_mem_end = __StackTop;
__StackOneBottom = __StackOneTop - SIZEOF(.stack1_dummy);
__StackBottom = __StackTop - SIZEOF(.stack_dummy);
PROVIDE(__stack = __StackTop);

View File

@ -39,7 +39,7 @@ CIRCUITPY_AUDIOBUSIO ?= 1
CIRCUITPY_AUDIOCORE ?= 1
CIRCUITPY_AUDIOPWMIO ?= 1
CIRCUITPY_AUDIOMIXER = 1
CIRCUITPY_AUDIOMIXER ?= 1
INTERNAL_LIBM = 1

View File

@ -3,6 +3,7 @@
#include "pico-sdk-configboard.h"
// alphabetized
#define LIB_CMSIS_CORE (1)
#define LIB_PICO_BINARY_INFO (0)
#define LIB_PICO_PRINTF_NONE (0)
#define LIB_PICO_PRINTF_PICO (0)
@ -23,4 +24,9 @@
#define PICO_STDIO_IGNORE_NESTED_STDOUT (0)
#define PICO_USE_CRT_PRINTF (0)
#define PICO_USE_OPTIMISTIC_SBRK (0)
// Stack guards cause a hard fault when 32 bytes around the stack bottom are
// accessed. These backtraces aren't always helpful and this conflicts with our
// own stack checking.
#define PICO_USE_STACK_GUARDS (0)
#include "include/cmsis/rename_exceptions.h"

View File

@ -228,14 +228,14 @@ bool port_has_fixed_stack(void) {
}
// From the linker script
extern uint32_t __HeapLimit;
extern uint32_t __StackTop;
extern uint32_t _ld_cp_dynamic_mem_start;
extern uint32_t _ld_cp_dynamic_mem_end;
uint32_t *port_stack_get_limit(void) {
return &__HeapLimit;
return &_ld_cp_dynamic_mem_start;
}
uint32_t *port_stack_get_top(void) {
return &__StackTop;
return &_ld_cp_dynamic_mem_end;
}
uint32_t *port_heap_get_bottom(void) {
@ -246,13 +246,14 @@ uint32_t *port_heap_get_top(void) {
return port_stack_get_top();
}
extern uint32_t __scratch_x_start__;
void port_set_saved_word(uint32_t value) {
__scratch_x_start__ = value;
// Store in a watchdog scratch register instead of RAM. 4-7 are used by the
// sdk. 0 is used by alarm. 1-3 are free.
watchdog_hw->scratch[1] = value;
}
uint32_t port_get_saved_word(void) {
return __scratch_x_start__;
return watchdog_hw->scratch[1];
}
static volatile bool ticks_enabled;
@ -305,14 +306,12 @@ void port_idle_until_interrupt(void) {
* \brief Default interrupt handler for unused IRQs.
*/
extern void HardFault_Handler(void); // provide a prototype to avoid a missing-prototypes diagnostic
__attribute__((used)) void HardFault_Handler(void) {
#ifdef ENABLE_MICRO_TRACE_BUFFER
// Turn off the micro trace buffer so we don't fill it up in the infinite
// loop below.
REG_MTB_MASTER = 0x00000000 + 6;
#endif
reset_into_safe_mode(SAFE_MODE_HARD_FAULT);
__attribute__((used)) void __not_in_flash_func(HardFault_Handler)(void) {
// Only safe mode from core 0 which is running CircuitPython. Core 1 faulting
// should not be fatal to CP. (Fingers crossed.)
if (get_core_num() == 0) {
reset_into_safe_mode(SAFE_MODE_HARD_FAULT);
}
while (true) {
asm ("nop;");
}

View File

@ -278,14 +278,8 @@ endif
ifeq ($(CIRCUITPY_PIXELMAP),1)
SRC_PATTERNS += _pixelmap/%
endif
ifeq ($(CIRCUITPY_QRIO),1)
SRC_PATTERNS += qrio/%
endif
ifeq ($(CIRCUITPY_RAINBOWIO),1)
SRC_PATTERNS += rainbowio/%
endif
ifeq ($(CIRCUITPY_RGBMATRIX),1)
SRC_PATTERNS += rgbmatrix/%
ifeq ($(CIRCUITPY_PICODVI),1)
SRC_PATTERNS += picodvi/%
endif
ifeq ($(CIRCUITPY_PS2IO),1)
SRC_PATTERNS += ps2io/%
@ -296,9 +290,18 @@ endif
ifeq ($(CIRCUITPY_PWMIO),1)
SRC_PATTERNS += pwmio/%
endif
ifeq ($(CIRCUITPY_QRIO),1)
SRC_PATTERNS += qrio/%
endif
ifeq ($(CIRCUITPY_RAINBOWIO),1)
SRC_PATTERNS += rainbowio/%
endif
ifeq ($(CIRCUITPY_RANDOM),1)
SRC_PATTERNS += random/%
endif
ifeq ($(CIRCUITPY_RGBMATRIX),1)
SRC_PATTERNS += rgbmatrix/%
endif
ifeq ($(CIRCUITPY_RP2PIO),1)
SRC_PATTERNS += rp2pio/%
endif

View File

@ -339,6 +339,11 @@ CFLAGS += -DCIRCUITPY_OS=$(CIRCUITPY_OS)
CIRCUITPY_PEW ?= 0
CFLAGS += -DCIRCUITPY_PEW=$(CIRCUITPY_PEW)
# CIRCUITPY_PICODVI is handled in the raspberrypi tree.
# Only for RP2 chips. Assume not a raspberrypi build.
CIRCUITPY_PICODVI ?= 0
CFLAGS += -DCIRCUITPY_PICODVI=$(CIRCUITPY_PICODVI)
CIRCUITPY_PIXELBUF ?= $(CIRCUITPY_FULL_BUILD)
CFLAGS += -DCIRCUITPY_PIXELBUF=$(CIRCUITPY_PIXELBUF)

View File

@ -151,15 +151,27 @@ STATIC mp_obj_t bitmap_subscr(mp_obj_t self_in, mp_obj_t index_obj, mp_obj_t val
uint16_t x = 0;
uint16_t y = 0;
if (mp_obj_is_small_int(index_obj)) {
mp_int_t i = mp_arg_validate_int_min(MP_OBJ_SMALL_INT_VALUE(index_obj), 0, MP_QSTR_index);
uint16_t width = common_hal_displayio_bitmap_get_width(self);
x = i % width;
y = i / width;
mp_int_t i = MP_OBJ_SMALL_INT_VALUE(index_obj);
int total_length = self->width * self->height;
if (i < 0 || i >= total_length) {
mp_raise_IndexError_varg(translate("%q must be %d-%d"), MP_QSTR_index, 0, total_length - 1);
}
x = i % self->width;
y = i / self->width;
} else {
mp_obj_t *items;
mp_obj_get_array_fixed_n(index_obj, 2, &items);
x = mp_arg_validate_int_range(mp_obj_get_int(items[0]), 0, self->width - 1, MP_QSTR_x);
y = mp_arg_validate_int_range(mp_obj_get_int(items[1]), 0, self->height - 1, MP_QSTR_y);
mp_int_t x_in = mp_obj_get_int(items[0]);
if (x_in < 0 || x_in >= self->width) {
mp_raise_IndexError_varg(translate("%q must be %d-%d"), MP_QSTR_x, 0, self->width - 1);
}
mp_int_t y_in = mp_obj_get_int(items[1]);
if (y_in < 0 || y_in >= self->height) {
mp_raise_IndexError_varg(translate("%q must be %d-%d"), MP_QSTR_y, 0, self->height - 1);
}
x = x_in;
y = y_in;
}
if (value_obj == MP_OBJ_SENTINEL) {

View File

@ -55,6 +55,13 @@ uint16_t displayio_colorconverter_compute_rgb565(uint32_t color_rgb888) {
return r5 << 11 | g6 << 5 | b5;
}
uint8_t displayio_colorconverter_compute_rgb332(uint32_t color_rgb888) {
uint32_t r3 = (color_rgb888 >> 21);
uint32_t g3 = (color_rgb888 >> 13) & 0x7;
uint32_t b2 = (color_rgb888 >> 6) & 0x3;
return r3 << 5 | g3 << 2 | b2;
}
uint8_t displayio_colorconverter_compute_rgbd(uint32_t color_rgb888) {
uint32_t r1 = (color_rgb888 >> 23) & 0x1;
uint32_t g1 = (color_rgb888 >> 15) & 0x1;
@ -309,6 +316,11 @@ void displayio_convert_color(const _displayio_colorspace_t *colorspace, bool dit
output_color->pixel = pixel;
output_color->opaque = true;
return;
} else if (colorspace->depth == 8 && !colorspace->grayscale) {
uint8_t packed = displayio_colorconverter_compute_rgb332(pixel);
output_color->pixel = packed;
output_color->opaque = true;
return;
} else if (colorspace->depth == 4) {
uint8_t packed;
if (colorspace->sevencolor) {

View File

@ -57,6 +57,7 @@ uint32_t displayio_colorconverter_dither_noise_2(uint32_t x, uint32_t y);
void displayio_convert_color(const _displayio_colorspace_t *colorspace, bool dither, const displayio_input_pixel_t *input_pixel, displayio_output_pixel_t *output_color);
uint16_t displayio_colorconverter_compute_rgb565(uint32_t color_rgb888);
uint8_t displayio_colorconverter_compute_rgb332(uint32_t color_rgb888);
uint8_t displayio_colorconverter_compute_rgbd(uint32_t color_rgb888);
uint8_t displayio_colorconverter_compute_luma(uint32_t color_rgb888);
uint8_t displayio_colorconverter_compute_chroma(uint32_t color_rgb888);

View File

@ -152,6 +152,10 @@ void common_hal_displayio_release_displays(void) {
} else if (displays[i].bus_base.type == &videocore_framebuffer_type) {
common_hal_videocore_framebuffer_deinit(&displays[i].videocore);
#endif
#if CIRCUITPY_PICODVI
} else if (displays[i].bus_base.type == &picodvi_framebuffer_type) {
common_hal_picodvi_framebuffer_deinit(&displays[i].picodvi);
#endif
}
displays[i].fourwire_bus.base.type = &mp_type_NoneType;
}
@ -266,6 +270,13 @@ void reset_displays(void) {
// The framebuffer is allocated outside of the heap so it doesn't
// need to be moved.
#endif
#if CIRCUITPY_PICODVI
} else if (displays[i].bus_base.type == &picodvi_framebuffer_type) {
picodvi_framebuffer_obj_t *vc = &displays[i].picodvi;
if (!any_display_uses_this_framebuffer(&vc->base)) {
common_hal_picodvi_framebuffer_deinit(vc);
}
#endif
} else {
// Not an active display bus.
continue;

View File

@ -47,9 +47,13 @@
#if CIRCUITPY_SHARPDISPLAY
#include "shared-module/sharpdisplay/SharpMemoryFramebuffer.h"
#endif
// Port unique frame buffers.
#if CIRCUITPY_VIDEOCORE
#include "bindings/videocore/Framebuffer.h"
#endif
#if CIRCUITPY_PICODVI
#include "bindings/picodvi/Framebuffer.h"
#endif
typedef struct {
union {
@ -71,6 +75,9 @@ typedef struct {
#if CIRCUITPY_VIDEOCORE
videocore_framebuffer_obj_t videocore;
#endif
#if CIRCUITPY_PICODVI
picodvi_framebuffer_obj_t picodvi;
#endif
};
union {
mp_obj_base_t display_base;

View File

@ -103,37 +103,43 @@ void supervisor_start_terminal(uint16_t width_px, uint16_t height_px) {
if (reset_tiles) {
uint8_t *tiles = (uint8_t *)tilegrid_tiles->ptr;
// Adjust the display dimensions to account for scale of the outer group.
width_px /= scale;
height_px /= scale;
// Number of tiles from the left edge to inset the status bar.
size_t min_left_padding = 0;
#if CIRCUITPY_REPL_LOGO
status_bar->x = supervisor_blinka_sprite.pixel_width + 1;
// Blinka + 1 px padding minimum
min_left_padding = supervisor_blinka_sprite.pixel_width + 1;
// Align the status bar to the bottom of the logo.
status_bar->y = supervisor_blinka_sprite.pixel_height - status_bar->tile_height;
#else
status_bar->x = 0;
status_bar->y = 0;
#endif
status_bar->top_left_y = 0;
status_bar->width_in_tiles = width_in_tiles;
status_bar->width_in_tiles = (width_px - min_left_padding) / status_bar->tile_width;
status_bar->height_in_tiles = 1;
status_bar->pixel_width = width_in_tiles * status_bar->tile_width;
status_bar->pixel_width = status_bar->width_in_tiles * status_bar->tile_width;
status_bar->pixel_height = status_bar->tile_height;
// Right align the status bar.
status_bar->x = width_px - status_bar->pixel_width;
status_bar->top_left_y = 0;
status_bar->tiles = tiles;
status_bar->full_change = true;
scroll_area->x = 0;
scroll_area->top_left_y = 0;
scroll_area->width_in_tiles = width_in_tiles;
scroll_area->height_in_tiles = height_in_tiles - 1;
scroll_area->pixel_width = width_in_tiles * scroll_area->tile_width;
scroll_area->pixel_height = (height_in_tiles - 1) * scroll_area->tile_height;
scroll_area->height_in_tiles = height_in_tiles;
#if CIRCUITPY_REPL_LOGO
scroll_area->y = blinka_bitmap.height;
#else
scroll_area->y = status_bar->tile_height;
scroll_area->height_in_tiles -= 1;
#endif
int16_t extra_height = (scroll_area->pixel_height + scroll_area->y) - (height_px / scale);
// Subtract extra height so that the bottom line fully shows. The top line will be under the
// title bar and Blinka logo.
scroll_area->y -= extra_height;
scroll_area->pixel_width = scroll_area->width_in_tiles * scroll_area->tile_width;
scroll_area->pixel_height = scroll_area->height_in_tiles * scroll_area->tile_height;
// Right align the scroll area to give margin to the start of each line.
scroll_area->x = width_px - scroll_area->pixel_width;
scroll_area->top_left_y = 0;
// Align the scroll area to the bottom so that the newest line isn't cutoff. The top line
// may be clipped by the status bar and that's ok.
scroll_area->y = height_px - scroll_area->pixel_height;
scroll_area->tiles = tiles + width_in_tiles;
scroll_area->full_change = true;

View File

@ -77,6 +77,8 @@ enum {
// Maximum needs of one display: max(4 if RGBMATRIX, 1 if SHARPDISPLAY, 0)
#if CIRCUITPY_RGBMATRIX
4
#elif CIRCUITPY_PICODVI
2
#elif CIRCUITPY_SHARPDISPLAY
1
#else

View File

@ -123,7 +123,7 @@ void usb_set_defaults(void) {
};
#if CIRCUITPY_USB_IDENTIFICATION
supervisor_allocation *usb_identification_allocation;
supervisor_allocation *usb_identification_allocation = NULL;
#endif
// Some dynamic USB data must be saved after boot.py. How much is needed?

View File

@ -87,7 +87,11 @@ def main():
submodules = ["tools/"] # for huffman
elif TARGET == "windows":
# This builds one board from a number of ports so fill out a bunch of submodules
submodules = ["extmod/ulab", "lib/", "tools/", "ports/", "data/nvm.toml"]
for port in ("atmel-samd", "nrf", "raspberrypi", "stm"):
submodules.append(f"ports/{port}")
submodules.extend(PORT_DEPS[port])
unique_submodules = set(submodules)
submodules = list(unique_submodules)
elif TARGET == "website":
submodules = ["tools/adabot/"]
submodules_tags = ["frozen/"]

View File

@ -17,7 +17,7 @@ MMFAR = SCB + 0x034 # (R/W) MemManage Fault Address Register */
BFAR = SCB + 0x038 # (R/W) BusFault Address Register */
AFSR = SCB + 0x03C # (R/W) Auxiliary Fault Status Register */
PARTS = {0xC27: "Cortex M7"}
PARTS = {0xC27: "Cortex M7", 0xC60: "Cortex M0+"}
EXCEPTIONS = {
0: "Thread mode",
@ -40,18 +40,35 @@ class CortexMFault(gdb.Command):
i = gdb.selected_inferior()
return i.read_memory(address, 4).cast("I")[0]
def invoke(self, arg, from_tty):
cpuid = self._read(CPUID)
implementer = cpuid >> 24
if implementer != 0x41:
raise RuntimeError()
variant = (cpuid >> 20) & 0xF
constant = (cpuid >> 16) & 0xF
if constant != 0xF:
raise RuntimeError()
revision = cpuid & 0xF
part_no = (cpuid >> 4) & 0xFFF
print(PARTS[part_no])
def _armv6m_fault(self):
vtor = self._read(VTOR)
print("vtor", hex(vtor))
icsr = self._read(ICSR)
if (icsr & (1 << 23)) != 0:
print("No preempted exceptions")
else:
print("Another exception was preempted")
vectactive = icsr & 0x1FF
print(hex(icsr), vectactive)
if vectactive != 0:
if vectactive in EXCEPTIONS:
vectactive = EXCEPTIONS[vectactive]
else:
vectactive -= 16
print("Active interrupt:", vectactive)
vectpending = (icsr >> 12) & 0x1FF
if vectpending != 0:
if vectpending in EXCEPTIONS:
vectpending = EXCEPTIONS[vectpending]
else:
vectpending -= 16
print("Pending interrupt:", vectpending)
def _armv7m_fault(self):
icsr = self._read(ICSR)
if (icsr & (1 << 11)) != 0:
print("No preempted exceptions")
@ -65,6 +82,7 @@ class CortexMFault(gdb.Command):
print(vectactive - 16)
vtor = self._read(VTOR)
print("vtor", hex(vtor))
# print(hex(self._read(SHCSR)))
cfsr = self._read(CFSR)
ufsr = cfsr >> 16
@ -102,5 +120,23 @@ class CortexMFault(gdb.Command):
print("Bus fault when reading vector table")
print("VTOR", hex(vtor))
def invoke(self, arg, from_tty):
cpuid = self._read(CPUID)
implementer = cpuid >> 24
if implementer != 0x41:
raise RuntimeError()
variant = (cpuid >> 20) & 0xF
architecture = (cpuid >> 16) & 0xF
revision = cpuid & 0xF
part_no = (cpuid >> 4) & 0xFFF
print(PARTS[part_no])
if architecture == 0xF:
self._armv7m_fault()
elif architecture == 0xC:
self._armv6m_fault()
else:
raise RuntimeError(f"Unknown architecture {architecture:x}")
CortexMFault()