diff options
-rw-r--r-- | examples/vga-leaf.cpp | 178 |
1 files changed, 116 insertions, 62 deletions
diff --git a/examples/vga-leaf.cpp b/examples/vga-leaf.cpp index db20eb2..16204ba 100644 --- a/examples/vga-leaf.cpp +++ b/examples/vga-leaf.cpp @@ -1,13 +1,48 @@ -// Low-level, non-wirish demonstration of VGA +/* + Crude VGA Output + + Outputs a red and white leaf to VGA. This implementation is crude and noisy, + but a fun demo. It should run most VGA monitors at 640x480, though it does + not follow the timing spec very carefully. Real twisted or shielded wires, + proper grounding, and not doing this on a breadboard are recommended (but + it seems to work ok without). + + SerialUSB is disabled to get rid of most interrupts (which mess with timing); + the SysTick is probably the source of the remaining flickers. This means that + you have to use perpetual bootloader or the reset button to flash new + programs. + + How to wire this to a VGA port: + D5 via ~200ohms to VGA Red (1) + D6 via ~200ohms to VGA Green (2) + D7 via ~200ohms to VGA Blue (3) + D11 to VGA VSync (14) (swapped?) + D12 to VGA HSync (13) (swapped?) + GND to VGA Ground (5) + GND to VGA Sync Ground (10) + + See also: + - http://pinouts.ru/Video/VGA15_pinout.shtml + - http://www.epanorama.net/documents/pc/vga_timing.html + + Created 20 July 2010 + By Bryan Newbold for LeafLabs + This code is released with no strings attached. + + */ #include "wirish.h" #define LED_PIN 13 -#define VGA_R 5 // B6 -#define VGA_G 6 // A8 -#define VGA_B 7 // A9 -#define VGA_V 11 // A6 -#define VGA_H 12 // A7 + +// Pinouts +#define VGA_R 5 // STM32: B6 +#define VGA_G 6 // STM32: A8 +#define VGA_B 7 // STM32: A9 +#define VGA_V 11 // STM32: A6 +#define VGA_H 12 // STM32: A7 + +// These low level macros make GPIO writes much faster #define VGA_R_HIGH (GPIOB_BASE)->BSRR = BIT(6) #define VGA_R_LOW (GPIOB_BASE)->BRR = BIT(6) #define VGA_G_HIGH (GPIOA_BASE)->BSRR = BIT(8) @@ -24,65 +59,95 @@ void isr_start(void); void isr_stop(void); void isr_update(void); -void setup() -{ +uint8 toggle; +uint16 x = 0; // X coordinate +uint16 y = 0; // Y coordinate +uint8 v_active = 1; // Are we in the image? + +// 1-bit! +uint8 logo[18][16] = { + {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}, + {0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,}, + {0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,}, + {0,0,0,0,1,0,0,1,0,0,1,0,0,0,0,0,}, + {0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,0,}, + {0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,}, + {0,0,1,0,0,1,0,1,0,1,0,0,1,0,0,0,}, + {0,1,0,0,0,0,1,1,1,0,0,0,0,1,0,0,}, + {0,1,0,1,0,0,0,1,0,0,0,1,0,1,0,0,}, + {1,0,0,0,1,0,0,1,0,0,1,0,0,0,1,0,}, + {1,0,0,0,0,1,0,1,0,1,0,0,0,0,1,0,}, + {1,0,0,0,0,0,1,1,1,0,0,0,0,0,1,0,}, + {0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,}, + {0,0,1,1,0,0,0,1,0,0,0,1,1,0,0,0,}, + {0,0,0,0,1,1,1,0,1,1,1,0,0,0,0,0,}, + {0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,}, + {0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,}, + {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}, }; + +void setup() { + + // Setup our pins pinMode(LED_PIN, OUTPUT); - digitalWrite(LED_PIN, 1); pinMode(VGA_R, OUTPUT); pinMode(VGA_G, OUTPUT); pinMode(VGA_B, OUTPUT); pinMode(VGA_V, OUTPUT); pinMode(VGA_H, OUTPUT); - - /* Send a message out USART2 */ - Serial2.begin(9600); - Serial2.println("Video time..."); + digitalWrite(VGA_R, LOW); + digitalWrite(VGA_G, LOW); + digitalWrite(VGA_B, LOW); + digitalWrite(VGA_H, HIGH); + digitalWrite(VGA_V, HIGH); // This gets rid of the majority of the interrupt artifacts; // a SysTick.end() is required as well SerialUSB.end(); - digitalWrite(VGA_R, 0); - digitalWrite(VGA_G, 0); - digitalWrite(VGA_B, 0); - digitalWrite(VGA_H,1); - digitalWrite(VGA_V,1); - - timer_set_prescaler(4,0); - timer_set_mode(4, 1, TIMER_OUTPUTCOMPARE); - timer_set_mode(4, 2, TIMER_OUTPUTCOMPARE); - timer_set_mode(4, 3, TIMER_OUTPUTCOMPARE); - timer_set_mode(4, 4, TIMER_OUTPUTCOMPARE); - timer_set_reload(4, 2287); - timer_set_compare_value(4,1,200); - timer_set_compare_value(4,2,300); - timer_set_compare_value(4,3,2170); // 2219 max... - timer_set_compare_value(4,4,1); - timer_attach_interrupt(4,1,isr_porch); - timer_attach_interrupt(4,2,isr_start); - timer_attach_interrupt(4,3,isr_stop); - timer_attach_interrupt(4,4,isr_update); - - timer_set_count(4,0); + + // Configure + Timer4.pause(); // while we configure + Timer4.setPrescaleFactor(1); // Full speed + Timer4.setChannel1Mode(TIMER_OUTPUTCOMPARE); + Timer4.setChannel2Mode(TIMER_OUTPUTCOMPARE); + Timer4.setChannel3Mode(TIMER_OUTPUTCOMPARE); + Timer4.setChannel4Mode(TIMER_OUTPUTCOMPARE); + Timer4.setOverflow(2287); // Total line time + + Timer4.setCompare1(200); + Timer4.attachCompare1Interrupt(isr_porch); + Timer4.setCompare2(300); + Timer4.attachCompare2Interrupt(isr_start); + Timer4.setCompare3(2170); + Timer4.attachCompare3Interrupt(isr_stop); + Timer4.setCompare4(1); // Could be zero I guess + Timer4.attachCompare4Interrupt(isr_update); + + Timer4.setCount(0); // Ready... + Timer4.resume(); // Go! } void loop() { - // everything happens in the interrupts! + toggle ^= 1; + digitalWrite(LED_PIN, toggle); + delay(100); + + // Everything happens in the interrupts! } -int toggle = 0; -uint16 x = 0; -uint16 y = 0; -uint8 v_active = 1; +// This ISR will end horizontal sync for most of the image and +// setup the vertical sync for higher line counts void isr_porch(void) { VGA_H_HIGH; y++; + // Back to the top if(y>=523) { y=1; v_active = 1; return; } + // Other vsync stuff below the image if(y>=492) { VGA_V_HIGH; return; @@ -91,37 +156,22 @@ void isr_porch(void) { VGA_V_LOW; return; } - if(y>=479) { // 479 + if(y>=479) { v_active = 0; return; } } -uint8 logo[18][16] = { - {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}, - {0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,}, - {0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,}, - {0,0,0,0,1,0,0,1,0,0,1,0,0,0,0,0,}, - {0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,0,}, - {0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,}, - {0,0,1,0,0,1,0,1,0,1,0,0,1,0,0,0,}, - {0,1,0,0,0,0,1,1,1,0,0,0,0,1,0,0,}, - {0,1,0,1,0,0,0,1,0,0,0,1,0,1,0,0,}, - {1,0,0,0,1,0,0,1,0,0,1,0,0,0,1,0,}, - {1,0,0,0,0,1,0,1,0,1,0,0,0,0,1,0,}, - {1,0,0,0,0,0,1,1,1,0,0,0,0,0,1,0,}, - {0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,}, - {0,0,1,1,0,0,0,1,0,0,0,1,1,0,0,0,}, - {0,0,0,0,1,1,1,0,1,1,1,0,0,0,0,0,}, - {0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,}, - {0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,}, - {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}, }; - -void isr_start(void) { +// This is the main horizontal sweep +void isr_start(void) { + // Skip if we're not in the image at all if(!v_active) { return; } + + // Start Red VGA_R_LOW; VGA_R_HIGH; + // For each "pixel" (really 20 or so screen pixels?) go red or white for(x=0; x<32; x++) { if(logo[y/28][x/2]) { VGA_G_HIGH; @@ -132,12 +182,16 @@ void isr_start(void) { } } } + +// End of the horizontal line void isr_stop(void) { if(!v_active) { return; } VGA_R_LOW; VGA_G_LOW; VGA_B_LOW; } + +// Setup horizonal sync void isr_update(void) { VGA_H_LOW; } |