aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--examples/vga-leaf.cpp178
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;
}