Close
0%
0%

Pi Pico PAL TV Pong

Final project of the Raspberry Pi Pico and RP2040 Deep Dive course

Similar projects worth following
For the final project of the Raspberry Pi Pico Deep Dive course, you are going to implement a Pong game with PAL TV output. The idea is to use the Pi Pico's PIO peripheral to generate the composite PAL signals.

You can use the following template, which includes a PAL TV as well as four push buttons wired to a Pi Pico:

https://wokwi.com/arduino/projects/301402156123030026

See the "Details" section for explanation about the TV signal you need to generate and some useful hints.

Please tag your projects with "PI-PONG".

Pi Pico Pong on PAL TV

You are going to implement a Pong game with PAL TV output. The idea is to use the Pi Pico's PIO peripheral to generate the composite PAL signals.

Deliverables

You'll need to submit a working pong game that takes advantage of the PIO peripheral to generate the composite PAL signal. You don't need to test it against a physical TV - if it works in the simulation, that's good enough.

In addition, please include your ARM assembly implementation of gcd() and square() - see class 3 homework for details.

You can choose whether to write your solution in C++ (i.e. using the Arduino Core for the Pi Pico) or in MicroPython. From my experience, MicroPython can be a challenging as you need to quickly feed the data into the PIO peripheral. 

The pong game can be very basic. Two paddles, one ball, bouncing and moving the paddle with the buttons is enough. But feel free to add fancy features such as sound, 2-player support, scoring, etc. You can find some inspiration here.

Please create a Hackaday project page for your submission and add the "PI-PONG" tag so we can easily find it.

If you have any questions, you're invited to ask on the #rp2040js discord channel.

Simulation Template

You can use this simulation template for your project. The project pinout is as follows:

Pi Pico Pin

Description

0

UART TX - for debugging with the Serial Monitor

2

PAL SYNC

3

PAL DATA

4

Player 1 - Up button

5

Player 1 - Down button

6

Player 2 - Up button

7

Player 2 - Down button

Notes:

  1. Player 2 is optional. You can also let the computer control the second player.
  2. The buttons have keyboard shortcuts: w/s for player 1 up/down buttons, and up arrow/down arrow for player 2 up/down buttons.

Composite PAL Signal

PAL works at 25 frames per second. However, each frame is split into two parts, called "fields". The first field contains the odd lines, and the second field contains the even lines. This is called interlacing. Each frame takes 40ms (so you get 25 frames/second), and each field takes 20ms (half that time).

Your code would need to generate and send these frames to the TV. I strongly recommend to prototype the code in the simulator. Testing on a physical TV is totally optional, but if you are interested, I included the instructions below.

To generate the frames, you'll need to drive two signals: SYNC and DATA. In the simulation, the DATA signal is connected to the IN pin of the wokwi-tv element.

The SYNC signal indicates the start of a new frame or the start of a new line, depending on the length of the signal. This is how the TV knows when to move to the next line, or draw the next frame.

The DATA signal indicates the color of the current pixel. Drive it high for white, low for black. The faster you drive the DATA signal, the better horizontal resolution that you get. For instance, if you change the DATA signal 2 million times per second (2MHz), you get about 128 pixels/line resolution (each line is 64us, so 500ns per pixel gives at 128 pixels/line).

Sync Signals

Note: You can skip to the next section if you just want to get it to work in the simulator.

In general, there are three types of synchronization signals:

Short Vertical 

SYNC goes low for 2us, then high for 30us

Long Vertical 

SYNC goes low for 30us, then high for 2us

Horizontal

SYNC goes low for 4us, then high for 60us (rest of the scanline)

Whenever the SYNC goes low, the DATA signal also has to be kept low (see below, "Composite signal", to know why).

Each field starts with a sequence of several Short Vertical and Long Vertical sync pulses. The amount of the Vertical Short pulses varies between even and odd fields:

Odd field

6 Short Vertical pulses, then 5 Long Vertical pulses, then 5 Short Vertical pulses

Even field

5 Short Vertical pulses, then 5 Long Vertical pulses, then 4 Short Vertical pulses

After sending the sequence of vertical synchronization sequence, you need to send 305 horizontal synchronization pulses, one...

