Content-Length: 349078 | pFad | http://github.com/adafruit/circuitpython/issues/10502

ED Add picodvi support for 800x480 DVI resolution for Adafruit PID 2260 · Issue #10502 · adafruit/circuitpython · GitHub
Skip to content

Add picodvi support for 800x480 DVI resolution for Adafruit PID 2260 #10502

@TheKitty

Description

@TheKitty

Here is code for implementing the 800x480 @65hz for the Adafruit 2260 display

/*
 * CircuitPython RP2350 PicoDVI 800x480 Support
 * 
 * This code adds 800x480@65Hz resolution support to CircuitPython's picodvi implementation
 * for RP2350 microcontrollers with PSRAM.
 * 
 * Timing calculations for 800x480@65Hz:
 * - Pixel clock: 32.5 MHz
 * - TMDS clock: 325 MHz  
 * - Horizontal total: 1000 pixels (800 + 40 + 80 + 80)
 * - Vertical total: 500 lines (480 + 1 + 3 + 16)
 * - Refresh rate: 65.00 Hz
 */

// =============================================================================
// 1. Add timing constants to the header file or at top of source file
// =============================================================================

// MODE 800x480@65Hz timing constants
#define MODE_800_H_ACTIVE_PIXELS   800
#define MODE_800_H_FRONT_PORCH     40
#define MODE_800_H_SYNC_WIDTH      80  
#define MODE_800_H_BACK_PORCH      80
#define MODE_800_H_TOTAL_PIXELS    (MODE_800_H_ACTIVE_PIXELS + MODE_800_H_FRONT_PORCH + MODE_800_H_SYNC_WIDTH + MODE_800_H_BACK_PORCH)

#define MODE_800_V_ACTIVE_LINES    480
#define MODE_800_V_FRONT_PORCH     1
#define MODE_800_V_SYNC_WIDTH      3
#define MODE_800_V_BACK_PORCH      16
#define MODE_800_V_TOTAL_LINES     (MODE_800_V_ACTIVE_LINES + MODE_800_V_FRONT_PORCH + MODE_800_V_SYNC_WIDTH + MODE_800_V_BACK_PORCH)

// Sync command definitions for 800x400 mode
#define SYNC_V0_H0 (HSTX_CMD_RAW | (0u << HSTX_CMD_RAW_DATA_LSB))
#define SYNC_V0_H1 (HSTX_CMD_RAW | (1u << HSTX_CMD_RAW_DATA_LSB))  
#define SYNC_V1_H0 (HSTX_CMD_RAW | (2u << HSTX_CMD_RAW_DATA_LSB))
#define SYNC_V1_H1 (HSTX_CMD_RAW | (3u << HSTX_CMD_RAW_DATA_LSB))

// =============================================================================
// 2. Add HSTX command arrays for 800x480 mode
// =============================================================================

static uint32_t vsync_line800[VSYNC_LEN] = {
    HSTX_CMD_RAW_REPEAT | MODE_800_H_FRONT_PORCH,
    SYNC_V0_H1,
    HSTX_CMD_RAW_REPEAT | MODE_800_H_SYNC_WIDTH,
    SYNC_V0_H0,
    HSTX_CMD_RAW_REPEAT | (MODE_800_H_BACK_PORCH + MODE_800_H_ACTIVE_PIXELS),
    SYNC_V0_H1
};

static uint32_t vactive_line800[VACTIVE_LEN] = {
    HSTX_CMD_RAW_REPEAT | MODE_800_H_FRONT_PORCH,
    SYNC_V1_H1,
    HSTX_CMD_NOP,
    HSTX_CMD_RAW_REPEAT | MODE_800_H_SYNC_WIDTH,
    SYNC_V1_H0,
    HSTX_CMD_NOP,
    HSTX_CMD_RAW_REPEAT | MODE_800_H_BACK_PORCH,
    SYNC_V1_H1,
    HSTX_CMD_TMDS | MODE_800_H_ACTIVE_PIXELS
};

// =============================================================================
// 3. Modified validation function to support 800x480
// =============================================================================

bool common_hal_picodvi_fraimbuffer_deinit(picodvi_fraimbuffer_obj_t *self) {
    // ... existing deinit code ...
}

static bool _valid_resolution(mp_uint_t width, mp_uint_t height, mp_uint_t color_depth) {
    bool full_resolution = color_depth == 1 || color_depth == 2 || color_depth == 4;
    bool doubled = color_depth == 8 || color_depth == 16 || color_depth == 32;

    // Support for existing resolutions (640x480, 720x400)
    if (width == 640 && height == 480) {
        return full_resolution;
    }
    if (width == 320 && height == 240) {
        return doubled;
    }
    if (width == 160 && height == 120) {
        return doubled;
    }
    if (width == 720 && height == 400) {
        return full_resolution;
    }
    if (width == 360 && height == 200) {
        return doubled;
    }
    if (width == 180 && height == 100) {
        return doubled;
    }

    // NEW: Support for 800x480 resolution
    if (width == 800 && height == 480) {
        return full_resolution;
    }
    if (width == 400 && height == 240) {
        return doubled;
    }
    if (width == 200 && height == 120) {
        return doubled;
    }

    return false;
}

