PIC16 Video Demo
Last Update: 2023-06-02
The goal of this demo was to show that it is possible to generate a composite video signal using nothing but a PIC16F84A with 68 bytes of memory and 1K words of program space without using external (non-passive) hardware. If you're here for a video demo or to see the source code, then feel free to skip to the bottom of the page.
The first thing to figure out was what the video signal that I needed to generate looks like. After some looking around on the internet, I found a few diagrams explaining how composite video is structured and ended up with this signal:
Each line of the video takes 64us to complete and is broken up into four main sections:
- 4.7us for the "sync" pulse to tell the screen/TV to perform a horizontal sync (starts a line)
- 5.8us for the "back porch" which is a blanking period on the left side of the screen
- 52us for the active video region (AVR) which is where the video data is located
- 1.5us for the "front porch" which blanks the line on the right side of the screen
Since the video signal is composite, the data fed in the AVR is generally both the luminance (brightness) and chrominance (color) signals combined. In my case, since I am only planning on generating black and white video, the chrominance part can be ignored and the voltage level in the AVR is simply a gradient from black to white.
Also, if you are wondering why I went with a PAL video signal instead of NTSC, since the PIC runs at 20MHz (but ~5MIPS) the timings were easier to generate for PAL.
> The Boards
The boards were designed in KiCad-5 with the goal of primarily using through-hole components for ease of assembling and adding patches if any were needed. The exception to this was the USB to serial adaptor IC and the passive components on the USB signal pair, which are SMT. The hardware included on board is very minimal—only enough to get the PIC running.
This minimal construction might leave one wondering why not add something as simple as a 74x165 parallel to serial shift register connected to one of the PIC's GPIO ports? While this would have most likely increased the possible horizontal resolution of the video output, the goal of the project was to show that it is possible to generate a video signal with no active components besides the PIC.
If someone is looking to improve the board, adding a shift register to handle video data shifting or adding a UART interface IC could be possible routes for improvement.
> Demos!
The descriptions below are primarily a high-level overview of each of the demos. There's more details on their respective GitHub folders in the repo (link at end of page).
Sync Demo
This one's not really a demo, but it is the first proof of concept that I made during the development process. The sync demo only generates the sync signal and handles passing execution to the AVR code. If you are looking for a place to start writing a program of your own, then this might be a good starting point. I will make note that I did something odd when I wrote the sync code: the line counter starts from the max value and decrements until it reaches 0, ending the frame. This makes it a bit more difficult to use the built in line counter for drawing at specific positions on screen (I usually ended up inverting and masking the bits in the count that I needed to reverse the count direction). I kept this oddity in mind while designing the VGA module for the ZMIPS-1000 console, where I made the top of the screen 0 and counted up, going down the screen.
Text Demo
The text demo gives the user an 8x4 text display which can be communicated with over a USB-serial port. It supports clearing the screen (with cursor position reset), newlines, backspace (del), line wrapping, and auto scrolling. The serial data line is sampled during the beginning of the scan line, just after the sync code executes. As a result, during the time that a character is being sent over the serial port, the lines that are drawn while the character is in transmission are delayed, shifting them to the right. This results in some glitching in the output signal, creating a rather cool effect.
The bitmap data for the characters is stored in a lookup table, which takes up a significant portion of the 1k words for program storage and is why not every character is represented.
Game Demo [BRIK]
The last demo is the game: BRIK. BRIK is a very simple Flappy Bird-like game where the goal is to navigate the "brik" through a series of pillars. The game makes use of a single control input for starting, "bouncing," and restarting the game. There's a built-in score counter too, but it doesn't display during the time that the game in the active state (due to programming reasons, but I can always say it's "for suspense").
This was the first game that I have programmed in assembly, so it was an interesting challenge. All the "high level stuff" such as having a graphics API or having state management built already for you just doesn't exist, so I had to figure out how to implement it while also fitting the game into the limited program space and timing available. Overall, it was a fun experience and I would recommend trying something like this out to anyone who wants to do some low level programming.
Something to improve is the random number generator. I tried creating my own algorithm for it, but it ended up having a very short period (~30), meaning that once you play long enough, the "course" starts to repeat.
> Dev Photos
Interesting patterns that were created in the process of designing the above demos
> Links
GitHub Repo
YouTube Demo