Figure 12 - VGA timing for 20MHz based on the 640 x 480 standard
Figure 12 - VGA timing for 20MHz based on the 640 x 480 standard

There are many VGA modes that include varying timing values to create the horizontal and vertical frame. The horizontal timing is defined in clocks, as it is the pixel clock that determines how many pixels can be sent to the monitor during a line. An entire horizontal line is made up of the horizontal active pixels (HPX), the horizontal front porch (HFP), the horizontal sync pulse (HSP), and the horizontal back porch (HBP). The porches are just intervals of free time added to the timing to allow older monitors to retrace the electron beam, and although they are not needed in LCD monitors, the standards remain the same. These blank spots will be exploited as free time to hand back to the main loop. The negative sign shown after the sync pulses just denote that a sync pin goes low when it is to be activated. A low sync signal will cause the monitor to move the beam back to the left side of the screen so a new line can begin. This beam shift occurs on the rising edge of the horizontal sync signal.

On the vertical definition, there are very similar timing names, although they are counted in lines, not clocks. Every vertical line represents a full horizontal line, and the porches are also added to allow older monitors extra time to send the beam back to the top of the screen. Because a vertical line includes the entire length of a horizontal line in clocks, there is a lot of free time during these blank lines for the main loop to do its work. Of course, during the active vertical lines (VLN), the video engine will be busy spewing out pixels, but that still leaves 44 free lines, each containing an entire length of horizontal clock cycles. If you consider the 20MHz timing diagram, then these 44 free lines give the main loop 27984 free clock cycles on every frame (44 Lines X 636 Clocks).

Of course, the odd 25.175MHz pixel clock requirement of the standard 640 x 480 VGA screen is not very friendly to the AVR unless you want to try over clocking it a bit, so a little "fudging" will be needed to mess with the pixel clock values. By luck, the cross multiplying of the horizontal active pixel value of 640 came out to 508.44, which was close enough to steal from the porch in order to bring it to 512. As you will see in the later code examples, having 512 clocks to draw a line of active pixels was just perfect for the AVR, which could spit out a pixel on every second cycle, giving a possible horizontal resolution of 256 pixels. Having the horizontal line as a full byte made the graphics math very fast as well as greatly simplifying the SRAM addressing later on. Cross multiplying the horizontal values is done by multiplying the true horizontal value with your desired clock value and then dividing the result by 25.175. So, to calculate the horizontal active pixel time (HPX), multiply 640 by 20 and then divide the result by 25.175. The values shown in the chart are a very good starting point, and work great on all the monitors I tested them with.

Notice that for both the real 25.175MHz standard and the "hacked" 20MHz version, the vertical definition remains exactly the same. This is because the vertical is built by lines, and it does not matter what the pixel rate is because all lines have to be sent at the same time. So really, your maximum pixel clock only determines the maximum horizontal resolution, but regardless, you need to draw 480 active lines as well as the sync and porch lines. To deal with the fact that memory is very limited in this project, vertical active lines are simply doubled, tripled, or quadrupled to keep the timings the same but make the pixels larger. This is why the final version creates a 240 pixel vertical resolution, as the lines are sent twice to divide the 480 lines into 240 lines.

Figure 13 - The main loop just flashes an LED during the free time
Figure 13 - The main loop just flashes an LED during the free time

In this first basic test, the video driver interrupt send the required sync signals to the monitor and then draws out the entire color palette as 256 boxes by dividing the 256 pixel horizontal line by 16 and the 240 vertical lines by 15. During this time, the "MAIN LOOP" just counts from zero to 65536 and then turns an LED on and off to show how much free time is available when the video driver interrupt is complete. Each time the LED cycles, about 65000 cycles have passed, a lot of time to render your games and demos. This basic test is a good way to ensure your DAC is working properly and that your wiring is correct. If your monitor fails to lock, recheck the hardware.

In this version of the video driver interrupt, the code is kept as minimal as possible in order to show that it is nothing more than a basic cycle counting state machine. The first thing that is shown under "VIDEO RENDERING INTERRUPT" is the recalculated timing values along with their actual pixel clock counts. Basically, the horizontal counter just needs to count from 0 to 635 and then increment the vertical counter from 0 to 524, setting the sync and active states along the way. Sounds easy, right?

