/****************************************************************************** * Copyright 2013-2015 Espressif Systems * 2015 <>< Charles Lohr * 2017 Hrvoje Cavrak */ #include "slc_register.h" #include #include "user_interface.h" #include "pin_mux_register.h" #include "signetics_video_rom.h" #include #define WS_I2S_BCK 1 #define WS_I2S_DIV 2 #define LINE_BUFFER_LENGTH 160 #define PAL_LINES 625 #define LINETYPES 6 #define SYNC_LEVEL 0x99999999 #define WHITE_LEVEL 0xffffffff #define BLACK_LEVEL 0xbbbbbbbb extern uint8_t terminal_ram[40 * 24]; uint32_t i2s_dma_buffer[LINE_BUFFER_LENGTH*LINETYPES]; static struct sdio_queue i2sBufDesc[PAL_LINES]; int current_pixel_line; LOCAL void slc_isr(void) { struct sdio_queue *finishedDesc; uint32 slc_intr_status; uint8_t x; //Grab int status slc_intr_status = READ_PERI_REG(SLC_INT_STATUS); //clear all intr flags WRITE_PERI_REG(SLC_INT_CLR, 0xffffffff); if (slc_intr_status & SLC_RX_EOF_INT_ST) { //The DMA subsystem is done with this block: Push it on the queue so it can be re-used. finishedDesc=(struct sdio_queue*)READ_PERI_REG(SLC_RX_EOF_DES_ADDR); struct sdio_queue * next = (struct sdio_queue *)finishedDesc->next_link_ptr; uint32_t * buffer_pointer = (uint32_t*)next->buf_ptr; if( next->unused > 1) { current_pixel_line = 0; } else if( next->unused ) { uint8_t pixel_column = 30; /* Determines which terminal line we are currently rendering, 192 pixel lines >> 3 = 24 text lines. Each is 40 bytes long. */ uint8_t *terminal_line = &terminal_ram[40 * (current_pixel_line >> 3)]; /* 8 banks (one for each char line) of 192 bytes contain character definitions. Each character is pre-encoded to optimize performance. */ uint32_t *character_row = &Signetics_2513_Modulated_Video_ROM[(current_pixel_line & 0b111) * 192]; /* For each character in the line */ for( x = 0; x < 40; x++ ) { /* Get pre-encoded character definition from array */ uint32_t *character = &character_row[3*(terminal_line[x] & 0x3F)]; /* Use loop unrolling to improve performance */ buffer_pointer[pixel_column++] = *character++; buffer_pointer[pixel_column++] = *character++; buffer_pointer[pixel_column++] = *character++; } current_pixel_line++; } } } /* PAL signals */ #define SHORT_SYNC_INTERVAL 5 #define LONG_SYNC_INTERVAL 75 #define BACK_PORCH 20 #define NORMAL_SYNC_INTERVAL 10 #define LINE_SIGNAL_INTERVAL 150 #define SHORT_SYNC 0 #define LONG_SYNC 1 #define BLACK_SIGNAL 2 #define SHORT_TO_LONG 3 #define LONG_TO_SHORT 4 #define LINE_SIGNAL 5 //Initialize I2S subsystem for DMA circular buffer use void ICACHE_FLASH_ATTR testi2s_init() { int x, y; uint32_t * line = i2s_dma_buffer; uint8_t single_line_timings[20] = { SHORT_SYNC_INTERVAL, LONG_SYNC_INTERVAL, SHORT_SYNC_INTERVAL, LONG_SYNC_INTERVAL, LONG_SYNC_INTERVAL, SHORT_SYNC_INTERVAL, LONG_SYNC_INTERVAL, SHORT_SYNC_INTERVAL, NORMAL_SYNC_INTERVAL, LINE_SIGNAL_INTERVAL, SHORT_SYNC_INTERVAL, LONG_SYNC_INTERVAL, SHORT_SYNC_INTERVAL, LONG_SYNC_INTERVAL, LONG_SYNC_INTERVAL, SHORT_SYNC_INTERVAL, LONG_SYNC_INTERVAL, SHORT_SYNC_INTERVAL, NORMAL_SYNC_INTERVAL, LINE_SIGNAL_INTERVAL }; uint32_t single_line_levels[20] = { SYNC_LEVEL, BLACK_LEVEL, SYNC_LEVEL, BLACK_LEVEL, SYNC_LEVEL, BLACK_LEVEL, SYNC_LEVEL, BLACK_LEVEL, SYNC_LEVEL, BLACK_LEVEL, SYNC_LEVEL, BLACK_LEVEL, SYNC_LEVEL, BLACK_LEVEL, SYNC_LEVEL, BLACK_LEVEL, SYNC_LEVEL, BLACK_LEVEL, SYNC_LEVEL, BLACK_LEVEL, }; uint8_t i, signal; for (signal = 0; signal < 20; signal++) for (i=0; i < single_line_timings[signal]; i++, *line++ = single_line_levels[signal]) ; /* Reference: http://martin.hinner.info/vga/pal_tv_diagram_interlace.jpg */ uint16_t pal_lines[42] = { /* Lines Pattern Unused */ 3, LONG_SYNC, 0, 4, LONG_TO_SHORT, 0, 6, SHORT_SYNC, 0, 57, BLACK_SIGNAL, 0, 250, LINE_SIGNAL, 1, 311, BLACK_SIGNAL, 0, 313, SHORT_SYNC, 0, 314, SHORT_TO_LONG, 0, 316, LONG_SYNC, 0, 318, SHORT_SYNC, 0, 370, BLACK_SIGNAL, 0, 562, LINE_SIGNAL, 1, 623, BLACK_SIGNAL, 0, 626, SHORT_SYNC, 0, }; uint16_t *pal_line = pal_lines; //Initialize DMA buffer descriptors in such a way that they will form a circular //buffer. for (x=0; x