Skip to content

Commit

Permalink
Improve Fruit Jam
Browse files Browse the repository at this point in the history
* Add bus, port_numbers and speed to USB devices. Allows for
  differentiating identical devices.
* Autodetect display and adjust if widescreen
* Enable I2S DAC via its reset pin
* Show keyboard emoji in status bar. (Only works over serial connection
  now. Built in font doesn't have the emoji.)
  • Loading branch information
tannewt committed Mar 5, 2025
1 parent e8de36a commit b12b3e0
Show file tree
Hide file tree
Showing 23 changed files with 319 additions and 35 deletions.
3 changes: 2 additions & 1 deletion .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,8 @@
url = https://github.com/adafruit/Adafruit_CircuitPython_Wave.git
[submodule "ports/raspberrypi/lib/Pico-PIO-USB"]
path = ports/raspberrypi/lib/Pico-PIO-USB
url = https://github.com/adafruit/Pico-PIO-USB.git
url = https://github.com/tannewt/Pico-PIO-USB.git
branch = better_timeouts
[submodule "lib/micropython-lib"]
path = lib/micropython-lib
url = https://github.com/micropython/micropython-lib.git
Expand Down
2 changes: 1 addition & 1 deletion lib/tinyusb
Submodule tinyusb updated 105 files
18 changes: 15 additions & 3 deletions ports/raspberrypi/boards/adafruit_fruit_jam/board.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
#include "shared-bindings/usb_host/Port.h"
#include "supervisor/board.h"

#include "common-hal/picodvi/__init__.h"

// Use the MP_WEAK supervisor/shared/board.c versions of routines not defined here.


Expand All @@ -25,12 +27,22 @@ bool board_reset_pin_number(uint8_t pin_number) {

return true;
}
// Set I2S out of reset.
if (pin_number == 22) {
gpio_put(pin_number, 1);
gpio_set_dir(pin_number, GPIO_OUT);
gpio_set_function(pin_number, GPIO_FUNC_SIO);

return true;
}
return false;
}
#endif

#if defined(DEFAULT_USB_HOST_DATA_PLUS)
void board_init(void) {
common_hal_usb_host_port_construct(DEFAULT_USB_HOST_DATA_PLUS, DEFAULT_USB_HOST_DATA_MINUS);
#if defined(DEFAULT_USB_HOST_DATA_PLUS)
common_hal_usb_host_port_construct(DEFAULT_USB_HOST_DATA_PLUS, DEFAULT_USB_HOST_DATA_MINUS);
#endif

picodvi_autoconstruct();
}
#endif
14 changes: 14 additions & 0 deletions ports/raspberrypi/boards/adafruit_fruit_jam/mpconfigboard.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,18 @@
#define DEFAULT_USB_HOST_DATA_MINUS (&pin_GPIO2)
#define DEFAULT_USB_HOST_5V_POWER (&pin_GPIO11)

#define DEFAULT_DVI_BUS_CLK_DN (&pin_GPIO12)
#define DEFAULT_DVI_BUS_CLK_DP (&pin_GPIO13)
#define DEFAULT_DVI_BUS_RED_DN (&pin_GPIO14)
#define DEFAULT_DVI_BUS_RED_DP (&pin_GPIO15)
#define DEFAULT_DVI_BUS_GREEN_DN (&pin_GPIO16)
#define DEFAULT_DVI_BUS_GREEN_DP (&pin_GPIO17)
#define DEFAULT_DVI_BUS_BLUE_DN (&pin_GPIO18)
#define DEFAULT_DVI_BUS_BLUE_DP (&pin_GPIO19)

#define CIRCUITPY_PSRAM_CHIP_SELECT (&pin_GPIO47)

// #define CIRCUITPY_CONSOLE_UART_TX (&pin_GPIO44)
// #define CIRCUITPY_CONSOLE_UART_RX (&pin_GPIO45)

