|
| 1 | +# SPDX-FileCopyrightText: 2024 Jeff Epler for Adafruit Industries |
| 2 | +# |
| 3 | +# SPDX-License-Identifier: MIT |
| 4 | + |
| 5 | +import array |
| 6 | + |
| 7 | +import ulab |
| 8 | +import rp2pio |
| 9 | +import board |
| 10 | +import adafruit_pioasm |
| 11 | +import picodvi |
| 12 | +import displayio |
| 13 | + |
| 14 | +vdata = board.D9 # Followed by hsync on D10 & vsync on D11 |
| 15 | +pixel_sync_out = board.D5 |
| 16 | +pixel_frequency = 10_694_250 # oddly specific |
| 17 | +clocks_per_pixel = 10 |
| 18 | +fine_pixel = 0 |
| 19 | +active_lines = 240 |
| 20 | +blanking_lines = 18 |
| 21 | +active_pixels = 640 |
| 22 | +blanking_pixels = 58 |
| 23 | +total_lines = active_lines + blanking_lines |
| 24 | + |
| 25 | +displayio.release_displays() |
| 26 | + |
| 27 | +dvi_pins = dict( |
| 28 | + clk_dp=board.CKP, |
| 29 | + clk_dn=board.CKN, |
| 30 | + red_dp=board.D0P, |
| 31 | + red_dn=board.D0N, |
| 32 | + green_dp=board.D1P, |
| 33 | + green_dn=board.D1N, |
| 34 | + blue_dp=board.D2P, |
| 35 | + blue_dn=board.D2N, |
| 36 | +) |
| 37 | + |
| 38 | +try: |
| 39 | + dvi = picodvi.Framebuffer(640, 240, **dvi_pins, color_depth=1) |
| 40 | +except ValueError: |
| 41 | + print( |
| 42 | + "Note: This version of CircuitPython does not support 640x240\n." |
| 43 | + "Display will be compressed vertically." |
| 44 | + ) |
| 45 | + displayio.release_displays() |
| 46 | + dvi = picodvi.Framebuffer(640, 480, **dvi_pins, color_depth=1) |
| 47 | + |
| 48 | +# Clear the display |
| 49 | +ulab.numpy.frombuffer(dvi, dtype=ulab.numpy.uint8)[:] = 0 |
| 50 | + |
| 51 | +def instruction_gen(): |
| 52 | + yield total_lines - 2 |
| 53 | + for _ in range(blanking_lines): |
| 54 | + yield from (1, 0) # 0 active pixels is special-cased |
| 55 | + for _ in range(active_lines): |
| 56 | + yield from (blanking_pixels - 1, active_pixels - 1) |
| 57 | + |
| 58 | + |
| 59 | +instruction = array.array("L", instruction_gen()) |
| 60 | +print(instruction) |
| 61 | + |
| 62 | +jmp_0 = adafruit_pioasm.Program("jmp 0") |
| 63 | + |
| 64 | +program = adafruit_pioasm.Program( |
| 65 | + f""" |
| 66 | +.side_set 1 |
| 67 | +
|
| 68 | + .wrap_target |
| 69 | + out y, 32 ; get total line count |
| 70 | + wait 0, pin 2 |
| 71 | + wait 1, pin 2 ; wait for vsync |
| 72 | +
|
| 73 | +wait_line_inactive: |
| 74 | + out x, 32 ; get total line count |
| 75 | + wait 0, pin 1 |
| 76 | + wait 1, pin 1; wait for hsync |
| 77 | +
|
| 78 | +wait_line_active: |
| 79 | + nop [{clocks_per_pixel-2}] |
| 80 | + jmp x--, wait_line_active ; count off non-active pixels |
| 81 | +
|
| 82 | + out x, 32 [{fine_pixel}] ; get line active pixels & perform fine pixel adjust |
| 83 | + jmp !x, wait_line_inactive ; no pixels this line, wait next hsync |
| 84 | +
|
| 85 | +capture_active_pixels: |
| 86 | + in pins, 1 side 1 |
| 87 | + jmp x--, capture_active_pixels [{clocks_per_pixel-2}] ; more pixels |
| 88 | + jmp y--, wait_line_inactive ; more lines? |
| 89 | + .wrap |
| 90 | +""" |
| 91 | +) |
| 92 | + |
| 93 | +pio = rp2pio.StateMachine( |
| 94 | + program.assembled, |
| 95 | + frequency=pixel_frequency * clocks_per_pixel, |
| 96 | + first_in_pin=vdata, |
| 97 | + in_pin_count=3, |
| 98 | + # in_pin_pull_up=True, |
| 99 | + first_sideset_pin=pixel_sync_out, |
| 100 | + auto_pull=True, |
| 101 | + pull_threshold=32, |
| 102 | + auto_push=True, |
| 103 | + push_threshold=32, |
| 104 | + offset=0, |
| 105 | + **program.pio_kwargs, |
| 106 | +) |
| 107 | + |
| 108 | +words_per_row = 640 // 32 |
| 109 | +first_row = (dvi.height - 240) // 2 # adjust text to center if in 640x480 mode |
| 110 | +buf = memoryview(dvi).cast("L")[ |
| 111 | + first_row * words_per_row : (first_row + active_lines) * words_per_row |
| 112 | +] |
| 113 | +assert len(buf) == 4800 # Check that the right amount will be transferred |
| 114 | + |
| 115 | +b = array.array("L", [0]) |
| 116 | + |
| 117 | +while True: |
| 118 | + pio.run(jmp_0.assembled) |
| 119 | + pio.clear_rxfifo() |
| 120 | + pio.write_readinto(instruction, buf) |
0 commit comments