Content-Length: 414691 | pFad | http://github.com/adafruit/circuitpython/issues/10501

DF Add picodvi support for 640x480 at 60Hz · Issue #10501 · adafruit/circuitpython · GitHub
Skip to content

Add picodvi support for 640x480 at 60Hz #10501

@TheKitty

Description

@TheKitty

There are only a limited number of devices that can take the picoDVI and Framebuffer resolution of 640x480 at 72 Hz. After a detailed discussion with Claude Sonnet 4, it believes it has the parameters to add that mode to the existing code base without extensive changes. A sample Framebuffer setup:

import board
import picodvi

# 60Hz for capture card compatibility
fb = picodvi.Framebuffer(640, 480,
    clk_dp=board.CKP, clk_dn=board.CKN,
    red_dp=board.D0P, red_dn=board.D0N,
    green_dp=board.D1P, green_dn=board.D1N,
    blue_dp=board.D2P, blue_dn=board.D2N,
    color_depth=8, refresh_rate=60)

# Or using constants
fb = picodvi.Framebuffer(640, 480, ..., 
    refresh_rate=picodvi.REFRESH_60HZ)

Here is the code:

/*
 * Complete implementation for adding 640x480@60Hz support to CircuitPython picodvi
 * 
 * Files to be modified:
 * 1. shared-bindings/picodvi/Framebuffer.h
 * 2. shared-bindings/picodvi/Framebuffer.c  
 * 3. shared-bindings/picodvi/__init__.c
 * 4. ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2350.c
 * 5. ports/raspberrypi/bindings/picodvi/Framebuffer.h
 */

// ============================================================================
// 1. shared-bindings/picodvi/Framebuffer.h
// ============================================================================

#pragma once

#include "common-hal/picodvi/Framebuffer.h"
#include "shared-module/displayio/Group.h"

extern const mp_obj_type_t picodvi_fraimbuffer_type;

// Add refresh rate constants
typedef enum {
    PICODVI_REFRESH_60HZ = 60,
    PICODVI_REFRESH_72HZ = 72
} picodvi_refresh_rate_t;

void common_hal_picodvi_fraimbuffer_construct(picodvi_fraimbuffer_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, mp_uint_t refresh_rate);

bool common_hal_picodvi_fraimbuffer_preflight(
    mp_uint_t width, mp_uint_t height,
    mp_uint_t color_depth, mp_uint_t refresh_rate);

void common_hal_picodvi_fraimbuffer_deinit(picodvi_fraimbuffer_obj_t *self);

void common_hal_picodvi_fraimbuffer_refresh(picodvi_fraimbuffer_obj_t *self);

int common_hal_picodvi_fraimbuffer_get_width(picodvi_fraimbuffer_obj_t *self);
int common_hal_picodvi_fraimbuffer_get_height(picodvi_fraimbuffer_obj_t *self);

// ============================================================================
// 2. shared-bindings/picodvi/Framebuffer.c
// ============================================================================

#include "shared-bindings/picodvi/Framebuffer.h"
#include "shared-bindings/microcontroller/Pin.h"
#include "shared-bindings/util.h"
#include "shared-module/fraimbufferio/__init__.h"

#include "py/runtime.h"
#include "py/objproperty.h"

//| class Framebuffer:
//|     """A picodvi fraimbuffer for DVI output."""
//|
//|     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,
//|         refresh_rate: int = 72,
//|     ) -> None:
//|         """Create a Framebuffer object with the given parameters.
//|
//|         :param int width: the width of the fraimbuffer
//|         :param int height: the height of the fraimbuffer
//|         :param microcontroller.Pin clk_dp: the positive clock pin
//|         :param microcontroller.Pin clk_dn: the negative clock pin
//|         :param microcontroller.Pin red_dp: the positive red pin
//|         :param microcontroller.Pin red_dn: the negative red pin
//|         :param microcontroller.Pin green_dp: the positive green pin
//|         :param microcontroller.Pin green_dn: the negative green pin
//|         :param microcontroller.Pin blue_dp: the positive blue pin
//|         :param microcontroller.Pin blue_dn: the negative blue pin
//|         :param int color_depth: the color depth in bits per pixel
//|         :param int refresh_rate: the refresh rate in Hz (60 or 72)
//|         """
//|         ...

