Code: Select all
/*********************************************************************
*
* 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);
}
}
}