Add PicoDVI support

PicoDVI in CP support 640x480 and 800x480 on Feather DVI, Pico and
Pico W. 1 and 2 bit grayscale are full resolution. 8 and 16 bit
color are half resolution.

Memory layout is modified to give the top most 4k of ram to the
second core. Its MPU is used to prevent flash access after startup.

The port saved word is moved to a watchdog scratch register so that
it doesn't get overwritten by other things in RAM.

Right align status bar and scroll area. This normally gives a few
pixels of padding on the left hand side and improves the odds it is
readable in a case. Fixes #7562

Fixes c stack checking. The length was correct but the top was being
set to the current stack pointer instead of the correct top.
Fixes #7643

This makes Bitmap subscr raise IndexError instead of ValueError
when the index arguments are wrong.
This commit is contained in:
Scott Shawcroft 2023-04-07 11:59:55 -07:00
parent 09c2c5ef0e
commit 66edcf5d03
No known key found for this signature in database
GPG Key ID: 0DFD512649C052DA
35 changed files with 1134 additions and 409 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

@ -98,6 +98,9 @@ msgstr ""
#: ports/raspberrypi/common-hal/analogio/AnalogOut.c
#: ports/raspberrypi/common-hal/rtc/RTC.c ports/stm/common-hal/alarm/__init__.c
#: ports/stm/common-hal/canio/Listener.c ports/stm/common-hal/rtc/RTC.c
#: shared-bindings/audiobusio/I2SOut.c shared-bindings/audiobusio/PDMIn.c
#: shared-bindings/keypad/KeyMatrix.c shared-bindings/keypad/Keys.c
#: shared-bindings/keypad/ShiftRegisterKeys.c
msgid "%q"
msgstr ""
@ -123,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
@ -161,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 ""
@ -193,7 +197,7 @@ msgstr ""
msgid "%q must be array of type 'H'"
msgstr ""
#: shared-bindings/synthio/MidiTrack.c shared-bindings/synthio/__init__.c
#: shared-module/synthio/__init__.c
msgid "%q must be array of type 'h'"
msgstr ""
@ -465,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 ""
@ -473,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 ""
@ -1117,10 +1123,6 @@ msgstr ""
msgid "I2C peripheral in use"
msgstr ""
#: shared-bindings/audiobusio/I2SOut.c
msgid "I2SOut not available"
msgstr ""
#: ports/raspberrypi/bindings/rp2pio/StateMachine.c
msgid "In-buffer elements must be <= 4 bytes long"
msgstr ""
@ -1223,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"
@ -1708,10 +1712,6 @@ msgstr ""
msgid "Oversample must be multiple of 8."
msgstr ""
#: shared-bindings/audiobusio/PDMIn.c
msgid "PDMIn not available"
msgstr ""
#: shared-bindings/pwmio/PWMOut.c
msgid ""
"PWM frequency not writable when variable_frequency is False on construction."

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,265 @@
/*
* 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

@ -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()