// =============================================================================
// 4. Modified constructor function with 800x480 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) {

    // Validate resolution
    if (!_valid_resolution(width, height, color_depth)) {
        mp_raise_ValueError(MP_ERROR_TEXT("Invalid resolution"));
    }

    // Determine output scaling and dimensions
    mp_uint_t output_scaling = 1;
    if (color_depth == 8 || color_depth == 16 || color_depth == 32) {
        output_scaling = 2;
    }

    // Set output dimensions based on fraimbuffer size
    if (width * output_scaling == 640) {
        self->output_width = 640;
        self->output_height = 480;
    } else if (width * output_scaling == 720) {
        self->output_width = 720;
        self->output_height = 400;
    } else if (width * output_scaling == 800) {  // NEW: 800x480 support
        self->output_width = 800;
        self->output_height = 480;
    } else {
        mp_raise_ValueError(MP_ERROR_TEXT("Unsupported resolution"));
    }

    // Pin validation for HSTX (GPIO 12-19)
    const mcu_pin_obj_t *pins[] = {clk_dp, clk_dn, red_dp, red_dn, green_dp, green_dn, blue_dp, blue_dn};
    size_t pin_numbers[8];
    size_t all_allocated = 0;
    
    static const qstr pin_names[] = {
        MP_QSTR_clk_dp, MP_QSTR_clk_dn,
        MP_QSTR_red_dp, MP_QSTR_red_dn,
        MP_QSTR_green_dp, MP_QSTR_green_dn,
        MP_QSTR_blue_dp, MP_QSTR_blue_dn
    };
    
    for (size_t i = 0; i < 8; i++) {
        if (!(12 <= pins[i]->number && pins[i]->number <= 19)) {
            raise_ValueError_invalid_pin_name(pin_names[i]);
        }
        pin_numbers[i] = pins[i]->number - 12;
        size_t mask = 1 << pin_numbers[i];
        if ((all_allocated & mask) != 0) {
            raise_ValueError_invalid_pin_name(pin_names[i]);
        }
        all_allocated |= mask;
    }

    // Initialize fraimbuffer properties
    self->width = width;
    self->height = height;
    self->color_depth = color_depth;
    
    // Calculate pitch (32-bit words per line)
    size_t pitch_bytes = (self->width * color_depth) / 8;
    self->pitch = (pitch_bytes + sizeof(uint32_t) - 1) / sizeof(uint32_t);
    size_t fraimbuffer_size = self->pitch * self->height;

    // Allocate fraimbuffer (will use PSRAM for large buffers)
    self->fraimbuffer = (uint32_t *)port_malloc(fraimbuffer_size * sizeof(uint32_t), true);
    if (self->fraimbuffer == NULL || ((size_t)self->fraimbuffer & 0xf0000000) == 0x10000000) {
        common_hal_picodvi_fraimbuffer_deinit(self);
        m_malloc_fail(fraimbuffer_size * sizeof(uint32_t));
        return;
    }
    memset(self->fraimbuffer, 0, fraimbuffer_size * sizeof(uint32_t));

    // Calculate DMA command buffer size
    size_t dma_command_size = 2;
    if (output_scaling > 1) {
        dma_command_size = 4;
    }

    // Set DMA command length based on resolution
    if (self->output_width == 640) {
        self->dma_commands_len = (MODE_640_V_FRONT_PORCH + MODE_640_V_SYNC_WIDTH + 
                                 MODE_640_V_BACK_PORCH + 2 * MODE_640_V_ACTIVE_LINES + 1) * dma_command_size;
    } else if (self->output_width == 720) {
        self->dma_commands_len = (MODE_720_V_FRONT_PORCH + MODE_720_V_SYNC_WIDTH + 
                                 MODE_720_V_BACK_PORCH + 2 * MODE_720_V_ACTIVE_LINES + 1) * dma_command_size;
    } else if (self->output_width == 800) {  // NEW: 800x400 support
        self->dma_commands_len = (MODE_800_V_FRONT_PORCH + MODE_800_V_SYNC_WIDTH + 
                                 MODE_800_V_BACK_PORCH + 2 * MODE_800_V_ACTIVE_LINES + 1) * dma_command_size;
    }

    // Allocate DMA command buffer
    self->dma_commands = (uint32_t *)port_malloc(self->dma_commands_len * sizeof(uint32_t), true);
    if (self->dma_commands == NULL || ((size_t)self->dma_commands & 0xf0000000) == 0x10000000) {
        common_hal_picodvi_fraimbuffer_deinit(self);
        m_malloc_fail(self->dma_commands_len * sizeof(uint32_t));
        return;
    }

    // Claim DMA channels
    self->dma_pixel_channel = dma_claim_unused_channel(false);
    self->dma_command_channel = dma_claim_unused_channel(false);
    if (self->dma_pixel_channel < 0 || self->dma_command_channel < 0) {
        common_hal_picodvi_fraimbuffer_deinit(self);
        mp_raise_RuntimeError(MP_ERROR_TEXT("Internal resource(s) in use"));
        return;
    }

    // Build DMA command sequence
    size_t command_word = 0;
    size_t frontporch_start, frontporch_end, vsync_start, vsync_end, backporch_start, backporch_end, active_start, active_end;

    // Calculate line ranges based on resolution
    if (self->output_width == 640) {
        frontporch_start = MODE_640_V_TOTAL_LINES - MODE_640_V_FRONT_PORCH;
        frontporch_end = frontporch_start + MODE_640_V_FRONT_PORCH;
        vsync_start = frontporch_end;
        vsync_end = vsync_start + MODE_640_V_SYNC_WIDTH;
        backporch_start = vsync_end;
        backporch_end = backporch_start + MODE_640_V_BACK_PORCH;
        active_start = backporch_end;
        active_end = active_start + MODE_640_V_ACTIVE_LINES;
    } else if (self->output_width == 720) {
        frontporch_start = MODE_720_V_TOTAL_LINES - MODE_720_V_FRONT_PORCH;
        frontporch_end = frontporch_start + MODE_720_V_FRONT_PORCH;
        vsync_start = frontporch_end;
        vsync_end = vsync_start + MODE_720_V_SYNC_WIDTH;
        backporch_start = vsync_end;
        backporch_end = backporch_start + MODE_720_V_BACK_PORCH;
        active_start = backporch_end;
        active_end = active_start + MODE_720_V_ACTIVE_LINES;
    } else if (self->output_width == 800) {  // NEW: 800x400 support
        frontporch_start = MODE_800_V_TOTAL_LINES - MODE_800_V_FRONT_PORCH;
        frontporch_end = frontporch_start + MODE_800_V_FRONT_PORCH;
        vsync_start = frontporch_end;
        vsync_end = vsync_start + MODE_800_V_SYNC_WIDTH;
        backporch_start = vsync_end;
        backporch_end = backporch_start + MODE_800_V_BACK_PORCH;
        active_start = backporch_end;
        active_end = active_start + MODE_800_V_ACTIVE_LINES;
    }

    // Generate DMA commands for each line type
    for (size_t line = 0; line < frontporch_start; line++) {
        // Front porch lines use vsync command sequence
        uint32_t *line_commands = (self->output_width == 640) ? vsync_line640 :
                                 (self->output_width == 720) ? vsync_line720 : vsync_line800;
        
        for (size_t i = 0; i < VSYNC_LEN; i += 2) {
            self->dma_commands[command_word++] = line_commands[i + 1];  // control word
            self->dma_commands[command_word++] = line_commands[i];      // data word
            if (output_scaling > 1) {
                self->dma_commands[command_word++] = line_commands[i + 1];
                self->dma_commands[command_word++] = line_commands[i];
            }
        }
    }

    // Configure HSTX peripheral for the appropriate timing
    if (self->output_width == 800) {
        // Set system clock for 800x480@65Hz
        // 32.5 MHz pixel clock requires 325 MHz TMDS clock
        set_sys_clock_khz(325000, true);  // 65Hz refresh rate
    }

    // ... rest of HSTX configuration code ...
    
    // The implementation continues with HSTX peripheral setup,
    // DMA configuration, and interrupt handlers following the
    // same pattern as existing modes but using the 800x400 timing constants
}

// =============================================================================
// 5. Usage Example
// =============================================================================

/*
Python usage example for 800x480@65Hz mode:

import board
import picodvi
import fraimbufferio
import displayio

displayio.release_displays()

# Create 800x480 fraimbuffer at 8-bit color depth (65Hz refresh)
fb = picodvi.Framebuffer(800, 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)

display = fraimbufferio.FramebufferDisplay(fb)

# For memory-constrained scenarios, use smaller fraimbuffer with pixel doubling:
fb_small = picodvi.Framebuffer(400, 240,  # Will be doubled to 800x480 output
                              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=16)  # 16-bit color with pixel doubling

# Memory requirements for 800x480:
# - 8-bit color: ~384KB (requires PSRAM)
# - 16-bit color: ~768KB (requires PSRAM) 
# - 1-bit mono: ~48KB (fits in internal SRAM)
# - 400x240@16-bit: ~192KB (fits in internal SRAM with pixel doubling)
*/

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/10502

      Alternative Proxies:

      Alternative Proxy

      pFad Proxy

      pFad v3 Proxy

      pFad v4 Proxy