/* ** NTSC Video using T3 and Output Compare interrupts ** ** Graphic page ** ** Modified to produce properly sized and spaced pre-and post equalization pulses as well as vertical sync pulses */ #include #include "Graphic.h" // I/O definitions #define SYNC _LATG0 // output #define SDO _RF8 // SPI1 SDO // timing definitions for NTSC video vertical state machine #define V_NTSC 262 // total number of lines composing a frame #define VSYNC_N 3 // V sync lines // count the number of remaining black lines top+bottom #define VBLANK_N (V_NTSC -VRES - VSYNC_N) #define PREEQ_N 3 // pre equalization, 3 lines #define POSTEQ_N 3 // post equalization, 3 lines // definition of the vertical sync state machine #define SV_PREEQ 0 #define SV_SYNC 1 #define SV_POSTEQ 2 #define SV_BLANKLINE 3 //modified* #define SV_LINE 4 //modified* // timing definitions for NTSC video horizontal state machine #define H_NTSC 1016 // total number of Tcy in a line (63.5us) #define HSYNC_T 90 // Tcy in a horizontal synch pulse #define BPORCH_T 90 // Tcy in a back porch #define PIX_T 3 // Tcy in each pixel, valid values are only 2 or 3 #define _FAR __attribute__(( far)) int _FAR VMap[VRES * (HRES/16)]; volatile int *VPtr; volatile int HCount, VCount, VState, HState; // next state table int VS[5] = { SV_SYNC, SV_POSTEQ, SV_BLANKLINE, SV_LINE, SV_PREEQ}; //modified* // next counter table int VC[5] = { VSYNC_N, POSTEQ_N, VBLANK_N, VRES, PREEQ_N}; //modified* void _ISRFAST _T3Interrupt( void) { // Start a Synch pulse SYNC = 0; // decrement the vertical counter VCount--; // vertical state machine switch ( VState) { case SV_PREEQ: // horizontal sync pulse OC3R = HSYNC_T; OC3CON = 0x0009; // single event OC1R = (H_NTSC/2); //modified* OC1CON = 0x0009; // single event OC2R = (H_NTSC/2) + HSYNC_T; //modified* OC2CON = 0x0009; // single event break; case SV_SYNC: // vertical sync pulse OC3R = (H_NTSC/2) - HSYNC_T; OC3CON = 0x0009; // single event OC1R = (H_NTSC/2); //modified* OC1CON = 0x0009; // single event OC2R = H_NTSC - HSYNC_T; //modified* OC2CON = 0x0009; // single event break; case SV_POSTEQ: // horizontal sync pulse OC3R = HSYNC_T; OC3CON = 0x0009; // single event OC1R = (H_NTSC/2); //modified* OC1CON = 0x0009; // single event OC2R = (H_NTSC/2) + HSYNC_T; //modified* OC2CON = 0x0009; // single event // on the last posteq prepare for the new frame if ( VCount == 0) { VPtr = VMap; } break; case SV_BLANKLINE: //Blank line OC3R = HSYNC_T; OC3CON = 0x0009; // single event break; default: case SV_LINE: // horizontal sync pulse OC3R = HSYNC_T; OC3CON = 0x0009; // single event // activate OC4 for the SPI loading OC4R = HSYNC_T + BPORCH_T; OC4CON = 0x0009; // single event HCount = HRES/128; // loads 8x16 bits at a time break; } //switch // advance the state machine if ( VCount == 0) { VCount = VC[ VState]; VState = VS[ VState]; } // clear the interrupt flag _T3IF = 0; } // T3Interrupt //-------------------------------------------START INTERRUPT ROUTINES---------------------------------------- void _ISRFAST _OC1Interrupt( void)//modified* { SYNC = 0; // bring the output down to SYNC level _OC1IF = 0; // clear the interrupt flag } // OC1Interrupt void _ISRFAST _OC2Interrupt( void)//modified* { SYNC = 1; // bring the output up to the black level _OC2IF = 0; // clear the interrupt flag } // OC2Interrupt void _ISRFAST _OC3Interrupt( void) { SYNC = 1; // bring the output up to the black level _OC3IF = 0; // clear the interrupt flag } // OC3Interrupt void _ISRFAST _OC4Interrupt( void) { // load SPI FIFO with 8 x 16-bit words = 128 pixels SPI1BUF = *VPtr++; SPI1BUF = *VPtr++; SPI1BUF = *VPtr++; SPI1BUF = *VPtr++; SPI1BUF = *VPtr++; SPI1BUF = *VPtr++; SPI1BUF = *VPtr++; SPI1BUF = *VPtr++; if ( --HCount > 0) { // activate again in time for the next SPI load OC4R += ( PIX_T * 7 * 16); OC4CON = 0x0009; // single event } // clear the interrupt flag _OC4IF = 0; } // OC4Interrupt //-------------------------------------------END INTERRUPT ROUTINES---------------------------------------- void initVideo( void) { // set the priority levels _T3IP = 4; // this is the default value anyway _OC1IP = 4; //modified* _OC2IP = 4; //modified* _OC3IP = 4; _OC4IP = 4; TMR3 = 0; // clear the timer PR3 = H_NTSC; // set the period register to NTSC line // 2.1 configure Timer3 modules T3CON = 0x8000; // enabled, prescaler 1:1, internal clock // 2.2 init Timer3/OC1/OC2/OC3/OC4 Interrupts, clear the flag _OC1IF = 0; _OC1IE = 1; //modified* _OC2IF = 0; _OC2IE = 1; //modified* _OC3IF = 0; _OC3IE = 1; _OC4IF = 0; _OC4IE = 1; _T3IF = 0; _T3IE = 1; // 2.3 init the processor priority level _IPL = 0; // this is the default value anyway // init the SPI1 if ( PIX_T == 2) SPI1CON1 = 0x043B; // Master, 16 bit, disable SCK, disable SS, prescaler 1:3 else SPI1CON1 = 0x0437; // Master, 16 bit, disable SCK, disable SS, prescaler 1:2 SPI1CON2 = 0x0001; // Enhanced mode, 8x FIFO SPI1STAT = 0x8000; // enable SPI port // init PORTF for the Sync _TRISG0 = 0; // output the SYNC pin // init the vertical sync state machine VState = SV_PREEQ; VCount = PREEQ_N; } // initVideo void clearScreen( void) { int i; int *v; v = (int *)&VMap[0]; // clear the screen for ( i=0; i < (VRES*( HRES/16)); i++) *v++ = 0; } //clearScreen void haltVideo( void) { T3CONbits.TON = 0; // turn off the vertical state machine } //haltVideo void synchV( void) { while ( VCount != 1); } // synchV void plot( unsigned x, unsigned y) { if ((x>4)] |= (0x8000 >> (x & 0xf)); } // plot #define abs( a) (((a)> 0) ? (a) : -(a)) void line( int x0, int y0, int x1, int y1) { int steep, t ; int deltax, deltay, error; int x, y; int ystep; steep = ( abs(y1 - y0) > abs(x1 - x0)); if ( steep ) { // swap x and y t = x0; x0 = y0; y0 = t; t = x1; x1 = y1; y1 = t; } if (x0 > x1) { // swap ends t = x0; x0 = x1; x1 = t; t = y0; y0 = y1; y1 = t; } deltax = x1 - x0; deltay = abs(y1 - y0); error = 0; y = y0; if (y0 < y1) ystep = 1; else ystep = -1; for (x = x0; x < x1; x++) { if ( steep) plot(y,x); else plot(x,y); error += deltay; if ( (error<<1) >= deltax) { y += ystep; error -= deltax; } // if } // for } // line