29 #include "freertos/FreeRTOS.h" 30 #include "freertos/task.h" 32 #include "soc/i2s_struct.h" 33 #include "soc/i2s_reg.h" 34 #include "driver/periph_ctrl.h" 35 #include "rom/lldesc.h" 47 VGABaseController::VGABaseController()
52 void VGABaseController::init()
54 m_DMABuffers =
nullptr;
55 m_DMABuffersCount = 0;
56 m_DMABuffersHead =
nullptr;
57 m_DMABuffersVisible =
nullptr;
58 m_primitiveProcessingSuspended = 1;
59 m_isr_handle =
nullptr;
60 m_doubleBufferOverDMA =
false;
67 void VGABaseController::begin(gpio_num_t redGPIO, gpio_num_t greenGPIO, gpio_num_t blueGPIO, gpio_num_t HSyncGPIO, gpio_num_t VSyncGPIO)
72 setupGPIO(redGPIO, VGA_RED_BIT, GPIO_MODE_OUTPUT);
73 setupGPIO(greenGPIO, VGA_GREEN_BIT, GPIO_MODE_OUTPUT);
74 setupGPIO(blueGPIO, VGA_BLUE_BIT, GPIO_MODE_OUTPUT);
77 setupGPIO(HSyncGPIO, VGA_HSYNC_BIT, GPIO_MODE_OUTPUT);
78 setupGPIO(VSyncGPIO, VGA_VSYNC_BIT, GPIO_MODE_OUTPUT);
80 RGB222::lowBitOnly =
true;
86 void VGABaseController::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)
88 begin(red0GPIO, green0GPIO, blue0GPIO, HSyncGPIO, VSyncGPIO);
91 setupGPIO(red1GPIO, VGA_RED_BIT + 1, GPIO_MODE_OUTPUT);
92 setupGPIO(green1GPIO, VGA_GREEN_BIT + 1, GPIO_MODE_OUTPUT);
93 setupGPIO(blue1GPIO, VGA_BLUE_BIT + 1, GPIO_MODE_OUTPUT);
95 RGB222::lowBitOnly =
false;
101 void VGABaseController::begin()
103 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);
107 void VGABaseController::end()
111 esp_intr_free(m_isr_handle);
112 m_isr_handle =
nullptr;
114 suspendBackgroundPrimitiveExecution();
121 void VGABaseController::setupGPIO(gpio_num_t gpio,
int bit, gpio_mode_t mode)
123 configureGPIO(gpio, mode);
124 gpio_matrix_out(gpio, I2S1O_DATA_OUT0_IDX + bit,
false,
false);
128 void VGABaseController::freeBuffers()
130 if (m_DMABuffersCount > 0) {
131 heap_caps_free((
void*)m_HBlankLine_withVSync);
132 heap_caps_free((
void*)m_HBlankLine);
136 setDMABuffersCount(0);
141 void VGABaseController::freeViewPort()
143 for (uint8_t * * poolPtr = m_viewPortMemoryPool; *poolPtr; ++poolPtr) {
144 heap_caps_free((
void*) *poolPtr);
147 heap_caps_free(m_viewPort);
148 m_viewPort =
nullptr;
149 if (isDoubleBuffered())
150 heap_caps_free(m_viewPortVisible);
151 m_viewPortVisible =
nullptr;
158 bool VGABaseController::setDMABuffersCount(
int buffersCount)
160 if (buffersCount == 0) {
161 if (m_DMABuffersVisible && m_DMABuffersVisible != m_DMABuffers)
162 heap_caps_free( (
void*) m_DMABuffersVisible );
163 heap_caps_free( (
void*) m_DMABuffers );
164 m_DMABuffers =
nullptr;
165 m_DMABuffersVisible =
nullptr;
166 m_DMABuffersCount = 0;
170 if (buffersCount != m_DMABuffersCount) {
173 if (m_doubleBufferOverDMA && m_DMABuffersHead ==
nullptr) {
174 m_DMABuffersHead = (lldesc_t*) heap_caps_malloc(
sizeof(lldesc_t), MALLOC_CAP_DMA);
175 m_DMABuffersHead->eof = m_DMABuffersHead->sosf = m_DMABuffersHead->offset = 0;
176 m_DMABuffersHead->owner = 1;
177 m_DMABuffersHead->size = 0;
178 m_DMABuffersHead->length = 0;
179 m_DMABuffersHead->buf = m_HBlankLine;
180 m_DMABuffersHead->qe.stqe_next =
nullptr;
184 m_DMABuffers = (lldesc_t*) heap_caps_realloc((
void*)m_DMABuffers, buffersCount *
sizeof(lldesc_t), MALLOC_CAP_DMA);
185 if (m_doubleBufferOverDMA && isDoubleBuffered())
186 m_DMABuffersVisible = (lldesc_t*) heap_caps_realloc((
void*)m_DMABuffersVisible, buffersCount *
sizeof(lldesc_t), MALLOC_CAP_DMA);
188 m_DMABuffersVisible = m_DMABuffers;
189 if (!m_DMABuffers || !m_DMABuffersVisible)
192 auto buffersHead = m_DMABuffersHead ? m_DMABuffersHead : &m_DMABuffers[0];
194 for (
int i = 0; i < buffersCount; ++i) {
195 m_DMABuffers[i].eof = 0;
196 m_DMABuffers[i].sosf = 0;
197 m_DMABuffers[i].offset = 0;
198 m_DMABuffers[i].owner = 1;
199 m_DMABuffers[i].qe.stqe_next = (lldesc_t*) (i == buffersCount - 1 ? buffersHead : &m_DMABuffers[i + 1]);
200 if (m_doubleBufferOverDMA && isDoubleBuffered()) {
201 m_DMABuffersVisible[i].eof = 0;
202 m_DMABuffersVisible[i].sosf = 0;
203 m_DMABuffersVisible[i].offset = 0;
204 m_DMABuffersVisible[i].owner = 1;
205 m_DMABuffersVisible[i].qe.stqe_next = (lldesc_t*) (i == buffersCount - 1 ? buffersHead : &m_DMABuffersVisible[i + 1]);
209 m_DMABuffersCount = buffersCount;
218 bool VGABaseController::convertModelineToTimings(
char const * modeline, VGATimings * timings)
221 int hdisp, hsyncstart, hsyncend, htotal, vdisp, vsyncstart, vsyncend, vtotal;
222 char HSyncPol = 0, VSyncPol = 0;
225 int count = sscanf(modeline,
"\"%[^\"]\" %g %d %d %d %d %d %d %d %d %n", timings->label, &freq, &hdisp, &hsyncstart, &hsyncend, &htotal, &vdisp, &vsyncstart, &vsyncend, &vtotal, &pos);
227 if (count == 10 && pos > 0) {
229 timings->frequency = freq * 1000000;
230 timings->HVisibleArea = hdisp;
231 timings->HFrontPorch = hsyncstart - hdisp;
232 timings->HSyncPulse = hsyncend - hsyncstart;
233 timings->HBackPorch = htotal - hsyncend;
234 timings->VVisibleArea = vdisp;
235 timings->VFrontPorch = vsyncstart - vdisp;
236 timings->VSyncPulse = vsyncend - vsyncstart;
237 timings->VBackPorch = vtotal - vsyncend;
238 timings->HSyncLogic =
'-';
239 timings->VSyncLogic =
'-';
240 timings->scanCount = 1;
241 timings->multiScanBlack = 0;
245 char const * pc = modeline + pos;
247 if (*pc ==
'+' || *pc ==
'-') {
249 timings->HSyncLogic = HSyncPol = *pc;
250 else if (!VSyncPol) {
251 timings->VSyncLogic = VSyncPol = *pc;
252 while (*pc && *pc !=
' ')
265 timings->scanCount = 2;
269 timings->scanCount = 4;
289 timings->multiScanBlack = 1;
296 while (*pc && *pc !=
' ')
311 void VGABaseController::suspendBackgroundPrimitiveExecution()
313 ++m_primitiveProcessingSuspended;
319 void VGABaseController::resumeBackgroundPrimitiveExecution()
321 m_primitiveProcessingSuspended = tmax(0, m_primitiveProcessingSuspended - 1);
325 void VGABaseController::startGPIOStream()
327 m_GPIOStream.play(m_timings.frequency, m_DMABuffers);
331 void VGABaseController::setResolution(
char const * modeline,
int viewPortWidth,
int viewPortHeight,
bool doubleBuffered)
334 if (convertModelineToTimings(modeline, &timings))
335 setResolution(timings, viewPortWidth, viewPortHeight, doubleBuffered);
339 void VGABaseController::setResolution(VGATimings
const& timings,
int viewPortWidth,
int viewPortHeight,
bool doubleBuffered)
347 setScreenSize(m_timings.HVisibleArea, m_timings.VVisibleArea);
349 setDoubleBuffered(doubleBuffered);
351 m_HVSync = packHVSync(
false,
false);
353 m_HLineSize = m_timings.HFrontPorch + m_timings.HSyncPulse + m_timings.HBackPorch + m_timings.HVisibleArea;
355 m_HBlankLine_withVSync = (uint8_t*) heap_caps_malloc(m_HLineSize, MALLOC_CAP_DMA);
356 m_HBlankLine = (uint8_t*) heap_caps_malloc(m_HLineSize, MALLOC_CAP_DMA);
358 m_viewPortWidth = ~3 & (viewPortWidth <= 0 || viewPortWidth >= m_timings.HVisibleArea ? m_timings.HVisibleArea : viewPortWidth);
359 m_viewPortHeight = viewPortHeight <= 0 || viewPortHeight >= m_timings.VVisibleArea ? m_timings.VVisibleArea : viewPortHeight;
365 m_viewPortCol = (m_timings.HVisibleArea - m_viewPortWidth) / 2;
366 m_viewPortRow = (m_timings.VVisibleArea - m_viewPortHeight) / 2;
369 m_viewPortCol = m_viewPortCol & ~3;
370 m_viewPortRow = m_viewPortRow & ~3;
372 m_rawFrameHeight = m_timings.VVisibleArea + m_timings.VFrontPorch + m_timings.VSyncPulse + m_timings.VBackPorch;
375 setDMABuffersCount(calcRequiredDMABuffersCount(m_viewPortHeight));
384 setDMABuffersCount(calcRequiredDMABuffersCount(m_viewPortHeight));
392 if (m_doubleBufferOverDMA)
393 m_DMABuffersHead->qe.stqe_next = (lldesc_t*) &m_DMABuffersVisible[0];
399 void VGABaseController::allocateViewPort(uint32_t allocCaps,
int rowlen)
401 int linesCount[FABGLIB_VIEWPORT_MEMORY_POOL_COUNT];
403 int remainingLines = m_viewPortHeight;
404 m_viewPortHeight = 0;
406 if (isDoubleBuffered())
410 while (remainingLines > 0 && poolsCount < FABGLIB_VIEWPORT_MEMORY_POOL_COUNT) {
411 int largestBlock = heap_caps_get_largest_free_block(allocCaps);
412 linesCount[poolsCount] = tmin(remainingLines, largestBlock / rowlen);
413 if (linesCount[poolsCount] == 0)
415 m_viewPortMemoryPool[poolsCount] = (uint8_t*) heap_caps_malloc(linesCount[poolsCount] * rowlen, allocCaps);
416 remainingLines -= linesCount[poolsCount];
417 m_viewPortHeight += linesCount[poolsCount];
420 m_viewPortMemoryPool[poolsCount] =
nullptr;
423 if (isDoubleBuffered()) {
424 m_viewPortHeight /= 2;
425 m_viewPortVisible = (
volatile uint8_t * *) heap_caps_malloc(
sizeof(uint8_t*) * m_viewPortHeight, MALLOC_CAP_32BIT | MALLOC_CAP_INTERNAL);
427 m_viewPort = (
volatile uint8_t * *) heap_caps_malloc(
sizeof(uint8_t*) * m_viewPortHeight, MALLOC_CAP_32BIT | MALLOC_CAP_INTERNAL);
428 if (!isDoubleBuffered())
429 m_viewPortVisible = m_viewPort;
430 for (
int p = 0, l = 0; p < poolsCount; ++p) {
431 uint8_t * pool = m_viewPortMemoryPool[p];
432 for (
int i = 0; i < linesCount[p]; ++i) {
433 if (l + i < m_viewPortHeight)
434 m_viewPort[l + i] = pool;
436 m_viewPortVisible[l + i - m_viewPortHeight] = pool;
444 uint8_t IRAM_ATTR VGABaseController::packHVSync(
bool HSync,
bool VSync)
446 uint8_t hsync_value = (m_timings.HSyncLogic ==
'+' ? (HSync ? 1 : 0) : (HSync ? 0 : 1));
447 uint8_t vsync_value = (m_timings.VSyncLogic ==
'+' ? (VSync ? 1 : 0) : (VSync ? 0 : 1));
448 return (vsync_value << VGA_VSYNC_BIT) | (hsync_value << VGA_HSYNC_BIT);
452 uint8_t IRAM_ATTR VGABaseController::preparePixelWithSync(RGB222 rgb,
bool HSync,
bool VSync)
454 return packHVSync(HSync, VSync) | (rgb.B << VGA_BLUE_BIT) | (rgb.G << VGA_GREEN_BIT) | (rgb.R << VGA_RED_BIT);
458 int VGABaseController::calcRequiredDMABuffersCount(
int viewPortHeight)
460 int rightPadSize = m_timings.HVisibleArea - m_viewPortWidth - m_viewPortCol;
461 int buffersCount = m_timings.scanCount * (m_rawFrameHeight + viewPortHeight);
463 switch (m_timings.HStartingBlock) {
466 buffersCount += m_timings.scanCount * (rightPadSize > 0 ? viewPortHeight : 0);
470 buffersCount += m_timings.scanCount * viewPortHeight;
474 buffersCount += m_timings.scanCount * viewPortHeight;
478 buffersCount += m_timings.scanCount * (m_viewPortCol > 0 ? viewPortHeight : 0);
488 void VGABaseController::fillHorizBuffers(
int offsetX)
492 fill(m_HBlankLine, 0, m_HLineSize, 0, 0, 0,
false,
false);
493 fill(m_HBlankLine_withVSync, 0, m_HLineSize, 0, 0, 0,
false,
true);
497 int16_t porchSum = m_timings.HFrontPorch + m_timings.HBackPorch;
498 m_timings.HFrontPorch = tmax(8, (int16_t)m_timings.HFrontPorch - offsetX);
499 m_timings.HBackPorch = tmax(8, porchSum - m_timings.HFrontPorch);
500 m_timings.HFrontPorch = porchSum - m_timings.HBackPorch;
504 switch (m_timings.HStartingBlock) {
507 syncPos = m_timings.HFrontPorch;
515 syncPos = m_timings.HBackPorch + m_timings.HVisibleArea + m_timings.HFrontPorch;
519 syncPos = m_timings.HVisibleArea + m_timings.HFrontPorch;
523 fill(m_HBlankLine, syncPos, m_timings.HSyncPulse, 0, 0, 0,
true,
false);
524 fill(m_HBlankLine_withVSync, syncPos, m_timings.HSyncPulse, 0, 0, 0,
true,
true);
528 void VGABaseController::fillVertBuffers(
int offsetY)
530 int16_t porchSum = m_timings.VFrontPorch + m_timings.VBackPorch;
531 m_timings.VFrontPorch = tmax(1, (int16_t)m_timings.VFrontPorch - offsetY);
532 m_timings.VBackPorch = tmax(1, porchSum - m_timings.VFrontPorch);
533 m_timings.VFrontPorch = porchSum - m_timings.VBackPorch;
543 int VVisibleArea_pos = 0;
544 int VFrontPorch_pos = VVisibleArea_pos + m_timings.VVisibleArea;
545 int VSync_pos = VFrontPorch_pos + m_timings.VFrontPorch;
546 int VBackPorch_pos = VSync_pos + m_timings.VSyncPulse;
548 int rightPadSize = m_timings.HVisibleArea - m_viewPortWidth - m_viewPortCol;
550 for (
int line = 0, DMABufIdx = 0; line < m_rawFrameHeight; ++line) {
552 bool isVVisibleArea = (line < VFrontPorch_pos);
553 bool isVFrontPorch = (line >= VFrontPorch_pos && line < VSync_pos);
554 bool isVSync = (line >= VSync_pos && line < VBackPorch_pos);
555 bool isVBackPorch = (line >= VBackPorch_pos);
557 for (
int scan = 0; scan < m_timings.scanCount; ++scan) {
559 bool isStartOfVertFrontPorch = (line == VFrontPorch_pos && scan == 0);
563 setDMABufferBlank(DMABufIdx++, m_HBlankLine_withVSync, m_HLineSize, scan, isStartOfVertFrontPorch);
565 }
else if (isVFrontPorch || isVBackPorch) {
567 setDMABufferBlank(DMABufIdx++, m_HBlankLine, m_HLineSize, scan, isStartOfVertFrontPorch);
569 }
else if (isVVisibleArea) {
571 int visibleAreaLine = line - VVisibleArea_pos;
572 bool isViewport = visibleAreaLine >= m_viewPortRow && visibleAreaLine < m_viewPortRow + m_viewPortHeight;
573 int HInvisibleAreaSize = m_HLineSize - m_timings.HVisibleArea;
577 switch (m_timings.HStartingBlock) {
580 setDMABufferBlank(DMABufIdx++, m_HBlankLine, HInvisibleAreaSize + m_viewPortCol, scan, isStartOfVertFrontPorch);
581 setDMABufferView(DMABufIdx++, visibleAreaLine - m_viewPortRow, scan, isStartOfVertFrontPorch);
582 if (rightPadSize > 0)
583 setDMABufferBlank(DMABufIdx++, m_HBlankLine + HInvisibleAreaSize, rightPadSize, scan, isStartOfVertFrontPorch);
587 setDMABufferBlank(DMABufIdx++, m_HBlankLine, m_timings.HSyncPulse + m_timings.HBackPorch + m_viewPortCol, scan, isStartOfVertFrontPorch);
588 setDMABufferView(DMABufIdx++, visibleAreaLine - m_viewPortRow, scan, isStartOfVertFrontPorch);
589 setDMABufferBlank(DMABufIdx++, m_HBlankLine + m_HLineSize - m_timings.HFrontPorch - rightPadSize, m_timings.HFrontPorch + rightPadSize, scan, isStartOfVertFrontPorch);
593 setDMABufferBlank(DMABufIdx++, m_HBlankLine, m_timings.HBackPorch + m_viewPortCol, scan, isStartOfVertFrontPorch);
594 setDMABufferView(DMABufIdx++, visibleAreaLine - m_viewPortRow, scan, isStartOfVertFrontPorch);
595 setDMABufferBlank(DMABufIdx++, m_HBlankLine + m_HLineSize - m_timings.HFrontPorch - m_timings.HSyncPulse - rightPadSize, m_timings.HFrontPorch + m_timings.HSyncPulse + rightPadSize, scan, isStartOfVertFrontPorch);
599 if (m_viewPortCol > 0)
600 setDMABufferBlank(DMABufIdx++, m_HBlankLine, m_viewPortCol, scan, isStartOfVertFrontPorch);
601 setDMABufferView(DMABufIdx++, visibleAreaLine - m_viewPortRow, scan, isStartOfVertFrontPorch);
602 setDMABufferBlank(DMABufIdx++, m_HBlankLine + m_timings.HVisibleArea - rightPadSize, HInvisibleAreaSize + rightPadSize, scan, isStartOfVertFrontPorch);
608 setDMABufferBlank(DMABufIdx++, m_HBlankLine, m_HLineSize, scan, isStartOfVertFrontPorch);
622 void VGABaseController::setDMABufferBlank(
int index,
void volatile * address,
int length,
int scan,
bool isStartOfVertFrontPorch)
624 int size = (length + 3) & (~3);
625 m_DMABuffers[index].eof = 0;
626 m_DMABuffers[index].size = size;
627 m_DMABuffers[index].length = length;
628 m_DMABuffers[index].buf = (uint8_t*) address;
629 onSetupDMABuffer(&m_DMABuffers[index], isStartOfVertFrontPorch, scan,
false, 0);
630 if (m_doubleBufferOverDMA && isDoubleBuffered()) {
631 m_DMABuffersVisible[index].eof = 0;
632 m_DMABuffersVisible[index].size = size;
633 m_DMABuffersVisible[index].length = length;
634 m_DMABuffersVisible[index].buf = (uint8_t*) address;
635 onSetupDMABuffer(&m_DMABuffersVisible[index], isStartOfVertFrontPorch, scan,
false, 0);
640 bool VGABaseController::isMultiScanBlackLine(
int scan)
642 return (scan > 0 && m_timings.multiScanBlack == 1 && m_timings.HStartingBlock ==
FrontPorch);
649 void VGABaseController::setDMABufferView(
int index,
int row,
int scan,
volatile uint8_t * * viewPort,
bool onVisibleDMA)
651 uint8_t * bufferPtr =
nullptr;
652 if (isMultiScanBlackLine(scan))
653 bufferPtr = (uint8_t *) (m_HBlankLine + m_HLineSize - m_timings.HVisibleArea);
655 bufferPtr = (uint8_t *) viewPort[row];
656 lldesc_t
volatile * DMABuffers = onVisibleDMA ? m_DMABuffersVisible : m_DMABuffers;
657 DMABuffers[index].size = (m_viewPortWidth + 3) & (~3);
658 DMABuffers[index].length = m_viewPortWidth;
659 DMABuffers[index].buf = bufferPtr;
666 void VGABaseController::setDMABufferView(
int index,
int row,
int scan,
bool isStartOfVertFrontPorch)
668 setDMABufferView(index, row, scan, m_viewPort,
false);
669 if (!isMultiScanBlackLine(scan))
670 onSetupDMABuffer(&m_DMABuffers[index], isStartOfVertFrontPorch, scan,
true, row);
671 if (isDoubleBuffered()) {
672 setDMABufferView(index, row, scan, m_viewPortVisible,
true);
673 if (!isMultiScanBlackLine(scan))
674 onSetupDMABuffer(&m_DMABuffersVisible[index], isStartOfVertFrontPorch, scan,
true, row);
679 void volatile * VGABaseController::getDMABuffer(
int index,
int * length)
681 *length = m_DMABuffers[index].length;
682 return m_DMABuffers[index].buf;
691 int VGABaseController::fill(uint8_t
volatile * buffer,
int startPos,
int length, uint8_t red, uint8_t green, uint8_t blue,
bool HSync,
bool VSync)
693 uint8_t pattern = preparePixelWithSync((RGB222){red, green, blue}, HSync, VSync);
694 for (
int i = 0; i < length; ++i, ++startPos)
695 VGA_PIXELINROW(buffer, startPos) = pattern;
700 void VGABaseController::moveScreen(
int offsetX,
int offsetY)
702 suspendBackgroundPrimitiveExecution();
703 fillVertBuffers(offsetY);
704 fillHorizBuffers(offsetX);
705 resumeBackgroundPrimitiveExecution();
709 void VGABaseController::shrinkScreen(
int shrinkX,
int shrinkY)
711 VGATimings * currTimings = getResolutionTimings();
713 currTimings->HBackPorch = tmax(currTimings->HBackPorch + 4 * shrinkX, 4);
714 currTimings->HFrontPorch = tmax(currTimings->HFrontPorch + 4 * shrinkX, 4);
716 currTimings->VBackPorch = tmax(currTimings->VBackPorch + shrinkY, 1);
717 currTimings->VFrontPorch = tmax(currTimings->VFrontPorch + shrinkY, 1);
719 setResolution(*currTimings, m_viewPortWidth, m_viewPortHeight, isDoubleBuffered());
723 void IRAM_ATTR VGABaseController::swapBuffers()
725 tswap(m_viewPort, m_viewPortVisible);
726 if (m_doubleBufferOverDMA) {
727 tswap(m_DMABuffers, m_DMABuffersVisible);
728 m_DMABuffersHead->qe.stqe_next = (lldesc_t*) &m_DMABuffersVisible[0];
This file contains fabgl::VGABaseController definition.
This file contains fabgl::GPIOStream definition.
This file contains some utility classes and functions.