diff --git a/.github/workflows/micropython.yml b/.github/workflows/micropython.yml index 5026a8d..28e6fbc 100644 --- a/.github/workflows/micropython.yml +++ b/.github/workflows/micropython.yml @@ -19,26 +19,23 @@ jobs: strategy: matrix: include: - - name: pga2040 - board: pga2040 + - name: presto + board: presto + variant: modules: default - - name: pga2350 - board: pga2350 - modules: default - - name: pga2350-psram # Friendly-name for output files - board: pga2350 # / - variant: PSRAM # //mpconfigvariant_.cmake - modules: default # /modules/.cmake env: # MicroPython version will be contained in github.event.release.tag_name for releases RELEASE_FILE: ${{ matrix.name }}-${{ github.event.release.tag_name || github.sha }}-micropython PIMORONI_PICO_DIR: "${{ github.workspace }}/pimoroni-pico" - MICROPY_BOARD_DIR: "${{ github.workspace }}/src-${{ github.sha }}/${{ matrix.BOARD }}" USER_C_MODULES: "${{ github.workspace }}/src-${{ github.sha }}/modules/${{ matrix.modules }}.cmake" + USER_FS_MANIFEST: "${{ github.workspace }}/src-${{ github.sha }}/modules/${{ matrix.modules }}.txt" + USER_FS_SOURCE: "${{ github.workspace }}/src-${{ github.sha }}/modules/littlefs" TAG_OR_SHA: ${{ github.event.release.tag_name || github.sha }} MICROPY_BOARD: ${{ matrix.board }} MICROPY_BOARD_VARIANT: ${{ matrix.variant }} + MICROPY_BOARD_DIR: "${{ github.workspace }}/src-${{ github.sha }}/${{ matrix.BOARD }}" + MICROPY_FROZEN_MANIFEST: "${{ github.workspace }}/src-${{ github.sha }}/modules/${{ matrix.modules }}.py" BOARD_NAME: ${{ matrix.name }} BUILD_TOOLS: src-${{ github.sha }}/ci/micropython.sh @@ -81,12 +78,19 @@ jobs: source $BUILD_TOOLS micropython_clone - - name: "Py_Decl: Checkout py_decl" + - name: "Py_Decl: Checkout" uses: actions/checkout@v4 with: repository: gadgetoid/py_decl ref: v0.0.2 path: py_decl + + - name: "dir2uf2: Checkout" + uses: actions/checkout@v4 + with: + repository: gadgetoid/dir2uf2 + ref: v0.0.7 + path: dir2uf2 - name: "MicroPython: Build MPY Cross" run: | @@ -106,24 +110,47 @@ jobs: source $BUILD_TOOLS cmake_build - - name: "Py_Decl: Verify UF2" + - name: "Py_Decl: Verify .uf2" shell: bash run: | python3 py_decl/py_decl.py --to-json --verify build-${{ matrix.name }}/${{ env.RELEASE_FILE }}.uf2 - - name: Store .uf2 as artifact + - name: "dir2uf2: Append filesystem to .uf2" + shell: bash + run: | + python3 -m pip install littlefs-python==0.12.0 + ./dir2uf2/dir2uf2 --fs-compact --append-to build-${{ matrix.name }}/${{ env.RELEASE_FILE }}.uf2 --manifest ${{env.USER_FS_MANIFEST}} --filename with-filesystem.uf2 ${{env.USER_FS_SOURCE}}/ + + - name: "Artifacts: Upload .uf2" uses: actions/upload-artifact@v4 with: name: ${{ env.RELEASE_FILE }}.uf2 path: build-${{ matrix.name }}/${{ env.RELEASE_FILE }}.uf2 - - name: Upload .uf2 + - name: "Artifacts: Upload .uf2 with filesystem" + uses: actions/upload-artifact@v4 + with: + name: ${{ env.RELEASE_FILE }}-with-filesystem.uf2 + path: ${{ env.RELEASE_FILE }}-with-filesystem.uf2 + + - name: "Release: Upload .uf2" if: github.event_name == 'release' uses: actions/upload-release-asset@v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: - asset_path: build-${{ matrix.name }}/firmware.uf2 + asset_path: build-${{ matrix.name }}/${{ env.RELEASE_FILE }}.uf2 upload_url: ${{ github.event.release.upload_url }} asset_name: ${{ env.RELEASE_FILE }}.uf2 + asset_content_type: application/octet-stream + + - name: "Release: Upload .uf2 with filesystem" + if: github.event_name == 'release' + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + asset_path: ${{ env.RELEASE_FILE }}-with-filesystem.uf2 + upload_url: ${{ github.event.release.upload_url }} + asset_name: ${{ env.RELEASE_FILE }}-with-filesystem.uf2 asset_content_type: application/octet-stream \ No newline at end of file diff --git a/README.md b/README.md index 009bbe1..4af50f6 100644 --- a/README.md +++ b/README.md @@ -1,16 +1 @@ -# Pimoroni PGA Boilerplate - -This repository is intended to provide a baseline MicroPython build for PGA2040 -and PGA2350, in addition to being a minimal example of how you might set up your -own custom MicroPython flavour to support your PGA-based project. - -Use this repository as a boilerplate to set up your own project, and GitHub actions -should automatically handle building MicroPython for you. - -## Contents - -* pga2040 - MicroPython and Pico SDK board definitions for PGA2040 -* pga2350 - MicroPython and Pico SDK board definitions for PGA2350, with PSRAM variant -* modules/c/example - An example MicroPython C++ module, demonstrating C class bindings -* modules/py_frozen - Python files intended to be frozen into the firmware -* modules/py_littlefs - Python files intended to be visible/editable in the LittleFS user filesystem \ No newline at end of file +# Pimoroni Presto \ No newline at end of file diff --git a/ci/micropython.sh b/ci/micropython.sh index 0a4b53a..2cbff92 100644 --- a/ci/micropython.sh +++ b/ci/micropython.sh @@ -52,6 +52,7 @@ function cmake_configure { -DMICROPY_BOARD_DIR=$MICROPY_BOARD_DIR \ -DMICROPY_BOARD=$MICROPY_BOARD \ -DMICROPY_BOARD_VARIANT=$MICROPY_BOARD_VARIANT \ + -DMICROPY_FROZEN_MANIFEST=$MICROPY_FROZEN_MANIFEST \ -DCMAKE_C_COMPILER_LAUNCHER=ccache \ -DCMAKE_CXX_COMPILER_LAUNCHER=ccache } diff --git a/drivers/st7701/CMakeLists.txt b/drivers/st7701/CMakeLists.txt new file mode 100644 index 0000000..941e05b --- /dev/null +++ b/drivers/st7701/CMakeLists.txt @@ -0,0 +1 @@ +include(${CMAKE_CURRENT_LIST_DIR}/st7701_presto.cmake) \ No newline at end of file diff --git a/drivers/st7701/st7701.cpp b/drivers/st7701/st7701.cpp new file mode 100644 index 0000000..be51963 --- /dev/null +++ b/drivers/st7701/st7701.cpp @@ -0,0 +1,524 @@ +#include "st7701.hpp" + +#include +#include +#include +#include "hardware/structs/xip_ctrl.h" +#include "hardware/structs/bus_ctrl.h" +#include "hardware/platform_defs.h" + +#ifndef NO_QSTR +#include "st7701_parallel.pio.h" +#include "st7701_timing.pio.h" +#endif + +namespace pimoroni { + enum dcx { + CMD = 0x00, + DATA = 0x01 + }; + + enum reg { + SWRESET = 0x01, // Software Reset + SLPOUT = 0x11, // Sleep Out + PTLON = 0x12, // Partial Display Mode On + NORON = 0x13, // Normal Display Mode On + INVOFF = 0x20, // Display Inversion Off + INVON = 0x21, // Display Inversion On + ALLPOFF = 0x22, // All Pixels Off + ALLPON = 0x23, // All Pixels On + GAMSET = 0x26, // Gamma Set + DISPOFF = 0x28, // Display Off + DISPON = 0x29, // Display On + TEOFF = 0x34, // Tearing Effect Line Off (kinda vsync) + TEON = 0x35, // Tearing Effect Line On (kinda vsync) + MADCTL = 0x36, // Display data access control + IDMOFF = 0x38, // Idle Mode Off + IDMON = 0x39, // Idle Mode On + COLMOD = 0x3A, // Interface Pixel Format + GSL = 0x45, // Get Scan Line + // Command2_BK0 + PVGAMCTRL = 0xB0, // Positive Voltage Gamma Control + NVGAMCTRL = 0xB1, // Negative Voltage Gamma Control + DGMEN = 0xB8, // Digital Gamma Enable + DGMLUTR = 0xB9, // Digital Gamma LUT for Red + DGMLUTB = 0xBA, // Digital Gamma Lut for Blue + LNESET = 0xC0, // Display Line Setting + PORCTRL = 0xC1, // Porch Control + INVSET = 0xC2, // Inversion Selection & Frame Rate Control + RGBCTRL = 0xC3, // RGB Control + PARCTRL = 0xC5, // Partial Mode Control + SDIR = 0xC7, // X-direction Control + PDOSET = 0xC8, // Pseudo-Dot Inversion Diving Settign + COLCTRL = 0xCD, // Colour Control + SRECTRL = 0xE0, // Sunlight Readable Enhancement + NRCTRL = 0xE1, // Noise Reduce Control + SECTRL = 0xE2, // Sharpness Control + CCCTRL = 0xE3, // Color Calibration Control + SKCTRL = 0xE4, // Skin Tone Preservation Control + // Command2_BK1 + VHRS = 0xB0, // Vop amplitude + VCOMS = 0xB1, // VCOM amplitude + VGHSS = 0xB2, // VGH voltage + TESTCMD = 0xB3, // TEST command + VGLS = 0xB5, // VGL voltage + VRHDV = 0xB6, // VRH_DV voltage + PWCTRL1 = 0xB7, // Power Control 1 + PWCTRL2 = 0xB8, // Power Control 2 + PCLKS1 = 0xBA, // Power pumping clock selection 1 + PCLKS2 = 0xBC, // Power pumping clock selection 2 + PDR1 = 0xC1, // Source pre_drive timing set 1 + PDR2 = 0xC2, // Source pre_drive timing set 2 + // Command2_BK3 + NVMEN = 0xC8, // NVM enable + NVMSET = 0xCA, // NVM manual control + PROMACT = 0xCC, // NVM program active + // Other + CND2BKxSEL = 0xFF, + }; + + +#define TIMING_V_PULSE 8 +#define TIMING_V_BACK (13 + TIMING_V_PULSE) +#define TIMING_V_DISPLAY (480 + TIMING_V_BACK) +#define TIMING_V_FRONT (5 + TIMING_V_DISPLAY) +#define TIMING_H_FRONT 4 +#define TIMING_H_PULSE 25 +#define TIMING_H_BACK 30 +#define TIMING_H_DISPLAY 480 + +#define LOW_PRIO_IRQ0 (NUM_IRQS - NUM_USER_IRQS) + +static ST7701* st7701_inst; + +// This ISR is triggered whenever the timing SM's FIFO is not full +void __no_inline_not_in_flash_func(timing_isr)() { + st7701_inst->drive_timing(); +} + +void __no_inline_not_in_flash_func(ST7701::drive_timing)() +{ + while (!pio_sm_is_tx_fifo_full(st_pio, timing_sm)) { + uint32_t instr; + switch (timing_phase) { + case 0: + // Front Porch + instr = 0x4000A042u; // HSYNC high, NOP + if (timing_row >= TIMING_V_PULSE) instr |= 0x80000000u; // VSYNC high if not in VSYNC pulse + instr |= (TIMING_H_FRONT - 3) << 16; + pio_sm_put(st_pio, timing_sm, instr); + break; + + case 1: + // HSYNC + instr = 0x0000A042u; // HSYNC low, NOP + if (timing_row >= TIMING_V_PULSE) instr |= 0x80000000u; // VSYNC high if not in VSYNC pulse + instr |= (TIMING_H_PULSE - 3) << 16; + pio_sm_put(st_pio, timing_sm, instr); + break; + + case 2: + // Back Porch, trigger pixel channels if in display window + instr = 0x40000000u; // HSYNC high + if (timing_row >= TIMING_V_PULSE) instr |= 0x80000000u; // VSYNC high if not in VSYNC pulse + if (timing_row >= TIMING_V_BACK && timing_row < TIMING_V_DISPLAY) instr |= 0xC004u; // IRQ 4, triggers the data SM + else instr |= 0xA042u; // NOP + instr |= (TIMING_H_BACK - 3) << 16; + pio_sm_put(st_pio, timing_sm, instr); + //printf(".\n"); + break; + + case 3: + // Display, trigger next frame at frame end + instr = 0x40000000u; // HSYNC high + if (timing_row == TIMING_V_DISPLAY) instr |= 0xC001u; // irq 1, to trigger queueing DMA for a new frame + else if (timing_row >= TIMING_V_BACK - 1 && timing_row < TIMING_V_DISPLAY) instr |= 0xC000u; // irq 0, to trigger queueing DMA for a new line + else instr |= 0xA042u; // NOP + if (timing_row >= TIMING_V_PULSE) instr |= 0x80000000u; // VSYNC high if not in VSYNC pulse + instr |= (TIMING_H_DISPLAY - 3) << 16; + pio_sm_put(st_pio, timing_sm, instr); + + if (++timing_row >= TIMING_V_FRONT) timing_row = 0; + break; + } + + timing_phase = (timing_phase + 1) & 3; + } +} + +// This ISR is triggered at the end of each line transferred +void __no_inline_not_in_flash_func(end_of_line_isr()) { + st7701_inst->handle_end_of_line(); +} + +void __no_inline_not_in_flash_func(ST7701::handle_end_of_line()) +{ + if (st_pio->irq & 0x2) start_frame_xfer(); + else start_line_xfer(); +} + +void __no_inline_not_in_flash_func(ST7701::start_line_xfer()) +{ + hw_clear_bits(&st_pio->irq, 0x1); + dma_channel_abort(st_dma); + dma_channel_wait_for_finish_blocking(st_dma); + pio_sm_set_enabled(st_pio, parallel_sm, false); + pio_sm_clear_fifos(st_pio, parallel_sm); + pio_sm_exec_wait_blocking(st_pio, parallel_sm, pio_encode_jmp(parallel_offset)); + pio_sm_exec_wait_blocking(st_pio, parallel_sm, pio_encode_mov(pio_osr, pio_null)); + pio_sm_set_enabled(st_pio, parallel_sm, true); + + dma_channel_set_read_addr(st_dma, &line_buffer[width * (display_row & (NUM_LINE_BUFFERS - 1))], true); + ++display_row; + + irq_set_pending(LOW_PRIO_IRQ0); +} + +void ST7701::start_frame_xfer() +{ + hw_clear_bits(&st_pio->irq, 0x2); + + if (next_framebuffer) { + framebuffer = next_framebuffer; + next_framebuffer = nullptr; + + init_framebuffer(); + } + + display_row = 0; + fill_row = 0; + irq_set_pending(LOW_PRIO_IRQ0); + + waiting_for_vsync = false; + __sev(); +} + +void ST7701::init_framebuffer() { + // If framebuffer not in cache XIP memory, do nothing. + if ((intptr_t)framebuffer >= 0x14000000) return; + + // Flush the frame buffer from cache + uint8_t* framebuffer_maintenance = (uint8_t*)framebuffer + 0x8000000; + for (int i = 3; i < width*height << 1; i += 8) + framebuffer_maintenance[i] = 0; // Clean + + // Switch to uncached reads + framebuffer += 0x2000000; +} + +void __no_inline_not_in_flash_func(line_fill_isr()) { + st7701_inst->fill_next_line(); +} + +void __no_inline_not_in_flash_func(ST7701::fill_next_line()) { + while (fill_row < height && fill_row < display_row + NUM_LINE_BUFFERS - 1) { + memcpy(&line_buffer[width * (fill_row & (NUM_LINE_BUFFERS - 1))], &framebuffer[width * fill_row], width << 1); + ++fill_row; + } +} + + ST7701::ST7701(uint16_t width, uint16_t height, Rotation rotation, SPIPins control_pins, uint16_t* framebuffer, + uint d0, uint hsync, uint vsync, uint lcd_de, uint lcd_dot_clk) : + DisplayDriver(width, height, rotation), + spi(control_pins.spi), + spi_cs(control_pins.cs), spi_sck(control_pins.sck), spi_dat(control_pins.mosi), lcd_bl(control_pins.bl), + d0(d0), hsync(hsync), vsync(vsync), lcd_de(lcd_de), lcd_dot_clk(lcd_dot_clk), + framebuffer(framebuffer) + { + st7701_inst = this; + } + + void ST7701::init() { + init_framebuffer(); + + line_buffer = (uint16_t*)malloc(NUM_LINE_BUFFERS * width * sizeof(line_buffer[0])); + + st_pio = pio1; + parallel_sm = pio_claim_unused_sm(st_pio, true); + parallel_offset = pio_add_program(st_pio, &st7701_parallel_program); + timing_sm = pio_claim_unused_sm(st_pio, true); + timing_offset = pio_add_program(st_pio, &st7701_timing_program); + + spi_init(spi, SPI_BAUD); + gpio_set_function(spi_cs, GPIO_FUNC_SIO); + gpio_set_dir(spi_cs, GPIO_OUT); + gpio_set_function(spi_dat, GPIO_FUNC_SPI); + gpio_set_function(spi_sck, GPIO_FUNC_SPI); + + // ST7701 3-line Serial Interface + // 9th bit = D/CX + // low = command + // high = data + spi_set_format(spi, 9, SPI_CPOL_0, SPI_CPHA_0, SPI_MSB_FIRST); + + //gpio_init(wr_sck); + //gpio_set_dir(wr_sck, GPIO_OUT); + //gpio_set_function(wr_sck, GPIO_FUNC_SIO); + pio_gpio_init(st_pio, hsync); + pio_gpio_init(st_pio, vsync); + pio_gpio_init(st_pio, lcd_de); + pio_gpio_init(st_pio, lcd_dot_clk); + + for(auto i = 0u; i < 18; i++) { + //gpio_set_function(d0 + i, GPIO_FUNC_SIO); + //gpio_set_dir(d0 + i, GPIO_OUT); + //gpio_init(d0 + 0); gpio_set_dir(d0 + i, GPIO_OUT); + pio_gpio_init(st_pio, d0 + i); + } + + pio_sm_set_consecutive_pindirs(st_pio, parallel_sm, d0, 18, true); + pio_sm_set_consecutive_pindirs(st_pio, parallel_sm, hsync, 4, true); + + pio_sm_config c = st7701_parallel_program_get_default_config(parallel_offset); + + sm_config_set_out_pins(&c, d0, 18); + sm_config_set_sideset_pins(&c, lcd_de); + sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX); + sm_config_set_out_shift(&c, true, true, 32); + + // Determine clock divider + //constexpr uint32_t max_pio_clk = 32 * MHZ; + //const uint32_t sys_clk_hz = clock_get_hz(clk_sys); + //const uint32_t clk_div = (sys_clk_hz + max_pio_clk - 1) / max_pio_clk; + const uint32_t clk_div = 5; + sm_config_set_clkdiv(&c, clk_div); + + pio_sm_init(st_pio, parallel_sm, parallel_offset, &c); + pio_sm_exec(st_pio, parallel_sm, pio_encode_out(pio_y, 32)); + pio_sm_put(st_pio, parallel_sm, width-1); + pio_sm_set_enabled(st_pio, parallel_sm, true); + + c = st7701_timing_program_get_default_config(timing_offset); + + sm_config_set_out_pins(&c, hsync, 2); + sm_config_set_sideset_pins(&c, lcd_dot_clk); + sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX); + sm_config_set_out_shift(&c, false, true, 32); + sm_config_set_clkdiv(&c, clk_div); + + pio_sm_init(st_pio, timing_sm, timing_offset, &c); + pio_sm_set_enabled(st_pio, timing_sm, true); + + st_dma = dma_claim_unused_channel(true); + dma_channel_config config = dma_channel_get_default_config(st_dma); + channel_config_set_transfer_data_size(&config, DMA_SIZE_32); + channel_config_set_high_priority(&config, true); + channel_config_set_dreq(&config, pio_get_dreq(st_pio, parallel_sm, true)); + dma_channel_configure(st_dma, &config, &st_pio->txf[parallel_sm], line_buffer, width >> 1, false); + + hw_set_bits(&bus_ctrl_hw->priority, (BUSCTRL_BUS_PRIORITY_PROC1_BITS | BUSCTRL_BUS_PRIORITY_DMA_R_BITS | BUSCTRL_BUS_PRIORITY_DMA_W_BITS)); + + printf("Begin SPI setup\n"); + + common_init(); + + printf("Setup screen timing\n"); + + irq_set_exclusive_handler(LOW_PRIO_IRQ0, line_fill_isr); + irq_set_enabled(LOW_PRIO_IRQ0, true); + irq_set_pending(LOW_PRIO_IRQ0); + + // Setup timing + hw_set_bits(&st_pio->inte1, 0x010 << timing_sm); // TX not full + irq_set_exclusive_handler(PIO1_IRQ_1, timing_isr); + irq_set_enabled(PIO1_IRQ_1, true); + + hw_set_bits(&st_pio->inte0, 0x300); // IRQ 0 + irq_set_exclusive_handler(PIO1_IRQ_0, end_of_line_isr); + irq_set_enabled(PIO1_IRQ_0, true); + } + + void ST7701::common_init() { + // if a backlight pin is provided then set it up for + // pwm control + if(lcd_bl != PIN_UNUSED) { + #if 0 + pwm_config cfg = pwm_get_default_config(); + pwm_set_wrap(pwm_gpio_to_slice_num(lcd_bl), 65535); + pwm_init(pwm_gpio_to_slice_num(lcd_bl), &cfg, true); + gpio_set_function(lcd_bl, GPIO_FUNC_PWM); + set_backlight(0); // Turn backlight off initially to avoid nasty surprises + #else + gpio_init(lcd_bl); + gpio_set_dir(lcd_bl, GPIO_OUT); + gpio_put(lcd_bl, 0); + #endif + } + + command(reg::SWRESET); + + sleep_ms(150); + + // Commmand 2 BK0 - kinda a page select + command(reg::CND2BKxSEL, 5, "\x77\x01\x00\x00\x10"); + + if(width == 480 && height == 480) { + // TODO: Figure out what's actually display specific + command(reg::MADCTL, 1, "\x00"); // Normal scan direction and RGB pixels + command(reg::LNESET, 2, "\x3b\x00"); // (59 + 1) * 8 = 480 lines + command(reg::PORCTRL, 2, "\x0d\x05"); // 13 VBP, 5 VFP + command(reg::INVSET, 2, "\x32\x05"); + command(reg::COLCTRL, 1, "\x08"); // LED polarity reversed + command(reg::PVGAMCTRL, 16, "\x00\x11\x18\x0e\x11\x06\x07\x08\x07\x22\x04\x12\x0f\xaa\x31\x18"); + command(reg::NVGAMCTRL, 16, "\x00\x11\x19\x0e\x12\x07\x08\x08\x08\x22\x04\x11\x11\xa9\x32\x18"); + } + + // Command 2 BK1 - Voltages and power and stuff + command(reg::CND2BKxSEL, 5, "\x77\x01\x00\x00\x11"); + command(reg::VHRS, 1, "\x60"); // 4.7375v + command(reg::VCOMS, 1, "\x32"); // 0.725v + command(reg::VGHSS, 1, "\x07"); // 15v + command(reg::TESTCMD, 1, "\x80"); // y tho? + command(reg::VGLS, 1, "\x49"); // -10.17v + command(reg::PWCTRL1, 1, "\x85"); // Middle/Min/Min bias + command(reg::PWCTRL2, 1, "\x21"); // 6.6 / -4.6 + command(reg::PDR1, 1, "\x78"); // 1.6uS + command(reg::PDR2, 1, "\x78"); // 6.4uS + + // Begin Forbidden Knowledge + // This sequence is probably specific to TL040WVS03CT15-H1263A. + // It is not documented in the ST7701s datasheet. + // TODO: 👇 W H A T ! ? 👇 + command(0xE0, 3, "\x00\x1b\x02"); + command(0xE1, 11, "\x08\xa0\x00\x00\x07\xa0\x00\x00\x00\x44\x44"); + command(0xE2, 12, "\x11\x11\x44\x44\xed\xa0\x00\x00\xec\xa0\x00\x00"); + command(0xE3, 4, "\x00\x00\x11\x11"); + command(0xE4, 2, "\x44\x44"); + command(0xE5, 16, "\x0a\xe9\xd8\xa0\x0c\xeb\xd8\xa0\x0e\xed\xd8\xa0\x10\xef\xd8\xa0"); + command(0xE6, 4, "\x00\x00\x11\x11"); + command(0xE7, 2, "\x44\x44"); + command(0xE8, 16, "\x09\xe8\xd8\xa0\x0b\xea\xd8\xa0\x0d\xec\xd8\xa0\x0f\xee\xd8\xa0"); + command(0xEB, 7, "\x02\x00\xe4\xe4\x88\x00\x40"); + command(0xEC, 2, "\x3c\x00"); + command(0xED, 16, "\xab\x89\x76\x54\x02\xff\xff\xff\xff\xff\xff\x20\x45\x67\x98\xba"); + command(0x36, 1, "\x00"); + // End Forbidden Knowledge + + // Command 2 BK3 + command(reg::CND2BKxSEL, 5, "\x77\x01\x00\x00\x13"); + //command(reg::COLMOD, 1, "\x77"); // 24 bits per pixel... + command(reg::COLMOD, 1, "\x66"); // 18 bits per pixel... + //command(reg::COLMOD, 1, "\x55"); // 16 bits per pixel... + + command(reg::INVON); + sleep_ms(1); + command(reg::SLPOUT); + sleep_ms(120); + command(reg::DISPON); + sleep_ms(50); + + // TODO: Support rotation + // configure_display(rotation); + + if(lcd_bl != PIN_UNUSED) { + //update(); // Send the new buffer to the display to clear any previous content + sleep_ms(50); // Wait for the update to apply + #if 0 + set_backlight(255); // Turn backlight on now surprises have passed + #else + gpio_put(lcd_bl, 1); + #endif + } + } + + void ST7701::cleanup() { + if(dma_channel_is_claimed(st_dma)) { + dma_channel_abort(st_dma); + dma_channel_unclaim(st_dma); + } + if(spi) return; // SPI mode needs no further tear down + + if(pio_sm_is_claimed(st_pio, parallel_sm)) { + pio_sm_set_enabled(st_pio, parallel_sm, false); + pio_sm_drain_tx_fifo(st_pio, parallel_sm); + pio_sm_unclaim(st_pio, parallel_sm); + } + } + + void ST7701::configure_display(Rotation rotate) { + uint8_t madctl = 0; + + if(rotate == ROTATE_90 || rotate == ROTATE_270) { + std::swap(width, height); + } + + // 480x480 Square Display + if(width == 480 && height == 480) { + madctl = 0; + } + + command(reg::MADCTL, 1, (char *)&madctl); + } + + void ST7701::write_blocking_dma(const uint8_t *src, size_t len) { + while (dma_channel_is_busy(st_dma)) + ; + dma_channel_set_trans_count(st_dma, len, false); + dma_channel_set_read_addr(st_dma, src, true); + } + + void ST7701::write_blocking_parallel(const uint8_t *src, size_t len) { + write_blocking_dma(src, len); + dma_channel_wait_for_finish_blocking(st_dma); + + // This may cause a race between PIO and the + // subsequent chipselect deassert for the last pixel + while(!pio_sm_is_tx_fifo_empty(st_pio, parallel_sm)) + ; + } + + void ST7701::command(uint8_t command, size_t len, const char *data) { + static uint16_t _data[20] = {0}; + gpio_put(spi_cs, 0); + + // Add leading byte for 9th D/CX bit + uint16_t _command = (dcx::CMD << 8) | command; + spi_write16_blocking(spi, &_command, 1); + + if(data) { + // Add leading bytes for 9th D/CX bits + // TODO: OOOF - I *think* this is how 9bit SPI is supposed to work!? + // We'd probably be better off with some dedicated PIO tomfoolery + for(auto i = 0u; i < len; i++) { + _data[i] = (dcx::DATA << 8) | data[i]; + } + spi_write16_blocking(spi, _data, len); + } + + gpio_put(spi_cs, 1); + } + + void ST7701::update(PicoGraphics *graphics) { + //uint8_t cmd = reg::RAMWR; + + // TODO: Where's my buffer at? Where's my buffer at? + // I left it out back in the PSRAM chip, y'all + // And now I cannot find it even though it's 60k tall + + if(graphics->pen_type == PicoGraphics::PEN_RGB565) { // Display buffer is screen native + // command(cmd, width * height * sizeof(uint16_t), (const char*)graphics->frame_buffer); + } else { + /*graphics->frame_convert(PicoGraphics::PEN_RGB565, [this](void *data, size_t length) { + if (length > 0) { + write_blocking_dma((const uint8_t*)data, length); + } + else { + dma_channel_wait_for_finish_blocking(st_dma); + } + });*/ + } + } + + void ST7701::set_backlight(uint8_t brightness) { + // gamma correct the provided 0-255 brightness value onto a + // 0-65535 range for the pwm counter + float gamma = 2.8; + uint16_t value = (uint16_t)(pow((float)(brightness) / 255.0f, gamma) * 65535.0f + 0.5f); + pwm_set_gpio_level(lcd_bl, value); + } + + void ST7701::wait_for_vsync() { + waiting_for_vsync = true; + while (waiting_for_vsync) __wfe(); + } +} diff --git a/drivers/st7701/st7701.hpp b/drivers/st7701/st7701.hpp new file mode 100644 index 0000000..74edcdd --- /dev/null +++ b/drivers/st7701/st7701.hpp @@ -0,0 +1,95 @@ +#pragma once + +#include "hardware/spi.h" +#include "hardware/dma.h" +#include "hardware/gpio.h" +#include "hardware/pio.h" +#include "hardware/pwm.h" +#include "hardware/clocks.h" +#include "common/pimoroni_common.hpp" +#include "common/pimoroni_bus.hpp" +#include "libraries/pico_graphics/pico_graphics.hpp" + +#include +#include + +namespace pimoroni { + + class ST7701 : public DisplayDriver { + spi_inst_t *spi = PIMORONI_SPI_DEFAULT_INSTANCE; + + //-------------------------------------------------- + // Variables + //-------------------------------------------------- + private: + + // interface pins with our standard defaults where appropriate + uint spi_cs; + uint spi_sck; + uint spi_dat; + uint lcd_bl; + uint parallel_sm; + uint timing_sm; + PIO st_pio; + uint parallel_offset; + uint timing_offset; + uint st_dma; + uint line_dma; + + uint d0 = 1; // First pin of 18-bit parallel interface + uint hsync = 19; + uint vsync = 20; + uint lcd_de = 21; + uint lcd_dot_clk = 22; + + static const uint32_t SPI_BAUD = 8'000'000; + + // Must be a power of 2 + static constexpr int NUM_LINE_BUFFERS = 4; + + public: + // Parallel init + ST7701(uint16_t width, uint16_t height, Rotation rotation, SPIPins control_pins, uint16_t* framebuffer, + uint d0=1, uint hsync=19, uint vsync=20, uint lcd_de = 21, uint lcd_dot_clk = 22); + + void init(); + void cleanup() override; + void update(PicoGraphics *graphics) override; + void set_backlight(uint8_t brightness) override; + + void set_framebuffer(uint16_t* next_fb) { + next_framebuffer = next_fb; + } + + void wait_for_vsync(); + + // Only to be called by ISR + void drive_timing(); + void handle_end_of_line(); + void fill_next_line(); + + private: + void common_init(); + void configure_display(Rotation rotate); + void write_blocking_dma(const uint8_t *src, size_t len); + void write_blocking_parallel(const uint8_t *src, size_t len); + void command(uint8_t command, size_t len = 0, const char *data = NULL); + + void start_line_xfer(); + void start_frame_xfer(); + void init_framebuffer(); + + // Timing status + uint16_t timing_row = 0; + uint16_t timing_phase = 0; + volatile bool waiting_for_vsync = false; + + uint16_t* framebuffer; + uint16_t* next_framebuffer = nullptr; + + uint16_t* line_buffer; + int display_row = 0; + int fill_row = 0; + }; + +} diff --git a/drivers/st7701/st7701_parallel.pio b/drivers/st7701/st7701_parallel.pio new file mode 100644 index 0000000..0a52e8a --- /dev/null +++ b/drivers/st7701/st7701_parallel.pio @@ -0,0 +1,16 @@ +; Output 18 bit parallel RGB666 data every clock +; Wait for irq 4 from the timing SM between each row +; Side-set is data enable + +.program st7701_parallel +.side_set 1 + +.wrap_target + mov x, y side 0 ; y needs to be set to width-1 at init time + wait 1 irq 4 side 0 ; wait for the irq from the timing SM + nop side 1 [1] ; This reduces the offset of the image on the screen from 4 to 3 pixels +loop: ; Not really sure why the offset is there, enable should only have 5ns setup + out pins, 16 side 1 + jmp x-- loop side 1 + mov pins, null side 1 +.wrap diff --git a/drivers/st7701/st7701_presto.cmake b/drivers/st7701/st7701_presto.cmake new file mode 100644 index 0000000..c4fe455 --- /dev/null +++ b/drivers/st7701/st7701_presto.cmake @@ -0,0 +1,12 @@ +add_library(st7701_presto INTERFACE) + +target_sources(st7701_presto INTERFACE + ${CMAKE_CURRENT_LIST_DIR}/st7701.cpp) + +pico_generate_pio_header(st7701_presto ${CMAKE_CURRENT_LIST_DIR}/st7701_parallel.pio) +pico_generate_pio_header(st7701_presto ${CMAKE_CURRENT_LIST_DIR}/st7701_timing.pio) + +target_include_directories(st7701_presto INTERFACE ${CMAKE_CURRENT_LIST_DIR}) + +# Pull in pico libraries that we need +target_link_libraries(st7701_presto INTERFACE pico_stdlib pimoroni_bus hardware_spi hardware_pwm hardware_pio hardware_dma pico_graphics) diff --git a/drivers/st7701/st7701_timing.pio b/drivers/st7701/st7701_timing.pio new file mode 100644 index 0000000..3cf3210 --- /dev/null +++ b/drivers/st7701/st7701_timing.pio @@ -0,0 +1,20 @@ +; Set two sync pins and then wait for a number of cycles based on input word: +; 31: VSync +; 30: HSync +; 29-16: Delay duration in pixel clocks, total loop duration is delay + 3 +; 15-0: Instruction to run after delay, sensible options are: +; nop : 0xa042 ; Do nothing +; irq n : 0xc00n ; Signal to CPU or a data PIO +; Side set is data clock, which runs continuously +.program st7701_timing +.side_set 1 + +.wrap_target + out pins, 2 side 0 ; Set VS & HS + out x, 14 side 1 ; Loop count +sync_loop: + nop side 0 + jmp x--, sync_loop side 1 + out exec, 16 side 0 + nop side 1 +.wrap diff --git a/modules/c/example/example.c b/modules/c/example/example.c deleted file mode 100644 index 68156b8..0000000 --- a/modules/c/example/example.c +++ /dev/null @@ -1,37 +0,0 @@ -#include - -MP_DEFINE_CONST_FUN_OBJ_1(example__del__obj, example__del__); - -MP_DEFINE_CONST_FUN_OBJ_3(example_method_obj, example_method); - -// Class Methods -static const mp_rom_map_elem_t example_locals_dict_table[] = { - { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&example__del__obj) }, - { MP_ROM_QSTR(MP_QSTR_mul), MP_ROM_PTR(&example_method_obj) } -}; - -static MP_DEFINE_CONST_DICT(example_locals_dict, example_locals_dict_table); - -MP_DEFINE_CONST_OBJ_TYPE( - Example_type, - MP_QSTR_Example, - MP_TYPE_FLAG_NONE, - make_new, example_make_new, - locals_dict, (mp_obj_dict_t*)&example_locals_dict -); - -// Module Methods -static const mp_map_elem_t example_globals_table[] = { - { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_example) }, // Module name - { MP_OBJ_NEW_QSTR(MP_QSTR_Example), (mp_obj_t)&Example_type }, // Class name & type -}; - -static MP_DEFINE_CONST_DICT(mp_module_example_globals, example_globals_table); - -const mp_obj_module_t example_user_c_module = { - .base = { &mp_type_module }, - .globals = (mp_obj_dict_t*)&mp_module_example_globals, -}; - -// First argument should match the __name__ given in the globals table -MP_REGISTER_MODULE(MP_QSTR_example, example_user_c_module); \ No newline at end of file diff --git a/modules/c/example/example.cpp b/modules/c/example/example.cpp deleted file mode 100644 index b65268f..0000000 --- a/modules/c/example/example.cpp +++ /dev/null @@ -1,67 +0,0 @@ -#include // Required for allocating C classes into MicroPython heap with placement new - -class Example { - public: - Example () { - - } - - int mul(int a, int b) { - return a * b; - } - - ~Example () { - - } -}; - -// Explicitly typed variant of MP_OBJ_TO_PTR to make C++ happy -#define MP_OBJ_TO_PTR_T(o, t) ((t *)(o)) - -// Macro for assigning *self in class methods -#define __self__ MP_OBJ_TO_PTR_T(self_in, Example_obj_t) - - -extern "C" { -#include "example.h" - -typedef struct Example_obj_t { - mp_obj_base_t base; - void *pins; - Example *cls; -} Example_obj_t; - -mp_obj_t example_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { - Example_obj_t *self = mp_obj_malloc_with_finaliser(Example_obj_t, &Example_type); - - // Use C++ "placement new" to place an instance of our C++ class in - // memory allocated by MicroPython's m_new. - self->cls = new(m_new(Example, 1)) Example(); - - return MP_OBJ_FROM_PTR(self); -} - -mp_obj_t example__del__(mp_obj_t self_in) { - // Explicitly call the destructor - // self->class->~Example(); - - // Or use delete - delete(__self__->cls); - - // Explicitly inform the GC that the memory is free - // Usually this object is deleted and the pointer goes out of scope - // and gets GC'd anyway so we don't *need* to do this. - m_del(Example, __self__->cls, 1); - - return mp_const_none; -} - -mp_obj_t example_method(mp_obj_t self_in, mp_obj_t a_in, mp_obj_t b_in) { - int a = mp_obj_get_int(a_in); - int b = mp_obj_get_int(b_in); - - int c = __self__->cls->mul(a, b); - - return mp_obj_new_int(c); -} -}; \ No newline at end of file diff --git a/modules/c/example/example.h b/modules/c/example/example.h deleted file mode 100644 index 085dcb2..0000000 --- a/modules/c/example/example.h +++ /dev/null @@ -1,8 +0,0 @@ -#include "py/runtime.h" - -extern const mp_obj_type_t Example_type; - -extern mp_obj_t example_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args); -extern mp_obj_t example__del__(mp_obj_t self_in); - -extern mp_obj_t example_method(mp_obj_t self_in, mp_obj_t a_in, mp_obj_t b_in); \ No newline at end of file diff --git a/modules/c/example/micropython.cmake b/modules/c/example/micropython.cmake deleted file mode 100644 index 853de6e..0000000 --- a/modules/c/example/micropython.cmake +++ /dev/null @@ -1,16 +0,0 @@ -# Create an INTERFACE library for our CPP module. -add_library(usermod_pga_example INTERFACE) - -# Add our source files to the library. -target_sources(usermod_pga_example INTERFACE - ${CMAKE_CURRENT_LIST_DIR}/example.cpp - ${CMAKE_CURRENT_LIST_DIR}/example.c -) - -# Add the current directory as an include directory. -target_include_directories(usermod_pga_example INTERFACE - ${CMAKE_CURRENT_LIST_DIR} -) - -# Link our INTERFACE library to the usermod target. -target_link_libraries(usermod INTERFACE usermod_pga_example) \ No newline at end of file diff --git a/modules/c/presto/micropython.cmake b/modules/c/presto/micropython.cmake new file mode 100644 index 0000000..257ef8c --- /dev/null +++ b/modules/c/presto/micropython.cmake @@ -0,0 +1,22 @@ +add_library(usermod_presto INTERFACE) + +target_sources(usermod_presto INTERFACE + ${CMAKE_CURRENT_LIST_DIR}/presto.c + ${CMAKE_CURRENT_LIST_DIR}/presto.cpp + ${CMAKE_CURRENT_LIST_DIR}../../../drivers/presto/st7701.cpp + ${PIMORONI_PICO_PATH}/libraries/pico_graphics/pico_graphics_pen_rgb565.cpp +) +pico_generate_pio_header(usermod_presto ${CMAKE_CURRENT_LIST_DIR}/../../../drivers/presto/st7701_parallel.pio) +pico_generate_pio_header(usermod_presto ${CMAKE_CURRENT_LIST_DIR}/../../../drivers/presto/st7701_timing.pio) + +target_include_directories(usermod_presto INTERFACE + ${CMAKE_CURRENT_LIST_DIR} + ${PIMORONI_PICO_PATH}/libraries/pico_graphics/ + ${CMAKE_CURRENT_LIST_DIR}/../../../drivers/presto/ +) + +target_compile_definitions(usermod_presto INTERFACE + MODULE_PRESTO_ENABLED=1 +) + +target_link_libraries(usermod INTERFACE usermod_presto) \ No newline at end of file diff --git a/modules/c/presto/presto.c b/modules/c/presto/presto.c new file mode 100644 index 0000000..45e01a1 --- /dev/null +++ b/modules/c/presto/presto.c @@ -0,0 +1,45 @@ +#include "presto.h" + + +/***** Methods *****/ + +MP_DEFINE_CONST_FUN_OBJ_1(Presto___del___obj, Presto___del__); +MP_DEFINE_CONST_FUN_OBJ_2(Presto_update_obj, Presto_update); + +/***** Binding of Methods *****/ + +static const mp_rom_map_elem_t Presto_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&Presto___del___obj) }, + { MP_ROM_QSTR(MP_QSTR_update), MP_ROM_PTR(&Presto_update_obj) }, + + { MP_ROM_QSTR(MP_QSTR_WIDTH), MP_ROM_INT(WIDTH) }, + { MP_ROM_QSTR(MP_QSTR_HEIGHT), MP_ROM_INT(HEIGHT) }, +}; + +static MP_DEFINE_CONST_DICT(Presto_locals_dict, Presto_locals_dict_table); + + +MP_DEFINE_CONST_OBJ_TYPE( + Presto_type, + MP_QSTR_Presto, + MP_TYPE_FLAG_NONE, + make_new, Presto_make_new, + locals_dict, (mp_obj_dict_t*)&Presto_locals_dict +); + +/***** Globals Table *****/ +static const mp_map_elem_t presto_globals_table[] = { + { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_presto) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_Presto), (mp_obj_t)&Presto_type }, +}; + +static MP_DEFINE_CONST_DICT(mp_module_presto_globals, presto_globals_table); + +/***** Module Definition *****/ + +const mp_obj_module_t presto_user_cmodule = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t*)&mp_module_presto_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_presto, presto_user_cmodule); \ No newline at end of file diff --git a/modules/c/presto/presto.cpp b/modules/c/presto/presto.cpp new file mode 100644 index 0000000..61ebedf --- /dev/null +++ b/modules/c/presto/presto.cpp @@ -0,0 +1,145 @@ +#include "drivers/st7701_presto/st7701.hpp" +#include "libraries/pico_graphics/pico_graphics.hpp" +#include "micropython/modules/util.hpp" +#include +#include + + +#include "hardware/structs/ioqspi.h" +#include "hardware/structs/qmi.h" +#include "hardware/structs/xip_ctrl.h" + + +using namespace pimoroni; + + +extern "C" { +#include "presto.h" +#include "py/builtin.h" +#include + +void __printf_debug_flush() { + for(auto i = 0u; i < 10; i++) { + sleep_ms(1); + mp_event_handle_nowait(); + } +} + +int mp_vprintf(const mp_print_t *print, const char *fmt, va_list args); + +void presto_debug(const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + int ret = mp_vprintf(&mp_plat_print, fmt, ap); + va_end(ap); + __printf_debug_flush(); + (void)ret; +} + +/***** Variables Struct *****/ +typedef struct _presto_obj_t { + mp_obj_base_t base; + ST7701* presto; + uint16_t* next_fb; + uint16_t* curr_fb; +} _presto_obj_t; + +typedef struct _ModPicoGraphics_obj_t { + mp_obj_base_t base; + PicoGraphics *graphics; + DisplayDriver *display; +} ModPicoGraphics_obj_t; + + + +void presto_core1_entry() { + multicore_fifo_push_blocking(0); // TODO: Remove, debug to signal core has actually started + + ST7701 *presto = (ST7701*)multicore_fifo_pop_blocking(); + + presto->init(); + multicore_fifo_push_blocking(0); // Todo handle issues here?*/ + while (1) __wfe(); +} + +#define stack_size 4096u +static uint32_t core1_stack[stack_size] = {0}; + +mp_obj_t presto_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + _presto_obj_t *self = nullptr; + + enum { ARG_pio, ARG_sm, ARG_pins, ARG_common_pin, ARG_direction, ARG_counts_per_rev, ARG_count_microsteps, ARG_freq_divider }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_pio, MP_ARG_INT }, + { MP_QSTR_sm, MP_ARG_INT } + }; + + // Parse args. + 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); + + presto_debug("malloc self\n"); + self = mp_obj_malloc_with_finaliser(_presto_obj_t, &presto_type); + + presto_debug("set fb pointers\n"); + self->curr_fb = m_new(uint16_t, 28800); // (uint16_t*)0x11000000; + self->next_fb = self->curr_fb; //(uint16_t*)0x11080000; + + presto_debug("m_new_class(ST7701...\n"); + ST7701 *presto = m_new_class(ST7701, WIDTH, HEIGHT, ROTATE_0, + SPIPins{spi1, LCD_CS, LCD_CLK, LCD_DAT, PIN_UNUSED, LCD_DC, BACKLIGHT}, + self->next_fb, + LCD_D0); + + self->presto = presto; + + presto_debug("launch core1\n"); + multicore_reset_core1(); + //multicore_launch_core1(presto_core1_entry); + // multicore_launch_core1 probably uses malloc for its stack, and will return but apparently not launch core1 on MicroPython + multicore_launch_core1_with_stack(presto_core1_entry, core1_stack, stack_size); + presto_debug("waiting for core1...\n"); + multicore_fifo_pop_blocking(); + presto_debug("core1 running...\n"); + + presto_debug("signal core1\n"); + multicore_fifo_push_blocking((uintptr_t)self->presto); + int res = multicore_fifo_pop_blocking(); + presto_debug("core1 returned\n"); + + //presto_debug("presto->init(): "); + //presto->init(); + //presto_debug("ok\n"); + + if(res != 0) { + mp_raise_msg(&mp_type_RuntimeError, "presto: failed to start ST7701 on Core1."); + } + + return MP_OBJ_FROM_PTR(self); +} + +extern mp_obj_t presto_update(mp_obj_t self_in, mp_obj_t graphics_in) { + _presto_obj_t *self = MP_OBJ_TO_PTR2(self_in, _presto_obj_t); + ModPicoGraphics_obj_t *picographics = MP_OBJ_TO_PTR2(graphics_in, ModPicoGraphics_obj_t); + + // Wait for any pending flip to finish + self->presto->wait_for_vsync(); + + self->presto->set_framebuffer(self->next_fb); + + // Flip the buffers + std::swap(self->next_fb, self->curr_fb); + + // Make sure PicoGraphics is drawing into our current front buffer + picographics->graphics->frame_buffer = (uint8_t *)self->next_fb; + + return mp_const_none; +} + +mp_obj_t presto___del__(mp_obj_t self_in) { + _presto_obj_t *self = MP_OBJ_TO_PTR2(self_in, _presto_obj_t); + m_del_class(ST7701, self->presto); + return mp_const_none; +} + +} \ No newline at end of file diff --git a/modules/c/presto/presto.h b/modules/c/presto/presto.h new file mode 100644 index 0000000..1c104b4 --- /dev/null +++ b/modules/c/presto/presto.h @@ -0,0 +1,21 @@ +// Include MicroPython API. +#include "py/runtime.h" + +/***** Constants *****/ +static const uint BACKLIGHT = 38; + +static const int WIDTH = 480; +static const int HEIGHT = 480; +static const uint LCD_CLK = 26; +static const uint LCD_CS = 28; +static const uint LCD_DAT = 27; +static const uint LCD_DC = -1; +static const uint LCD_D0 = 1; + +/***** Extern of Class Definition *****/ +extern const mp_obj_type_t Presto_type; + +/***** Extern of Class Methods *****/ +extern mp_obj_t Presto_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args); +extern mp_obj_t Presto_update(mp_obj_t self_in, mp_obj_t graphics_in); +extern mp_obj_t Presto___del__(mp_obj_t self_in); \ No newline at end of file diff --git a/modules/default.cmake b/modules/default.cmake index 37272f6..5288acc 100644 --- a/modules/default.cmake +++ b/modules/default.cmake @@ -14,13 +14,13 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}") set(CMAKE_C_STANDARD 11) set(CMAKE_CXX_STANDARD 17) -include(c/example/micropython) +include(c/presto/micropython) -# include(micropython-common) -# enable_ulab() +include(micropython-common) +enable_ulab() # C++ Magic Memory -# include(cppmem/micropython) +include(cppmem/micropython) # Disable build-busting C++ exceptions include(micropython-disable-exceptions) \ No newline at end of file diff --git a/modules/default.py b/modules/default.py new file mode 100644 index 0000000..eef495b --- /dev/null +++ b/modules/default.py @@ -0,0 +1,8 @@ +# Include the manifest.py from micropython/ports/rp2/boards/manifest.py +include("$(PORT_DIR)/boards/manifest.py") + +# Include the manifest.py from micropython//manifest.py +include("$(BOARD_DIR)/manifest.py") + +# Include pga/modules/py_frozen +freeze("py_frozen/") \ No newline at end of file diff --git a/modules/frozen.py b/modules/default.txt similarity index 100% rename from modules/frozen.py rename to modules/default.txt diff --git a/modules/littlefs.txt b/modules/littlefs.txt deleted file mode 100644 index f2e897a..0000000 --- a/modules/littlefs.txt +++ /dev/null @@ -1 +0,0 @@ -lib/*/*.py \ No newline at end of file diff --git a/pga2040/manifest.py b/pga2040/manifest.py deleted file mode 100644 index 8505dcb..0000000 --- a/pga2040/manifest.py +++ /dev/null @@ -1,5 +0,0 @@ -# Include the manifest.py from micropython/ports/rp2/boards/manifest.py -include("$(PORT_DIR)/boards/manifest.py") - -# Include frozen.py from pga/modules/frozen.py -include("../modules/frozen.py") \ No newline at end of file diff --git a/pga2040/mpconfigboard.cmake b/pga2040/mpconfigboard.cmake deleted file mode 100644 index c2bc3c5..0000000 --- a/pga2040/mpconfigboard.cmake +++ /dev/null @@ -1,13 +0,0 @@ -# cmake file for the Pimoroni PGA2040 -set(PICO_BOARD "pga2040") -set(PICO_PLATFORM "rp2040") - -# Make sure we find pga2040.h (PICO_BOARD) in the current dir -set(PICO_BOARD_HEADER_DIRS ${CMAKE_CURRENT_LIST_DIR}) - -# Board specific version of the frozen manifest -set(MICROPY_FROZEN_MANIFEST ${MICROPY_BOARD_DIR}/manifest.py) - -# If USER_C_MODULES or MicroPython customisations use malloc then -# there needs to be some RAM reserved for the C heap -set(MICROPY_C_HEAP_SIZE 4096) diff --git a/pga2040/mpconfigboard.h b/pga2040/mpconfigboard.h deleted file mode 100644 index 6cfced4..0000000 --- a/pga2040/mpconfigboard.h +++ /dev/null @@ -1,6 +0,0 @@ -// Board and hardware specific configuration -#define MICROPY_HW_BOARD_NAME "PGA2040" - -// Portion of onboard flash to reserve for the user filesystem -// PGA2040 has 8MB flash, so reserve 1MiB for the firmware and leave 7MiB -#define MICROPY_HW_FLASH_STORAGE_BYTES (7 * 1024 * 1024) diff --git a/pga2040/pga2040.h b/pga2040/pga2040.h deleted file mode 100644 index 25b9dd2..0000000 --- a/pga2040/pga2040.h +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. - * - * SPDX-License-Identifier: BSD-3-Clause - */ - -// ----------------------------------------------------- -// NOTE: THIS HEADER IS ALSO INCLUDED BY ASSEMBLER SO -// SHOULD ONLY CONSIST OF PREPROCESSOR DIRECTIVES -// ----------------------------------------------------- - -// pico_cmake_set PICO_PLATFORM=rp2040 - -#ifndef _BOARDS_PIMORONI_PGA2040_H -#define _BOARDS_PIMORONI_PGA2040_H - -// For board detection -#define PIMORONI_PGA2040 - -// --- UART --- -#ifndef PICO_DEFAULT_UART -#define PICO_DEFAULT_UART 0 -#endif - -#ifndef PICO_DEFAULT_UART_TX_PIN -#define PICO_DEFAULT_UART_TX_PIN 0 -#endif - -#ifndef PICO_DEFAULT_UART_RX_PIN -#define PICO_DEFAULT_UART_RX_PIN 1 -#endif - -// --- LED --- -// no PICO_DEFAULT_LED_PIN -// no PICO_DEFAULT_WS2812_PIN - -// --- I2C --- -#ifndef PICO_DEFAULT_I2C -#define PICO_DEFAULT_I2C 0 -#endif -#ifndef PICO_DEFAULT_I2C_SDA_PIN -#define PICO_DEFAULT_I2C_SDA_PIN 4 -#endif -#ifndef PICO_DEFAULT_I2C_SCL_PIN -#define PICO_DEFAULT_I2C_SCL_PIN 5 -#endif - -// --- SPI --- -#ifndef PICO_DEFAULT_SPI -#define PICO_DEFAULT_SPI 0 -#endif -#ifndef PICO_DEFAULT_SPI_SCK_PIN -#define PICO_DEFAULT_SPI_SCK_PIN 18 -#endif -#ifndef PICO_DEFAULT_SPI_TX_PIN -#define PICO_DEFAULT_SPI_TX_PIN 19 -#endif -#ifndef PICO_DEFAULT_SPI_RX_PIN -#define PICO_DEFAULT_SPI_RX_PIN 16 -#endif -#ifndef PICO_DEFAULT_SPI_CSN_PIN -#define PICO_DEFAULT_SPI_CSN_PIN 17 -#endif - -// --- FLASH --- -#define PICO_BOOT_STAGE2_CHOOSE_W25Q080 1 - -#ifndef PICO_FLASH_SPI_CLKDIV -#define PICO_FLASH_SPI_CLKDIV 2 -#endif - -// pico_cmake_set_default PICO_FLASH_SIZE_BYTES = (8 * 1024 * 1024) -#ifndef PICO_FLASH_SIZE_BYTES -#define PICO_FLASH_SIZE_BYTES (8 * 1024 * 1024) -#endif -// All boards have B1 RP2040 -#ifndef PICO_RP2040_B0_SUPPORTED -#define PICO_RP2040_B0_SUPPORTED 0 -#endif - -#endif diff --git a/pga2040/pins.csv b/pga2040/pins.csv deleted file mode 100644 index 16e3340..0000000 --- a/pga2040/pins.csv +++ /dev/null @@ -1,28 +0,0 @@ -GP0,GPIO0 -GP1,GPIO1 -GP2,GPIO2 -GP3,GPIO3 -GP4,GPIO4 -GP5,GPIO5 -GP6,GPIO6 -GP7,GPIO7 -GP8,GPIO8 -GP9,GPIO9 -GP10,GPIO10 -GP11,GPIO11 -GP12,GPIO12 -GP13,GPIO13 -GP14,GPIO14 -GP15,GPIO15 -GP16,GPIO16 -GP17,GPIO17 -GP18,GPIO18 -GP19,GPIO19 -GP20,GPIO20 -GP21,GPIO21 -GP22,GPIO22 -GP25,GPIO25 -GP26,GPIO26 -GP27,GPIO27 -GP28,GPIO28 -LED,GPIO25 diff --git a/pga2350/manifest.py b/pga2350/manifest.py deleted file mode 100644 index 8505dcb..0000000 --- a/pga2350/manifest.py +++ /dev/null @@ -1,5 +0,0 @@ -# Include the manifest.py from micropython/ports/rp2/boards/manifest.py -include("$(PORT_DIR)/boards/manifest.py") - -# Include frozen.py from pga/modules/frozen.py -include("../modules/frozen.py") \ No newline at end of file diff --git a/pga2350/mpconfigboard.h b/pga2350/mpconfigboard.h deleted file mode 100644 index 802a1a4..0000000 --- a/pga2350/mpconfigboard.h +++ /dev/null @@ -1,14 +0,0 @@ -// Board and hardware specific configuration - -// Board and hardware specific configuration -#ifndef MICROPY_HW_BOARD_NAME -// Might be defined by mpconfigvariant_VARIANT.cmake -#define MICROPY_HW_BOARD_NAME "PGA2350" -#endif - -// Portion of onboard flash to reserve for the user filesystem -// PGA2350 has 16MB flash, so reserve 2MiB for the firmware and leave 14MiB -#define MICROPY_HW_FLASH_STORAGE_BYTES (14 * 1024 * 1024) - -// Alias the chip select pin specified by pga2350.h -#define MICROPY_HW_PSRAM_CS_PIN PIMORONI_PGA2350_PSRAM_CS_PIN diff --git a/pga2350/mpconfigvariant.cmake b/pga2350/mpconfigvariant.cmake deleted file mode 100644 index e69de29..0000000 diff --git a/pga2350/mpconfigvariant_PSRAM.cmake b/pga2350/mpconfigvariant_PSRAM.cmake deleted file mode 100644 index 8bee1f2..0000000 --- a/pga2350/mpconfigvariant_PSRAM.cmake +++ /dev/null @@ -1,5 +0,0 @@ -# Override the MicroPython board name -list(APPEND MICROPY_DEF_BOARD - "MICROPY_HW_ENABLE_PSRAM=1" - "MICROPY_HW_BOARD_NAME=\"PGA2350 (PSRAM)\"" -) diff --git a/presto/manifest.py b/presto/manifest.py new file mode 100644 index 0000000..4819e73 --- /dev/null +++ b/presto/manifest.py @@ -0,0 +1 @@ +# Board-specific frozen libs go here \ No newline at end of file diff --git a/pga2350/mpconfigboard.cmake b/presto/mpconfigboard.cmake similarity index 88% rename from pga2350/mpconfigboard.cmake rename to presto/mpconfigboard.cmake index d25ae32..72c0077 100644 --- a/pga2350/mpconfigboard.cmake +++ b/presto/mpconfigboard.cmake @@ -1,5 +1,5 @@ # cmake file for the Pimoroni PGA2350 -set(PICO_BOARD "pga2350") +set(PICO_BOARD "presto") set(PICO_PLATFORM "rp2350-arm-s") set(PICO_NUM_GPIOS 48) @@ -11,4 +11,4 @@ set(MICROPY_FROZEN_MANIFEST ${MICROPY_BOARD_DIR}/manifest.py) # If USER_C_MODULES or MicroPython customisations use malloc then # there needs to be some RAM reserved for the C heap -set(MICROPY_C_HEAP_SIZE 4096) \ No newline at end of file +set(MICROPY_C_HEAP_SIZE 4096) diff --git a/presto/mpconfigboard.h b/presto/mpconfigboard.h new file mode 100644 index 0000000..d9af249 --- /dev/null +++ b/presto/mpconfigboard.h @@ -0,0 +1,13 @@ +// Board and hardware specific configuration + +#define MICROPY_HW_BOARD_NAME "Presto" + +// Portion of onboard flash to reserve for the user filesystem +// PGA2350 has 16MB flash, so reserve 2MiB for the firmware and leave 14MiB +#define MICROPY_HW_FLASH_STORAGE_BYTES (14 * 1024 * 1024) + +// Alias the chip select pin specified by presto.h +#define MICROPY_HW_PSRAM_CS_PIN PIMORONI_PRESTO_PSRAM_CS_PIN +#define MICROPY_HW_ENABLE_PSRAM (1) + +#define MICROPY_PY_THREAD (0) \ No newline at end of file diff --git a/pga2350/pins.csv b/presto/pins.csv similarity index 100% rename from pga2350/pins.csv rename to presto/pins.csv diff --git a/pga2350/pga2350.h b/presto/presto.h similarity index 90% rename from pga2350/pga2350.h rename to presto/presto.h index 9690f13..4bc9521 100644 --- a/pga2350/pga2350.h +++ b/presto/presto.h @@ -9,19 +9,18 @@ // SHOULD ONLY CONSIST OF PREPROCESSOR DIRECTIVES // ----------------------------------------------------- -// This header may be included by other board headers as "boards/pimoroni_pga2350.h" +// This header may be included by other board headers as "boards/pimoroni_presto.h" // pico_cmake_set PICO_PLATFORM=rp2350 -#ifndef _BOARDS_PIMORONI_PGA2350_H -#define _BOARDS_PIMORONI_PGA2350_H +#ifndef _BOARDS_PIMORONI_PRESTO_H +#define _BOARDS_PIMORONI_PRESTO_H // For board detection -#define PIMORONI_PGA2350 -#define PIMORONI_PGA2350_16MB +#define PIMORONI_PRESTO // --- BOARD SPECIFIC --- -#define PIMORONI_PGA2350_PSRAM_CS_PIN 47 +#define PIMORONI_PRESTO_PSRAM_CS_PIN 47 // --- UART --- #ifndef PICO_DEFAULT_UART