// #define CIRCUITPY_DEBUG_TINYUSB 0
3 changes: 3 additions & 0 deletions ports/raspberrypi/boards/adafruit_fruit_jam/mpconfigboard.mk
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,6 @@ CHIP_PACKAGE = B
CHIP_FAMILY = rp2

EXTERNAL_FLASH_DEVICES = "W25Q128JVxQ"

# CIRCUITPY_DISPLAY_FONT = $(TOP)/tools/fonts/unifont-16.0.02-all.bdf
# CIRCUITPY_FONT_EXTRA_CHARACTERS = "🖮🖱️"
2 changes: 0 additions & 2 deletions ports/raspberrypi/boards/adafruit_fruit_jam/pins.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@ static const mp_rom_map_elem_t board_module_globals_table[] = {
{ MP_OBJ_NEW_QSTR(MP_QSTR_A4), MP_ROM_PTR(&pin_GPIO44) },
{ MP_OBJ_NEW_QSTR(MP_QSTR_A5), MP_ROM_PTR(&pin_GPIO45) },

// On-board switch reverses D0 and D1 connections to RX and TX.

{ MP_OBJ_NEW_QSTR(MP_QSTR_D6), MP_ROM_PTR(&pin_GPIO6) },
{ MP_OBJ_NEW_QSTR(MP_QSTR_D7), MP_ROM_PTR(&pin_GPIO7) },
{ MP_OBJ_NEW_QSTR(MP_QSTR_D8), MP_ROM_PTR(&pin_GPIO8) },
Expand Down
5 changes: 5 additions & 0 deletions ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2350.c
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,11 @@ void common_hal_picodvi_framebuffer_construct(picodvi_framebuffer_obj_t *self,
// streaming support.
self->framebuffer = (uint32_t *)port_malloc(framebuffer_size * sizeof(uint32_t), true);
if (self->framebuffer == NULL || ((size_t)self->framebuffer & 0xf0000000) == 0x10000000) {
if (self->framebuffer != NULL) {
// Return the memory in PSRAM.
port_free(self->framebuffer);
self->framebuffer = NULL;
}
m_malloc_fail(framebuffer_size * sizeof(uint32_t));
return;
}
Expand Down
57 changes: 52 additions & 5 deletions ports/raspberrypi/common-hal/picodvi/__init__.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
#include "supervisor/port_heap.h"

#if defined(DEFAULT_DVI_BUS_CLK_DP)
static bool picodvi_autoconstruct_enabled(void) {
static bool picodvi_autoconstruct_enabled(mp_int_t *default_width, mp_int_t *default_height) {
char buf[sizeof("detect")];
buf[0] = 0;

Expand All @@ -42,6 +42,48 @@ static bool picodvi_autoconstruct_enabled(void) {
return false;
}
bool probed = common_hal_busio_i2c_probe(i2c, 0x50);
if (probed) {
uint8_t edid[128];
uint8_t out[1] = {0};
common_hal_busio_i2c_write_read(i2c, 0x50, out, 1, edid, sizeof(edid));
bool edid_ok = true;
if (edid[0] != 0x00 || edid[1] != 0xFF || edid[2] != 0xFF || edid[3] != 0xFF || edid[4] != 0xFF || edid[5] != 0xFF || edid[6] != 0xFF || edid[7] != 0x00) {
edid_ok = false;
}
uint8_t checksum = 0;
for (size_t i = 0; i < sizeof(edid); i++) {
checksum += edid[i];
}
if (checksum != 0) {
edid_ok = false;
}

if (edid_ok) {
uint8_t established_timings = edid[35];
if ((established_timings & 0xa0) == 0) {
// Check that 720x400@70Hz or 640x480@60Hz is supported. If not
// and we read EDID ok, then don't autostart.
probed = false;
} else {
size_t offset = 54;
uint16_t preferred_pixel_clock = edid[offset] | (edid[offset + 1] << 8);
if (preferred_pixel_clock != 0) {
size_t preferred_width = ((edid[offset + 4] & 0xf0) << 4) | edid[offset + 2];
size_t preferred_height = ((edid[offset + 7] & 0xf0) << 4) | edid[offset + 5];
// Use 720x400 on 1080p, 4k and 8k displays.
if ((established_timings & 0x80) != 0 &&
preferred_width % 1920 == 0 &&
preferred_height % 1080 == 0) {
*default_width = 720 / 2;
*default_height = 400 / 2;
} else {
*default_width = 640 / 2;
*default_height = 480 / 2;
}
}
}
}
}
common_hal_busio_i2c_unlock(i2c);
return probed;
}
Expand All @@ -53,11 +95,13 @@ void picodvi_autoconstruct(void) {
return;
}

if (!picodvi_autoconstruct_enabled()) {
mp_int_t default_width = 320;
mp_int_t default_height = 240;
if (!picodvi_autoconstruct_enabled(&default_width, &default_height)) {
return;
}

mp_int_t width = 320;
mp_int_t width = default_width;
mp_int_t height = 0;
mp_int_t color_depth = 16;
mp_int_t rotation = 0;
Expand All @@ -75,6 +119,9 @@ void picodvi_autoconstruct(void) {
case 320:
height = 240;
break;
case 360:
height = 200;
break;
}
}

Expand All @@ -85,8 +132,8 @@ void picodvi_autoconstruct(void) {

if (!common_hal_picodvi_framebuffer_preflight(width, height, color_depth)) {
// invalid configuration, set back to default
width = 320;
height = 240;
width = default_width;
height = default_height;
color_depth = 16;
}

Expand Down
1 change: 1 addition & 0 deletions py/circuitpy_defns.mk
Original file line number Diff line number Diff line change
Expand Up @@ -739,6 +739,7 @@ SRC_SHARED_MODULE_ALL = \
usb/__init__.c \
usb/core/__init__.c \
usb/core/Device.c \
usb/util/__init__.c \
ustack/__init__.c \
vectorio/Circle.c \
vectorio/Polygon.c \
Expand Down
2 changes: 2 additions & 0 deletions shared-bindings/usb/__init__.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

#include "shared-bindings/usb/__init__.h"
#include "shared-bindings/usb/core/__init__.h"
#include "shared-bindings/usb/util/__init__.h"
#include "supervisor/usb.h"

//| """PyUSB-compatible USB host API
Expand All @@ -21,6 +22,7 @@
static mp_rom_map_elem_t usb_module_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_usb) },
{ MP_ROM_QSTR(MP_QSTR_core), MP_OBJ_FROM_PTR(&usb_core_module) },
{ MP_ROM_QSTR(MP_QSTR_util), MP_OBJ_FROM_PTR(&usb_util_module) },
};

static MP_DEFINE_CONST_DICT(usb_module_globals, usb_module_globals_table);
Expand Down
53 changes: 53 additions & 0 deletions shared-bindings/usb/core/Device.c
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,16 @@
//| ...
//|

//| def __del__(self) -> None:
//| """Closes any resources used for this device."""
//| ...
static mp_obj_t usb_core_device_deinit(mp_obj_t self_in) {
usb_core_device_obj_t *self = MP_OBJ_TO_PTR(self_in);
common_hal_usb_core_device_deinit(self);
return mp_const_none;
}
static MP_DEFINE_CONST_FUN_OBJ_1(usb_core_device_deinit_obj, usb_core_device_deinit);

//| idVendor: int
//| """The USB vendor ID of the device"""
static mp_obj_t usb_core_device_obj_get_idVendor(mp_obj_t self_in) {
Expand Down Expand Up @@ -105,6 +115,44 @@ MP_DEFINE_CONST_FUN_OBJ_1(usb_core_device_get_manufacturer_obj, usb_core_device_
MP_PROPERTY_GETTER(usb_core_device_manufacturer_obj,
(mp_obj_t)&usb_core_device_get_manufacturer_obj);

//| bus: int
//| """The bus number of the root hub this device is connected to."""
//|
static mp_obj_t usb_core_device_obj_get_bus(mp_obj_t self_in) {
usb_core_device_obj_t *self = MP_OBJ_TO_PTR(self_in);
return MP_OBJ_NEW_SMALL_INT(common_hal_usb_core_device_get_bus(self));
}
MP_DEFINE_CONST_FUN_OBJ_1(usb_core_device_get_bus_obj, usb_core_device_obj_get_bus);

MP_PROPERTY_GETTER(usb_core_device_bus_obj,
(mp_obj_t)&usb_core_device_get_bus_obj);

//| port_numbers: tuple[int] | None
//| """The port topology of the devices location. None when connected to the
//| root port (aka bus)."""
//|
static mp_obj_t usb_core_device_obj_get_port_numbers(mp_obj_t self_in) {
usb_core_device_obj_t *self = MP_OBJ_TO_PTR(self_in);
return common_hal_usb_core_device_get_port_numbers(self);
}
MP_DEFINE_CONST_FUN_OBJ_1(usb_core_device_get_port_numbers_obj, usb_core_device_obj_get_port_numbers);

MP_PROPERTY_GETTER(usb_core_device_port_numbers_obj,
(mp_obj_t)&usb_core_device_get_port_numbers_obj);


//| speed: int
//| """The speed of the device. One of `usb.util.SPEED_LOW`, `usb.util.SPEED_FULL`, `usb.util.SPEED_HIGH` or 0 for unknown."""
//|
static mp_obj_t usb_core_device_obj_get_speed(mp_obj_t self_in) {
usb_core_device_obj_t *self = MP_OBJ_TO_PTR(self_in);
return MP_OBJ_NEW_SMALL_INT(common_hal_usb_core_device_get_speed(self));
}
MP_DEFINE_CONST_FUN_OBJ_1(usb_core_device_get_speed_obj, usb_core_device_obj_get_speed);

MP_PROPERTY_GETTER(usb_core_device_speed_obj,
(mp_obj_t)&usb_core_device_get_speed_obj);

//| def set_configuration(self, configuration: int = 1) -> None:
//| """Set the active configuration.
//|
Expand Down Expand Up @@ -302,11 +350,16 @@ MP_DEFINE_CONST_FUN_OBJ_2(usb_core_device_attach_kernel_driver_obj, usb_core_dev


static const mp_rom_map_elem_t usb_core_device_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&usb_core_device_deinit_obj) },
{ MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&usb_core_device_deinit_obj) },
{ MP_ROM_QSTR(MP_QSTR_idVendor), MP_ROM_PTR(&usb_core_device_idVendor_obj) },
{ MP_ROM_QSTR(MP_QSTR_idProduct), MP_ROM_PTR(&usb_core_device_idProduct_obj) },
{ MP_ROM_QSTR(MP_QSTR_serial_number), MP_ROM_PTR(&usb_core_device_serial_number_obj) },
{ MP_ROM_QSTR(MP_QSTR_product), MP_ROM_PTR(&usb_core_device_product_obj) },
{ MP_ROM_QSTR(MP_QSTR_manufacturer), MP_ROM_PTR(&usb_core_device_manufacturer_obj) },
{ MP_ROM_QSTR(MP_QSTR_bus), MP_ROM_PTR(&usb_core_device_bus_obj) },
{ MP_ROM_QSTR(MP_QSTR_port_numbers), MP_ROM_PTR(&usb_core_device_port_numbers_obj) },
{ MP_ROM_QSTR(MP_QSTR_speed), MP_ROM_PTR(&usb_core_device_speed_obj) },

{ MP_ROM_QSTR(MP_QSTR_set_configuration), MP_ROM_PTR(&usb_core_device_set_configuration_obj) },
{ MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&usb_core_device_write_obj) },
Expand Down
5 changes: 5 additions & 0 deletions shared-bindings/usb/core/Device.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,16 @@
extern const mp_obj_type_t usb_core_device_type;