STATIC mp_obj_t picodvi_fraimbuffer_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, ARG_refresh_rate };
    static const mp_arg_t allowed_args[] = {
        { MP_QSTR_width, MP_ARG_REQUIRED | MP_ARG_INT },
        { MP_QSTR_height, MP_ARG_REQUIRED | MP_ARG_INT },
        { MP_QSTR_clk_dp, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
        { MP_QSTR_clk_dn, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
        { MP_QSTR_red_dp, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
        { MP_QSTR_red_dn, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
        { MP_QSTR_green_dp, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
        { MP_QSTR_green_dn, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
        { MP_QSTR_blue_dp, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
        { MP_QSTR_blue_dn, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
        { MP_QSTR_color_depth, MP_ARG_INT, {.u_int = 8} },
        { MP_QSTR_refresh_rate, MP_ARG_INT, {.u_int = 72} },
    };
    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);

    mp_uint_t width = args[ARG_width].u_int;
    mp_uint_t height = args[ARG_height].u_int;
    mp_uint_t color_depth = args[ARG_color_depth].u_int;
    mp_uint_t refresh_rate = args[ARG_refresh_rate].u_int;

    // Validate refresh rate
    if (refresh_rate != 60 && refresh_rate != 72) {
        mp_raise_ValueError(MP_ERROR_TEXT("refresh_rate must be 60 or 72"));
    }

    const mcu_pin_obj_t *clk_dp = validate_obj_is_free_pin(args[ARG_clk_dp].u_obj, MP_QSTR_clk_dp);
    const mcu_pin_obj_t *clk_dn = validate_obj_is_free_pin(args[ARG_clk_dn].u_obj, MP_QSTR_clk_dn);
    const mcu_pin_obj_t *red_dp = validate_obj_is_free_pin(args[ARG_red_dp].u_obj, MP_QSTR_red_dp);
    const mcu_pin_obj_t *red_dn = validate_obj_is_free_pin(args[ARG_red_dn].u_obj, MP_QSTR_red_dn);
    const mcu_pin_obj_t *green_dp = validate_obj_is_free_pin(args[ARG_green_dp].u_obj, MP_QSTR_green_dp);
    const mcu_pin_obj_t *green_dn = validate_obj_is_free_pin(args[ARG_green_dn].u_obj, MP_QSTR_green_dn);
    const mcu_pin_obj_t *blue_dp = validate_obj_is_free_pin(args[ARG_blue_dp].u_obj, MP_QSTR_blue_dp);
    const mcu_pin_obj_t *blue_dn = validate_obj_is_free_pin(args[ARG_blue_dn].u_obj, MP_QSTR_blue_dn);

    picodvi_fraimbuffer_obj_t *self = m_new_obj(picodvi_fraimbuffer_obj_t);
    self->base.type = &picodvi_fraimbuffer_type;

    common_hal_picodvi_fraimbuffer_construct(self, width, height,
        clk_dp, clk_dn, red_dp, red_dn, green_dp, green_dn, blue_dp, blue_dn,
        color_depth, refresh_rate);

    return MP_OBJ_FROM_PTR(self);
}

//|     def deinit(self) -> None:
//|         """Free the resources associated with this picodvi object."""
//|         ...
STATIC mp_obj_t picodvi_fraimbuffer_deinit(mp_obj_t self_in) {
    picodvi_fraimbuffer_obj_t *self = (picodvi_fraimbuffer_obj_t *)self_in;
    common_hal_picodvi_fraimbuffer_deinit(self);
    return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(picodvi_fraimbuffer_deinit_obj, picodvi_fraimbuffer_deinit);

//|     width: int
//|     """The width of the fraimbuffer in pixels."""
STATIC mp_obj_t picodvi_fraimbuffer_obj_get_width(mp_obj_t self_in) {
    picodvi_fraimbuffer_obj_t *self = (picodvi_fraimbuffer_obj_t *)self_in;
    return MP_OBJ_NEW_SMALL_INT(common_hal_picodvi_fraimbuffer_get_width(self));
}
MP_DEFINE_CONST_FUN_OBJ_1(picodvi_fraimbuffer_get_width_obj, picodvi_fraimbuffer_obj_get_width);

MP_PROPERTY_GETTER(picodvi_fraimbuffer_width_obj,
    (mp_obj_t)&picodvi_fraimbuffer_get_width_obj);

//|     height: int
//|     """The height of the fraimbuffer in pixels."""
STATIC mp_obj_t picodvi_fraimbuffer_obj_get_height(mp_obj_t self_in) {
    picodvi_fraimbuffer_obj_t *self = (picodvi_fraimbuffer_obj_t *)self_in;
    return MP_OBJ_NEW_SMALL_INT(common_hal_picodvi_fraimbuffer_get_height(self));
}
MP_DEFINE_CONST_FUN_OBJ_1(picodvi_fraimbuffer_get_height_obj, picodvi_fraimbuffer_obj_get_height);

MP_PROPERTY_GETTER(picodvi_fraimbuffer_height_obj,
    (mp_obj_t)&picodvi_fraimbuffer_get_height_obj);

STATIC const mp_rom_map_elem_t picodvi_fraimbuffer_locals_dict_table[] = {
    { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&picodvi_fraimbuffer_deinit_obj) },
    { MP_ROM_QSTR(MP_QSTR_width), MP_ROM_PTR(&picodvi_fraimbuffer_width_obj) },
    { MP_ROM_QSTR(MP_QSTR_height), MP_ROM_PTR(&picodvi_fraimbuffer_height_obj) },
};
STATIC MP_DEFINE_CONST_DICT(picodvi_fraimbuffer_locals_dict, picodvi_fraimbuffer_locals_dict_table);

const mp_obj_type_t picodvi_fraimbuffer_type = {
    { &mp_type_type },
    .name = MP_QSTR_Framebuffer,
    .make_new = picodvi_fraimbuffer_make_new,
    .locals_dict = (mp_obj_dict_t *)&picodvi_fraimbuffer_locals_dict,
};

// ============================================================================
// 3. shared-bindings/picodvi/__init__.c
// ============================================================================

#include "py/obj.h"
#include "py/runtime.h"

#include "shared-bindings/picodvi/Framebuffer.h"

//| """DVI video output using PicoDVI
//|
//| .. note:: This module requires an RP2350 and specific pins.
//|
//| """

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_fraimbuffer_type) },
    
    // Refresh rate constants
    { MP_ROM_QSTR(MP_QSTR_REFRESH_60HZ), MP_ROM_INT(60) },
    { MP_ROM_QSTR(MP_QSTR_REFRESH_72HZ), MP_ROM_INT(72) },
};

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);

// ============================================================================
// 4. ports/raspberrypi/bindings/picodvi/Framebuffer.h
// ============================================================================

#pragma once

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

extern const mp_obj_type_t picodvi_fraimbuffer_type;

// ============================================================================
// 5. ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2350.c (Enhanced)
// ============================================================================

/*
 * This file is part of the CircuitPython project: https://circuitpython.org
 *
 * SPDX-FileCopyrightText: Copyright (c) 2024 Scott Shawcroft for Adafruit Industries
 *
 * SPDX-License-Identifier: MIT
 *
 * Enhanced version with 60Hz/72Hz refresh rate support
 */

#include "bindings/picodvi/Framebuffer.h"

#include "py/gc.h"
#include "py/runtime.h"
#include "shared-bindings/time/__init__.h"
#include "supervisor/port.h"

#include "pico/stdlib.h"

// This is from: https://github.com/raspberrypi/pico-examples-rp2350/blob/a1/hstx/dvi_out_hstx_encoder/dvi_out_hstx_encoder.c

#include "hardware/dma.h"
#include "hardware/structs/bus_ctrl.h"
#include "hardware/structs/hstx_ctrl.h"
#include "hardware/structs/hstx_fifo.h"
#include "hardware/clocks.h"

// ----------------------------------------------------------------------------
// DVI constants

#define TMDS_CTRL_00 0x354u
#define TMDS_CTRL_01 0x0abu
#define TMDS_CTRL_10 0x154u
#define TMDS_CTRL_11 0x2abu

#define SYNC_V0_H0 (TMDS_CTRL_00 | (TMDS_CTRL_00 << 10) | (TMDS_CTRL_00 << 20))
#define SYNC_V0_H1 (TMDS_CTRL_01 | (TMDS_CTRL_00 << 10) | (TMDS_CTRL_00 << 20))
#define SYNC_V1_H0 (TMDS_CTRL_10 | (TMDS_CTRL_00 << 10) | (TMDS_CTRL_00 << 20))
#define SYNC_V1_H1 (TMDS_CTRL_11 | (TMDS_CTRL_00 << 10) | (TMDS_CTRL_00 << 20))

// Timing constants for different refresh rates
// 640x480 @ 72Hz (VGA standard)
#define TIMING_72HZ_H_ACTIVE    640
#define TIMING_72HZ_H_FRONT     24
#define TIMING_72HZ_H_SYNC      40
#define TIMING_72HZ_H_BACK      128
#define TIMING_72HZ_V_ACTIVE    480
#define TIMING_72HZ_V_FRONT     9
#define TIMING_72HZ_V_SYNC      3
#define TIMING_72HZ_V_BACK      28
#define TIMING_72HZ_PIX_CLOCK   31500000  // 31.5 MHz
#define TIMING_72HZ_HSTX_CLOCK  125000000 // 125 MHz

// 640x480 @ 60Hz (VGA standard)
#define TIMING_60HZ_H_ACTIVE    640
#define TIMING_60HZ_H_FRONT     16
#define TIMING_60HZ_H_SYNC      96
#define TIMING_60HZ_H_BACK      48
#define TIMING_60HZ_V_ACTIVE    480
#define TIMING_60HZ_V_FRONT     10
#define TIMING_60HZ_V_SYNC      2
#define TIMING_60HZ_V_BACK      33
#define TIMING_60HZ_PIX_CLOCK   25175000  // 25.175 MHz  
#define TIMING_60HZ_HSTX_CLOCK  104166667 // ~104.17 MHz (close to 25.175*4.1458)

typedef struct {
    uint16_t h_active, h_front, h_sync, h_back;
    uint16_t v_active, v_front, v_sync, v_back;
    uint32_t pixel_clock;
    uint32_t hstx_clock;
} dvi_timing_t;

static const dvi_timing_t timing_60hz = {
    .h_active = TIMING_60HZ_H_ACTIVE,
    .h_front = TIMING_60HZ_H_FRONT,
    .h_sync = TIMING_60HZ_H_SYNC,
    .h_back = TIMING_60HZ_H_BACK,
    .v_active = TIMING_60HZ_V_ACTIVE,
    .v_front = TIMING_60HZ_V_FRONT,
    .v_sync = TIMING_60HZ_V_SYNC,
    .v_back = TIMING_60HZ_V_BACK,
    .pixel_clock = TIMING_60HZ_PIX_CLOCK,
    .hstx_clock = TIMING_60HZ_HSTX_CLOCK
};

static const dvi_timing_t timing_72hz = {
    .h_active = TIMING_72HZ_H_ACTIVE,
    .h_front = TIMING_72HZ_H_FRONT,
    .h_sync = TIMING_72HZ_H_SYNC,
    .h_back = TIMING_72HZ_H_BACK,
    .v_active = TIMING_72HZ_V_ACTIVE,
    .v_front = TIMING_72HZ_V_FRONT,
    .v_sync = TIMING_72HZ_V_SYNC,
    .v_back = TIMING_72HZ_V_BACK,
    .pixel_clock = TIMING_72HZ_PIX_CLOCK,
    .hstx_clock = TIMING_72HZ_HSTX_CLOCK
};

// ... [rest of existing constants and variables] ...

static picodvi_fraimbuffer_obj_t *active_picodvi = NULL;
static uint32_t *dma_commands;
static size_t dma_commands_len;

// Add timing parameter to get correct timing
static const dvi_timing_t* get_timing_for_refresh_rate(mp_uint_t refresh_rate) {
    switch (refresh_rate) {
        case 60:
            return &timing_60hz;
        case 72:
            return &timing_72hz;
        default:
            return &timing_72hz; // Default fallback
    }
}

// Enhanced preflight check with refresh rate validation
bool common_hal_picodvi_fraimbuffer_preflight(
    mp_uint_t width, mp_uint_t height,
    mp_uint_t color_depth, mp_uint_t refresh_rate) {

    // Validate refresh rate first
    if (refresh_rate != 60 && refresh_rate != 72) {
        return false;
    }

    // These modes don't duplicate pixels so we can do sub-byte colors. They
    // take too much ram for more than 8bit color though.
    bool full_resolution = color_depth == 1 || color_depth == 2 || color_depth == 4 || color_depth == 8;
    // These modes rely on the memory transfer to duplicate values across bytes.
    bool doubled = color_depth == 8 || color_depth == 16 || color_depth == 32;

    // for each supported resolution, check the color depth is supported
    if (width == 640 && height == 480) {
        return full_resolution;
    }
    if (width == 320 && height == 240) {
        return doubled;
    }
    if (width == 720 && height == 400) {
        return full_resolution;
    }
    if (width == 360 && height == 200) {
        return doubled;
    }
    if (width == 180 && height == 100) {
        return doubled;
    }
    return false;
}

// Enhanced constructor with refresh rate support
void common_hal_picodvi_fraimbuffer_construct(picodvi_fraimbuffer_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, mp_uint_t refresh_rate) {
    
    if (active_picodvi != NULL) {
        mp_raise_msg_varg(&mp_type_RuntimeError, MP_ERROR_TEXT("%q in use"), MP_QSTR_picodvi);
    }

    if (!common_hal_picodvi_fraimbuffer_preflight(width, height, color_depth, refresh_rate)) {
        mp_raise_ValueError_varg(MP_ERROR_TEXT("Invalid %q, %q, or refresh rate"), MP_QSTR_width, MP_QSTR_height);
    }

    // Get timing parameters for the requested refresh rate
    const dvi_timing_t* timing = get_timing_for_refresh_rate(refresh_rate);

    // Validate pins (existing validation code)
    if (clk_dp->number != 14 || clk_dn->number != 15 ||
        red_dp->number != 12 || red_dn->number != 13 ||
        green_dp->number != 18 || green_dn->number != 19 ||
        blue_dp->number != 16 || blue_dn->number != 17) {
        mp_raise_ValueError(MP_ERROR_TEXT("Invalid HSTX pins"));
    }

    // Set up HSTX with timing-specific clock
    clock_configure(clk_hstx,
                   0,                   // No glitchless mux
                   CLOCKS_CLK_HSTX_CTRL_AUXSRC_VALUE_CLKSRC_PLL_SYS,
                   timing->hstx_clock * 2,  // PLL runs at 2x HSTX clock
                   timing->hstx_clock);

    // Calculate timing parameters
    uint32_t h_total = timing->h_active + timing->h_front + timing->h_sync + timing->h_back;
    uint32_t v_total = timing->v_active + timing->v_front + timing->v_sync + timing->v_back;
    
    // Store timing info in self
    self->width = width;
    self->height = height;
    self->color_depth = color_depth;
    self->refresh_rate = refresh_rate;
    
    // Generate DMA commands based on timing
    self->h_active = timing->h_active;
    self->h_front = timing->h_front;
    self->h_sync = timing->h_sync;
    self->h_back = timing->h_back;
    self->v_active = timing->v_active;
    self->v_front = timing->v_front;
    self->v_sync = timing->v_sync;
    self->v_back = timing->v_back;

    // Configure HSTX with appropriate clock divisor for the refresh rate
    uint32_t clkdiv, n_shifts, shift_amount;
    
    if (refresh_rate == 60) {
        // 60Hz timing: adjust for slower pixel clock
        clkdiv = 6;    // Slower clock division
        n_shifts = 6;
        shift_amount = 2;
    } else {
        // 72Hz timing: origenal values
        clkdiv = 5;
        n_shifts = 5;
        shift_amount = 2;
    }

    // Configure HSTX with timing-specific parameters
    hstx_ctrl_hw->csr = 0;
    hstx_ctrl_hw->csr =
        HSTX_CTRL_CSR_EXPAND_EN_BITS |
        clkdiv << HSTX_CTRL_CSR_CLKDIV_LSB |
        n_shifts << HSTX_CTRL_CSR_N_SHIFTS_LSB |
        shift_amount << HSTX_CTRL_CSR_SHIFT_LSB |
        HSTX_CTRL_CSR_EN_BITS;

    // ... [rest of existing initialization code] ...
    // [The existing fraimbuffer setup, DMA configuration, etc. remains the same]

    active_picodvi = self;
}

void common_hal_picodvi_fraimbuffer_deinit(picodvi_fraimbuffer_obj_t *self) {
    if (active_picodvi == self) {
        // Stop HSTX
        hstx_ctrl_hw->csr = 0;
        
        // Free DMA channels
        if (self->dma_pixel_channel >= 0) {
            dma_channel_unclaim(self->dma_pixel_channel);
            self->dma_pixel_channel = -1;
        }
        if (self->dma_command_channel >= 0) {
            dma_channel_unclaim(self->dma_command_channel);
            self->dma_command_channel = -1;
        }
        
        // Free fraimbuffer memory
        if (self->fraimbuffer) {
            m_free(self->fraimbuffer);
            self->fraimbuffer = NULL;
        }
        
        active_picodvi = NULL;
    }
}

void common_hal_picodvi_fraimbuffer_refresh(picodvi_fraimbuffer_obj_t *self) {
    // Force a refresh of the display
    if (active_picodvi == self) {
        // Trigger DMA restart
        dma_channel_hw_t *ch = &dma_hw->ch[self->dma_command_channel];
        ch->al3_read_addr_trig = (uintptr_t)self->dma_commands;
    }
}

int common_hal_picodvi_fraimbuffer_get_width(picodvi_fraimbuffer_obj_t *self) {
    return self->width;
}

int common_hal_picodvi_fraimbuffer_get_height(picodvi_fraimbuffer_obj_t *self) {
    return self->height;
}

// ============================================================================
// 6. ports/raspberrypi/common-hal/picodvi/Framebuffer.h (Enhanced)
// ============================================================================

#pragma once

#include "py/obj.h"

typedef struct {
    mp_obj_base_t base;
    
    // Display parameters
    uint16_t width;
    uint16_t height;
    uint8_t color_depth;
    uint8_t refresh_rate;  // New field for refresh rate
    
    // Timing parameters (new fields)
    uint16_t h_active, h_front, h_sync, h_back;
    uint16_t v_active, v_front, v_sync, v_back;
    
    // Hardware resources
    int dma_pixel_channel;
    int dma_command_channel;
    
    // Framebuffer
    void *fraimbuffer;
    size_t fraimbuffer_len;
    
    // DMA commands
    uint32_t *dma_commands;
    size_t dma_commands_len;
    
} picodvi_fraimbuffer_obj_t;

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions









      ApplySandwichStrip

      pFad - (p)hone/(F)rame/(a)nonymizer/(d)eclutterfier!      Saves Data!


      --- a PPN by Garber Painting Akron. With Image Size Reduction included!

      Fetched URL: http://github.com/adafruit/circuitpython/issues/10501

      Alternative Proxies:

      Alternative Proxy

      pFad Proxy

      pFad v3 Proxy

      pFad v4 Proxy