mr. city's arduino section

Project 1: Output VGA Signals to a CRT Monitor

This is code for the Arduino microcontroller that will display vertical lines on a VGA screen at a resolution of approximately 376x282 @ 60 Hz. You will probably need a CRT monitor to get this to work correctly; it doesn't work on my LCD monitor. (If you can figure out the timings that'll work on an LCD, be my guest!)

Arduino VGA SchematicThe schematic is shown at left. Click on it in order to see it in full size. As you can see, Arduino pins 13, 12, 7, 4, and 2 are connected to a female D-SUB connector, but not before going through some resistors first. 13 & 12 (sync) go out to one 220-ohm resistor each, and 7, 4, & 2 (the colors) each go to their own voltage divider with R1 = 1200 ohms and R2 = 180 ohms.

Addenda/Notes:


Here are a few places I got guidance from on my quest to write this firmware:


#include <avr/interrupt.h>
// horizontal line counter (negative is during vertical blanking)
int hzCount = -10;

unsigned short int OCR1AMEM = 0;
  
// ==============
// Pinout:
#define HSYNC 0b00100000;   // 13: HSYNC (Port B5)
#define VSYNC 0b00010000;   // 12: VSYNC (Port B4)
#define BLACK   0b00000000;   // Black
#define BLUE    0b10000000;   // 7:  Blue  (Port D7)
#define GREEN   0b00010000;   // 4:  Green (Port D4)
#define RED     0b00000100;   // 2:  Red   (Port D2)
#define CYAN    0b10010000;   // Cyan
#define YELLOW  0b00010100;   // Yellow
#define MAGENTA 0b10000100;   // Magenta
#define WHITE   0b10010100;   // White
// Others go to GND
// =======

// hurry up & wait
#define NOM    asm("nop");
#define NOM5   asm("nop\nnop\nnop\nnop\nnop");
#define NOM10  asm("nop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\nnop");
#define NOM20  asm("nop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\nnop");

void hLine(void)
{
  PORTB = PORTB ^ HSYNC;         // invert HSYNC - negative polarity
  NOM; NOM; NOM; NOM; NOM; NOM; NOM;
  delayMicroseconds(3);    // wait for "sync length" time
  PORTB = PORTB ^ HSYNC;          // invert HSYNC again
  NOM; NOM; NOM; NOM; NOM; NOM; NOM;
  delayMicroseconds(4);   // wait for back porch time
  // draw a line on-screen 4 pixels wide (the "eq" operation + 3 nops)
  PORTD = RED;
  NOM; NOM; NOM;
  PORTD = BLACK;
  NOM; NOM; NOM;
  PORTD = GREEN;
  NOM; NOM; NOM;
  PORTD = BLACK;
  NOM; NOM; NOM;
  PORTD = BLUE;
  NOM; NOM; NOM;
  PORTD = BLACK;
  NOM; NOM; NOM;
  PORTD = CYAN;
  NOM; NOM; NOM;
  PORTD = BLACK;
  NOM; NOM; NOM;
  PORTD = MAGENTA;
  NOM; NOM; NOM;
  PORTD = BLACK;
  NOM; NOM; NOM;
  PORTD = YELLOW;
  NOM; NOM; NOM;
  PORTD = BLACK;
  NOM; NOM; NOM;
  PORTD = WHITE;
  NOM; NOM; NOM;
  PORTD = BLACK;
  NOM; NOM; NOM;
  
  // three pixels at a time
  PORTD = RED;
  NOM; NOM;
  PORTD = BLACK;
  NOM; NOM;
  PORTD = GREEN;
  NOM; NOM;
  PORTD = BLACK;
  NOM; NOM;
  PORTD = BLUE;
  NOM; NOM;
  PORTD = BLACK;
  NOM; NOM;
  PORTD = CYAN;
  NOM; NOM;
  PORTD = BLACK;
  NOM; NOM;
  PORTD = MAGENTA;
  NOM; NOM;
  PORTD = BLACK;
  NOM; NOM;
  PORTD = YELLOW;
  NOM; NOM;
  PORTD = BLACK;
  NOM; NOM;
  PORTD = WHITE;
  NOM; NOM;
  PORTD = BLACK;
  NOM; NOM;
  
  // two pixels at a time
  PORTD = RED;
  NOM;
  PORTD = BLACK;
  NOM;
  PORTD = GREEN;
  NOM;
  PORTD = BLACK;
  NOM;
  PORTD = BLUE;
  NOM;
  PORTD = BLACK;
  NOM;
  PORTD = CYAN;
  NOM;
  PORTD = BLACK;
  NOM;
  PORTD = MAGENTA;
  NOM;
  PORTD = BLACK;
  NOM;
  PORTD = YELLOW;
  NOM;
  PORTD = BLACK;
  NOM;
  PORTD = WHITE;
  NOM;
  PORTD = BLACK;
  NOM;
  
  // one pixel at a time, with black spaces
  PORTD = RED;
  PORTD = BLACK;
  PORTD = GREEN;
  PORTD = BLACK;
  PORTD = BLUE;
  PORTD = BLACK;
  PORTD = CYAN;
  PORTD = BLACK;
  PORTD = MAGENTA;
  PORTD = BLACK;
  PORTD = YELLOW;
  PORTD = BLACK;
  PORTD = WHITE;
  PORTD = BLACK;
  
  // one pixel at a time, no black
  PORTD = RED;
  PORTD = GREEN;
  PORTD = BLUE;
  PORTD = CYAN;
  PORTD = MAGENTA;
  PORTD = YELLOW;
  PORTD = WHITE;
  PORTD = BLACK;
}

