We had an art festival this month, and my students encouraged me to submit a piece. The theme was “Inside my Mind”, so I thought about how I could represent the near-incomprehensibility emergent nature of human thought.
I decided to build an electronic device that would carry a nearly-random signal to a computer display. The screen displayed coloured lines that, if looked at individually, would appear to be changing colour randomly. However, if you sit back and take in the whole screen, patterns — both recurring and transient — begin to emerge. In the still image above, the monitor on the left is displaying the output (along with a pesky dialogue box in the middle of the screen).
To build this, I used an Arduino that was programmed to output a signal via a VGA cable. The Arduino’s 16 MHz clock speed isn’t fast enough to output a signal pixel-by-pixel, but by counting cycles (the “nop”s in the code below) I was able to get close enough that the signal could be interpreted and displayed by a monitor.
The code is below. I enjoyed this project, and learned a lot about timing in microcontrollers, but wouldn’t recommend it to anyone else, as much of the time is spent fine-tuning the timings.
#include <avr/io.h>
#define VSYNC_LOW PORTD &= ~_BV(6)
#define VSYNC_HIGH PORTD |= _BV(6)
#define HSYNC_LOW PORTD &= ~_BV(7)
#define HSYNC_HIGH PORTD |= _BV(7)
void setup() {
}
void loop() {
int i = 0;
cli();
DDRB = 0xFF;
DDRD = 0xFF;
byte noise = B00001100;
//Loop Over-And-Over Again
while(1){
// Vertical Data
i=0;
while(i < 600){
//2.2uS Back Porch
byte noise = byte(micros());
noise &= B00011100;
__asm__(“nop\n\t””nop\n\t””nop\n\t””nop\n\t””nop\n\t””nop\n\t””nop\n\t””nop\n\t”);
__asm__(“nop\n\t”);
//20uS Color Data
PORTB ^= noise;
delayMicroseconds(19);
__asm__(“nop\n\t””nop\n\t””nop\n\t””nop\n\t””nop\n\t””nop\n\t””nop\n\t””nop\n\t”);
__asm__(“nop\n\t””nop\n\t”);
PORTB ^= B00000000;
//1uS Front Porch
__asm__(“nop\n\t””nop\n\t””nop\n\t””nop\n\t””nop\n\t””nop\n\t””nop\n\t””nop\n\t”);
__asm__(“nop\n\t””nop\n\t””nop\n\t””nop\n\t””nop\n\t””nop\n\t””nop\n\t””nop\n\t”);
//3.2uS Horizontal Sync
HSYNC_HIGH;
delayMicroseconds(3);
__asm__(“nop\n\t””nop\n\t”);
HSYNC_LOW;
i++;
}
// Vertical Porch
while(i < 1){
delayMicroseconds(2);
__asm__(“nop\n\t””nop\n\t””nop\n\t””nop\n\t””nop\n\t””nop\n\t”);
//20uS Color Data
delayMicroseconds(20);// 20uS
//1uS Front Porch
delayMicroseconds(1); // 1uS
//HSYNC for 3.2uS
HSYNC_HIGH;
delayMicroseconds(3);
__asm__(“nop\n\t””nop\n\t”);
HSYNC_LOW;
i++;
}
// Vertical Sync
i=0;
VSYNC_HIGH;
while(i<4){
//2.2uS Back Porch
delayMicroseconds(2);
__asm__(“nop\n\t””nop\n\t””nop\n\t””nop\n\t””nop\n\t””nop\n\t”);
//20 uS Of Color Data
delayMicroseconds(20);// 20uS
//1uS Front Porch
delayMicroseconds(1); // 1uS
//HSYNC for 3.2uS
HSYNC_HIGH;
delayMicroseconds(3);
__asm__(“nop\n\t””nop\n\t”);
HSYNC_LOW;
i++;
}
VSYNC_LOW;
i=0;
while(i < 22){
//2.2uS Back Porch
delayMicroseconds(2);
__asm__(“nop\n\t””nop\n\t””nop\n\t””nop\n\t””nop\n\t””nop\n\t”);
//20uS Color Data
delayMicroseconds(20);// 20uS
//1uS Front Porch
delayMicroseconds(1); // 1uS
//HSYNC for 3.2uS
HSYNC_HIGH;
delayMicroseconds(3);
__asm__(“nop\n\t””nop\n\t”);
HSYNC_LOW;
i++;
}
}
}