![](images/spacer.gif) | Code: /********************************************************************* * * Based on "NTSC TV interface" examples from * http://hackaday.io/project/2032-pic32-oscilloscope * Bruce Land Cornell University * June 2014 * This code uses many cool ideas from * Programming 32-bit Microcontrollers in C: Exploring the PIC32 * by Lucio Di Jasio * * Uses two Compare units from one timer to do sync and video timing *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * VIDEO is PortA.1 * SYNC is PortB.1 * */
#include <plib.h> #include <xc.h> // need for pps #include <stdio.h> #include <stdlib.h>
// Recalculated to 64 MHz to fit into NTSC screen // 8MHZ 4MHz 64MHz 32 <-----<--- 64MHz #pragma config FNOSC = FRCPLL, POSCMOD = OFF, FPLLIDIV = DIV_2, FPLLMUL = MUL_16, FPBDIV = DIV_2, FPLLODIV = DIV_1 #pragma config FWDTEN = OFF #pragma config FSOSCEN = OFF, JTAGEN = OFF // core frequency we're running at // peripherals at 40 MHz #define SYS_FREQ 64000000
#define DX 256 #define DY 200
// main screen buffer array 256 wide x 200 high = 51200 pixels // 51200/32 = 1600 integer words #define SCREENSZ 1600 int screen_buffer[SCREENSZ] ; volatile int *screen_buffer_addr = screen_buffer ; volatile int *screen_ptr ;
// The DMA channels #define DMAchn1 1 #define DMApri0 0
// video timing #define line_cycles 2032 // 63.5 uSec at 32 MHz Fpb, prescaler=1 #define line_offset 352 // offset to start video screen #define us_5_cycles 160 // 5 uSec at 32 MHz Fpb, prescaler=1
// video active lines -- 200 total #define image_start 20 #define image_end (image_start+DY) #define top 0 #define left 0 #define right (DX-1) #define bottom (DY-1)
// Current line number which is modified // by a state machine in the timer2 ISR volatile int LineCount = 0 ; // ISR driven 1/60 second time volatile int time_tick_60_hz = 0 ; // ISR driven seconds counter volatile int time_seconds = 0;
#include "nedofont.h"
#include "katya_bw.xbm" #include "dolphin.xbm"
// == OC3 ISR ============================================ // VECTOR 14 is OC3 vector -- set up of ipl3 in main // vector names from int_1xx_2xx.h void __ISR(14, ipl3) OC3Handler(void) // 14 { // mPORTBSetBits(BIT_1); // Convert DMA to SPI control DmaChnSetEventControl(DMAchn1, DMA_EV_START_IRQ(_SPI1_TX_IRQ)); // //DmaChnEnable(DMAchn1); // clear the timer interrupt flag -- name from // http://people.ece.cornell.edu/land/courses/ece4760/PIC32/Microchip_stuff/32-bit-Peripheral-Library-Guide.pdf // Table 8.2 mOC3ClearIntFlag(); // mPORTBClearBits(BIT_1); // for profiling the ISR execution time }
volatile short sound[]={ 750,880,1006,1124,1232,1324,1399,1454,1488,1499,1488,1454,1399,1324,1232,1125, 1006,880,750,619,493,375,267,175,100,45,11,0,11,45,100,175, 267,374,493,619,749,880,1006,1124,1232,1324,1399,1454,1488,1499,1488,1454, 1399,1324,1232,1125,1006,880,750,619,493,375,267,175,100,45,11,0, 11,45,100,175,267,374,493,619,749,880,1006,1124,1232,1324,1399,1454, 1488,1499,1488,1454,1399,1324,1232,1125,1006,880,750,619,493,375,267,175, 100,45,11,0,11,45,100,175,267,374,493,619,749,880,1006,1124, 1232,1324,1399,1454,1488,1499,1488,1454,1399,1324,1232,1125,1006,880,750,619, 493,375,267,175,100,45,11,0,11,45,100,175,267,374,493,619, 749,880,1006,1124,1232,1324,1399,1454,1488,1499,1488,1454,1399,1324,1232,1125, 1006,880,750,619,493,375,267,175,100,45,11,0,11,45,100,175, 267,374,493,619,749,880,1006,1124,1232,1324,1399,1454,1488,1499,1488,1454, 1399,1324,1232,1125,1006,880,750,619,493,375,267,175,100,45,11,0, 11,45,100,175,267,374,493,619,749,880,1006,1124,1232,1324,1399,1454, 1488,1499,1488,1454,1399,1324,1232,1125,1006,880,750,619,493,375,267,175, 100,45,11,0,11,45,100,175,267,374,493,619,749,880,1006,1124, 1232,1324,1399,1454,1488,1499,1488,1454 };
// == Timer 2 ISR ========================================= void __ISR(_TIMER_2_VECTOR, ipl2) Timer2Handler(void) { //mPORTBSetBits(BIT_1); // for profiling the ISR execution time // update the current scanline number LineCount++ ;
OpenOC5(OC_ON | OC_TIMER2_SRC | OC_CONTINUE_PULSE, line_offset+18+sound[LineCount], line_offset+12); // start the DMA byte blaster to the screen if (LineCount >= image_start && LineCount < image_end){ // set the Chan1 DMA transfer parameters: source & destination address, // source & destination size, number of bytes per event // 32 bytes / line with 4 bytes per transfer (SPI in 32 bit mode) //screen_ptr = screen_buffer + ((LineCount - image_start)<<5) ; DmaChnSetTxfer(DMAchn1, (void*)screen_ptr, (void*)&SPI1BUF, 32, 4, 4); //32 // IRO 17 is the output compare 3 interrupt (See datasheet table 7.1) DmaChnSetEventControl(DMAchn1, DMA_EV_START_IRQ(17)); // // turn it on for 32 bytes DmaChnEnable(DMAchn1); // increment the image memory pointer for the next ISR pass screen_ptr += 8; // 8 32-bit words per line } // update the frame time_tick immediately after image is copied else if(LineCount==image_end) { // a general propose time base if((++time_tick_60_hz%60)==0) time_seconds++; } // == SYNC state machine ==== // begin long (Vertical) synch after line 247 else if (LineCount==248) {OC2R = line_cycles - us_5_cycles ;} // back to regular sync after line 250 // the first condition eliminates sync for one line (to avoid duplicate) else if (LineCount==250) {OC2R = 0 ;} else if (LineCount==251) {OC2R = us_5_cycles ;} // start new frame after line 262 and reset the image memory pointer else if (LineCount==263) { LineCount = 1; // reset for the next frame screen_ptr = screen_buffer_addr; } // clear the timer interrupt flag mT2ClearIntFlag(); //mPORTBClearBits(BIT_1); // for profiling the ISR execution time }
//== plot a point ========================================================= //plot one point //at x,y with color 1=white 0=black 2=invert void video_pt(int x, int y, char c) { //each line has 18 bytes //calculate i based upon this and x,y // the word with the pixel in it //int i = (x/32) + y*8 if (c==1) screen_buffer[(x >> 5) + (y << 3)] |= 1<<(31-(x & 0x1f)); else if (c==0) screen_buffer[(x >> 5) + (y << 3)] &= ~(1<<(31-(x & 0x1f))); else // c==2 screen_buffer[(x >> 5) + (y << 3)] ^= 1<<(31-(x & 0x1f)); }
//================================== //plot a line //at x1,y1 to x2,y2 with color 1=white 0=black 2=invert //NOTE: this function requires signed chars //Code is from David Rodgers, //"Procedural Elements of Computer Graphics",1985 void video_line(int x1, int y1, int x2, int y2, char c) { int e; signed int dx,dy,j, temp; signed char s1,s2, xchange; signed int x,y;
x = x1; y = y1;
//take absolute value if (x2 < x1) { dx = x1 - x2; s1 = -1; }
else if (x2 == x1) { dx = 0; s1 = 0; }
else { dx = x2 - x1; s1 = 1; }
if (y2 < y1) { dy = y1 - y2; s2 = -1; }
else if (y2 == y1) { dy = 0; s2 = 0; }
else { dy = y2 - y1; s2 = 1; }
xchange = 0;
if (dy>dx) { temp = dx; dx = dy; dy = temp; xchange = 1; }
e = ((int)dy<<1) - dx;
for (j=0; j<=dx; j++) { video_pt(x,y,c);
if (e>=0) { if (xchange==1) x = x + s1; else y = y + s2; e = e - ((int)dx<<1); }
if (xchange==1) y = y + s2; else x = x + s1;
e = e + ((int)dy<<1); } }
//================================== //return the value of one point //at x,y with color 1=white 0=black char video_state(int x, int y) { //The following construction detects exactly one bit at the x,y location return (screen_buffer[(x >> 5) + (y << 3)] & (1<<(31-(x & 0x1f))))?1:0 ; }
//================================== // put a big character on the screen // c is index into bitmap void video_putchar(int x, int y, int c) { int j = (x>>2)+(y<<6); int shf = (3-(x&3))<<3; int msk = ~(255<<shf); unsigned char *ptr = font8x8[c-32]; screen_buffer[j]=(screen_buffer[j] & msk)|(*(ptr++)<<shf);j+=8; screen_buffer[j]=(screen_buffer[j] & msk)|(*(ptr++)<<shf);j+=8; screen_buffer[j]=(screen_buffer[j] & msk)|(*(ptr++)<<shf);j+=8; screen_buffer[j]=(screen_buffer[j] & msk)|(*(ptr++)<<shf);j+=8; screen_buffer[j]=(screen_buffer[j] & msk)|(*(ptr++)<<shf);j+=8; screen_buffer[j]=(screen_buffer[j] & msk)|(*(ptr++)<<shf);j+=8; screen_buffer[j]=(screen_buffer[j] & msk)|(*(ptr++)<<shf);j+=8; screen_buffer[j]=(screen_buffer[j] & msk)|(*ptr<<shf); }
//================================== // put a string of big characters on the screen void video_string(int x, int y, char *str) { char i; for (i=0; str[i]!=0; i++) { video_putchar(x++,y,str[i]); } }
// ======================================================================== int main(void) { int* iptr; unsigned long i,j,n,m,stored; // global time int time, seconds, x, y, k, c; char time_string[10] ;
// Configure the device for maximum performance but do not change the PBDIV // Given the options, this function will change the flash wait states, RAM // wait state and enable prefetch cache but will not change the PBDIV. // The PBDIV value is already set via the pragma FPBDIV option above.. SYSTEMConfig(SYS_FREQ, SYS_CFG_WAIT_STATES | SYS_CFG_PCACHE);
//make sure analog is cleared //ANSELA =0; //ANSELB =0;
// timer interrupt ////////////////////////// // Set up timer2 on, interrupts, internal clock, prescalar 1, toggle rate // 63.5 microSec OpenTimer2(T2_ON | T2_SOURCE_INT | T2_PS_1_1, line_cycles); // set up the timer interrupt with a priority of 2 ConfigIntTimer2(T2_INT_ON | T2_INT_PRIOR_2); mT2ClearIntFlag(); // and clear the interrupt flag
// Compare match setup ////////////////////// //Set up compare match unit to produce sync pulses // 5 uSec low // or 63.5-5 = 58.5 microSec (2340 ticks) low // pulse duration will be controlled in Timer2 ISR // #define OpenOC2( config, value1, value2) ( OC2RS = (value1), OC2R = (value2), OC2CON = (config) ) OpenOC2(OC_ON | OC_TIMER2_SRC | OC_CONTINUE_PULSE, line_cycles-1, us_5_cycles); // OC2 is PPS group 2, map to RPB1 (pin 5) PPSOutput(2, RPB1, OC2);
// OC3 setup ///////////////////////////////// // Compare unit for video timing, // using the interrupt flag to trigger the first DMA, // then use the ISR to change the DMA control to SPI // #define OpenOC2( config, value1, value2) ( OC2RS = (value1), OC2R = (value2), OC2CON = (config) ) // Pulse needs to be TWO cycles long OpenOC3(OC_ON | OC_TIMER2_SRC | OC_CONTINUE_PULSE, line_offset+2, line_offset); // turn on ISR so that DMA can covert to SPI control ConfigIntOC3(OC_INT_PRIOR_1 | OC_INT_ON); //3 // mOC3ClearIntFlag(); // and clear the interrupt flag
// OC4 setup ///////////////////////////////// // OpenOC4(OC_ON | OC_TIMER2_SRC | OC_CONTINUE_PULSE, line_offset+2, line_offset); // OC4 is PPS group 3, map to RPA4 (pin 12) PPSOutput(3, RPA4, OC4);
// OC5 setup ///////////////////////////////// // OpenOC5(OC_ON | OC_TIMER2_SRC | OC_CONTINUE_PULSE, line_offset+2, line_offset); // OC5 is PPS group 3, map to RPA2 (pin 9) PPSOutput(3, RPA2, OC5); // SPI configure ///////////////////////////// // SCK1 is pin 25 RB14 // SDO1 is PPS group 2, map to RPA1 (pin 3) // SDI1 is PPS group 2, map to RPB8 (pin 17) ??? // SS1 input is PPS group 1, map to RPB7 (pin 16) for framing // specify PPS group, signal, logical pin name PPSInput (1, SS1, RPB7); PPSOutput(2, RPA1, SDO1); // control sync for DAC mPORTBSetPinsDigitalOut(BIT_0|BIT_2|BIT_3); mPORTBSetBits(BIT_0); mPORTBClearBits(BIT_2); mPORTBClearBits(BIT_3);
// divide Fpb by N, configure the I/O ports. 32 bit transfer SpiChnOpen(SPI_CHANNEL1, SPI_OPEN_ON | SPI_OPEN_MODE32 | SPI_OPEN_MSTEN , 6); // N=6
//=== DMA Channel 1 ================================ // Open DMA Chan1 and chain from channel zero DmaChnOpen(DMAchn1, DMApri0, DMA_OPEN_DEFAULT);
// setup system wide interrupts /// INTEnableSystemMultiVectoredInt();
iptr = (int*)katya_bw_bits; // iptr = (int*)dolphin_bits; for(i=0;i<SCREENSZ;i++) { k = iptr[i]; c = 0; n = 1; m = 0x80000000; for(j=0;j<32;j++) { if(!(k&n)) c|=m; n <<= 1; m >>= 1; } screen_buffer[i] = c; } // Draw the screen boundaries video_line(left,top, right,top, 1); // top video_line(right,top, right,bottom, 1); // right video_line(right,bottom, left,bottom, 1); // bottom video_line(left,top, left,bottom ,1); // left
// Draw a title video_string(1,1,"nedoPC-32-A v1.0 (March 2015) ");
seconds = stored = n = x = y = 0; c = 32; srand(11111); while(1) { n++; #if 0 // random characters k = rand(); video_putchar(k&31,2+((k>>5)&15),++c); if(c==255) c=31; #endif #if 0 // random lines k = rand(); c = k&255; k=16+((k>>8)%168); video_line(x,y,c,k,2); x = c; y = k; #endif #if 0 // random pixels k = rand(); video_pt(k&255,16+((k>>8)%168),2); #endif if(time != time_tick_60_hz) { time = time_tick_60_hz ; stored = n; n = 0; } if(seconds != time_seconds) { seconds = time_seconds; sprintf(time_string, "%d:%4.4X ",seconds,PORTB);//stored); video_string(1, 23, time_string); if(seconds&1) mPORTBClearBits(BIT_2); else mPORTBSetBits(BIT_2);
if(seconds&2) mPORTBClearBits(BIT_3); else mPORTBSetBits(BIT_3); } }
}
| ![](images/spacer.gif) |