uVGA Arduino code
// *** ALL MY Code is 100% free of cost and liability ***
//Timer code adopted from : http://www.gammon.com.au/forum/?id=11608
//---------------------------------------------------------------------------//
#include "TimerHelpers.h"
#include < avr/pgmspace.h >
#include < avr/sleep.h >
#define nop asm volatile ("nop\n\t")
#define nop_10 nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;
const int horizontalChunks = 44/2; // 2#'s per byte: 76543210 is: xbgrxBGR (where r comes before R)
volatile int verticalBackPorchLineCnt = 0;
const byte verticalBackPorchLines = 10;
volatile int LinesDrawn = 0;
volatile byte vChunk = 0;
byte vChunkpart = 0;
#define partsPerChunk 12
const int verticalPixels = 480;
const int verticalChunks = verticalPixels/partsPerChunk;
// Random Mode
uint16_t randCnt = 0;
boolean RandomMode = true;
byte randHori=0,randVert=0,randVal=0;
// Memory some of these could be volatile, but meh..
const boolean DrawBox = false; // for debug
boolean ClearEnb = false;
boolean SetEnb = false;
boolean DrawEnb = true;
byte verticalOffset = 0;
byte horizontalOffset = 0;
byte FrameCnt = 0;
byte FSM[5] = {0,0,0,0,0};
byte PixelMap[verticalChunks*2][horizontalChunks];
// PINS (do not change)
const byte redPin = 4;
const byte greenPin = 5;
const byte bluePin = 6;
const byte hSyncPin = 3; // Timer 2 OC2B (why it can't move)
const byte vSyncPin = 10; // Timer 1 OC1B (why it can't move)
//---------------------------------------------------------------------------//
#define F_CPU 16000000UL
#define BAUD 115200
// #define BAUD 9600
#include < util/setbaud.h >
//---------------------------------------------------------------------------//
void uart_init(void) {
UBRR0H = UBRRH_VALUE;
UBRR0L = UBRRL_VALUE;
#if USE_2X
UCSR0A |= _BV(U2X0);
#else
UCSR0A &= ~(_BV(U2X0));
#endif
UCSR0C = _BV(UCSZ01) | _BV(UCSZ00); /* 8-bit data */
UCSR0B = _BV(RXEN0);// | _BV(TXEN0); /* Enable RX // and TX */
}
//---------------------------------------------------------------------------//
ISR(TIMER1_OVF_vect) { // vSync
sei(); // Let this interupt get interupted (makes sure the line timing is right)
FrameCnt++;
if(SetEnb) setPixels();
if(ClearEnb) clearPixels();
else {
LinesDrawn = 0;
vChunk = verticalOffset;
verticalBackPorchLineCnt = verticalBackPorchLines;
if(RandomMode) randomFunction();
}
if(FrameCnt==0) { // every 256 frames do something that takes long
DrawEnb = false; // So the picture doesn't get messed up
if(RandomMode) {
boolean test=true;
for(int i=0;i < verticalChunks;i++)
for(int j=0;j < horizontalChunks;j++)
if(PixelMap[i*2][j]!=0) test=false;
if(test) setPixels();
}
DrawEnb = true;
}
}
//---------------------------------------------------------------------------//
ISR(TIMER2_OVF_vect){ // hSync
if(verticalBackPorchLineCnt==0) {
if(LinesDrawn!=verticalPixels) drawCurrentLine();
} else {
verticalBackPorchLineCnt--;
}
pollSerial();
}
//---------------------------------------------------------------------------//
void setup() {
uart_init();
// clearPixels();
setPixels();
if(DrawBox) {
for(int i=0;i < verticalChunks;i++)
for(int j=0; j< horizontalChunks;j++) {
PixelMap[i*2][j] = ((i==0)||(j==0)||(i==verticalChunks-1)||(j==horizontalChunks-1)||(i==j))? (1<<4)+4 : 0;
PixelMap[i*2+1][j] = PixelMap[i*2][j];
}
}
// Disable Timer 0
TIMSK0 = 0; // No interrupts on Timer 0
OCR0A = 0; // pin D6
OCR0B = 0; // pin D5
// Timer 1 - vSync (every 16666.667us, width:2 lines = 63.4us ~= 64us)
pinMode(vSyncPin, OUTPUT);
Timer1::setMode(15, Timer1::PRESCALE_1024, Timer1::CLEAR_B_ON_COMPARE);
OCR1A = 259; // pin D9 : 16666us / (1024us/16) = 260 -> [0,259]
OCR1B = 0; // pin D10 : 64us / (1024us/16) = 1 -> [0,0]
TIFR1 = _BV(TOV1); // Clear overflow flag
TIMSK1 = _BV(TOIE1); // Interrupt on overflow on timer 1
// Timer 2 - hSync (every (1/60)/525=31.746us ~= 32us, width:98 pixels = 3.8us ~= 4us)
pinMode(hSyncPin, OUTPUT);
Timer2::setMode(7, Timer2::PRESCALE_8, Timer2::CLEAR_B_ON_COMPARE);
OCR2A = 63; // pin D11 : 32us / (8us/16) = 64 -> [0,63]
OCR2B = 7; // pin D3 : 4us / (8us/16) = 8 -> [0,7]
TIFR2 = _BV(TOV2); // Clear overflow flag
TIMSK2 = _BV(TOIE2); // Interrupt on overflow on timer 2
// Sleep between horizontal sync pulses
set_sleep_mode(SLEEP_MODE_IDLE);
// Pins for outputting the colour information
pinMode(redPin, OUTPUT);
pinMode(greenPin, OUTPUT);
pinMode(bluePin, OUTPUT);
}
//---------------------------------------------------------------------------//
void drawCurrentLine() {
register byte* PixelMapPtr = &(PixelMap[vChunk*2][horizontalOffset]);
// Increment LinesDrawn and chunks here to add/overlap to/with front porch
LinesDrawn++;
if(vChunkpart == partsPerChunk-1) {
vChunkpart = 0;
if(vChunk == verticalChunks-1) {vChunk=0;nop;nop;nop;nop;} // Balanced
else {vChunk++; } // Balanced
} else {
nop_10; // Balancer
vChunkpart++;
}
if(DrawEnb) {
for(register byte i=0;i < horizontalChunks;i++) {
PORTD = *PixelMapPtr;
PixelMapPtr++;
nop;nop;
PORTD <<= 4;
}
nop;nop; // Stretch final pixel
PORTD = 0; // and set to black
}
}
//---------------------------------------------------------------------------//
void clearPixels() {
ClearEnb = false; // handshake
for(uint16_t y=0;y < verticalChunks*2;y++)
for(uint16_t x=0;x < horizontalChunks;x++)
PixelMap[y][x] = 0;
}
//---------------------------------------------------------------------------//
void setPixels() {
SetEnb = false; // handshake
for(uint16_t y=0;y < verticalChunks*2;y++)
for(uint16_t x=0;x < horizontalChunks;x++)
PixelMap[y][x] = 255;
}
//---------------------------------------------------------------------------//
void randomFunction() {
// Random shifting
verticalOffset ^= randVal;
verticalOffset %= verticalChunks;
horizontalOffset ^= randVal;
horizontalOffset %= horizontalChunks;
// Pixel Values
// PixelMap[randVert*2+0][randHori] |= randVal; // used for toggle testing
// PixelMap[randVert*2+1][randHori] |= randVal; // copy
PixelMap[randVert*2+0][randHori] &= 255^randVal;
PixelMap[randVert*2+1][randHori] &= 255^randVal;
// PixelMap[randVert*2+0][randHori] ^= randVal; // Two copies (used for horizontal tracking)
// PixelMap[randVert*2+1][randHori] ^= randVal; // Two copies
// Next Horizontal
randHori -= randVal;
randHori %= horizontalChunks;
// Next Value and Vertical
randVal += analogRead(A0);
randVert += randVal;
randVert *= randVal;
randVert %= verticalChunks;
}
//---------------------------------------------------------------------------//
void pollSerial() {
if(UCSR0A & (1<<7)) ShiftFSM(UDR0);
else CrunchFSM();
}
//---------------------------------------------------------------------------//
void CrunchFSM() {
if((FSM[0]==FSM[4])&&(FSM[0]=='_')) { // Pixel Code
PixelMap[FSM[2]*2+0][FSM[1]] = FSM[3];
PixelMap[FSM[2]*2+1][FSM[1]] = FSM[3];
} else if((FSM[0]==FSM[1])&&(FSM[1]==FSM[3])&&(FSM[3]==FSM[4])) { // Other Code (@2)
if (FSM[0]=='.'){ClearEnb=true;verticalOffset=0;horizontalOffset=0;}
else if(FSM[0]=='r') RandomMode = !RandomMode;
else if(FSM[0]=='v') verticalOffset = FSM[2];
else if(FSM[0]=='h') horizontalOffset = FSM[2];
FSM[0] = 128; // So we don't toggle back and forth (and don't stick at zero)
FSM[1] = 128; // might not be needed
FSM[3] = 128; // might not be needed
FSM[4] = 128; // might not be needed
}
}
//---------------------------------------------------------------------------//
void ShiftFSM(char newChar) {
// Unroll loop for slight speedup
FSM[4]=FSM[3];
FSM[3]=FSM[2];
FSM[2]=FSM[1];
FSM[1]=FSM[0];
FSM[0]=newChar;
}
//---------------------------------------------------------------------------//
// Loop sleep to ensure we start up clean when the INTs kick
void loop() {while(true) sleep_mode();}
// For some reason we need a while loop
//(otw the serial-rx/fsm-related screen-glips get way worse)
//---------------------------------------------------------------------------//