/* NCP-010 VGA Framebuffer for dsPIC Version 11/24/2010-A (C) 2010 Jason Hunt nulluser@gmail.com Reference: L. BAGHLI 08/01/2008 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. File: main.c HSync connected to RF0 VSync connected to RF1 RGB lines connected to RF3 */ /* Sine loopup table */ // data = sin(ang) << 14 , 256 steps const int sin_data[] = {0,402,804,1205,1606,2006,2404,2801,3196,3590,3981,4370,4756,5139,5520,5897,6270, 6639,7005,7366,7723,8076,8423,8765,9102,9434,9760,10080,10394,10702,11003,11297,11585,11866, 12140,12406,12665,12916,13160,13395,13623,13842,14053,14256,14449,14635,14811,14978,15137,15286,15426, 15557,15679,15791,15893,15986,16069,16143,16207,16261,16305,16340,16364,16379,16384,16379,16364,16340, 16305,16261,16207,16143,16069,15986,15893,15791,15679,15557,15426,15286,15137,14978,14811,14635,14449, 14256,14053,13842,13623,13395,13160,12916,12665,12406,12140,11866,11585,11297,11003,10702,10394,10080, 9760,9434,9102,8765,8423,8076,7723,7366,7005,6639,6270,5897,5520,5139,4756,4370,3981, 3590,3196,2801,2404,2006,1606,1205,804,402,0,-402,-804,-1205,-1606,-2006,-2404,-2801, -3196,-3590,-3981,-4370,-4756,-5139,-5520,-5897,-6270,-6639,-7005,-7366,-7723,-8076,-8423,-8765,-9102, -9434,-9760,-10080,-10394,-10702,-11003,-11297,-11585,-11866,-12140,-12406,-12665,-12916,-13160,-13395,-13623,-13842, -14053,-14256,-14449,-14635,-14811,-14978,-15137,-15286,-15426,-15557,-15679,-15791,-15893,-15986,-16069,-16143,-16207, -16261,-16305,-16340,-16364,-16379,-16384,-16379,-16364,-16340,-16305,-16261,-16207,-16143,-16069,-15986,-15893,-15791, -15679,-15557,-15426,-15286,-15137,-14978,-14811,-14635,-14449,-14256,-14053,-13842,-13623,-13395,-13160,-12916,-12665, -12406,-12140,-11866,-11585,-11297,-11003,-10702,-10394,-10080,-9760,-9434,-9102,-8765,-8423,-8076,-7723,-7366, -7005,-6639,-6270,-5897,-5520,-5139,-4756,-4370,-3981,-3590,-3196,-2801,-2404,-2006,-1606,-1205,-804, -402}; /* Cosine loopup table */ const int cos_data[] = {16384,16379,16364,16340,16305,16261,16207,16143,16069,15986,15893,15791,15679,15557,15426,15286,15137, 14978,14811,14635,14449,14256,14053,13842,13623,13395,13160,12916,12665,12406,12140,11866,11585,11297, 11003,10702,10394,10080,9760,9434,9102,8765,8423,8076,7723,7366,7005,6639,6270,5897,5520, 5139,4756,4370,3981,3590,3196,2801,2404,2006,1606,1205,804,402,0,-402,-804,-1205, -1606,-2006,-2404,-2801,-3196,-3590,-3981,-4370,-4756,-5139,-5520,-5897,-6270,-6639,-7005,-7366,-7723, -8076,-8423,-8765,-9102,-9434,-9760,-10080,-10394,-10702,-11003,-11297,-11585,-11866,-12140,-12406,-12665,-12916, -13160,-13395,-13623,-13842,-14053,-14256,-14449,-14635,-14811,-14978,-15137,-15286,-15426,-15557,-15679,-15791,-15893, -15986,-16069,-16143,-16207,-16261,-16305,-16340,-16364,-16379,-16384,-16379,-16364,-16340,-16305,-16261,-16207,-16143, -16069,-15986,-15893,-15791,-15679,-15557,-15426,-15286,-15137,-14978,-14811,-14635,-14449,-14256,-14053,-13842,-13623, -13395,-13160,-12916,-12665,-12406,-12140,-11866,-11585,-11297,-11003,-10702,-10394,-10080,-9760,-9434,-9102,-8765, -8423,-8076,-7723,-7366,-7005,-6639,-6270,-5897,-5520,-5139,-4756,-4370,-3981,-3590,-3196,-2801,-2404, -2006,-1606,-1205,-804,-402,-0,402,804,1205,1606,2006,2404,2801,3196,3590,3981,4370, 4756,5139,5520,5897,6270,6639,7005,7366,7723,8076,8423,8765,9102,9434,9760,10080,10394, 10702,11003,11297,11585,11866,12140,12406,12665,12916,13160,13395,13623,13842,14053,14256,14449,14635, 14811,14978,15137,15286,15426,15557,15679,15791,15893,15986,16069,16143,16207,16261,16305,16340,16364, 16379}; #include _FOSC(CSW_FSCM_OFF & XT_PLL8); _FWDT(WDT_OFF); _FBORPOR(PBOR_OFF & BORV_27 & PWRT_16 & MCLR_EN); _FGS(CODE_PROT_OFF); #define ABS(a) (( a ) < 0 ? - (a) : a) // Abs value #define FOSC 80000000 // Osc 10 MHZ xtal HS PLLx8 #define FCY (FOSC / 4) // Instruction clock #define HOST_BAUD 57600 // Baud for host pc #define hsync _LATF0 // Hsync signal #define vsync _LATF1 // Vsync signal #define TIMER1_INTERVAL 635 // Horz Freq 31.50 khz #define DISP_ROWS 80 // Number of rows and columns in the display buffer #define DISP_COLS 128 #define VGA_ROWS 480 // Visible VGA scanlines #define VGA_LINES 525 // Actual VGA scanlines #define COL_BYTES (DISP_COLS / 8) // Bytes per column #define MODE_DRAW 0 // Draw mode #define MODE_ERASE 1 // Erase Mode #define MAX_TRIANGLE 12 // Max number of triangles #define MAX_POINTS 8 // Max number of points #define MAX_OBJECT 2 // Max number of objects // Vertex type typedef struct { int x, y, z; // Local coords int rx, ry, rz; // Rotated unsigned char osx, osy; // Old Screen coords unsigned char sx, sy; // Screen coords } point_type; // Triangle type typedef struct { unsigned char p0, p1, p2; // Index into point array // This is smaller than three pointers } triangle_type; // Object Type typedef struct { int tx, ty, tz; // Position point_type points[MAX_POINTS]; unsigned char num_points; triangle_type triangles[MAX_TRIANGLE]; unsigned char num_triangles; unsigned char ang_x; // Rotation Angles unsigned char ang_y; unsigned char ang_z; unsigned char dx; } object_type; unsigned int p1 = 40; // Tuning Params unsigned int p2 = 28; // unsigned int cur_row = 0; // Current vga row in the frame buffer unsigned int disp_row = 0; // Current display rom in the frame buffer unsigned char rc = 0; // Row counter for duplicate rows unsigned char send = 0; // Send param signal unsigned char refresh = 0; // Refresh display signal unsigned char draw_mode = 0; // Current drawing_mode unsigned char row_data[DISP_ROWS][COL_BYTES]; // The framebuffer object_type obj[MAX_OBJECT]; // The 3D object data unsigned char num_objects; /************* *** Serial *** *************/ /* Setup Serial Port */ void serial_init( void ) { U2BRG = (((FCY / HOST_BAUD) / 16) - 1); // Create and set baud counter U2MODEbits.UARTEN = 1; U2STAbits.UTXEN = 1; IFS1bits.U2RXIF = 0; // Clear interrupt flag IEC1bits.U2RXIE = 0; IEC1bits.U2TXIE = 0; } /* End of serial_init */ /* Send a char to the debug port */ void debug_ch( char c ) { while (U2STAbits.UTXBF); U2TXREG = c; } /* End of debug char */ /* Send a string to the debug port */ void debug( char *s ) { while(*s) debug_ch(*s++); } /* End of debug */ /* Deal with console data */ void host_data( unsigned char d ) { U2STAbits.FERR = 0; U2STAbits.OERR = 0; // Check for change in tuning params /* if (d=='q') p1++; if (d=='a' && p1 > 0) p1--; if (d=='w') p2++; if (d=='s' && p2 > 0) p2--;*/ if (d=='a') obj[0].tx--; if (d=='d') obj[0].tx++; if (d=='w') obj[0].ty--; if (d=='s') obj[0].ty++; if (d=='q' && obj[0].tz > 1) obj[0].tz--; if (d=='e') obj[0].tz++; if (d==' ') refresh = 1; send = 1; // Send param data } /* End of host_data */ /******************** *** End of Serial *** ********************/ /****************** *** VGA Related *** ******************/ void vga_clear( void ); /* Setup the vga output */ void vga_init( void ) { // Setup timer for 1ms and enble interrupt T1CONbits.TCKPS = 0; PR1 = TIMER1_INTERVAL; T1CONbits.TON = 1; // Enable timer IEC0bits.T1IE = 1; // Enable interrupt T2CONbits.TON = 1; // Enable timer SPI1STATbits.SPIEN = 1; // Enable SPI port SPI1CONbits.SMP = 1; SPI1CONbits.SPRE = 0b111; SPI1CONbits.PPRE = 0b10; SPI1CONbits.MSTEN = 1; vga_clear(); } /* End of vga_init */ /* Update a scan row */ inline void vga_row( void ) { hsync = 0; // Start Hsync if (cur_row >= VGA_LINES-1) // End of row { cur_row = 0; disp_row = 0; rc = 0; } if (cur_row == 11 || cur_row == 12) // Vsync on these scanlines vsync = 0; else vsync = 1; hsync = 1; // End Hsync cur_row++; if (cur_row < 45) return; // Do not write RGB data for non display area unsigned int i = COL_BYTES; // Column byte counter unsigned char *p = ((unsigned char *)&row_data[disp_row]); // asm("nop \n nop \n nop \n nop \n nop \n"); // Delay for row start // Deal with row repeating if (rc >= VGA_ROWS / DISP_ROWS) { rc = 0; disp_row ++; } else { asm("nop \n nop \n nop \n nop \n "); } // Write the row to spi while(i--) { // Wait for previous SPI to finish asm("nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop \n "); asm("nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop \n "); SPI1BUF = *p++; // Start SPI transfer } rc++; } /* End of vga_row */ /* Horz timter */ void __attribute__ ((__interrupt__, auto_psv)) _T1Interrupt ( void ) { IFS0bits.T1IF = 0; vga_row(); } /* End of timer 1 interrupt */ /************************* *** End of VGA Related *** *************************/ /************************** *** High Level Graphics *** **************************/ /* Return a random byte */ unsigned char rnd( void ) { static unsigned int s = 7347; s = 37513 * (unsigned long)s + 12291 + (TMR1 * 16); return (s >> 4); } /* End of rnd() */ /* Refresh the frame buffer */ void vga_rand( void ) { // Write the snow pattern unsigned int i, j; for (i = 1; i < DISP_ROWS-1; i++) { for (j = 0; j < COL_BYTES; j++) row_data[i][j] = rnd(); row_data[i][0] |= 0x80; row_data[i][COL_BYTES-1] |= 0x01; } for (i = 0; i < COL_BYTES; i++) { row_data[0][i] = 0xff; row_data[DISP_ROWS-1][i] = 0xff; } } /* End of vga_rand */ /* Clear the buffer */ void vga_clear( void ) { // Write the snow pattern unsigned int i, j; for (i = 0; i < DISP_ROWS; i++) { for (j = 0; j < COL_BYTES; j++) row_data[i][j] = 0; //row_data[i][0] |= 0x80; //row_data[i][COL_BYTES-1] |= 0x01; } /*for (i = 0; i < COL_BYTES; i++) { row_data[0][i] = 0xff; row_data[DISP_ROWS-1][i] = 0xff; }*/ } /* End of vga_clear */ /* Set a screen pixel */ inline void put_pixel( int x, int y ) { // if(x < 0||x>=DISP_COLS) return; // if(y < 0||y>=DISP_ROWS) return; row_data[y][x >> 3] |= 0x80 >> (x & 0x07); } /* End of put_pixel */ /* Set a screen pixel */ inline void clear_pixel( int x, int y ) { // if(x < 0||x>=DISP_COLS) return; // if(y < 0||y>=DISP_ROWS) return; row_data[y][x >> 3] &= ~(0x80 >> (x & 0x07)); } /* End of clear_pixel */ /* Draw a line */ // Straight from wiki reference void draw_line( int x0, int y0, int x1, int y1 ) { int steep; if (ABS(y1 - y0) > ABS(x1 - x0)) steep = 1; else steep = 0; int tmp; if (steep) { tmp = x0; x0 = y0; y0 = tmp; tmp = x1; x1 = y1; y1 = tmp; } if (x0 > x1) { tmp = x0; x0 = x1; x1 = tmp; tmp = y0; y0 = y1; y1 = tmp; } int deltax = x1 - x0; int deltay = ABS(y1 - y0); int error = deltax / 2; int ystep; int y = y0; if (y0 < y1) ystep = 1; else ystep = -1; int x; for (x = x0; x < x1; x++) { if (steep) { if (draw_mode == MODE_DRAW) put_pixel(y, x); else if (draw_mode == MODE_ERASE) clear_pixel(y, x); } else { if (draw_mode == MODE_DRAW) put_pixel(x, y); else if (draw_mode == MODE_ERASE) clear_pixel(x, y); } error = error - deltay; if (error < 0) { y = y + ystep; error = error + deltax; } } } /* End of draw_line */ /* Draw a triangle */ void draw_triangle ( triangle_type t, point_type *p ) { if (draw_mode == MODE_DRAW) { // Backface culling if (((long)p[t.p0].sy * (p[t.p2].sx - p[t.p1].sx) + (long)p[t.p1].sy * (p[t.p0].sx - p[t.p2].sx) + (long)p[t.p2].sy * (p[t.p1].sx - p[t.p0].sx)) > 0) return; draw_line(p[t.p0].sx, p[t.p0].sy, p[t.p1].sx,p[t.p1].sy); draw_line(p[t.p1].sx, p[t.p1].sy, p[t.p2].sx,p[t.p2].sy); } else if (draw_mode == MODE_ERASE) { draw_line(p[t.p0].osx, p[t.p0].osy, p[t.p1].osx,p[t.p1].osy); draw_line(p[t.p1].osx, p[t.p1].osy, p[t.p2].osx,p[t.p2].osy); } } /* End of draw_triangle */ /* Draw all triangles */ void draw_triangles ( object_type *o ) { draw_mode = MODE_DRAW; unsigned int i; for (i = 0; i < o->num_triangles; i++) draw_triangle(o->triangles[i], o->points); } /* End of draw_triangles */ /* Draw all triangles */ void erase_triangles ( object_type *o ) { draw_mode = MODE_ERASE; unsigned int i; for (i = 0; i < o->num_triangles; i++) draw_triangle(o->triangles[i], o->points); } /* End of erase_triangles */ /* Rotation about origin */ inline void rotate( unsigned char a, int x1, int y1, int *nx, int *ny ) { *nx = ((unsigned long)x1 * cos_data[a] - (unsigned long)y1 * sin_data[a])>> 14; *ny = ((unsigned long)x1 * sin_data[a] + (unsigned long)y1 * cos_data[a])>> 14; } /* End of rotate */ /* Process all points */ void process_points ( object_type *o ) { unsigned int i; point_type *p = o->points; for (i = 0; i < o->num_points; i++) { // Rotate rotate(o->ang_y, p->x, p->z, &p->rx, &p->rz); rotate(o->ang_x, p->rz, p->y, &p->rz, &p->ry); rotate(o->ang_z, p->rx, p->ry, &p->rx, &p->ry); // Translate p->rx += o->tx; p->ry += o->ty; p->rz += o->tz; // Save old screen points p->osx = o->points[i].sx; p->osy = o->points[i].sy; // Project p->sx = DISP_COLS/2 + ((long)p->rx << 8) / p->rz; p->sy = DISP_ROWS/2 + ((long)p->ry << 8) / p->rz; *p++; } } /* End of process_points */ /* Update object data */ void update_object( object_type *o ) { o->ang_x += o->dx; o->ang_y++; o->ang_z++; } /* End of update object */ /* Render the buffer */ void draw_screen( void ) { unsigned int i; for (i = 0; i < num_objects; i++) { update_object(&obj[i]); process_points(&obj[i]); // Rotate about center of object erase_triangles (&obj[i]); // Erase Old triangles draw_triangles (&obj[i]); // Draw triangles } } /* End of draw_screen */ /* Add a point to the point array */ void add_point( object_type *o, int px, int py, int pz ) { o->points[o->num_points].x = px; o->points[o->num_points].y = py; o->points[o->num_points].z = pz; o->num_points++; } /* End of add_point */ /* Add a triangle to the triangle array */ void add_triangle( object_type *o, int p0, int p1, int p2 ) { o->triangles[o->num_triangles].p0 = p0; o->triangles[o->num_triangles].p1 = p1; o->triangles[o->num_triangles].p2 = p2; o->num_triangles++; } /* End of add_triangle */ /* Add a cube */ void add_cube(int x, int y, int z) { if (num_objects >= MAX_OBJECT) return; object_type *o = &obj[num_objects]; o->tx = x; // Position o->ty = y; o->tz = z; o->ang_x = 0; // Rotation Angles o->ang_y = 0; o->ang_z = 0; o->dx = 1; o->num_points = 0; // Setup the points add_point(o, -10, 10, -10); add_point(o, 10, 10, -10); add_point(o, -10, -10, -10); add_point(o, 10, -10, -10); add_point(o, -10, 10, 10); add_point(o, 10, 10, 10); add_point(o, -10, -10, 10); add_point(o, 10, -10, 10); o->num_triangles = 0; add_triangle(o, 2, 0, 1); add_triangle(o, 1, 3, 2); add_triangle(o, 2, 6, 4); add_triangle(o, 4, 0, 2); add_triangle(o, 4, 6, 7); add_triangle(o, 7, 5, 4); add_triangle(o, 1, 5, 7); add_triangle(o, 7, 3, 1); add_triangle(o, 4, 5, 1); add_triangle(o, 1, 0, 4); add_triangle(o, 7, 6, 2); add_triangle(o, 2, 3, 7); num_objects++; } /* End of add_cube */ /* Setup the objects */ void add_objects( void ) { num_objects = 0; add_cube(-15, 0, 150); add_cube(20, 0, 150); if (num_objects > 0) obj[1].dx = -1; } /* End of vga_objects */ /********************************* *** End of high level graphics *** *********************************/ /* Setup the device */ void device_init ( void ) { // Set port directions ADPCFG = 0xFFFF; TRISA = 0; TRISB = 0; TRISC = 0; TRISD = 0; TRISF = 0b00011000; } /* End of device_init */ /* main */ int main ( void ) { device_init(); serial_init(); vga_init(); vga_clear(); add_objects(); unsigned int c = 0; while(1) { TMR2 = 0; // while(TMR2 < 0x1000); // if(c++ > 50) { c = 0; draw_screen(); } //ang_z++; // Check for new byte if (U2STAbits.URXDA) host_data(U2RXREG & 0xff); // while (!(U2STAbits.URXDA)); // unsigned char tmp = U2RXREG & 0xff; // Redraw screen // if (refresh) { // refresh = 0; // draw_screen(); } // Send param info if (send) { send = 0; // char s[32]; // sprintf(s, "x %d y %d %c%c", x, y, 0x0d, 0x0a); // debug(s); } } return(0); } /* End of main */