6 #include "soc/i2s_struct.h" 7 #include "soc/i2s_reg.h" 8 #include "driver/periph_ctrl.h" 9 #include "rom/lldesc.h" 24 volatile int VGATextController::s_scanLine;
25 uint32_t VGATextController::s_blankPatternDWord;
26 uint32_t * VGATextController::s_fgbgPattern =
nullptr;
27 int VGATextController::s_textRow;
28 bool VGATextController::s_upperRow;
29 lldesc_t
volatile * VGATextController::s_frameResetDesc;
31 #if FABGLIB_VGAXCONTROLLER_PERFORMANCE_CHECK 32 volatile uint64_t s_vgatxtcycles = 0;
39 VGATextController::VGATextController()
40 : m_charData(nullptr),
42 m_cursorEnabled(false),
47 m_cursorForeground(0),
48 m_cursorBackground(15)
53 VGATextController::~VGATextController()
55 free((
void*)m_charData);
62 while (m_map !=
nullptr && s_scanLine < m_timings.
VVisibleArea)
72 *columns = VGATextController_COLUMNS;
73 if (*rows > VGATextController_ROWS)
74 *rows = VGATextController_ROWS;
79 void VGATextController::init(gpio_num_t VSyncGPIO)
81 m_DMABuffers =
nullptr;
86 int charDataSize = 256 * FONT_8x14.height * ((FONT_8x14.width + 7) / 8);
87 m_charData = (uint8_t*) heap_caps_malloc(charDataSize, MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
88 memcpy(m_charData, FONT_8x14.data, charDataSize);
93 void VGATextController::begin(gpio_num_t redGPIO, gpio_num_t greenGPIO, gpio_num_t blueGPIO, gpio_num_t HSyncGPIO, gpio_num_t VSyncGPIO)
98 setupGPIO(redGPIO, VGA_RED_BIT, GPIO_MODE_OUTPUT);
99 setupGPIO(greenGPIO, VGA_GREEN_BIT, GPIO_MODE_OUTPUT);
100 setupGPIO(blueGPIO, VGA_BLUE_BIT, GPIO_MODE_OUTPUT);
103 setupGPIO(HSyncGPIO, VGA_HSYNC_BIT, GPIO_MODE_OUTPUT);
104 setupGPIO(VSyncGPIO, VGA_VSYNC_BIT, GPIO_MODE_INPUT_OUTPUT);
106 RGB222::lowBitOnly =
true;
107 m_bitsPerChannel = 1;
112 void VGATextController::begin(gpio_num_t red1GPIO, gpio_num_t red0GPIO, gpio_num_t green1GPIO, gpio_num_t green0GPIO, gpio_num_t blue1GPIO, gpio_num_t blue0GPIO, gpio_num_t HSyncGPIO, gpio_num_t VSyncGPIO)
114 begin(red0GPIO, green0GPIO, blue0GPIO, HSyncGPIO, VSyncGPIO);
117 setupGPIO(red1GPIO, VGA_RED_BIT + 1, GPIO_MODE_OUTPUT);
118 setupGPIO(green1GPIO, VGA_GREEN_BIT + 1, GPIO_MODE_OUTPUT);
119 setupGPIO(blue1GPIO, VGA_BLUE_BIT + 1, GPIO_MODE_OUTPUT);
121 RGB222::lowBitOnly =
false;
122 m_bitsPerChannel = 2;
129 begin(GPIO_NUM_22, GPIO_NUM_21, GPIO_NUM_19, GPIO_NUM_18, GPIO_NUM_5, GPIO_NUM_4, GPIO_NUM_23, GPIO_NUM_15);
133 void VGATextController::setCursorForeground(
Color value)
135 m_cursorForeground = (int) value;
139 void VGATextController::setCursorBackground(
Color value)
141 m_cursorBackground = (int) value;
145 void VGATextController::setupGPIO(gpio_num_t gpio,
int bit, gpio_mode_t mode)
147 configureGPIO(gpio, mode);
148 gpio_matrix_out(gpio, I2S1O_DATA_OUT0_IDX + bit,
false,
false);
155 if (VGABaseController::convertModelineToTimings(VGATextController_MODELINE, &timings))
173 m_HVSync = packHVSync(
false,
false);
177 m_DMABuffers = (lldesc_t*) heap_caps_malloc(m_DMABuffersCount *
sizeof(lldesc_t), MALLOC_CAP_DMA);
179 m_lines = (uint32_t*) heap_caps_malloc(VGATextController_CHARHEIGHT * VGATextController_WIDTH, MALLOC_CAP_DMA);
182 m_blankLine = (uint8_t*) heap_caps_malloc(rawLineWidth, MALLOC_CAP_DMA);
183 m_syncLine = (uint8_t*) heap_caps_malloc(rawLineWidth, MALLOC_CAP_DMA);
192 for (
int i = 0, visLine = 0, invLine = 0; i < m_DMABuffersCount; ++i) {
197 m_DMABuffers[i].eof = (visLine == 0 || visLine == VGATextController_CHARHEIGHT / 2 ? 1 : 0);
198 m_DMABuffers[i].sosf = 0;
199 m_DMABuffers[i].offset = 0;
200 m_DMABuffers[i].owner = 1;
201 m_DMABuffers[i].qe.stqe_next = (lldesc_t*) &m_DMABuffers[i + 1];
203 m_DMABuffers[i].size = (m_DMABuffers[i].length + 3) & (~3);
204 m_DMABuffers[i].buf = (uint8_t*) m_blankLine;
209 m_DMABuffers[i].eof = 0;
210 m_DMABuffers[i].sosf = 0;
211 m_DMABuffers[i].offset = 0;
212 m_DMABuffers[i].owner = 1;
213 m_DMABuffers[i].qe.stqe_next = (lldesc_t*) &m_DMABuffers[i + 1];
215 m_DMABuffers[i].size = (m_DMABuffers[i].length + 3) & (~3);
216 m_DMABuffers[i].buf = (uint8_t*)(m_lines) + visLine * VGATextController_WIDTH;
219 if (visLine == VGATextController_CHARHEIGHT)
226 bool frameResetDesc = (invLine == 0);
229 s_frameResetDesc = &m_DMABuffers[i];
231 m_DMABuffers[i].eof = (frameResetDesc ? 1 : 0);
232 m_DMABuffers[i].sosf = 0;
233 m_DMABuffers[i].offset = 0;
234 m_DMABuffers[i].owner = 1;
235 m_DMABuffers[i].qe.stqe_next = (lldesc_t*) (i == m_DMABuffersCount - 1 ? &m_DMABuffers[0] : &m_DMABuffers[i + 1]);
236 m_DMABuffers[i].length = rawLineWidth;
237 m_DMABuffers[i].size = (m_DMABuffers[i].length + 3) & (~3);
240 m_DMABuffers[i].buf = (uint8_t*) m_blankLine;
242 m_DMABuffers[i].buf = (uint8_t*) m_syncLine;
253 s_blankPatternDWord = m_HVSync | (m_HVSync << 8) | (m_HVSync << 16) | (m_HVSync << 24);
255 if (s_fgbgPattern ==
nullptr) {
256 s_fgbgPattern = (uint32_t*) heap_caps_malloc(16384, MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
257 for (
int i = 0; i < 16; ++i)
258 for (
int fg = 0; fg < 16; ++fg)
259 for (
int bg = 0; bg < 16; ++bg) {
260 uint8_t fg_pat = preparePixel(RGB222((
Color)fg));
261 uint8_t bg_pat = preparePixel(RGB222((
Color)bg));
262 s_fgbgPattern[i | (bg << 4) | (fg << 8)] = (i & 0b1000 ? (fg_pat << 16) : (bg_pat << 16)) |
263 (i & 0b0100 ? (fg_pat << 24) : (bg_pat << 24)) |
264 (i & 0b0010 ? (fg_pat << 0) : (bg_pat << 0)) |
265 (i & 0b0001 ? (fg_pat << 8) : (bg_pat << 8));
271 CoreUsage::setBusiestCore(FABGLIB_VIDEO_CPUINTENSIVE_TASKS_CORE);
272 esp_intr_alloc_pinnedToCore(ETS_I2S1_INTR_SOURCE, ESP_INTR_FLAG_LEVEL1 | ESP_INTR_FLAG_IRAM, ISRHandler,
this, &m_isr_handle, FABGLIB_VIDEO_CPUINTENSIVE_TASKS_CORE);
274 m_GPIOStream.play(m_timings.
frequency, m_DMABuffers);
276 I2S1.int_clr.val = 0xFFFFFFFF;
277 I2S1.int_ena.out_eof = 1;
281 void VGATextController::freeBuffers()
283 heap_caps_free( (
void*) m_DMABuffers );
284 heap_caps_free((
void*) m_lines);
285 m_DMABuffers =
nullptr;
286 heap_caps_free((
void*) m_blankLine);
287 heap_caps_free((
void*) m_syncLine);
291 uint8_t IRAM_ATTR VGATextController::packHVSync(
bool HSync,
bool VSync)
293 uint8_t hsync_value = (m_timings.
HSyncLogic ==
'+' ? (HSync ? 1 : 0) : (HSync ? 0 : 1));
294 uint8_t vsync_value = (m_timings.
VSyncLogic ==
'+' ? (VSync ? 1 : 0) : (VSync ? 0 : 1));
295 return (vsync_value << VGA_VSYNC_BIT) | (hsync_value << VGA_HSYNC_BIT);
299 uint8_t IRAM_ATTR VGATextController::preparePixelWithSync(RGB222 rgb,
bool HSync,
bool VSync)
301 return packHVSync(HSync, VSync) | (rgb.B << VGA_BLUE_BIT) | (rgb.G << VGA_GREEN_BIT) | (rgb.R << VGA_RED_BIT);
305 void VGATextController::fillDMABuffers()
309 VGA_PIXELINROW(m_blankLine, x) = preparePixelWithSync((RGB222){0, 0, 0},
false,
false);
310 VGA_PIXELINROW(m_syncLine, x) = preparePixelWithSync((RGB222){0, 0, 0},
false,
true);
313 VGA_PIXELINROW(m_blankLine, x) = preparePixelWithSync((RGB222){0, 0, 0},
true,
false);
314 VGA_PIXELINROW(m_syncLine, x) = preparePixelWithSync((RGB222){0, 0, 0},
true,
true);
317 VGA_PIXELINROW(m_blankLine, x) = preparePixelWithSync((RGB222){0, 0, 0},
false,
false);
318 VGA_PIXELINROW(m_syncLine, x) = preparePixelWithSync((RGB222){0, 0, 0},
false,
true);
321 for (
int rx = 0; x < rawLineWidth; ++x, ++rx) {
322 VGA_PIXELINROW(m_blankLine, x) = preparePixelWithSync((RGB222){0, 0, 0},
false,
false);
323 VGA_PIXELINROW(m_syncLine, x) = preparePixelWithSync((RGB222){0, 0, 0},
false,
true);
324 for (
int i = 0; i < VGATextController_CHARHEIGHT; ++i)
325 VGA_PIXELINROW( ((uint8_t*)(m_lines) + i * VGATextController_WIDTH), rx) = preparePixelWithSync((RGB222){0, 0, 0},
false,
false);
330 void IRAM_ATTR VGATextController::ISRHandler(
void * arg)
332 #if FABGLIB_VGAXCONTROLLER_PERFORMANCE_CHECK 333 auto s1 = getCycleCount();
336 VGATextController * ctrl = (VGATextController *) arg;
338 if (I2S1.int_st.out_eof && ctrl->m_charData !=
nullptr) {
340 auto desc = (
volatile lldesc_t*) I2S1.out_eof_des_addr;
342 if (desc == s_frameResetDesc) {
348 if (ctrl->m_cursorEnabled) {
349 ++ctrl->m_cursorCounter;
350 if (ctrl->m_cursorCounter >= ctrl->m_cursorSpeed)
351 ctrl->m_cursorCounter = -ctrl->m_cursorSpeed;
354 if (ctrl->m_map ==
nullptr) {
355 I2S1.int_clr.val = I2S1.int_st.val;
356 #if FABGLIB_VGAXCONTROLLER_PERFORMANCE_CHECK 357 s_vgatxtcycles += getCycleCount() - s1;
362 }
else if (s_scanLine == 0) {
364 I2S1.int_clr.val = I2S1.int_st.val;
365 #if FABGLIB_VGAXCONTROLLER_PERFORMANCE_CHECK 366 s_vgatxtcycles += getCycleCount() - s1;
371 int scanLine = s_scanLine;
373 const int lineIndex = scanLine % VGATextController_CHARHEIGHT;
375 auto lines = ctrl->m_lines;
377 if (s_textRow < ctrl->m_rows) {
381 const auto cursorVisible = (ctrl->m_cursorEnabled && ctrl->m_cursorCounter >= 0 && s_textRow == ctrl->m_cursorRow);
383 cursorCol = ctrl->m_cursorCol;
384 cursorFGBG = (ctrl->m_cursorForeground << 4) | (ctrl->m_cursorBackground << 8);
387 const auto charData = ctrl->m_charData + (s_upperRow ? 0 : VGATextController_CHARHEIGHT / 2);
388 auto mapItemPtr = ctrl->m_map + s_textRow * VGATextController_COLUMNS;
390 for (
int col = 0; col < VGATextController_COLUMNS; ++col, ++mapItemPtr) {
392 const auto mapItem = *mapItemPtr;
394 int fgbg = (mapItem >> 4) & 0b111111110000;
396 const auto options = glyphMapItem_getOptions(mapItem);
400 fgbg = ((fgbg >> 4) & 0b11110000) | ((fgbg << 4) & 0b111100000000);
403 if (cursorVisible && col == cursorCol)
406 uint32_t * dest = lines + lineIndex * VGATextController_WIDTH /
sizeof(uint32_t) + col * VGATextController_CHARWIDTHBYTES * 2;
411 for (
int rowInChar = 0; rowInChar < VGATextController_CHARHEIGHT / 2; ++rowInChar) {
412 int v = s_fgbgPattern[fgbg];
415 dest += VGATextController_WIDTH /
sizeof(uint32_t);
420 const bool underline = (s_upperRow ==
false && options.underline);
421 const bool bold = options.bold;
423 auto charRowPtr = charData + glyphMapItem_getIndex(mapItem) * VGATextController_CHARHEIGHT * VGATextController_CHARWIDTHBYTES;
425 for (
int rowInChar = 0; rowInChar < VGATextController_CHARHEIGHT / 2; ++rowInChar) {
426 auto charRowData = *charRowPtr;
430 charRowData |= charRowData >> 1;
432 *dest = s_fgbgPattern[(charRowData >> 4) | fgbg];
433 *(dest + 1) = s_fgbgPattern[(charRowData & 0xF) | fgbg];
435 dest += VGATextController_WIDTH /
sizeof(uint32_t);
436 charRowPtr += VGATextController_CHARWIDTHBYTES;
441 dest -= VGATextController_WIDTH /
sizeof(uint32_t);
442 uint32_t v = s_fgbgPattern[0xF | fgbg];
459 for (
int i = 0; i < VGATextController_CHARHEIGHT / 2; ++i) {
460 auto dest = lines + ((scanLine + i) % VGATextController_CHARHEIGHT) * VGATextController_WIDTH /
sizeof(uint32_t);
461 for (
int i = 0; i < VGATextController_COLUMNS; ++i) {
462 *dest++ = s_blankPatternDWord;
463 *dest++ = s_blankPatternDWord;
468 scanLine += VGATextController_CHARHEIGHT / 2;
470 s_scanLine = scanLine;
474 #if FABGLIB_VGAXCONTROLLER_PERFORMANCE_CHECK 475 s_vgatxtcycles += getCycleCount() - s1;
478 I2S1.int_clr.val = I2S1.int_st.val;
void begin()
This is the 64 colors (8 GPIOs) initializer using default pinout.
This file contains fabgl::GPIOStream definition.
Color
This enum defines named colors.
void adjustMapSize(int *columns, int *rows)
Adjust columns and rows to the controller limits.
This file contains fabgl::VGATextController definition.
void setResolution(char const *modeline=nullptr, int viewPortWidth=-1, int viewPortHeight=-1, bool doubleBuffered=false)
Sets fixed resolution.
Specifies the VGA timings. This is a modeline decoded.
This file contains some utility classes and functions.
void setTextMap(uint32_t const *map, int rows)
Sets text map to display.