diff options
Diffstat (limited to 'examples/vga-leaf.cpp')
-rw-r--r-- | examples/vga-leaf.cpp | 162 |
1 files changed, 103 insertions, 59 deletions
diff --git a/examples/vga-leaf.cpp b/examples/vga-leaf.cpp index d1c6d7d..9d9fce2 100644 --- a/examples/vga-leaf.cpp +++ b/examples/vga-leaf.cpp @@ -1,21 +1,21 @@ /* - Crude VGA Output + 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). + Outputs a red and white leaf to VGA. 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. + SerialUSB and SysTick are disabled to get rid of the most frequently + occurring interrupts (which mess with timing). This means that you + have to use perpetual bootloader mode 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) + D6 via ~200ohms to VGA Red (1) + D7 via ~200ohms to VGA Green (2) + D8 via ~200ohms to VGA Blue (3) D11 to VGA VSync (14) (swapped?) D12 to VGA HSync (13) (swapped?) GND to VGA Ground (5) @@ -24,48 +24,71 @@ 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. - + + Modified 4 March 2011 + By Marti Bolivar + Disabled SysTick, kept up-to-date with libmaple. */ +// FIXME: generalize for Native and Mini + #include "wirish.h" -#define LED_PIN 13 +#define LED_PIN BOARD_LED_PIN -// Pinouts -#define VGA_R 5 // STM32: B6 -#define VGA_G 6 // STM32: A8 -#define VGA_B 7 // STM32: A9 +// Pinouts -- you also must change the GPIO macros below if you change +// these +#define VGA_R 6 // STM32: A8 +#define VGA_G 7 // STM32: A9 +#define VGA_B 8 // STM32: A10 #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) -#define VGA_G_LOW (GPIOA_BASE)->BRR = BIT(8) -#define VGA_B_HIGH (GPIOA_BASE)->BSRR = BIT(9) -#define VGA_B_LOW (GPIOA_BASE)->BRR = BIT(9) -#define VGA_V_HIGH (GPIOA_BASE)->BSRR = BIT(6) -#define VGA_V_LOW (GPIOA_BASE)->BRR = BIT(6) -#define VGA_H_HIGH (GPIOA_BASE)->BSRR = BIT(7) -#define VGA_H_LOW (GPIOA_BASE)->BRR = BIT(7) +// These low level (and STM32 specific) macros make GPIO writes much +// faster +#define ABSRR ((volatile uint32*)0x40010810) +#define ABRR ((volatile uint32*)0x40010814) + +#define RBIT 8 // (see pinouts) +#define GBIT 9 +#define BBIT 10 + +#define VGA_R_HIGH *ABSRR = BIT(RBIT) +#define VGA_R_LOW *ABRR = BIT(RBIT) +#define VGA_G_HIGH *ABSRR = BIT(GBIT) +#define VGA_G_LOW *ABRR = BIT(GBIT) +#define VGA_B_HIGH *ABSRR = BIT(BBIT) +#define VGA_B_LOW *ABRR = BIT(BBIT) + +#define ON_COLOR BIT(RBIT) +#define OFF_COLOR (BIT(RBIT) | BIT(GBIT) | BIT(BBIT)) + +// set has priority, so clear every bit and set some given bits: +#define VGA_COLOR(c) (*ABSRR = c | \ + BIT(RBIT+16) | BIT(GBIT+16) | BIT(BBIT+16)) + +#define VGA_V_HIGH *ABSRR = BIT(6) +#define VGA_V_LOW *ABRR = BIT(6) +#define VGA_H_HIGH *ABSRR = BIT(7) +#define VGA_H_LOW *ABRR = BIT(7) void isr_porch(void); void isr_start(void); void isr_stop(void); void isr_update(void); -uint8 toggle; uint16 x = 0; // X coordinate uint16 y = 0; // Y coordinate -uint8 v_active = 1; // Are we in the image? +uint16 logo_y = 0; // Y coordinate, mapped into valid logo index (for speed) +bool v_active = true; // Are we in the image? -// 1-bit! -uint8 logo[18][16] = { +const uint8 x_max = 16; +const uint8 y_max = 18; +uint32 logo[y_max][x_max] = { {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,}, @@ -99,9 +122,20 @@ void setup() { digitalWrite(VGA_H, HIGH); digitalWrite(VGA_V, HIGH); + // Fill the logo array with color patterns corresponding to its + // truth value. Note that we could get more tricky here, since + // there are 3 bits of color. + for (int y = 0; y < y_max; y++) { + for (int x = 0; x < x_max; x++) { + logo[y][x] = logo[y][x] ? ON_COLOR : OFF_COLOR; + } + } + // This gets rid of the majority of the interrupt artifacts; + // there's still a glitch for low values of y, but let's not worry + // about that. (Probably due to the hackish way vsync is done). SerialUSB.end(); - SystemTick.end(); + systick_disable(); // Configure Timer4.pause(); // while we configure @@ -120,14 +154,13 @@ void setup() { 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() { - toggle ^= 1; - digitalWrite(LED_PIN, toggle); + toggleLED(); delay(100); // Everything happens in the interrupts! @@ -139,51 +172,59 @@ void loop() { void isr_porch(void) { VGA_H_HIGH; y++; + logo_y = map(y, 0, 478, 0, y_max); // Back to the top - if(y>=523) { - y=1; - v_active = 1; + if(y >= 523) { + y = 1; + logo_y = 0; + v_active = true; return; } // Other vsync stuff below the image - if(y>=492) { + if(y >= 492) { VGA_V_HIGH; return; } - if(y>=490) { + if(y >= 490) { VGA_V_LOW; return; } - if(y>=479) { - v_active = 0; + if(y >= 479) { + v_active = false; return; } } // This is the main horizontal sweep -void isr_start(void) { +void isr_start(void) { // Skip if we're not in the image at all - if(!v_active) { return; } + 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; - VGA_B_HIGH; - } else { - VGA_G_LOW; - VGA_B_LOW; - } + // For each "pixel", go ON_COLOR or OFF_COLOR + for(x = 0; x < 16; x++) { + // setting the color several times is just an easy way to + // delay, so the image is wider. if you only do the following + // once, you'll be able to make the logo array a lot wider: + VGA_COLOR(logo[logo_y][x]); + VGA_COLOR(logo[logo_y][x]); + VGA_COLOR(logo[logo_y][x]); + VGA_COLOR(logo[logo_y][x]); + VGA_COLOR(logo[logo_y][x]); + VGA_COLOR(logo[logo_y][x]); } } // End of the horizontal line void isr_stop(void) { - if(!v_active) { return; } + if (!v_active) { + return; + } VGA_R_LOW; VGA_G_LOW; VGA_B_LOW; @@ -194,8 +235,11 @@ void isr_update(void) { VGA_H_LOW; } -int main(void) { +__attribute__((constructor)) void premain() { init(); +} + +int main(void) { setup(); while (1) { |