-
Notifications
You must be signed in to change notification settings - Fork 13
/
Copy pathESPVGAX2.cpp
216 lines (199 loc) · 5.58 KB
/
ESPVGAX2.cpp
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
206
207
208
209
210
211
212
213
214
215
216
#include "ESPVGAX2.h"
#include <math.h>
volatile uint32_t ESPVGAX2_ALIGN32 ESPVGAX2::fbw[ESPVGAX2_HEIGHT][ESPVGAX2_WWIDTH];
static volatile uint32_t *line;
static volatile uint32_t fby;
static uint32_t rby, yac;
static uint32_t vsync;
static uint32_t running;
static uint32_t wcnt;
volatile uint8_t *ESPVGAX2::fbb=(volatile uint8_t*)&ESPVGAX2::fbw[0];
// wait a fixed numbers of CPU cycles
#define NOP_DELAY_(N) asm(".rept " #N "\n\t nop \n\t .endr \n\t":::)
#define NOP_DELAY(N) NOP_DELAY_(N)
void
#if F_CPU==80000000L //bs segment does not fit for 160MHZ version
ICACHE_RAM_ATTR
#endif
vga_handler() {
//noInterrupts();
//begin negative HSYNC
GPOC=1<<ESPVGAX2_HSYNC_PIN;
#if F_CPU==80000000L
NOP_DELAY(160); //2us*80MHz
#else
NOP_DELAY(320); //2us*160MHz
#endif
//end negative HSYNC
GPOS=1<<ESPVGAX2_HSYNC_PIN;
//begin or end VSYNC, depending of value of vsync variable
ESP8266_REG(vsync)=1<<ESPVGAX2_VSYNC_PIN;
//write PIXELDATA
if (running && line) {
register volatile uint32_t *out = (volatile uint32_t *)0x60000300; //GPO
register uint32_t *in = (uint32_t*)line;
register uint32_t gpo0 = GPO & 0xffff0fff;
register uint32_t c;
//wait some time to align horizontal data
#if F_CPU==80000000L
NOP_DELAY(160);
#else
NOP_DELAY(360);
#endif
/*
* PIN D8 D5 D7 D6 ? ? ? ? ? ? D1 D2 D9 D4 D10 D3
* GPIO 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
* USE X B G R - - - - - - V H - - - -
*/
#define PUT8PIXELS \
c = *in++; \
*out = ((c << 12U) & 0x0000f000U) | gpo0; \
*out = ((c << 8U) & 0x0000f000U) | gpo0; \
*out = ((c << 4U) & 0x0000f000U) | gpo0; \
*out = ((c << 0U) & 0x0000f000U) | gpo0; \
*out = ((c >> 4U) & 0x0000f000U) | gpo0; \
*out = ((c >> 8U) & 0x0000f000U) | gpo0; \
*out = ((c >> 12U) & 0x0000f000U) | gpo0; \
*out = ((c >> 16U) & 0x0000f000U) | gpo0;
#define PUT64PIXELS \
PUT8PIXELS; PUT8PIXELS; PUT8PIXELS; PUT8PIXELS; \
PUT8PIXELS; PUT8PIXELS; PUT8PIXELS; PUT8PIXELS;
PUT64PIXELS;
PUT64PIXELS;
PUT64PIXELS;
PUT64PIXELS;
#if F_CPU==80000000L //80Mhz: send 280px
//PUT8PIXELS;
#else //160Mhz: send 320px
PUT64PIXELS;
//PUT8PIXELS;
//PUT8PIXELS;
#endif
*out = gpo0;
}
// prepare for the next vga_handler run
fby++;
switch (fby) {
case 525:
// restart from the beginning
fby=0;
rby=0;
yac=0;
/*
* feed the dog. keep ESP8266 WATCHDOG awake. VGA signal generation works
* well if there are ZERO calls to Arduino functions like delay or yield.
* These functions will perform many background tasks that generates some
* delays on the VGA signal stability but keeps the hardware WATCHDOG awake.
* I have not figured out why this happen, probably there are some hardware
* task that generate a jitter in the interrupt callback, like on ATMEGA MCU,
* see the VGAX dejitter nightmare
*/
if (0==(wcnt++%60)) //feed every 60 frames
wdt_reset();
break;
case 490:
// next line will begin negative VSYNC
vsync=0x308;
break;
case 492:
// next line will end negative VSYNC
vsync=0x304;
break;
};
// fetch the next line, or empty line in case of VGA lines [480..524]
line=(rby<ESPVGAX2_HEIGHT) ? ESPVGAX2::fbw[rby] : NULL;
if (yac%2)
rby++;
yac++;
}
void ESPVGAX2::begin() {
pinMode(ESPVGAX2_VSYNC_PIN, OUTPUT);
pinMode(ESPVGAX2_HSYNC_PIN, OUTPUT);
pinMode(ESPVGAX2_COLOR0_PIN, OUTPUT);
pinMode(ESPVGAX2_COLOR1_PIN, OUTPUT);
pinMode(ESPVGAX2_COLOR2_PIN, OUTPUT);
pinMode(ESPVGAX2_COLOR3_PIN, OUTPUT);
// prepare first line
fby=0;
line=fbw[0];
// begin with positive VSYNC
vsync=0x304;
running=1;
// install vga_handler interrupt
noInterrupts();
timer1_disable();
timer1_attachInterrupt(vga_handler);
timer1_isr_init();
timer1_enable(TIM_DIV256, TIM_EDGE, TIM_LOOP);
timer1_write(10);
//timer1_enable(TIM_DIV16, TIM_EDGE, TIM_LOOP);
//timer1_write(US_TO_RTC_TIMER_TICKS(32));
interrupts();
ESP.wdtDisable();
ESP.wdtEnable(WDTO_8S);
}
void ESPVGAX2::pause() {
running=0;
}
void ESPVGAX2::resume() {
running=1;
}
void ESPVGAX2::end() {
// disable installed interrupt
noInterrupts();
timer1_detachInterrupt();
interrupts();
}
static inline uint32_t getTicks() {
uint32_t ccount;
asm volatile ("rsr %0, ccount":"=a"(ccount));
return ccount;
}
void ESPVGAX2::delay(uint32_t msec) {
// predict the CPU ticks to be awaited
uint32_t us=msec*1000;
uint32_t start=getTicks();
uint32_t target=start+us*
#if F_CPU==80000000L
80
#else
160
#endif
;
uint32_t prev=start;
int overflow=0;
for (;;) {
uint32_t now=getTicks();
if (target<start) {
// overflow will occur
if (prev>now)
// overflow is occurred
overflow=1;
else if (overflow && now>target)
// end is reached
break;
} else if (now>target) {
// end is reached
break;
}
prev=now;
}
}
static uint64_t rand_next=1;
uint32_t ESPVGAX2::rand() {
rand_next = rand_next * 1103515245ULL + 12345;
return rand_next+((uint32_t)(rand_next / 65536) % 32768);
}
void ESPVGAX2::srand(unsigned int seed) {
rand_next = seed;
}
void ESPVGAX2::waitVSync() {
while (fby<ESPVGAX2_HEIGHT)
;
}
//include blit methods, implemented via a bunch of macros
#include "espvgax2_blit.h"
//include print methods, implemented via a bunch of macros
#include "espvgax2_print.h"
//include draw primitives methods
#include "espvgax2_draw.h"