페이지 정보작성자 키트 작성일2017-08-23 10:03 조회2,132회 댓글0건
Microcontrollers are well suited to display information on analog VGA monitors
If you have been working on microcontroller projects for any length of time, then you know that it can be difficult to display a large amount of data, especially when it contains more than just plain text. There are limits to how much useful information you can display on a basic character LCD, or even a dot matrix LCD, since most have low resolution, no color, and a poor refresh rate. I came up against this problem when working on a robotics project that needed to display a color image captured from a small camera and decided to see how much effort it would take to create an image on a VGA monitor using only a microcontroller to "bit bang" all of the necessary signals.
It took some time to learn how the five signals used on an analog VGA connection made an image appear on the screen, but the end results were much better than anticipated, providing a crisp 256 by 240 image on the monitor in 256 colors. Although it does take some intensive cycle accurate assembly programming, the basic coding is not very difficult to understand once you have learned what the monitor expects. In fact, making a microcontroller drive a VGA monitor is much easier than creating a video signal for a television because the VGA monitor does all of the difficult color coding for you as long as you send the video signals and sync pulses at precisely the right time. "Precisely" is the key!
This project is more like a tutorial, and is far from being perfect. The idea is to show how any microcontroller can be made to create a rock solid VGA image once timing parameters have been calculated properly. I am using an Atmel ATMega644 for this project because it has a large program memory to allow the storing of image data, but just about any microcontroller will work, once you understand the basics and decide on what type of image you want to display. This project will start off with a very minimal display system running from a single microcontroller and will progress up to a fully double buffered system that will display flicker free animations with a resolution of 256 by 240 and with 256 colors. Any VGA monitor with a 15 pin analog connector will work, but the old-school glass CRT monitors will probably display the "nicest" image due to having round pixels and a truly analog horizontal line. Newer LCD monitors will work just fine, but there may be a slight "banding" effect on horizontal lines if you cannot adjust a setting called "pixel" clock. Nonetheless, the image will be very clean, crisp, and colorful on any VGA monitor.
I would like to thank the community on the AVRFreaks Forum for suggesting this tutorial and for all of the help that has been offered over the years by the many knowledgeable members. I hope this small project inspires those who want to generate video with a microcontroller and look forward to seeing what others can do to improve and modify these ideas!
Figure 1 - You will need a female VGA connector to hook up to your monitor cable
You can either order a new 15 pin female D-Sub VGA connector such as the Digikey (A35116-ND), or simply salvage one by unsoldering it from an old VGA card. Cutting a VGA cable extender would also work, but then you will have to decide the wiring and solder the ends for use on your breadboard as you prototype this project. Old VGA cards are a dime-a-dozen at most surplus electronics shops. You can probably ask a computer repair shop for a fried VGA card to hack for parts. All you need is the female 15 pin VGA connector so that you can solder the necessary wires to connect to your breadboard.
Figure 2 - Soldering the necessary signal and ground wires to the connector
Once you have extracted your female VGA connector, find some solderless breadboard compatible wires and create the connections using the pin-out guide. As you can see, there are five signal wires and one ground wire. In actuality, there are five separate ground wires (pins 5, 6, 7, 8, 10), but because of the low clock rates we will be using here, they can all be joined together as a single ground wire. Multiple ground wires help reduce grounding loops in very high speed systems. There are also three analog color connections (pins 1, 2, 3) that tell the display how to mix the red, green and blue in order to create a single color pixel. The two synchronization connections (pins 13, 14) tell the display when to move the "beam" to the next line and next frame.
If you have never studied the VGA signal, then these five connections may seem incompatible with a typical microcontroller since three of them expect an analog voltage, but as will be shown soon, all you need is a few resistors and you can bit bang a very stable VGA image right from your software. I know...Why am I doing this when ICs like the Parallax Propeller can already generate VGA video? Simple. Because I like to exercise my grey matter, and it looks like magic when you pump out a VGA signal on a $2 microcontroller with only a few resistors connected! Yes, the cool factor always counts!
Figure 3 - An R2R resistor ladder will change digital signals into analog signals
Ok, let's dig right in and convert the digital signals from the microcontroller into the analog and digital signals that the VGA monitor will expect. The good news is that both the horizontal sync and vertical sync lines are fully compatible with any 5 volt or 3.3 volt microcontroller, so they can be connected directly from your output pins to the VGA connector. The three analog color lines that generate the red, green, and blue intensities are not digital, and the monitor expects some voltage between 0 volts and .7 volts (or close) on these three lines. Because the color signals are analog, you can effectively create an infinite amount of different colors depending on how many steps you have in your analog voltages. Most modern VGA systems have 256 steps (8 bits) in each color, allowing 24 bits of color information, or 16,777,216 total colors. Because this basic system is powered by an 8 bit microcontroller, there will only be a single 8 bit port used to generate all three analog color lines, so the maximum amount of colors we can get is only 256. This is the same number of colors used by the early VGA systems, and is more than enough to show clear text, graphics, and photographic images.
Wait...three colors and 8 bits - the division does not work here! Correct. It was common to simply weigh the colors with one less bit on red or green to get a palette that would have something like eight shades of red (2 bits), four shades of green (3 bits), and eight shades of blue (3 bits). This arrangement, or "color space" is called RRGGGBBB, representing the number of bits in each color position. I spent a lot of time messing around with various eight color spaces, and did not like any of the unbalanced arrangements as it made images look strange, and made it difficult to convert graphics from a program like Photoshop. I found a way to make each color have 2 bits (four shades), but then have a global 2 bits of intensity that could work on the 6 bits of color. In other words...four levels of 64 colors to make a nice even 256 color palette.
As shown in the image, each color has two resistors feeding it, creating a simple 2 bit digital to analog converter (DAC) that gives four levels of that color. Since each color has its own 2 bit DAC, there are a total of 64 possible colors (6 bits). The last two bits are connected to three more 2 bit DACS, which then feed the three colors, giving another 2 bits of intensity to each color. So now every group of 64 colors has its own four levels of intensity, giving a colors space of RRGGBBII. The diodes are necessary so that the individual 2 bit DACs do not feed back into each other, as this would mix the colors in an odd way. This simple RRGGBBII DAC is very easy to calculate in software, and makes conversion from Photoshop to data statements very easy to do in a simple language like Visual Basic.
Figure 4 - Adding the RRGGBBII DAC to the breadboard
The 1K and 2K resistors that form the resistor DAC are close enough to create the necessary analog voltages for any VGA monitor. You can substitute the 2K resistors for 2.2K, if your parts bin has the more common value, and it will work just fine. Because of the low bandwidth needed to generate 256 x 240 over the 640 x 480 VGA standard screen, the DAC does not have to be high precision like the ones used in real video cards. In fact, the slight bit of blurring caused by the resistors actually "smoothens" up the video a bit, since the large square pixels are almost too sharp due to the low resolution. I actually tried this project with a precision, high speed video DAC IC, and it looked worse due to being too sharp! The values of the diodes are also not important, but they do need to be low voltage signal diodes, not power diodes. The small red glass types (whisker diodes) are perfect.
Add the resistor DAC to the solderless breadboard on the top corner, allowing a bit of room to work and add to the circuit. The project will progress from a basic "bit banger" up to a dual buffer system, so the breadboard needs a bit of room to hold the SRAM and a few logic ICs later on if you plan to follow through to the more advanced VGA video generator version.
Figure 5 - The initial prototype will create video without a frame buffer
It is difficult to do anything with graphics when you don't have very much memory, or even enough to store the contents of a single frame. The AVR is capable of 10MHz bandwidth out of a port, so the maximum resolution that can be achieved over any of the VGA standards is 256 x 240 over the standard 640 x 480 screen. There is not enough IO horsepower to push pixels any faster than that, but this actually works out very well, allowing both the horizontal and vertical resolution to be addressed by 8 bits. As you will see later on, this 8 bit addressing makes computations many times faster than trying to push out something like 320 x 200 as that would require 16 bit math.
As this project progresses, an external SRAM capable of holding two full screens will be added, allowing for very complex graphics to be drawn in such a way as one buffer is on the screen while the AVR chews away on the other. This is called "double buffering", and is the way video games are done, allowing for frames to be swapped once all graphics are drawn. For now, we will concentrate on just making a VGA monitor lock onto the signal, and show the 256 possible colors. Using no frame buffer limits the amount of data that can be sent to the screen, but a lot can be done even with just the microcontroller pumping the monitor in real-time as will be shown. Start by connecting the VGA connector to the DAC, the horizontal sync to Pin B.0, vertical sync to Pin B.1, and a test LED to Pin B.2 through a 1K resistor. The test LED will flash in the background, showing that there is plenty of free time to draw graphics during the blanking periods (more in this later).
Figure 6 - The basic real-time VGA generator schematic
The Schematic shows how the microcontroller will be able to send the RRGGBBII color data to the RGB lines on the VGA connecter by first feeding it to the resistor DAC. The horizontal and vertical sync signals are fed to the monitor directly from the IO pins. The clock is a 20MHz oscillator module, but you will be fine with a 20MHz crystal as well. Actually, any speed would work, but you will have to do many changes to the source codes presented here as they are based on 20MHz, and every single cycle has to be accounted for during the video rendering interrupt.
Figure 7 - Connecting the port pins to the color palette DAC
I like to use wire colors that actually mean something, so the connection between the DAC as well as the VGA port have red, green, blue, and brown wiring to show the various colors and signals. All of my breadboard wiring is cut from CAT-5 wiring, as there are four colors, and the solid copper is perfect for breadboarding. Also, don't forget to run the ground wire from the VGA port to your breadboard circuit, or the monitor will fail to lock onto the signal.
Figure 8 - A VGA signal is based on horizontal and vertical timing
Generating a clean and stable VGA signal is not actually much of a task for a microcontroller, as long as you write code that accounts for every single clock cycle all of the time. Now this may sound like a monumental task when you consider a single frame of 640 x 480 video contains 419,200 cycles, and these 419,200 cycles have to happen 60 times per second with not a single cycle lost at any time! The good news is that the video rendering code can be placed in an interrupt service routine so that you are free to use the blanking periods to draw your graphics. In the crude hand drawing of a video frame, you can see that a video frame is made of an active area and a blanking area. The active area is the portion of the frame that is sent to your monitor and the blanking area is just wasted time that was needed in the days when a display used a cathode ray tube (CRT). CRT based video systems needed some time to actually bring the beam back from the side and bottom of the frame during a synchronization pulse, so this time was built in to compensate. Modern displays don't need this free time, but standards have been preserved to allow older monitors to function.
We will take advantage of this dead time to actually plot some graphics in memory so they are displayed during the next active period. There is not a huge amount of free time in a frame, only about 20% of the frame, but that is still almost 100,000 cycles that can be used to generate some video in the background. Later, when the second buffer is added, the flicker caused by seeing the graphics being drawn as the beam catches up to the buffer will be eliminated, allowing all drawing to happen in the background. The numbers and pulses shown along with the video frame in the drawing represent all that is needed in order to compute the timing necessary to create a VGA video signal. Of course, there is a lot more to the VGA standard, and many good documents are available on the web if you want to dig into the details. The timing is shown for the standard 640 x 480 VGA screen running on a 25.175MHz pixel clock with a refresh rate of 60Hz. Although I am going to "bodge" these numbers to force the clock down to 10MHz later, the concepts are all the same.
The green shaded area represents what happens on a horizontal line as the beam passes from left to right along the video screen to build an image. Horizontal time is measured in clocks or cycles, and it is the limiting factor as to how many pixels can be drawn in the short time it takes the beam to travel across the screen. Although it is up to the host to tell the monitor what "mode" is being used, the monitor will expect that you conform to the timing standards, sending sync pulses and pixels in the expected time frame at the correct times. For instance, the mode shown in the drawing of for the 640 x 480 VGA standard, so there are a total of 800 clocks across one horizontal line as represented by the green shaded area of the drawing. Out of those 800 clocks, 640 of them are used to send horizontal pixels during the active state (HPX). This is then followed by the front porch (HFP) of 16 clocks, then the horizontal sync pulse (HSP) is set low for a duration of 96 clocks. The line is then completed by the horizontal back porch (HBP) of 48 clocks.
The vertical definition is very similar to the horizontal definition, except that it is counted in lines instead of clocks. So one single line of vertical data is equal to one full line of 800 horizontal clocks. This is shown as the blue shaded area in the hand drawing. There are a total of 524 vertical lines in the 640 x 480 VGA standard, and this number cannot be changed due to the fact that a line is made up of one full horizontal clock period. The vertical active lines (VLN) are the lines that contain visible pixels, so there are 480 of these. After the active pixels are all drawn (640 x 480), the vertical front porch (VFP) happens for 11 lines, followed by a negative vertical sync pulse (VSP) for two more lines. The vertical frame ends with the vertical back porch of 31 lines and then the next frame is drawn. Each full frame happens at a rate of 60Hertz, so 60 frames per second are draw - a lot of work and careful timing for a microcontroller!
Figure 9 - Running the basic sync and palette generator on a monitor
Once you have your AVR connected to the DAC and sync pins, upload the basic sync and palette generator code presented below. The monitor should instantly lock onto the signal and display 256 large boxes, each with a different color. If your monitor fails to lock, or flashes an error, you may have the horizontal and vertical sync pins reversed. Don't worry, you cannot damage your monitor by hooking up the pins wrong, it just won't display anything. Once you have the video display working, navigate your monitor's menus (if there are any) and select the function to display the video source properties. Your monitor should be perfectly locked on and showing 640 x 480 @ 60Hz, as this is the format I am using in the source code.
Figure 10 - The monitor displays 256 pixels, but thinks it is showing 640 pixels
If you can't get your monitor to display anything, then you likely have a wiring problem on the VGA connector. The signal generated by this source code is as good (or better) as some accepted standards, so any error will be in the wiring, not the programming here. Now, before moving on to more advanced graphics drawing, I will go through the source code and explain all of the black magic that goes on in order to get the VGA monitor to play well with the microcontroller.
Figure 11 - The basic sync and palette generator assembly source code
Open the source code in whatever IDE you like to use and let's go through it one block at a time. As you can see, I am a "lefty", and don't much care for indented code...ack, I know! Also, these sources were done in a few hours, and could certainly be optimized and improved, but the point of this tutorial is to show how you can make a VGA signal by using these basic examples as a starting point. The source codes will also evolve and improve as each new version is made, resulting in a simple framework that you can use as a starting point to build VGA demos, games, and applications. I will be explaining the code following a comment block, as each important function has been labeled. Also note that after each timing critical instruction, the number of clock cycles is shown as a comment since these need to be counted precisely at all times.
< Here is the basic sync and palette generator in AVR assembly >
The first few blocks ("PORT SETUP" and "STARTUP SEQUENCE") just set the IO pins and initialize the stack pointer. Also in Startup Sequence, Timer1 is setup for a compare match interrupt that will happen every 636 cycles. This time defines an entire horizontal line, which has been calculated by cross multiplying the real 25.175MHz standard VGA timings with the required 20MHz AVR timings. All of the work will happen in the video interrupt, leaving the main program loop free to draw graphics only when there are no active lines of video. The nice thing about having all of the timing critical code in the interrupt is that your main loop code can just do whatever it wants without needing to worry about the completely insane timing needed to display a stable VGA image. The real puzzle is going to be where are these graphics going to be stored and how will the main program loop share this space with the VGA frame generator? First, let's look at how the timing for 20MHz was calculated.
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
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.
Figure 14 - An image is converted from a bitmap into data bytes
For the next version, I have decided to exercise the video DAC a bit and display a 256 color image directly from the AVR program memory to the monitor. For this task, I hacked together a simply Visual Basic program that would open a bitmap and then create a file containing comma delimited data bytes that correspond to the colors in the image. The conversion is not perfect because the PC bitmap contains 8 bit per color (24 bit image), and the DAC on this project can only display a fixed 2 bits per color. I just made a table and tried to match the best possible color.
Figure 15 - King Tut shown at 128 by 240 pixels using 256 colors
Not much has changed in the source code except that in the active pixel loop "HORIZONTAL ACTIVE LINE = 512 CYCLES / 4 = 128 PIXELS". The bytes are fetched from the internal program memory and then sent to the DAC. Because the LPM and OUT commands take up a total of 4 cycles, the maximum pixel rate that can be achieved using this method is 128 pixels (512/4 =128). Considering the low resolution and bad conversion from 24 bit color, the displayed image looks pretty good.
< Here is the progmem image generator source code >
Looking at the new source code, you can see that the active pixel loop just issues the command "lpm r16,z+ ;3", and "out portc,r16 ;1" for a total of 512 lines, basically reading and sending a byte every 4 clock cycles for the next 512 cycles. The image data is loaded into the program memory as one huge table located at "PIC:" under the heading "128 x 240 RRGGBBII IMAGE DATA". This basic VGA generator can now store and display low resolution images, but that is not much use since you cannot dynamically alter the display in the main loop yet. The LED just keeps on flashing, letting you know that there are over 65000 free cycles for you to use.
Figure 16 - The image from Figure 1 converted and sent to the monitor
The image from Figure 1 was also converted and stored to the program memory for display, but it did not look as good as old King Tut due to the conversion. The King Tut image was drawn by hand using a fixed palette on the old Amiga computer, so it actually looked okay on the VGA monitor after conversion, but this image was taken from a camera with 24 bits of color. Much was lost during the conversion, which is obvious in the jagged edges and missing shades. I did not spend much time on the converter program, but with a good dithering filter and matching system, images could certainly be converted to look much better. Maybe one day I will write a proper image converter and offer it for download.
Figure 17 - This image is drawn from a tiny internal frame buffer
The next incantation of the VGA generator code is much more useful but it now suffers a massive drop in total resolution, going from 128 by 240 pixels to only 56 by 60 pixels! The goal here is to show how the main loop and video driver will share the video memory, but the tiny 4K internal SRAM on the AVR is going to be used to keep things simple until the next and final version. Because there is only 4K available on the ATMega644, the resolution has to suffer a huge reduction in size. To make things even worse, the vertical resolution must divide by 480 as we have a fixed vertical resolution to work with. This version of the code will divide the horizontal resolution by 9 and the vertical resolution by 8 to achieve a 56 by 60 pixel image that takes up 3360 bytes. You can't use all of the 4096 bytes of the SRAM because the stack needs to live someplace, too. This is a decent tradeoff just to demonstrate the new code.
< Here is the wormhole animation source code in AVR assembly >
In the video driver interrupt, not much has changed except for the active pixel loop "HORIZONTAL ACTIVE LINE = 512 CYCLES / 9 = 56 PIXELS", where now bytes are fetched from the internal SRAM and sent to the video DAC. Having 9 clock cycles available between pixels allows the active pixel routine to work in a conditional loop now, greatly reducing the number instructions needed from 512 to only 10. Of course all of this is at the expense of resolution, and unless an AVR part comes available that includes a 64K or larger SRAM (unlikely), this code example is only good for study, not practical use. With massive block sized pixels, your only chance at a game may be Tetris or Pong, and the text capabilities will only be 7 by 7 characters! There is one improvement though, and that is the ability for the main loop to actually change the contents of the video buffer, creating a dynamic display.
In "MAIN PROGRAM LOOP", a lot of new (and very ugly) code has been added. The code basically sets the Z pointer to one of 10 locations in program memory, where a certain frame of a wormhole animation is read and then written to the display buffer SRAM. This low resolution animation was converted using the same method into data bytes to be stored in the program memory at the locations shown at "FRAME1:" to "FRAME10:". The main loop just increments a frame counter, then moves the 56 by 60 image from program memory to the SRAM, where can then be seen on the VGA display. I have also included another subroutine called "PRINT TEXT CHARACTER" that allows some text to be shown in the screen if a button is pressed down. Of course, at only 7 by 7 characters, the words "LUCID SCIENCE" take up almost the entire screen!
Figure 18 - The AVR generating the low res wormhole animation
등록된 댓글이 없습니다.