This project aims to display a scoreboard in a regular VGA monitor, remotely controlled with an Android device (cellphone, tablet) via Bluetooth.
One of the challenges was trying to use cheap components (except for the monitor) and Atmel’s ATtiny45 sounds great for this purpose. It can run at 20MHz with an external oscillator, the downside is that the number of I/O pins available is very limited. The external oscillator will take PB2 and PB3, thus using an external clock (a crystal with some circuitry that generates TTL) I could save PB2!, unfortunately I don’t have one, and is not easy to find here.
There are still 4 pins available, PB0 and PB1 will be used for the VGA HSYNC and VSYNC respectively, PB2 to generate the RGB (only one color!). PB5 to get the input information that I want to display, hopefully this will come from a bluetooth module specially tuned for this, or from another ATtiny45 in the worst case.
VGA signal generation
I knew that 20MHz would be enough to generate an acceptable VGA output because of Linus Akesson’s Craft project. In this case he uses an ATmega88 with more I/O ports. Like you can read in his page, this is all about VGA timing. Sending the horizontal and vertical sync pulses at the right time is essential, so the obvious language for having control of every cycle used was assembler (using the gnu compiler in this case).
You can read more about VGA timings in the Wikipedia and here. Basically I send a HSYNC pulse during the so called “horizontal blanking” period just before painting each of the 480 lines. After the 480, I need to send another 45 lines to make the vertical blanking period (no RGB is sent). During the blanking period I also need to send the VSYNC pulse, to signalize that the frame painting is about to start (talking in ‘lines’ the VSYNC pulse will be between line 10 and 12 of the vertical blanking).
The horizontal blanking can be divided in three parts: the front porch 0.94µs, the sync pulse 3.77µs and the back porch 1.89µs. I take advantage of these cycles to determine which information should be ‘painted’ according to the line number. Since I’m trying to paint 4 numbers to display the score, I’ve defined that each character is 8×10 so it can be represented in only 10 bytes. So I store the line that I need to paint for each character in 4 different registers. Also I need to repeat the line vertically to give some proportional height to the characters this is done by mapping line number to font lines in a fixed table. That will result in very ugly and big characters, but that’s OK for the scope of this project :).
No Eagle for now, but here is an ASCII schematic of the circuit.
ATtiny45 +-----------------+ | | IN ----| 1 (PB5) (VCC) 8 |---- +5V 22pf | | +--||----+------| 2 (PB3) (PB2) 7 |---- RGB | [ ] XTAL| | +--||----+------| 3 (PB4) (PB1) 6 |---- VSYNC | 22pf | | +---------------| 4 (GND) (PB0) 5 |---- HSYNC | | | GND +-----------------+
Note: I’ve used an external 12v power source with a 5v voltage regulator 78L05. In my case it was very important to put two filter capacitors in the input and output of the regulator, without them the noise generated by the power supply won’t let the monitor synchronize properly.
Let’s see some interesting parts.
Character definition. Like I said the characters have a fixed size of 8×10, in the code they look like this:
number0: .byte 0b00000000 .byte 0b00111000 .byte 0b01000100 .byte 0b01000100 .byte 0b01000100 .byte 0b01000100 .byte 0b01000100 .byte 0b00111000 .byte 0b00000000 .byte 0b00000000
The mapping between screen lines and font lines looks like this:
maplines1: ; map lines (from 50 to 205) .byte 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 .byte 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 .byte 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 .byte 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 .byte 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 .byte 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 .byte 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 ...
So lines 50 to 88 will map to the first character line (I keep 50 lines of margin at the top and 50 at the bottom, so I actually use 380 lines, divided by ten will result in 38 VGA lines for each font line).
Finally, to get the font line data given the font line number (in r17), I’ll use the lpm instruction like this:
linefor0: ldi ZL, lo8(number0) ldi ZH, hi8(number0) add ZL, r17 adc ZH, r22 lpm ret
The code doesn’t look complicated at all. The complicated part maybe was making sure to get the right timings, that will be measuring the cycles used between each part of the code. I found that AVR Studio is a great tool for debugging, however cycle measuring between two instructions in the code is something that can take some time, I could’t found conditional breakpoints, and clicking 480 times the “continue” button to test the limits can be a little bit annoying. So, I’ve made a little tool in Python / PySide to help me with this measuring. It’s like an assembler emulator with only a few instructions implemented and of course cycle calculation.
Everything, including the AVR debugger is available for download here. You can do whatever you want with it at your own risk, and in case you feel more comfortable with a license: BSD
Things to do for the next parts:
- Once my cheap Bluetooth module arrives from China, I’ll try to hack it. If I can’t modify its firmware at all, I’ll probably end up with a second ATtiny doing the communication between my VGA ATtiny board and the module.
- Make the Android program to communicate with the Bluetooth module (I guess its plenty of examples in the Internet)
- Last thing, would be trying to improve the characters on screen. I don’t have too much memory in the ATtiny, and certainly not much processing capacity between each pixel, but I guess I can try to emulate some squared pixels, maybe like this:
I could event try to get the yellowish color by putting some diodes in the correct RGB wires, but that’s for next chapter.