bool common_hal_usb_core_device_construct(usb_core_device_obj_t *self, uint8_t device_number);
void common_hal_usb_core_device_deinit(usb_core_device_obj_t *self);
uint16_t common_hal_usb_core_device_get_idVendor(usb_core_device_obj_t *self);
uint16_t common_hal_usb_core_device_get_idProduct(usb_core_device_obj_t *self);
mp_obj_t common_hal_usb_core_device_get_serial_number(usb_core_device_obj_t *self);
mp_obj_t common_hal_usb_core_device_get_product(usb_core_device_obj_t *self);
mp_obj_t common_hal_usb_core_device_get_manufacturer(usb_core_device_obj_t *self);
mp_int_t common_hal_usb_core_device_get_bus(usb_core_device_obj_t *self);
mp_obj_t common_hal_usb_core_device_get_port_numbers(usb_core_device_obj_t *self);
mp_int_t common_hal_usb_core_device_get_speed(usb_core_device_obj_t *self);

void common_hal_usb_core_device_set_configuration(usb_core_device_obj_t *self, mp_int_t configuration);
mp_int_t common_hal_usb_core_device_write(usb_core_device_obj_t *self, mp_int_t endpoint, const uint8_t *buffer, mp_int_t len, mp_int_t timeout);
mp_int_t common_hal_usb_core_device_read(usb_core_device_obj_t *self, mp_int_t endpoint, uint8_t *buffer, mp_int_t len, mp_int_t timeout);
Expand Down
48 changes: 48 additions & 0 deletions shared-bindings/usb/util/__init__.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// This file is part of the CircuitPython project: https://circuitpython.org
//
// SPDX-FileCopyrightText: Copyright (c) 2022 Scott Shawcroft for Adafruit Industries
//
// SPDX-License-Identifier: MIT

