libmaple/examples/vga-leaf.cpp

245 lines
6.7 KiB
C++
Raw Permalink Normal View History

2010-07-21 04:03:32 +02:00
/*
VGA Output
2010-07-21 04:03:32 +02:00
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).
2010-07-21 04:03:32 +02:00
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.
2010-07-21 04:03:32 +02:00
How to wire this to a VGA port:
D6 via ~200ohms to VGA Red (1)
D7 via ~200ohms to VGA Green (2)
D8 via ~200ohms to VGA Blue (3)
2010-07-21 04:03:32 +02:00
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
2010-07-21 04:03:32 +02:00
Created 20 July 2010
By Bryan Newbold for LeafLabs
This code is released with no strings attached.
*/
2010-07-20 21:34:35 +02:00
// FIXME: generalize for Native and Mini
Move public headers to include directories; related cleanups. Move libmaple/*.h to (new) libmaple/include/libmaple/. The new accepted way to include a libmaple header foo.h is with: #include <libmaple/foo.h> This is more polite in terms of the include namespace. It also allows us to e.g. implement the Arduino SPI library at all (which has header SPI.h; providing it was previously impossible on case-insensitive filesystems due to libmaple's spi.h). Similarly for Wirish. The old include style (#include "header.h") is now deprecated. libmaple/*.h: - Change include guard #defines from _FOO_H_ to _LIBMAPLE_FOO_H_. - Add license headers where they're missing - Add conditional extern "C" { ... } blocks where they're missing (they aren't always necessary, but we might was well do it against the future, while we're at it.). - Change includes from #include "foo.h" to #include <libmaple/foo.h>. - Move includes after extern "C". - Remove extra trailing newlines Note that this doesn't include the headers under libmaple/usb/ or libmaple/usb/usb_lib. These will get fixed later. libmaple/*.c: - Change includes from #include "foo.h" to #include <libmaple/foo.h>. Makefile: - Add I$(LIBMAPLE_PATH)/include/libmaple to GLOBAL_FLAGS. This allows for users (including Wirish) to migrate their code, but should go away ASAP, since it slows down compilation. Wirish: - Move wirish/**/*.h to (new) wirish/include/wirish/. This ignores the USB headers, which, as usual, are getting handled after everything else. - Similarly generify wirish/boards/ structure. For each supported board "foo", move wirish/boards/foo.h and wirish/boards/foo.cpp to wirish/boards/foo/include/board/board.h and wirish/boards/foo/board.cpp, respectively. Also remove the #ifdef hacks around the .cpp files. - wirish/rules.mk: put wirish/boards/foo/include in the include path (and add wirish/boards/foo/board.cpp to the list of sources to be compiled). This allows saying: #include <board/board.h> instead of the hack currently in place. We can allow the user to override this setting later to make adding custom board definitions easier. - Disable -Werror in libmaple/rules.mk, as the current USB warnings don't let the olimex_stm32_h103 board compile. We can re-enable -Werror once we've moved the board-specific bits out of libmaple proper. libraries, examples: - Update includes accordingly. - Miscellaneous cosmetic fixups. Signed-off-by: Marti Bolivar <mbolivar@leaflabs.com>
2011-11-15 18:45:43 +01:00
#include <wirish/wirish.h>
// 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
2010-07-21 04:03:32 +02:00
#define VGA_V 11 // STM32: A6
#define VGA_H 12 // STM32: A7
// 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);
2010-07-21 04:03:32 +02:00
uint16 x = 0; // X coordinate
uint16 y = 0; // Y coordinate
uint16 logo_y = 0; // Y coordinate, mapped into valid logo index (for speed)
bool v_active = true; // Are we in the image?
2010-07-21 04:03:32 +02:00
const uint8 x_max = 16;
const uint8 y_max = 18;
uint32 logo[y_max][x_max] = {
2010-07-21 04:03:32 +02:00
{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,}, };
HardwareTimer timer(4);
2010-07-21 04:03:32 +02:00
void setup() {
// Setup our pins
2011-05-02 20:33:05 +02:00
pinMode(BOARD_LED_PIN, OUTPUT);
pinMode(VGA_R, OUTPUT);
pinMode(VGA_G, OUTPUT);
pinMode(VGA_B, OUTPUT);
pinMode(VGA_V, OUTPUT);
pinMode(VGA_H, OUTPUT);
2010-07-21 04:03:32 +02:00
digitalWrite(VGA_R, LOW);
digitalWrite(VGA_G, LOW);
digitalWrite(VGA_B, LOW);
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();
systick_disable();
2010-07-21 04:03:32 +02:00
// Configure
timer.pause(); // while we configure
timer.setPrescaleFactor(1); // Full speed
timer.setMode(TIMER_CH1, TIMER_OUTPUT_COMPARE);
timer.setMode(TIMER_CH2, TIMER_OUTPUT_COMPARE);
timer.setMode(TIMER_CH3, TIMER_OUTPUT_COMPARE);
timer.setMode(TIMER_CH4, TIMER_OUTPUT_COMPARE);
timer.setOverflow(2287); // Total line time
timer.setCompare(TIMER_CH1, 200);
timer.attachInterrupt(TIMER_CH1, isr_porch);
timer.setCompare(TIMER_CH2, 300);
timer.attachInterrupt(TIMER_CH2, isr_start);
timer.setCompare(TIMER_CH3, 2170);
timer.attachInterrupt(TIMER_CH3, isr_stop);
timer.setCompare(TIMER_CH4, 1); // Could be zero, I guess
timer.attachInterrupt(TIMER_CH4, isr_update);
timer.setCount(0); // Ready...
timer.resume(); // Go!
}
void loop() {
toggleLED();
2010-07-21 04:03:32 +02:00
delay(100);
// Everything happens in the interrupts!
}
2010-07-21 04:03:32 +02:00
// 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++;
logo_y = map(y, 0, 478, 0, y_max);
2010-07-21 04:03:32 +02:00
// Back to the top
2011-05-02 20:33:05 +02:00
if (y >= 523) {
y = 1;
logo_y = 0;
v_active = true;
return;
}
2010-07-21 04:03:32 +02:00
// Other vsync stuff below the image
2011-05-02 20:33:05 +02:00
if (y >= 492) {
VGA_V_HIGH;
return;
}
2011-05-02 20:33:05 +02:00
if (y >= 490) {
VGA_V_LOW;
return;
}
2011-05-02 20:33:05 +02:00
if (y >= 479) {
v_active = false;
return;
}
}
2010-07-21 04:03:32 +02:00
// This is the main horizontal sweep
void isr_start(void) {
2010-07-21 04:03:32 +02:00
// Skip if we're not in the image at all
if (!v_active) {
return;
}
2010-07-21 04:03:32 +02:00
// Start Red
VGA_R_LOW;
VGA_R_HIGH;
// For each "pixel", go ON_COLOR or OFF_COLOR
2011-05-02 20:33:05 +02:00
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 bigger:
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]);
}
}
2010-07-21 04:03:32 +02:00
// End of the horizontal line
void isr_stop(void) {
if (!v_active) {
return;
}
VGA_R_LOW;
VGA_G_LOW;
VGA_B_LOW;
}
2010-07-21 04:03:32 +02:00
// Setup horizonal sync
void isr_update(void) {
VGA_H_LOW;
}
__attribute__((constructor)) void premain() {
init();
}
int main(void) {
setup();
2011-05-02 20:33:05 +02:00
while (true) {
loop();
}
return 0;
}