Since there is no timing relationship between the horizontal and vertical sync pulses, you are free to create the state machines in whatever order you want. In other words, front porch can be sent first, or you could start with the horizontal sync. The same holds true for the vertical definition, as long one state follows the other at the correct time. I have decided to start the horizontal state machine with "HORIZONTAL FRONT PORCH = 12 CYCLES", and the code under that block is used to correct the annoying problem with AVR interrupts - latency. Before an AVR interrupt can be serviced, the last instruction must be completed. Since instructions vary in cycles from 1 to 4 cycles in length, the interrupt has a random entry time, varying by up to four cycles. Yes sir, four measly cycles at 20MHz is enough to completely kill your video driver, causing the monitor to roll, distort or simply ignore you altogether. This little annoyance gave me trouble for many days until I finally figured out how to fix it. The code under the front porch block reads the value of the timer that triggered the interrupt, and then either skips or jumps based on certain values. A jump takes two cycles, and a skip only takes one cycle, so by the time all conditions are tested, the time is now equalized to completely remove the interrupt jitter. The values tested are compared only to the low byte of the timer, because the difference will only be between one and four, so the high byte is not needed. If you intend to alter the value of the timer from 636, then you will also have to figure out the correct values for the jumps and skips.

In the block called "HORIZONTAL AND VERTICAL SYNC = 76 CYCLES", there are 76 cycles to work with, which was enough time to do most of the timing calculations as well as set both the horizontal and vertical sync pins depending on the counter values. These values are stored in SRAM to free the registers for use in the main loop code. The code in the video driver interrupt may seem a bit "odd", including useless jumps and NOPs, but these are there so that no matter what condition or branch is set, the time through the loop is cycle exact all of the time. I kid you not when I say one bogus NOP will blow up your video to the point where you might not even get your monitor to show anything, so extremely vigilant cycle counting is always necessary. This is one of the reasons only those who are completely insane attempt to make microcontrollers display video signals! I have been told many times not to "send a microcontroller to do an FPGA's job", but I do enjoy a difficult challenge!

In "HORIZONTAL BACK PORCH = 36 CYCLES", there is mainly just wasted time using NOPs, but the code block also checks to see if there will be an active line of video coming so that it can either continue in the interrupt or just shoot back out to allow the main loop to grab up some more of that precious free time. If there will be a line of video, the code continues down and then sets up some of the registers used to calculate the current palette cube position and color.

Just as the comment indicates, the code block under "HORIZONTAL ACTIVE LINE = 512 CYCLES / 2 = 256 PIXELS" pushes the AVR to its IO limits by sending out a full byte to the DAC on every second clock cycle, creating a 10MHz pixel stream to send to the monitor. This horrendous waste of cycles just sets the value of PORT C (the video DAC port) and then wastes the next 32 cycles to create a line of 16 pixels of the same color. Since the horizontal active time is measure in 512 cycles, 32 cycles creates a 16 pixel line. I broke the entire active pixel time into NOPs so you could examine how the timing works during the horizontal active time. After 512 more instructions, the end of the active video line is reached and the DAC (PORT C) is then sent a zero to create the blanking period. You must set the video output to nothing (black) after the active line or your monitor will fail to lock or show the video properly.

This is now the end of the video driver interrupt, exiting after restoring the status register. No doubt, a lot of this will seem confusing if you have never tried to make a video signal in software, but the key is to know your timing and then to count every single clock cycle. If you have an instruction such as BRNE that can use 1 or 2 clock cycles, then you need to compensate by either accounting for the imbalance later in code or with a following NOP right away. This cycle counting and equalizing process is the real chore, and can be a huge challenge when your code becomes more complex. Luckily, the main loop does not have to suffer this fate once the video driver interrupt is doing its job properly.

Back Home Last Next
You are Viewing... Page 7 of 16
Lucid Science Electronics from the Fringe AtomicZombie Hack-a-day SparkFun