#include <stdarg.h>
#include <string.h>

#include "py/obj.h"
#include "py/objexcept.h"
#include "py/misc.h"
#include "py/mphal.h"
#include "py/runtime.h"

#include "shared-bindings/usb/util/__init__.h"

//| """USB Util
//|
//| This is a subset of the PyUSB util module.
//| """
//|
//| SPEED_LOW: int = ...
//| """A low speed, 1.5 Mbit/s device."""
//|
//| SPEED_FULL: int = ...
//| """A full speed, 12 Mbit/s device."""
//|
//| SPEED_HIGH: int = ...
//| """A high speed, 480 Mbit/s device."""
//|

static mp_rom_map_elem_t usb_util_module_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_usb_dot_util) },
// Speed constants
{ MP_ROM_QSTR(MP_QSTR_SPEED_LOW), MP_OBJ_NEW_SMALL_INT(PYUSB_SPEED_LOW) },
{ MP_ROM_QSTR(MP_QSTR_SPEED_FULL), MP_OBJ_NEW_SMALL_INT(PYUSB_SPEED_FULL) },
{ MP_ROM_QSTR(MP_QSTR_SPEED_HIGH), MP_OBJ_NEW_SMALL_INT(PYUSB_SPEED_HIGH) },
};

static MP_DEFINE_CONST_DICT(usb_util_module_globals, usb_util_module_globals_table);

const mp_obj_module_t usb_util_module = {
.base = { &mp_type_module },
.globals = (mp_obj_dict_t *)&usb_util_module_globals,
};

MP_REGISTER_MODULE(MP_QSTR_usb_dot_util, usb_util_module);
17 changes: 17 additions & 0 deletions shared-bindings/usb/util/__init__.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// This file is part of the CircuitPython project: https://circuitpython.org
//
// SPDX-FileCopyrightText: Copyright (c) 2022 Scott Shawcroft for Adafruit Industries
//
// SPDX-License-Identifier: MIT

#pragma once

#include <stdbool.h>

#include "py/obj.h"

extern const mp_obj_module_t usb_util_module;

#define PYUSB_SPEED_LOW 1
#define PYUSB_SPEED_FULL 2
#define PYUSB_SPEED_HIGH 3
Loading

0 comments on commit b12b3e0

Please sign in to comment.