6#include "soc/i2s_struct.h"
7#include "soc/i2s_reg.h"
8#include "driver/periph_ctrl.h"
14#include "fonts/font_8x14.h"
18#pragma GCC optimize ("O3")
26volatile int VGATextController::s_scanLine;
27uint32_t VGATextController::s_blankPatternDWord;
28uint32_t * VGATextController::s_fgbgPattern =
nullptr;
29int VGATextController::s_textRow;
30bool VGATextController::s_upperRow;
31lldesc_t
volatile * VGATextController::s_frameResetDesc;
32int16_t VGATextController::s_charWidthInBytes;
33int16_t VGATextController::s_columns;
34int16_t VGATextController::s_rows;
35int16_t VGATextController::s_charHeight;
38#if FABGLIB_VGAXCONTROLLER_PERFORMANCE_CHECK
39 volatile uint64_t s_vgatxtcycles = 0;
46VGATextController::VGATextController()
47 : m_charData(nullptr),
49 m_cursorEnabled(false),
54 m_cursorForeground(0),
55 m_cursorBackground(15),
62VGATextController::~VGATextController()
64 free((
void*)m_charData);
68void VGATextController::setTextMap(uint32_t
const * map,
int rows)
71 while (m_map !=
nullptr && s_scanLine < m_timings.VVisibleArea)
78void VGATextController::adjustMapSize(
int * columns,
int * rows)
87void VGATextController::setFont(FontInfo
const * value)
90 if (m_font->width != 8)
91 printf(
"VGATextController: Unsupported font width\n");
95void VGATextController::init(gpio_num_t VSyncGPIO)
97 m_DMABuffers =
nullptr;
103void VGATextController::begin(gpio_num_t redGPIO, gpio_num_t greenGPIO, gpio_num_t blueGPIO, gpio_num_t HSyncGPIO, gpio_num_t VSyncGPIO)
108 setupGPIO(redGPIO, VGA_RED_BIT, GPIO_MODE_OUTPUT);
109 setupGPIO(greenGPIO, VGA_GREEN_BIT, GPIO_MODE_OUTPUT);
110 setupGPIO(blueGPIO, VGA_BLUE_BIT, GPIO_MODE_OUTPUT);
113 setupGPIO(HSyncGPIO, VGA_HSYNC_BIT, GPIO_MODE_OUTPUT);
114 setupGPIO(VSyncGPIO, VGA_VSYNC_BIT, GPIO_MODE_INPUT_OUTPUT);
116 RGB222::lowBitOnly =
true;
117 m_bitsPerChannel = 1;
122void 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)
124 begin(red0GPIO, green0GPIO, blue0GPIO, HSyncGPIO, VSyncGPIO);
127 setupGPIO(red1GPIO, VGA_RED_BIT + 1, GPIO_MODE_OUTPUT);
128 setupGPIO(green1GPIO, VGA_GREEN_BIT + 1, GPIO_MODE_OUTPUT);
129 setupGPIO(blue1GPIO, VGA_BLUE_BIT + 1, GPIO_MODE_OUTPUT);
131 RGB222::lowBitOnly =
false;
132 m_bitsPerChannel = 2;
137void VGATextController::begin()
139 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);
143void VGATextController::setCursorForeground(
Color value)
145 m_cursorForeground = (int) value;
149void VGATextController::setCursorBackground(
Color value)
151 m_cursorBackground = (int) value;
155void VGATextController::setupGPIO(gpio_num_t gpio,
int bit, gpio_mode_t mode)
157 configureGPIO(gpio, mode);
158 gpio_matrix_out(gpio, I2S1O_DATA_OUT0_IDX + bit,
false,
false);
162void VGATextController::setResolution(
char const * modeline,
int viewPortWidth,
int viewPortHeight,
bool doubleBuffered)
165 if (VGABaseController::convertModelineToTimings(VGATextController_MODELINE, &timings))
166 setResolution(timings);
170void VGATextController::setResolution(
VGATimings const& timings)
181 setScreenSize(m_timings.HVisibleArea, m_timings.VVisibleArea);
184 s_charWidthInBytes = (m_font->width + 7) / 8;
185 s_charHeight = m_font->height;
186 s_columns = VGATextController_WIDTH / m_font->width;
187 s_rows = VGATextController_HEIGHT / m_font->height;
191 heap_caps_free(m_charData);
192 int charDataSize = 256 * m_font->height * ((m_font->width + 7) / 8);
193 m_charData = (uint8_t*) heap_caps_malloc(charDataSize, MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
194 memcpy(m_charData, m_font->data, charDataSize);
196 m_HVSync = packHVSync(
false,
false);
198 m_DMABuffersCount = 2 * m_timings.VVisibleArea + m_timings.VFrontPorch + m_timings.VSyncPulse + m_timings.VBackPorch;
200 m_DMABuffers = (lldesc_t*) heap_caps_malloc(m_DMABuffersCount *
sizeof(lldesc_t), MALLOC_CAP_DMA);
202 m_lines = (uint32_t*) heap_caps_malloc(s_charHeight * VGATextController_WIDTH, MALLOC_CAP_DMA);
204 int rawLineWidth = m_timings.HFrontPorch + m_timings.HSyncPulse + m_timings.HBackPorch + m_timings.HVisibleArea;
205 m_blankLine = (uint8_t*) heap_caps_malloc(rawLineWidth, MALLOC_CAP_DMA);
206 m_syncLine = (uint8_t*) heap_caps_malloc(rawLineWidth, MALLOC_CAP_DMA);
215 for (
int i = 0, visLine = 0, invLine = 0; i < m_DMABuffersCount; ++i) {
217 if (i < m_timings.VVisibleArea * 2) {
220 m_DMABuffers[i].eof = (visLine == 0 || visLine == s_charHeight / 2 ? 1 : 0);
221 m_DMABuffers[i].sosf = 0;
222 m_DMABuffers[i].offset = 0;
223 m_DMABuffers[i].owner = 1;
224 m_DMABuffers[i].qe.stqe_next = (lldesc_t*) &m_DMABuffers[i + 1];
225 m_DMABuffers[i].length = m_timings.HFrontPorch + m_timings.HSyncPulse + m_timings.HBackPorch;
226 m_DMABuffers[i].size = (m_DMABuffers[i].length + 3) & (~3);
227 m_DMABuffers[i].buf = (uint8_t*) m_blankLine;
232 m_DMABuffers[i].eof = 0;
233 m_DMABuffers[i].sosf = 0;
234 m_DMABuffers[i].offset = 0;
235 m_DMABuffers[i].owner = 1;
236 m_DMABuffers[i].qe.stqe_next = (lldesc_t*) &m_DMABuffers[i + 1];
237 m_DMABuffers[i].length = m_timings.HVisibleArea;
238 m_DMABuffers[i].size = (m_DMABuffers[i].length + 3) & (~3);
239 m_DMABuffers[i].buf = (uint8_t*)(m_lines) + visLine * VGATextController_WIDTH;
242 if (visLine == s_charHeight)
249 bool frameResetDesc = (invLine == 0);
252 s_frameResetDesc = &m_DMABuffers[i];
254 m_DMABuffers[i].eof = (frameResetDesc ? 1 : 0);
255 m_DMABuffers[i].sosf = 0;
256 m_DMABuffers[i].offset = 0;
257 m_DMABuffers[i].owner = 1;
258 m_DMABuffers[i].qe.stqe_next = (lldesc_t*) (i == m_DMABuffersCount - 1 ? &m_DMABuffers[0] : &m_DMABuffers[i + 1]);
259 m_DMABuffers[i].length = rawLineWidth;
260 m_DMABuffers[i].size = (m_DMABuffers[i].length + 3) & (~3);
262 if (invLine < m_timings.VFrontPorch || invLine >= m_timings.VFrontPorch + m_timings.VSyncPulse)
263 m_DMABuffers[i].buf = (uint8_t*) m_blankLine;
265 m_DMABuffers[i].buf = (uint8_t*) m_syncLine;
276 s_blankPatternDWord = m_HVSync | (m_HVSync << 8) | (m_HVSync << 16) | (m_HVSync << 24);
278 if (s_fgbgPattern ==
nullptr) {
279 s_fgbgPattern = (uint32_t*) heap_caps_malloc(16384, MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
280 for (
int i = 0; i < 16; ++i)
281 for (
int fg = 0; fg < 16; ++fg)
282 for (
int bg = 0; bg < 16; ++bg) {
283 uint8_t fg_pat = preparePixel(RGB222((
Color)fg));
284 uint8_t bg_pat = preparePixel(RGB222((
Color)bg));
285 s_fgbgPattern[i | (bg << 4) | (fg << 8)] = (i & 0b1000 ? (fg_pat << 16) : (bg_pat << 16)) |
286 (i & 0b0100 ? (fg_pat << 24) : (bg_pat << 24)) |
287 (i & 0b0010 ? (fg_pat << 0) : (bg_pat << 0)) |
288 (i & 0b0001 ? (fg_pat << 8) : (bg_pat << 8));
296 m_GPIOStream.play(m_timings.frequency, m_DMABuffers);
298 I2S1.int_clr.val = 0xFFFFFFFF;
299 I2S1.int_ena.out_eof = 1;
303void VGATextController::freeBuffers()
305 heap_caps_free( (
void*) m_DMABuffers );
306 heap_caps_free((
void*) m_lines);
307 m_DMABuffers =
nullptr;
308 heap_caps_free((
void*) m_blankLine);
309 heap_caps_free((
void*) m_syncLine);
313uint8_t IRAM_ATTR VGATextController::packHVSync(
bool HSync,
bool VSync)
315 uint8_t hsync_value = (m_timings.HSyncLogic ==
'+' ? (HSync ? 1 : 0) : (HSync ? 0 : 1));
316 uint8_t vsync_value = (m_timings.VSyncLogic ==
'+' ? (VSync ? 1 : 0) : (VSync ? 0 : 1));
317 return (vsync_value << VGA_VSYNC_BIT) | (hsync_value << VGA_HSYNC_BIT);
321uint8_t IRAM_ATTR VGATextController::preparePixelWithSync(RGB222 rgb,
bool HSync,
bool VSync)
323 return packHVSync(HSync, VSync) | (rgb.B << VGA_BLUE_BIT) | (rgb.G << VGA_GREEN_BIT) | (rgb.R << VGA_RED_BIT);
327void VGATextController::fillDMABuffers()
330 for (; x < m_timings.HFrontPorch; ++x) {
331 VGA_PIXELINROW(m_blankLine, x) = preparePixelWithSync((RGB222){0, 0, 0},
false,
false);
332 VGA_PIXELINROW(m_syncLine, x) = preparePixelWithSync((RGB222){0, 0, 0},
false,
true);
334 for (; x < m_timings.HFrontPorch + m_timings.HSyncPulse; ++x) {
335 VGA_PIXELINROW(m_blankLine, x) = preparePixelWithSync((RGB222){0, 0, 0},
true,
false);
336 VGA_PIXELINROW(m_syncLine, x) = preparePixelWithSync((RGB222){0, 0, 0},
true,
true);
338 for (; x < m_timings.HFrontPorch + m_timings.HSyncPulse + m_timings.HBackPorch; ++x) {
339 VGA_PIXELINROW(m_blankLine, x) = preparePixelWithSync((RGB222){0, 0, 0},
false,
false);
340 VGA_PIXELINROW(m_syncLine, x) = preparePixelWithSync((RGB222){0, 0, 0},
false,
true);
342 int rawLineWidth = m_timings.HFrontPorch + m_timings.HSyncPulse + m_timings.HBackPorch + m_timings.HVisibleArea;
343 for (
int rx = 0; x < rawLineWidth; ++x, ++rx) {
344 VGA_PIXELINROW(m_blankLine, x) = preparePixelWithSync((RGB222){0, 0, 0},
false,
false);
345 VGA_PIXELINROW(m_syncLine, x) = preparePixelWithSync((RGB222){0, 0, 0},
false,
true);
346 for (
int i = 0; i < s_charHeight; ++i)
347 VGA_PIXELINROW( ((uint8_t*)(m_lines) + i * VGATextController_WIDTH), rx) = preparePixelWithSync((RGB222){0, 0, 0},
false,
false);
352void IRAM_ATTR VGATextController::ISRHandler(
void * arg)
354 #if FABGLIB_VGAXCONTROLLER_PERFORMANCE_CHECK
355 auto s1 = getCycleCount();
358 VGATextController * ctrl = (VGATextController *) arg;
360 if (I2S1.int_st.out_eof && ctrl->m_charData !=
nullptr) {
362 auto desc = (
volatile lldesc_t*) I2S1.out_eof_des_addr;
364 if (desc == s_frameResetDesc) {
370 if (ctrl->m_cursorEnabled) {
371 ++ctrl->m_cursorCounter;
372 if (ctrl->m_cursorCounter >= ctrl->m_cursorSpeed)
373 ctrl->m_cursorCounter = -ctrl->m_cursorSpeed;
376 if (ctrl->m_map ==
nullptr) {
377 I2S1.int_clr.val = I2S1.int_st.val;
378 #if FABGLIB_VGAXCONTROLLER_PERFORMANCE_CHECK
379 s_vgatxtcycles += getCycleCount() - s1;
384 }
else if (s_scanLine == 0) {
386 I2S1.int_clr.val = I2S1.int_st.val;
387 #if FABGLIB_VGAXCONTROLLER_PERFORMANCE_CHECK
388 s_vgatxtcycles += getCycleCount() - s1;
393 int scanLine = s_scanLine;
395 const int lineIndex = scanLine % s_charHeight;
397 auto lines = ctrl->m_lines;
399 const int8_t downAdd = s_upperRow ? 0 : (s_charHeight & 1);
401 if (s_textRow < ctrl->m_rows) {
405 const auto cursorVisible = (ctrl->m_cursorEnabled && ctrl->m_cursorCounter >= 0 && s_textRow == ctrl->m_cursorRow);
407 cursorCol = ctrl->m_cursorCol;
408 cursorFGBG = (ctrl->m_cursorForeground << 4) | (ctrl->m_cursorBackground << 8);
411 const auto charData = ctrl->m_charData + (s_upperRow ? 0 : s_charHeight / 2);
412 auto mapItemPtr = ctrl->m_map + s_textRow * s_columns;
414 for (
int col = 0; col < s_columns; ++col, ++mapItemPtr) {
416 const auto mapItem = *mapItemPtr;
418 int fgbg = (mapItem >> 4) & 0b111111110000;
420 const auto options = glyphMapItem_getOptions(mapItem);
424 fgbg = ((fgbg >> 4) & 0b11110000) | ((fgbg << 4) & 0b111100000000);
427 if (cursorVisible && col == cursorCol)
430 uint32_t * dest = lines + lineIndex * VGATextController_WIDTH /
sizeof(uint32_t) + col * s_charWidthInBytes * 2;
435 for (
int rowInChar = 0; rowInChar < s_charHeight / 2 + downAdd; ++rowInChar) {
436 int v = s_fgbgPattern[fgbg];
439 dest += VGATextController_WIDTH /
sizeof(uint32_t);
444 const bool underline = (s_upperRow ==
false && options.underline);
445 const bool bold = options.bold;
447 auto charRowPtr = charData + glyphMapItem_getIndex(mapItem) * s_charHeight * s_charWidthInBytes;
449 for (
int rowInChar = 0; rowInChar < s_charHeight / 2 + downAdd; ++rowInChar) {
450 auto charRowData = *charRowPtr;
454 charRowData |= charRowData >> 1;
456 *dest = s_fgbgPattern[(charRowData >> 4) | fgbg];
457 *(dest + 1) = s_fgbgPattern[(charRowData & 0xF) | fgbg];
459 dest += VGATextController_WIDTH /
sizeof(uint32_t);
460 charRowPtr += s_charWidthInBytes;
465 dest -= VGATextController_WIDTH /
sizeof(uint32_t);
466 uint32_t v = s_fgbgPattern[0xF | fgbg];
483 for (
int i = 0; i < s_charHeight / 2 + downAdd; ++i) {
484 auto dest = lines + ((scanLine + i) % s_charHeight) * VGATextController_WIDTH /
sizeof(uint32_t);
485 for (
int i = 0; i < s_columns; ++i) {
486 *dest++ = s_blankPatternDWord;
487 *dest++ = s_blankPatternDWord;
492 scanLine += s_charHeight / 2 + downAdd;
494 s_scanLine = scanLine;
498 #if FABGLIB_VGAXCONTROLLER_PERFORMANCE_CHECK
499 s_vgatxtcycles += getCycleCount() - s1;
502 I2S1.int_clr.val = I2S1.int_st.val;
#define FABGLIB_VIDEO_CPUINTENSIVE_TASKS_CORE
This file contains some utility classes and functions.
Color
This enum defines named colors.
Specifies the VGA timings. This is a modeline decoded.
This file contains fabgl::GPIOStream definition.
This file contains fabgl::VGATextController definition.