29#include "freertos/FreeRTOS.h"
30#include "freertos/task.h"
32#include "driver/dac.h"
33#include "soc/i2s_reg.h"
34#include "driver/periph_ctrl.h"
36#include <soc/sens_reg.h>
39#include "devdrivers/cvbsgenerator.h"
42#pragma GCC optimize ("O2")
54static const struct CVBS_I_PAL_B : CVBSParams {
59 sampleRate_hz = 19250000.0;
61 subcarrierFreq_hz = 4433618.75;
77 preEqualizingPulseCount = 0;
79 postEqualizingPulseCount = 5;
80 endFieldEqualizingPulseCount = 5;
85 defaultVisibleSamples = 640;
86 defaultVisibleLines = 240;
87 fieldStartingLine[0] = 1;
88 fieldStartingLine[1] = 2;
93 double getComposite(
bool oddLine,
double phase,
double red,
double green,
double blue,
double *
Y)
const {
94 *
Y = red * .299 + green * .587 + blue * .114;
95 double U = 0.493 * (blue - *
Y);
96 double V = 0.877 * (red - *
Y);
97 return U * sin(phase) + (oddLine ? 1. : -1.) * V * cos(phase);
99 double getColorBurst(
bool oddLine,
double phase)
const {
101 return -sin(phase) + (oddLine ? 1. : -1.) * cos(phase);
104 bool lineHasColorBurst(
int frame,
int frameLine)
const {
105 if (((frame == 1 || frame == 3) && (frameLine < 7 || (frameLine > 309 && frameLine < 319) || frameLine > 621)) ||
106 ((frame == 2 || frame == 4) && (frameLine < 6 || (frameLine > 310 && frameLine < 320) || frameLine > 622)))
115static const struct CVBS_I_PAL_B_WIDE : CVBS_I_PAL_B {
116 CVBS_I_PAL_B_WIDE() : CVBS_I_PAL_B() {
117 desc =
"I-PAL-B-WIDE";
118 defaultVisibleSamples = 768;
119 defaultVisibleLines = 240;
125static const struct CVBS_P_PAL_B : CVBS_I_PAL_B {
126 CVBS_P_PAL_B() : CVBS_I_PAL_B() {
128 fieldStartingLine[0] = 1;
129 fieldStartingLine[1] = 1;
136static const struct CVBS_I_NTSC_M : CVBSParams {
139 sampleRate_hz = 14223774;
140 subcarrierFreq_hz = 3579545.45;
142 hline_us = 31.777782;
156 preEqualizingPulseCount = 6;
158 postEqualizingPulseCount = 6;
159 endFieldEqualizingPulseCount = 0;
164 defaultVisibleSamples = 640;
165 defaultVisibleLines = 200;
166 fieldStartingLine[0] = 1;
167 fieldStartingLine[1] = 2;
172 double getComposite(
bool oddLine,
double phase,
double red,
double green,
double blue,
double *
Y)
const {
173 *
Y = red * .299 + green * .587 + blue * .114;
174 double Q = .413 * (blue - *
Y) + .478 * (red - *
Y);
175 double I = -.269 * (blue - *
Y) + .736 * (red - *
Y);
176 return Q * sin(phase + TORAD(33.)) + I * cos(phase + TORAD(33.));
178 double getColorBurst(
bool oddLine,
double phase)
const {
180 return sin(phase + TORAD(180.));
182 bool lineHasColorBurst(
int frame,
int frameLine)
const {
190static const struct CVBS_I_NTSC_M_WIDE : CVBS_I_NTSC_M {
191 CVBS_I_NTSC_M_WIDE() : CVBS_I_NTSC_M() {
192 desc =
"I-NTSC-M-WIDE";
193 defaultVisibleSamples = 768;
194 defaultVisibleLines = 200;
200static const struct CVBS_P_NTSC_M : CVBS_I_NTSC_M {
201 CVBS_P_NTSC_M() : CVBS_I_NTSC_M() {
203 fieldStartingLine[0] = 1;
204 fieldStartingLine[1] = 1;
210static CVBSParams
const * CVBS_Standards[] = {
226volatile int CVBSGenerator::s_scanLine;
227volatile bool CVBSGenerator::s_VSync;
228volatile int CVBSGenerator::s_field;
229volatile int CVBSGenerator::s_frame;
230volatile int CVBSGenerator::s_frameLine;
231volatile int CVBSGenerator::s_activeLineIndex;
232volatile int CVBSGenerator::s_interFrameLine;
233volatile scPhases_t * CVBSGenerator::s_subCarrierPhase;
234volatile scPhases_t * CVBSGenerator::s_lineSampleToSubCarrierSample;
235volatile int16_t CVBSGenerator::s_firstVisibleSample;
236volatile int16_t CVBSGenerator::s_visibleSamplesCount;
239#if FABGLIB_VGAXCONTROLLER_PERFORMANCE_CHECK
240 volatile uint64_t s_compctrlcycles = 0;
244CVBSGenerator::CVBSGenerator() :
250 m_isr_handle(nullptr),
251 m_drawScanlineCallback(nullptr),
252 m_subCarrierPhases(),
255 s_lineSampleToSubCarrierSample =
nullptr;
262void CVBSGenerator::setVideoGPIO(gpio_num_t gpio)
269void CVBSGenerator::runDMA(lldesc_t
volatile * dmaBuffers)
272 periph_module_enable(PERIPH_I2S0_MODULE);
275 I2S0.conf.tx_reset = 1;
276 I2S0.conf.tx_reset = 0;
279 I2S0.lc_conf.in_rst = 1;
280 I2S0.lc_conf.in_rst = 0;
283 I2S0.conf.rx_fifo_reset = 1;
284 I2S0.conf.rx_fifo_reset = 0;
287 bool usePLL = m_params->sampleRate_hz == 16000000 || m_params->sampleRate_hz == 13333333.3334;
290 I2S0.conf_chan.tx_chan_mod = (m_gpio == GPIO_NUM_25 ? 3 : 4);
292 I2S0.conf_chan.tx_chan_mod = (m_gpio == GPIO_NUM_25 ? 1 : 2);
294 I2S0.fifo_conf.tx_fifo_mod_force_en = 1;
295 I2S0.fifo_conf.tx_fifo_mod = 1;
296 I2S0.fifo_conf.dscr_en = 1;
298 I2S0.conf.tx_mono = 1;
299 I2S0.conf.tx_start = 0;
300 I2S0.conf.tx_msb_right = 1;
301 I2S0.conf.tx_right_first = 1;
302 I2S0.conf.tx_slave_mod = 0;
303 I2S0.conf.tx_short_sync = 0;
304 I2S0.conf.tx_msb_shift = 0;
306 I2S0.conf2.lcd_en = 1;
307 I2S0.conf2.camera_en = 0;
311 I2S0.clkm_conf.clka_en = 0;
312 I2S0.clkm_conf.clkm_div_a = m_params->sampleRate_hz == 16000000 ? 2 : 1;
313 I2S0.clkm_conf.clkm_div_b = 1;
314 I2S0.clkm_conf.clkm_div_num = 2;
315 I2S0.sample_rate_conf.tx_bck_div_num = 2;
318 APLLParams p = { 0, 0, 0, 0 };
319 double error, out_freq;
320 uint8_t a = 1, b = 0;
321 APLLCalcParams(m_params->sampleRate_hz * 2., &p, &a, &b, &out_freq, &error);
322 I2S0.clkm_conf.val = 0;
323 I2S0.clkm_conf.clkm_div_b = b;
324 I2S0.clkm_conf.clkm_div_a = a;
325 I2S0.clkm_conf.clkm_div_num = 2;
326 I2S0.sample_rate_conf.tx_bck_div_num = 1;
327 rtc_clk_apll_enable(
true, p.sdm0, p.sdm1, p.sdm2, p.o_div);
328 I2S0.clkm_conf.clka_en = 1;
331 I2S0.sample_rate_conf.tx_bits_mod = 16;
334 s_field = m_params->fields - 1;
335 s_frame = m_params->frameGroupCount - 1;
339 if (m_isr_handle ==
nullptr) {
342 I2S0.int_clr.val = 0xFFFFFFFF;
343 I2S0.int_ena.out_eof = 1;
346 I2S0.out_link.addr = (uint32_t) &dmaBuffers[0];
347 I2S0.out_link.start = 1;
348 I2S0.conf.tx_start = 1;
353 dac_output_enable(DAC_CHANNEL_1);
354 dac_output_enable(DAC_CHANNEL_2);
357 dac_output_enable(m_gpio == GPIO_NUM_25 ? DAC_CHANNEL_1 : DAC_CHANNEL_2);
365volatile lldesc_t * CVBSGenerator::setDMANode(
int index,
volatile uint16_t * buf,
int len)
367 m_DMAChain[index].eof = 0;
368 m_DMAChain[index].sosf = 0;
369 m_DMAChain[index].owner = 1;
370 m_DMAChain[index].qe.stqe_next = (lldesc_t *) (m_DMAChain + index + 1);
371 m_DMAChain[index].offset = 0;
372 m_DMAChain[index].size = len *
sizeof(uint16_t);
373 m_DMAChain[index].length = len *
sizeof(uint16_t);
374 m_DMAChain[index].buf = (uint8_t*) buf;
375 return &m_DMAChain[index];
379void CVBSGenerator::closeDMAChain(
int index)
381 m_DMAChain[index].qe.stqe_next = (lldesc_t *) m_DMAChain;
386static int bestAlignValue(
int value)
406void CVBSGenerator::buildDMAChain()
408 int lineSamplesCount = round(m_params->line_us / m_sample_us);
409 int hlineSamplesCount = round(m_params->hline_us / m_sample_us);
411 printf(
"lineSamplesCount = %d\n", lineSamplesCount);
412 printf(
"hlineSamplesCount = %d\n\n", hlineSamplesCount);
415 lineSamplesCount = bestAlignValue(lineSamplesCount);
416 hlineSamplesCount = bestAlignValue(hlineSamplesCount);
418 m_actualLine_us = lineSamplesCount * m_sample_us;
419 m_actualHLine_us = hlineSamplesCount * m_sample_us;
421 printf(
"adj lineSamplesCount = %d\n", lineSamplesCount);
422 printf(
"adj hlineSamplesCount = %d\n", hlineSamplesCount);
423 printf(
"actual line duration = %.2f us\n", m_actualLine_us);
426 m_lsyncBuf = (
volatile uint16_t *) heap_caps_malloc(hlineSamplesCount *
sizeof(uint16_t), MALLOC_CAP_DMA);
428 int lsyncEnd = m_params->longPulse_us / m_sample_us;
429 int vedgeLen = ceil(m_params->vsyncEdge_us / m_sample_us);
430 for (
int s = 0, fallCount = vedgeLen, riseCount = 0; s < hlineSamplesCount; ++s) {
431 if (s < lsyncStart + vedgeLen)
432 m_lsyncBuf[s ^ 1] = (m_params->syncLevel + m_params->blackLevel * --fallCount / vedgeLen) << 8;
433 else if (s <= lsyncEnd - vedgeLen)
434 m_lsyncBuf[s ^ 1] = m_params->syncLevel << 8;
435 else if (s < lsyncEnd)
436 m_lsyncBuf[s ^ 1] = (m_params->syncLevel + m_params->blackLevel * ++riseCount / vedgeLen) << 8;
438 m_lsyncBuf[s ^ 1] = m_params->blackLevel << 8;
442 m_blackBuffer =
nullptr;
443 m_ssyncBuf = (
volatile uint16_t *) heap_caps_malloc(hlineSamplesCount *
sizeof(uint16_t), MALLOC_CAP_DMA);
445 int ssyncEnd = m_params->shortPulse_us / m_sample_us;
446 for (
int s = 0, fallCount = vedgeLen, riseCount = 0; s < hlineSamplesCount; ++s) {
447 if (s < ssyncStart + vedgeLen)
448 m_ssyncBuf[s ^ 1] = (m_params->syncLevel + m_params->blackLevel * --fallCount / vedgeLen) << 8;
449 else if (s <= ssyncEnd - vedgeLen)
450 m_ssyncBuf[s ^ 1] = m_params->syncLevel << 8;
451 else if (s < ssyncEnd)
452 m_ssyncBuf[s ^ 1] = (m_params->syncLevel + m_params->blackLevel * ++riseCount / vedgeLen) << 8;
454 m_ssyncBuf[s ^ 1] = m_params->blackLevel << 8;
455 if (!m_blackBuffer && !(s & 3)) {
456 m_blackBuffer = &m_ssyncBuf[s ^ 1];
457 m_blackBufferLength = hlineSamplesCount - s;
463 m_lineBuf = (
volatile uint16_t * *) malloc(CVBS_ALLOCATED_LINES *
sizeof(uint16_t*));
465 int hsyncEnd = (m_params->hsync_us + m_params->hsyncEdge_us) / m_sample_us;
466 int hedgeLen = ceil(m_params->hsyncEdge_us / m_sample_us);
467 for (
int l = 0; l < CVBS_ALLOCATED_LINES; ++l) {
468 m_lineBuf[l] = (
volatile uint16_t *) heap_caps_malloc(lineSamplesCount *
sizeof(uint16_t), MALLOC_CAP_DMA);
469 for (
int s = 0, fallCount = hedgeLen, riseCount = 0; s < lineSamplesCount; ++s) {
470 if (s < hsyncStart + hedgeLen)
471 m_lineBuf[l][s ^ 1] = (m_params->syncLevel + m_params->blackLevel * --fallCount / hedgeLen) << 8;
472 else if (s <= hsyncEnd - hedgeLen)
473 m_lineBuf[l][s ^ 1] = m_params->syncLevel << 8;
474 else if (s < hsyncEnd)
475 m_lineBuf[l][s ^ 1] = (m_params->syncLevel + m_params->blackLevel * ++riseCount / hedgeLen) << 8;
477 m_lineBuf[l][s ^ 1] = m_params->blackLevel << 8;
482 s_lineSampleToSubCarrierSample = (
volatile scPhases_t *) heap_caps_malloc(lineSamplesCount *
sizeof(scPhases_t), MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
483 double K = m_params->subcarrierFreq_hz * CVBS_SUBCARRIERPHASES / m_params->sampleRate_hz;
484 for (
int s = 0; s < lineSamplesCount; ++s)
485 s_lineSampleToSubCarrierSample[s] = round(fmod(K * s, CVBS_SUBCARRIERPHASES));
488 for (
int line = 0; line < 2; ++line) {
489 for (
int sample = 0; sample < CVBS_SUBCARRIERPHASES * 2; ++sample) {
490 double phase = 2. * M_PI * sample / CVBS_SUBCARRIERPHASES;
491 double burst = m_params->getColorBurst(line == 0, phase);
492 m_colorBurstLUT[line][sample] = (uint16_t)(m_params->blackLevel + m_params->burstAmp * burst) << 8;
497 int DMAChainLength = m_params->preEqualizingPulseCount +
498 m_params->vsyncPulseCount +
499 m_params->postEqualizingPulseCount +
500 m_params->endFieldEqualizingPulseCount +
501 ceil(m_params->fieldLines) * m_params->fields + 2;
503 m_DMAChain = (
volatile lldesc_t *) heap_caps_malloc(DMAChainLength *
sizeof(lldesc_t), MALLOC_CAP_DMA);
509 #define SHOWDMADETAILS 0
512 volatile lldesc_t * nodeptr =
nullptr;
520 for (
int frame = 1; frame <= m_params->frameGroupCount; ++frame) {
523 printf(
"frame = %d\n", frame);
527 m_subCarrierPhases[frame - 1] = (
volatile scPhases_t *) heap_caps_malloc(m_linesPerFrame *
sizeof(scPhases_t), MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
529 double frameLine = 1.0;
531 for (
int field = 1; field <= m_params->fields; ++field) {
534 printf(
" field = %d\n", field);
537 m_startingScanLine[field - 1] = m_params->fieldStartingLine[field - 1] - 1;
539 double fieldLine = 1.0;
540 bool startOfFieldISR =
false;
541 bool firstActiveLine =
true;
542 int activeLineIndex = 0;
544 while (fieldLine < m_params->fieldLines + 1.0) {
547 printf(
" frameLine = %f fieldLine = %f ", frameLine, fieldLine);
551 double subCarrierPhase = modf(m_params->subcarrierFreq_hz * aus / 1000000., &ipart);
553 if (m_params->lineHasColorBurst(frame, frameLine)) {
555 m_subCarrierPhases[frame - 1][(int)frameLine - 1] = subCarrierPhase * CVBS_SUBCARRIERPHASES;
558 m_subCarrierPhases[frame - 1][(int)frameLine - 1] = CVBS_NOBURSTFLAG;
564 int interFrameLine = m_params->fieldLines * m_params->fields * (frame - 1) + frameLine - 1;
565 bool oddLine = ((interFrameLine & 1) == 0);
566 printf(
"SCPhase = %.2f ", subCarrierPhase * 360.);
567 printf(
"BurstPhase = %.2f ", fmod(subCarrierPhase * 2. * M_PI + (oddLine ? 1 : -1) * 3. * M_PI / 4., 2. * M_PI) / M_PI * 180.);
571 if (fieldLine < m_params->preEqualizingPulseCount * .5 + 1.0) {
575 printf(
"node = %d - pre-equalizing pulse\n", node);
578 nodeptr = setDMANode(node++, m_ssyncBuf, hlineSamplesCount);
581 us += m_params->hline_us;
582 aus += m_actualHLine_us;
584 }
else if (fieldLine < (m_params->preEqualizingPulseCount + m_params->vsyncPulseCount) * .5 + 1.0) {
588 printf(
"node = %d - vsync pulse", node);
591 nodeptr = setDMANode(node++, m_lsyncBuf, hlineSamplesCount);
592 if (!startOfFieldISR) {
596 startOfFieldISR =
true;
607 us += m_params->hline_us;
608 aus += m_actualHLine_us;
610 }
else if (fieldLine < (m_params->preEqualizingPulseCount + m_params->vsyncPulseCount + m_params->postEqualizingPulseCount) * .5 + 1.0) {
614 printf(
"node = %d - post-equalizing pulse\n", node);
617 nodeptr = setDMANode(node++, m_ssyncBuf, hlineSamplesCount);
620 us += m_params->hline_us;
621 aus += m_actualHLine_us;
623 }
else if (fieldLine < m_params->fieldLines - m_params->endFieldEqualizingPulseCount * .5 + 1.0) {
628 printf(
"node = %d - active line, ", node);
631 if (firstActiveLine) {
632 m_firstActiveFrameLine[field - 1] = (int)frameLine - 1;
633 firstActiveLine =
false;
636 printf(
"FIRST ACTIVE, ");
641 if ((
int)fieldLine == m_firstVisibleFieldLine) {
642 m_firstVisibleFrameLine[field - 1] = (int)frameLine - 1;
644 printf(
"FIRST VISIBLE, ");
646 }
else if ((
int)fieldLine == m_lastVisibleFieldLine) {
647 m_lastVisibleFrameLine[field - 1] = (int)frameLine - 1;
649 printf(
"LAST VISIBLE, ");
654 if (modf(frameLine, &ipart) == .5) {
657 printf(
"ending half");
660 nodeptr = setDMANode(node++, m_lineBuf[activeLineIndex % CVBS_ALLOCATED_LINES] + hlineSamplesCount, hlineSamplesCount);
663 us += m_params->hline_us;
664 aus += m_actualHLine_us;
665 }
else if (fieldLine + 1.0 > m_params->fieldLines + 1.0 - m_params->endFieldEqualizingPulseCount * .5) {
668 printf(
"beginning half");
671 nodeptr = setDMANode(node++, m_lineBuf[activeLineIndex % CVBS_ALLOCATED_LINES], hlineSamplesCount);
674 us += m_params->hline_us;
675 aus += m_actualHLine_us;
682 nodeptr = setDMANode(node++, m_lineBuf[activeLineIndex % CVBS_ALLOCATED_LINES], lineSamplesCount);
685 us += m_params->line_us;
686 aus += m_actualLine_us;
690 if (frame == 1 && (activeLineIndex % (CVBS_ALLOCATED_LINES / 2)) == 0) {
705 printf(
"node = %d - end-field equalizing pulse\n", node);
708 nodeptr = setDMANode(node++, m_ssyncBuf, hlineSamplesCount);
711 us += m_params->hline_us;
712 aus += m_actualHLine_us;
735 closeDMAChain(node - 1);
741void CVBSGenerator::buildDMAChain_subCarrierOnly()
743 double fsamplesPerCycle = 1000000. / m_params->subcarrierFreq_hz / m_sample_us;
744 int samplesPerCycle = 1000000. / m_params->subcarrierFreq_hz / m_sample_us;
747 while ( (fsamplesPerCycle * cycles) - (
int)(fsamplesPerCycle * cycles) > 0.5)
750 samplesPerCycle = fsamplesPerCycle;
752 int count = (samplesPerCycle * cycles) & ~1;
756 m_lsyncBuf = (
volatile uint16_t *) heap_caps_malloc(count *
sizeof(uint16_t), MALLOC_CAP_DMA);
758 auto sinLUT =
new uint16_t[CVBS_SUBCARRIERPHASES * 2];
760 for (
int sample = 0; sample < CVBS_SUBCARRIERPHASES * 2; ++sample) {
761 double phase = 2. * M_PI * sample / CVBS_SUBCARRIERPHASES;
762 double value = sin(phase);
763 sinLUT[sample] = (uint16_t)(m_params->blackLevel + m_params->burstAmp * value) << 8;
766 double K = m_params->subcarrierFreq_hz * CVBS_SUBCARRIERPHASES / m_params->sampleRate_hz;
768 for (
int sample = 0; sample < count; ++sample) {
769 auto idx = (int)(K * sample) % CVBS_SUBCARRIERPHASES;
770 m_lsyncBuf[sample ^ 1] = sinLUT[idx];
776 m_DMAChain = (
volatile lldesc_t *) heap_caps_malloc(1 *
sizeof(lldesc_t), MALLOC_CAP_DMA);
777 setDMANode(0, m_lsyncBuf, count);
782CVBSParams
const * CVBSGenerator::getParamsFromDesc(
char const * desc)
784 for (
auto std : CVBS_Standards)
785 if (strcmp(std->desc, desc) == 0)
791void CVBSGenerator::setup(
char const * desc)
793 auto params = getParamsFromDesc(desc);
794 setup(params ? params : CVBS_Standards[0]);
798void CVBSGenerator::setup(CVBSParams
const * params)
802 m_sample_us = 1000000. / m_params->sampleRate_hz;
804 double activeLine_us = m_params->line_us - m_params->hsync_us - m_params->backPorch_us - m_params->frontPorch_us;
805 int maxVisibleSamples = activeLine_us / m_sample_us;
806 m_linesPerFrame = m_params->fieldLines * m_params->fields;
808 int usableFieldLines = m_params->fieldLines - m_params->blankLines;
809 m_visibleLines = imin(usableFieldLines, m_params->defaultVisibleLines);
812 m_visibleLines -= m_visibleLines % CVBS_ALLOCATED_LINES;
814 m_firstVisibleFieldLine = m_params->blankLines + ceil((usableFieldLines - m_visibleLines) / 2.0);
815 m_lastVisibleFieldLine = m_firstVisibleFieldLine + m_visibleLines - 1;
819 int blankSamples = m_params->hblank_us / m_sample_us;
820 int hsyncSamples = m_params->hsync_us / m_sample_us;
821 int backPorchSamples = m_params->backPorch_us / m_sample_us;
822 int usableVisibleSamples = maxVisibleSamples - blankSamples;
823 s_visibleSamplesCount = imin(usableVisibleSamples, m_params->defaultVisibleSamples);
824 s_firstVisibleSample = (hsyncSamples + backPorchSamples + blankSamples + (usableVisibleSamples - s_visibleSamplesCount) / 2) & ~1;
826 double subcarrierCycle_us = 1000000. / m_params->subcarrierFreq_hz;
828 m_firstColorBurstSample = (m_params->hsync_us + m_params->burstStart_us) / m_sample_us;
829 m_lastColorBurstSample = m_firstColorBurstSample + (subcarrierCycle_us * m_params->burstCycles) / m_sample_us - 1;
833void CVBSGenerator::run(
bool subCarrierOnly)
836 buildDMAChain_subCarrierOnly();
843void CVBSGenerator::stop()
847 periph_module_disable(PERIPH_I2S0_MODULE);
848 m_DMAStarted =
false;
851 esp_intr_free(m_isr_handle);
852 m_isr_handle =
nullptr;
858 heap_caps_free((
void*)m_DMAChain);
859 m_DMAChain =
nullptr;
862 heap_caps_free((
void*)m_ssyncBuf);
863 m_ssyncBuf =
nullptr;
867 heap_caps_free((
void*)m_lsyncBuf);
868 m_lsyncBuf =
nullptr;
872 for (
int i = 0; i < CVBS_ALLOCATED_LINES; ++i)
873 heap_caps_free((
void*)m_lineBuf[i]);
878 for (
int frame = 0; frame < m_params->frameGroupCount; ++frame)
879 if (m_subCarrierPhases[frame]) {
880 heap_caps_free((
void*)m_subCarrierPhases[frame]);
881 m_subCarrierPhases[frame] =
nullptr;
884 if (s_lineSampleToSubCarrierSample) {
885 heap_caps_free((
void*)s_lineSampleToSubCarrierSample);
886 s_lineSampleToSubCarrierSample =
nullptr;
895void CVBSGenerator::setDrawScanlineCallback(CVBSDrawScanlineCallback drawScanlineCallback,
void * arg)
897 m_drawScanlineCallback = drawScanlineCallback;
898 m_drawScanlineArg = arg;
902void IRAM_ATTR CVBSGenerator::ISRHandler(
void * arg)
904 #if FABGLIB_VGAXCONTROLLER_PERFORMANCE_CHECK
905 auto s1 = getCycleCount();
908 if (I2S0.int_st.out_eof) {
910 auto ctrl = (CVBSGenerator *) arg;
911 auto desc = (
volatile lldesc_t*) I2S0.out_eof_des_addr;
915 s_field = (s_field + 1) % ctrl->m_params->fields;
917 s_frame = (s_frame + 1) % ctrl->m_params->frameGroupCount;
918 s_interFrameLine = ctrl->m_linesPerFrame * s_frame + ctrl->m_firstActiveFrameLine[s_field];
919 s_frameLine = ctrl->m_firstActiveFrameLine[s_field];
920 s_subCarrierPhase = &(ctrl->m_subCarrierPhases[s_frame][s_frameLine]);
921 s_activeLineIndex = 0;
922 s_scanLine = ctrl->m_startingScanLine[s_field];
926 auto drawScanlineCallback = ctrl->m_drawScanlineCallback;
927 auto drawScanlineArg = ctrl->m_drawScanlineArg;
928 auto lineBuf = ctrl->m_lineBuf;
929 auto firstVisibleFrameLine = ctrl->m_firstVisibleFrameLine[s_field];
930 auto lastVisibleFrameLine = ctrl->m_lastVisibleFrameLine[s_field];
931 auto firstColorBurstSample = ctrl->m_firstColorBurstSample;
932 auto lastColorBurstSample = ctrl->m_lastColorBurstSample;
933 auto interlaceFactor = ctrl->m_params->interlaceFactor;
935 for (
int i = 0; i < CVBS_ALLOCATED_LINES / 2; ++i) {
937 auto fullLineBuf = (uint16_t*) lineBuf[s_activeLineIndex % CVBS_ALLOCATED_LINES];
939 if (*s_subCarrierPhase == CVBS_NOBURSTFLAG) {
941 uint16_t blk = ctrl->m_params->blackLevel << 8;
942 for (
int s = firstColorBurstSample; s <= lastColorBurstSample; ++s)
943 fullLineBuf[s ^ 1] = blk;
946 auto colorBurstLUT = ctrl->m_colorBurstLUT[s_interFrameLine & 1];
947 auto sampleLUT = CVBSGenerator::lineSampleToSubCarrierSample() + firstColorBurstSample;
948 for (
int s = firstColorBurstSample; s <= lastColorBurstSample; ++s)
949 fullLineBuf[s ^ 1] = colorBurstLUT[*sampleLUT++ + *s_subCarrierPhase];
953 auto visibleBuf = fullLineBuf + s_firstVisibleSample;
954 if (s_frameLine >= firstVisibleFrameLine && s_frameLine <= lastVisibleFrameLine) {
956 drawScanlineCallback(drawScanlineArg, visibleBuf, s_scanLine);
957 s_scanLine += interlaceFactor;
960 uint32_t blackFillX2 = (ctrl->m_params->blackLevel << 8) | (ctrl->m_params->blackLevel << (8 + 16));
961 for (
int col = 0; col < s_visibleSamplesCount; col += 2, visibleBuf += 2)
962 *((uint32_t*)(visibleBuf)) = blackFillX2;
972 if (s_frameLine >= lastVisibleFrameLine)
976 #if FABGLIB_VGAXCONTROLLER_PERFORMANCE_CHECK
977 s_compctrlcycles += getCycleCount() - s1;
980 I2S0.int_clr.val = I2S0.int_st.val;
#define FABGLIB_VIDEO_CPUINTENSIVE_TASKS_CORE
This file contains some utility classes and functions.