-
Notifications
You must be signed in to change notification settings - Fork 28
Expand file tree
/
Copy pathESPVGAX.cpp
More file actions
211 lines (199 loc) · 5.33 KB
/
ESPVGAX.cpp
File metadata and controls
211 lines (199 loc) · 5.33 KB
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
#include "ESPVGAX.h"
volatile uint32_t ESPVGAX_ALIGN32 ESPVGAX::fbw[ESPVGAX_HEIGHT][ESPVGAX_WWIDTH];
static volatile uint32_t ESPVGAX_ALIGN32 empty[ESPVGAX_WWIDTH];
static volatile uint32_t *line;
static volatile int fby;
static volatile int vsync;
static volatile int running;
#ifdef ESPVGAX_EXTRA_COLORS
volatile uint8_t props[525];
#endif
volatile uint8_t *ESPVGAX::fbb=(volatile uint8_t*)&ESPVGAX::fbw[0];
#include "espvgax_hspi.h"
// wait a fixed numbers of CPU cycles
#define NOP_DELAY(N) asm volatile(".rept " #N "\n\t nop \n\t .endr \n\t":::)
#define US_TO_RTC_TIMER_TICKS(t) \
((t) ? \
(((t) > 0x35A) ? \
(((t)>>2) * ((APB_CLK_FREQ>>4)/250000) + \
((t)&0x3) * ((APB_CLK_FREQ>>4)/1000000)) : \
(((t) *(APB_CLK_FREQ>>4)) / 1000000)) : \
0)
static inline uint32_t getTicks() {
uint32_t ccount;
asm volatile ("rsr %0, ccount":"=a"(ccount));
return ccount;
}
#define TICKS (getTicks())
void ICACHE_RAM_ATTR vga_handler() {
noInterrupts();
#if ESPVGAX_TIMER==0
// timer0 need to be scheduled again
timer0_write(TICKS+16*US_TO_RTC_TIMER_TICKS(32));
#endif
// begin negative HSYNC
GPOC=1<<ESPVGAX_HSYNC_PIN;
#ifdef ESPVGAX_EXTRA_COLORS
uint8_t pr=props[fby];
if (pr & ESPVGAX_PROP_COLOR1)
GP16O |= 1;
else
GP16O &= ~1;
if (pr & ESPVGAX_PROP_COLOR2)
GPOS=1<<ESPVGAX_EXTRA_COLOR2_PIN;
else
GPOC=1<<ESPVGAX_EXTRA_COLOR2_PIN;
#if F_CPU==80000000L
NOP_DELAY(100); // 2us*80MHz (- 60clock becouse extra colors require time)
#else
NOP_DELAY(400); // should be 320 (2us*160MHz) but 400 works well
#endif
#else // ESPVGAX_EXTRA_COLORS not defined
#if F_CPU==80000000L
NOP_DELAY(160); // 2us*80MHz
#else
NOP_DELAY(480); // should be 320 (2us*160MHz) but 480 works well
#endif
#endif
// end negative HSYNC
GPOS=1<<ESPVGAX_HSYNC_PIN;
// begin or end VSYNC, depending of value of vsync variable
ESP8266_REG(vsync)=1<<ESPVGAX_VSYNC_PIN;
//write PIXELDATA
if (running) {
HSPI_VGA_prepare();
HSPI_VGA_send();
}
// prepare for the next vga_handler run
fby++;
switch (fby) {
case 525:
// restart from the beginning
fby=0;
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=(fby<ESPVGAX_HEIGHT) ? ESPVGAX::fbw[fby] : empty;
interrupts();
/*
* 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
*/
ESP.wdtFeed();
}
void ESPVGAX::begin() {
pinMode(ESPVGAX_VSYNC_PIN, OUTPUT);
pinMode(ESPVGAX_HSYNC_PIN, OUTPUT);
pinMode(ESPVGAX_COLOR_PIN, OUTPUT);
#ifdef ESPVGAX_EXTRA_COLORS
pinMode(ESPVGAX_EXTRA_COLOR1_PIN, OUTPUT);
pinMode(ESPVGAX_EXTRA_COLOR2_PIN, OUTPUT);
#endif
// prepare first line
fby=0;
line=fbw[0];
// begin with positive VSYNC
vsync=0x304;
running=1;
// setup HSPI to output PIXELDATA on D7 PIN
HSPI_VGA_init();
// install vga_handler interrupt
noInterrupts();
#if ESPVGAX_TIMER==0
timer0_isr_init();
timer0_attachInterrupt(vga_handler);
timer0_write(TICKS+16*US_TO_RTC_TIMER_TICKS(32));
#else
timer1_isr_init();
timer1_attachInterrupt(vga_handler);
timer1_enable(TIM_DIV16, TIM_EDGE, TIM_LOOP);
timer1_write(US_TO_RTC_TIMER_TICKS(32));
#endif
interrupts();
}
void ESPVGAX::pause() {
running=0;
}
void ESPVGAX::resume() {
running=1;
}
void ESPVGAX::end() {
// disable installed interrupt
noInterrupts();
#if ESPVGAX_TIMER==0
timer0_detachInterrupt();
#else
timer1_detachInterrupt();
#endif
interrupts();
}
void ICACHE_RAM_ATTR ESPVGAX::delay(uint32_t msec) {
// predict the CPU ticks to be awaited
uint32_t us=msec*1000;
uint32_t start=TICKS;
uint32_t target=start+16*US_TO_RTC_TIMER_TICKS(us);
uint32_t prev=start;
int overflow=0;
for (;;) {
uint32_t now=TICKS;
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 ESPVGAX::rand() {
rand_next = rand_next * 1103515245ULL + 12345;
return rand_next+((uint32_t)(rand_next / 65536) % 32768);
}
void ESPVGAX::srand(unsigned int seed) {
rand_next = seed;
}
void ESPVGAX::setLinesProp(int y, int end, uint8_t prop) {
while (y<end)
setLineProp(y++, prop);
}
void ESPVGAX::setLineProp(int y, uint8_t prop) {
#ifdef ESPVGAX_EXTRA_COLORS
if (y>=ESPVGAX_HEIGHT)
return;
props[y]=prop;
#else
#endif
}
uint8_t ESPVGAX::getLineProp(int y) {
#ifdef ESPVGAX_EXTRA_COLORS
return props[y];
#else
return 0;
#endif
}
//include blit methods, implemented via a bunch of macros
#include "espvgax_blit.h"
//include print methods, implemented via a bunch of macros
#include "espvgax_print.h"
//include draw primitives methods
#include "espvgax_draw.h"