ISR(TIMER1_COMPA_vect)
{ 
  // do vsync with positive polarity
  if (hzCount == -10 || hzCount == -6) PORTB = PORTB ^ VSYNC
  // do hsync when the time is right
  if (hzCount > -1 || hzCount < 282) hLine();

  hzCount++;
  if (hzCount > 285) hzCount = -10;
}

void setup(void)
{
  // pins
  pinMode(2, OUTPUT);
  pinMode(7, OUTPUT);
  pinMode(4, OUTPUT);
  pinMode(12, OUTPUT);
  pinMode(13, OUTPUT);
  digitalWrite(13, LOW);  // start HSYNC low
  digitalWrite(12, HIGH);  // start VSYNC high
  digitalWrite(7, LOW);  // start blue low
  digitalWrite(4, LOW);  // start green low
  digitalWrite(2, LOW);  // start red low

  // hsync timer, see ATmega328 datasheet for more details
  
  // TIMSK0: Timer/Counter1 Interrupt Mask Register
  // TOIE0 = 1: when set, enables the Timer/Counter1 Overflow interrupt
  // This enables everything already set EXCEPT for TOIE0
  TIMSK0 &= !(1 << TOIE0);
  // temporarily disable interrupts
  cli();
  // Timer/Counter Control Registers
  // Timer/Counter1 Control Register A
  // Ignore any functionality provided by this Register
  TCCR1A = 0;
  // Timer/Counter1 Control Register B
  // WGM22 = 8: Set timer to CTC mode with TOP = OCR1A (WGM12 may also work?)
  // CS10 = 1: Do not prescale the clock
  TCCR1B = 1 << WGM12 | 1 << CS10;
  // Sets the TOP (highest) value the Counter will count to
  // F_ocr1a = 16MHz / (2 * prescale * OCR1A); F = 1/192 MHz: usu. 0x5FF; F = 1/57 MHz; usu. 1C7
  OCR1A = 0x01C8;
  // TIMSK1: Timer/Counter1 Interrupt Mask Register
  // OCIE1A = 2: when set, enables the Timer1 Output Compare A Match interrupt
  TIMSK1 = 1 << OCIE1A;
  // re-enable interrupts
  sei();
}

void loop() { //twiddle thumbs }


Project 2: Writing the Arduino Bootloader using an Arduino Uno

This simple workup was derived from this tutorial from Sparkfun. The only difference is I eschewed the command-line stuff they talk about and instead opted for a simple GUI approach provided by the Arduino SDK. The end goal here is to write the Arduino bootloader to a chip on a Duemilanove using an Arduino Uno SMD edition as the ISP. (Whether or not it's the SMD version shouldn't make a difference.)

One other note is I'm often driven to do things that aren't always obvious with the stuff I have. I didn't have the header sockets necessary to bridge the Arduino Uno output pins to the ICSP headers on the Duemilanove board. However, considering my age, I have an inordinate amount of old computer parts dating from the late 1980s to about 2000, and since a lot of this old stuff happens to have the same pin size as many of the DIY kits like Arduino, I leveraged an old "game port" (15-pin 2-row D-SUB connector) I had lying around to use as a bus for the ICSP signals. One end is the 15-pin female jack, and the other end looks like an old IDE cable, but skinnier (as it should be; less pins!). I used regular prototype wire to tenuously connect the 15-pin jack to the Uno's output pins. The Sparkfun tutorial describes the exact pinout you need to use. I'll go over the basic formula here for using the Arduino SDK to initiate the burning:

Note that you can probably flip around the instructions for your board arrangement. For instance, you can treat the word "Uno" as the device you intend to use as the programmer.


Project 3: TOP SECRET! Currently in progress. More to come once it's ready, commercially available, or whatever.


Go back to Mr. City's Web Life - Home Page. (However, you'll be hard-pressed to find anything technical on the rest of the site. :-P)