Read more »

  • Logic Analyzer Debugging

    Uri Shaked07/14/2021 at 22:06 0 comments

    A quick tip to help you debugging your projects: you can use the virtual logic analyzer to view the signals generated by your PIO machine!

    Add a logic analyzer to your project by clicking on the purple "+" button and selecting "Logic analyzer (8 channels)":

    Then connect pins D0 / D1 to your DATA / SYNC lines (these are Pico pins GP2 / GP3 if you use the tv-pong project template).

    Start the simulation, and you should see the Logic Analyzer's green activity LEDs blink as the data is coming in, as well as the number of samples collected:

    When you stop the simulation, the Logic Analyzer will download a VCD file with the recorded signals to your computer.

    You can use PulseView to open the file and view the recorded signals:

    For more information and detailed instructions on how to load the signal file into PulseView, check out the Logic Analyzer guide.

  • How to approach the project

    Uri Shaked06/25/2021 at 18:10 0 comments

    The Pi Pong project is not easy, and can even be overwhelming. It takes time and practice to wrap your head around the programming model of the Pi Pico's PIO, and it could be difficult to swallow this project in a single bite.

    You can find here a step-by-step breakdown, which can guide you through the implementation of the project. It's totally optional - use it if you want, or just go on your own.

    Step 1 - White Screen

    Fill the screen with white color. At minimum, you'll need to drive the DATA signal high, and generate at least one Long Vertical pulse, followed by a series of Horizontal sync pulses.

    And in pseudo code:

    SYNC ← HIGH
    Repeat forever:
      SYNC ← LOW
      wait 30us
      SYNC ← HIGH
      wait 2us
      repeat 300 (or a similar number) times:
        SYNC ← LOW
        wait 4us
        SYNC ← HIGH
        wait 60us

    I suggest coding the SYNC signal driving logic into a PIO machine, so you get precise timing. 

    Step 2 - Half white

    It's time to take control of the DATA line too! Make it go HIGH about 6 microseconds after the end of the Horizontal Sync signal, then LOW again after ~22 microseconds.

    You can start with controlling both DATA and SYNC using a single PIO machine, and then move on to controlling them with two distinct machines that synchronize using the IRQ and WAIT instructions.

    Pseudo code:

    DATA ← LOW
    SYNC ← HIGH
    Repeat forever:
      SYNC ← LOW
      wait 30us
      SYNC ← HIGH
      wait 2us
      repeat 300 (or a similar number) times:
        SYNC ← LOW
        wait 4us
        SYNC ← HIGH
        wait 10us
        DATA ← HIGH
        wait 22us 
        DATA ← LOW
        wait 28us

    Step 3 - Frame buffer

    Instead of hard-coding the data line to stay HIGH for the first half of each line, we'll let the program feed the data. I suggest starting with 32 bits (pixels) per line (not so great resolution, but still usable), then going up to 128 or even 256 bits.

    The idea is to have two PIO machines: one will drive the SYNC signal, and the second one will pull the data from the program one 32-bit word at a time, and then drive it to the DATA signal. Using the "Autopull" PIO setting is highly recommended!

    Your program will start both PIO machines, then feed the data to the Data machine. I suggest creating a buffer with the screen contents (frame buffer), and then repeatedly copy data from this buffer into the TX FIFO of the DATA-driving PIO machine. You can feed the data using an interrupt handler (either TXNFULL interrupt/IRQ triggered explicitly by the PIO machine code).

    Note: I recommend using C++ (Arduino core) for the game. But if you do use MicroPython, define your interrupt handler as a hardware interrupt (pass "hard=true" when defining the handler). Note that there are some limitations for what you can do inside an interrupt handler: for instance, memory allocation isn't allowed.

    Note 2Ideally, you can use the DMA controller to automatically feed data from memory into your Data machine. DMA is currently not supported in the emulator, but this will probably change in a few weeks. Until then, you can try the DMA approach with a physical Pi Pico and PAL TV (or logic analyzer).

    Sync machine pseudo code:

    SYNC ← HIGH
    Repeat forever:
      SYNC ← LOW
      wait 30us
      SYNC ← HIGH
      wait 2us
      repeat 300 (or a similar number) times:
        SYNC ← LOW
        wait 4us
        SYNC ← HIGH
        Trigger IRQ (to notify the Data machine)
        wait 60us

    Data machine pseudo code:

    Repeat forever:
      DATA ← LOW
      Optionally trigger an IRQ to notify the code we're ready for more data
      Wait for an IRQ from the Sync machine
      PULL 32-bit word from the TX FIFO
      Shift the one bit at a time through the DATA pins
    

    Step 4 (optional) - Perfection

    At this stage, the PAL TV driver should be ready for you to use in your code! All you have to do is to write the pixel data to the framebuffer, and the PIO machines will take care of generating the right signal.

    However, the SYNC signal is not perfect yet....

    Read more »

  • Composite signal: Connecting to a Real TV

    Uri Shaked06/18/2021 at 21:23 0 comments

    The PAL standard uses an analog signal. When running in the simulator, you don't have to worry about this, but if you want to run your game on a physical TV, then you'd need to generate the following voltage levels:

    • 0V for sync signals (HSYNC/VSYNC)
    • 0.3V for black pixels
    • 1V for white pixels

    The good news is: you only need a few resistors to convert the digital signal (that works in the simulator) to an analog one.

    Composite video usually uses RCA connectors. You'd need the make the following connections to the central pin of the RCA connector:

    1. SYNC pin through a 470Ω resistor
    2. DATA pin through a 270Ω resistor
    3. Optionally, another 75Ω that goes to the ground (some TVs will accept the signal without this extra resistor)

    Make sure you also connect the ground to the ring of the RCA connector.

    Or, if you prefer a schematic:

    How does this work? We implement a simple voltage divider to generate the required voltages, based on the two digital pin levels:

    SYNC

    DATA

    Output Voltage

    Calculation

    High 3.3V

    High 3.3V 

    1.004V

    (3.3*75)/((1/(1/470+1/270))+75)

    High 3.3V

    Low  0V

    0.366

    (3.3*(1/(1/75+1/270)))/(470+(1/(1/75+1/270)))

    Low  0V

    Low  0V

    0

    0

    As you can see, driving both SYNC/DATA high results in a about 1V, which is the white pixel level, driving SYNC high and DATA low results in about 0.37V, a bit above the black pixel level (but still good enough the maintain contrast), and driving both pins low results in 0 volts, that's the sync level.

    Using this setup and driving DATA high while SYNC is low, you can also generate a gray pixel level (0.637V), but that's definitely out of scope for this project.

View all 3 project logs

Enjoy this project?

Share

Discussions

Similar Projects

Does this project spark your interest?

Become a member to follow this project and never miss any updates