aboutsummaryrefslogtreecommitdiffstats
path: root/examples/vga-scope.cpp
blob: 8730cf03d228b9fff7b56370e51f237d1cf6714e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
/*
  VGA Oscilloscope demo.

  Connect a microphone or something like it to ANALOG_PIN (0V -- 3.3V
  only; 0.2V -- 3.1V will probably look nicer); an attached VGA
  monitor will display the signal roughly in real-time.

  The thick blue line corresponds roughly to 0V.

  This is a fairy crude hack, but it's fun to watch/toy around with.

  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:
  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)
  GND to VGA Sync Ground         (10)

  See also:
  - http://pinouts.ru/Video/VGA15_pinout.shtml
  - http://www.epanorama.net/documents/pc/vga_timing.html

  This code is released into the public domain.

  Authors:

  Bryan Newbold <bnewbold@leaflabs.com>
  Marti Bolivar <mbolivar@leaflabs.com>
 */

#include <wirish/wirish.h>
#include <libmaple/systick.h>

// FIXME: generalize for Native and Mini

#define ANALOG_PIN 18

// 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 (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 COLOR_WHITE (BIT(RBIT) | BIT(GBIT) | BIT(BBIT))
#define COLOR_BLACK 0
#define COLOR_RED   BIT(RBIT)
#define COLOR_GREEN BIT(GBIT)
#define COLOR_BLUE  BIT(BBIT)

#define BORDER_COLOR COLOR_BLUE

// 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);

void setup() {
    pinMode(BOARD_LED_PIN, OUTPUT);
    pinMode(ANALOG_PIN, INPUT_ANALOG);
    digitalWrite(BOARD_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("Time to kill the radio star...");

    // 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();

    digitalWrite(VGA_R, 0);
    digitalWrite(VGA_G, 0);
    digitalWrite(VGA_B, 0);
    digitalWrite(VGA_H, 1);
    digitalWrite(VGA_V, 1);

    timer_pause(TIMER4);
    timer_set_prescaler(TIMER4, 0);
    timer_set_mode(TIMER4, 1, TIMER_OUTPUT_COMPARE);
    timer_set_mode(TIMER4, 2, TIMER_OUTPUT_COMPARE);
    timer_set_mode(TIMER4, 3, TIMER_OUTPUT_COMPARE);
    timer_set_mode(TIMER4, 4, TIMER_OUTPUT_COMPARE);
    timer_set_reload(TIMER4, 2287);
    timer_set_compare(TIMER4, 1, 200);
    timer_set_compare(TIMER4, 2, 250);
    timer_set_compare(TIMER4, 3, 2170);  // 2219 max...
    timer_set_compare(TIMER4, 4, 1);
    timer_attach_interrupt(TIMER4, 1, isr_porch);
    timer_attach_interrupt(TIMER4, 2, isr_start);
    timer_attach_interrupt(TIMER4, 3, isr_stop);
    timer_attach_interrupt(TIMER4, 4, isr_update);

    timer_set_count(TIMER4, 0);
    timer_resume(TIMER4);
}

uint16 y = 0;
uint16 val = 0;
bool v_active = true;
const uint16 x_max = 60;        // empirically (and sloppily) determined

void isr_porch(void) {
    VGA_H_HIGH;
    y++;
    val = map(analogRead(ANALOG_PIN), 0, 4095, 0, x_max);
    if (y >= 523) {
        y = 1;
        v_active = true;
        return;
    }
    if (y >= 492) {
        VGA_V_HIGH;
        return;
    }
    if (y >= 490) {
        VGA_V_LOW;
        return;
    }
    if (y >= 479) {
        v_active = false;
        return;
    }

}

void isr_start(void) {
    if (!v_active) {
        return;
    }
    VGA_COLOR(BORDER_COLOR);
    for (int x = 0; x < val; x++) {
        VGA_COLOR(COLOR_BLACK);
    }
    VGA_COLOR(COLOR_WHITE);
    VGA_COLOR(COLOR_BLACK);
}

void isr_stop(void) {
    if (!v_active) {
        return;
    }
    VGA_COLOR(COLOR_BLACK);
}

void isr_update(void) {
    VGA_H_LOW;
}

void loop() {
    toggleLED();
    delay(100);
}

__attribute__((constructor)) void premain() {
    init();
}

int main(void) {
    setup();

    while (true) {
        loop();
    }
    return 0;
}