Code: Select all
/*******************************************************************
* Modified by A.A.Shabarshin (March-April 2015)
* Optimized, recalculated to 14.31818 MHz and added colors
*
* Original code was taken from "NTSC TV interface" examples:
* 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>
#define NOBACKGROUND
#define NOFONT
#if 0
#define SYS_FREQ 60000000
// 8MHZ 4MHz 60MHz 30 <-----<--- 60MHz
#pragma config FNOSC = FRCPLL, POSCMOD = OFF, FPLLIDIV = DIV_2, FPLLMUL = MUL_15, FPBDIV = DIV_2, FPLLODIV = DIV_1
#else
#define SYS_FREQ 57272720
// 14.31818MHZ 3.579545MHz 57.27272MHz 28.63636 <-----<--- 57.27272MHz
#pragma config FNOSC = PRIPLL, POSCMOD = HS, FPLLIDIV = DIV_4, FPLLMUL = MUL_16, FPBDIV = DIV_2, FPLLODIV = DIV_1
#endif
#pragma config FWDTEN = OFF
#pragma config FSOSCEN = OFF, JTAGEN = OFF
#define PITCH 23
#define OFFSET 96
#define TOFFSET (OFFSET/8)
#define DX (OFFSET+640)
#define DY (200)
#define SCREENSZ (DX*DY/32)
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 1818 // 63.5 uSec at 28.6 MHz Fpb, prescaler=1
#define line_offset 159 // offset to start video screen
#define us_5_cycles 135 // 5 uSec at 28.6 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 639
#define bottom 199
// 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;
// Absolute frame counter
volatile unsigned long time_frames = 0;
#ifndef NOFONT
#include "nedofont.h"
#endif
#ifndef NOBACKGROUND
#include "cgatrick.xbm"
#endif
#define WAIT 180 // wait 3 seconds before starting video
// == 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
{
if(time_frames>WAIT){
// 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,785,821,856,891,926,961,995,1028,1061,1093,1124,1155,1185,1213,1241,
1267,1292,1316,1339,1360,1380,1399,1416,1432,1446,1458,1469,1478,1486,1492,1496,
1499,1499,1499,1496,1492,1486,1478,1469,1458,1446,1432,1416,1399,1380,1360,1339,
1316,1292,1267,1241,1213,1185,1155,1125,1093,1061,1028,995,961,926,891,856,
821,785,750,714,678,643,608,573,538,504,471,438,406,375,344,314,
286,258,232,207,183,160,139,119,100,83,67,53,41,30,21,13,
7,3,0,0,0,3,7,13,21,30,41,53,67,83,100,119,
139,160,183,207,232,258,286,314,344,374,406,438,471,504,538,573,
608,643,678,714,749,785,821,856,891,926,961,995,1028,1061,1093,1124,
1155,1185,1213,1241,1267,1292,1316,1339,1360,1380,1399,1416,1432,1446,1458,1469,
1478,1486,1492,1496,1499,1499,1499,1496,1492,1486,1478,1469,1458,1446,1432,1416,
1399,1380,1360,1339,1316,1292,1267,1241,1213,1185,1155,1125,1093,1061,1028,995,
961,926,891,856,821,785,750,714,678,643,608,573,538,504,471,438,
406,375,344,314,286,258,232,207,183,160,139,119,100,83,67,53,
41,30,21,13,7,3,0,0,0,3,7,13,21,30,41,53,
67,83,100,119,139,160,183,207,232,258,286,314,344,374,406,438,
471,504,538,573,608,643,678,714
};
// == 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);
if(LineCount >= image_start + 20 && LineCount < image_end - 100)
OpenOC5(OC_ON | OC_TIMER2_SRC | OC_SINGLE_PULSE, line_offset+300, line_offset+200);
else
CloseOC5();
*/
// start the DMA byte blaster to the screen
if (LineCount >= image_start && LineCount < image_end){
if(time_frames>WAIT){
// 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, 92, 4, 4);
// 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 += PITCH; // PITCH 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;
time_frames++;
}
// 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) {
//calculate i based upon this and x,y
// the word with the pixel in it
//int i = (x/32) + y*PITCH
x += OFFSET;
if (c==1)
screen_buffer[(x >> 5) + (y * PITCH)] |= 1<<(31-(x & 0x1f));
else if (c==0)
screen_buffer[(x >> 5) + (y * PITCH)] &= ~(1<<(31-(x & 0x1f)));
else // c==2
screen_buffer[(x >> 5) + (y * PITCH)] ^= 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+OFFSET) >> 5) + (y * PITCH)] & (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) {
#ifndef NOFONT
x += TOFFSET;
int j = (x>>2)+(y<<3)*PITCH;
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+=PITCH;
screen_buffer[j]=(screen_buffer[j] & msk)|(*(ptr++)<<shf);j+=PITCH;
screen_buffer[j]=(screen_buffer[j] & msk)|(*(ptr++)<<shf);j+=PITCH;
screen_buffer[j]=(screen_buffer[j] & msk)|(*(ptr++)<<shf);j+=PITCH;
screen_buffer[j]=(screen_buffer[j] & msk)|(*(ptr++)<<shf);j+=PITCH;
screen_buffer[j]=(screen_buffer[j] & msk)|(*(ptr++)<<shf);j+=PITCH;
screen_buffer[j]=(screen_buffer[j] & msk)|(*(ptr++)<<shf);j+=PITCH;
screen_buffer[j]=(screen_buffer[j] & msk)|(*ptr<<shf);
#endif
}
//==================================
// 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* xolinedirect(int y){return &screen_buffer[PITCH*y+(OFFSET>>5)];}
// ========================================================================
int main(void)
{
int* iptr;
float f;
unsigned short us;
unsigned long i,j,n,m,stored,maxstored=0;
// global time
int time, seconds, x, y, k, c, *p;
char time_string[10],str[32];
// 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+40, line_offset+112);
// OC4 is PPS group 3, map to RPB13 (pin 24)
PPSOutput(3, RPB13, OC4);
// OC5 setup /////////////////////////////////
OpenOC5(OC_ON | OC_TIMER2_SRC | OC_CONTINUE_PULSE, line_cycles-200, line_cycles-100);
// 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 , 2); // N=6
//=== DMA Channel 1 ================================
// Open DMA Chan1 and chain from channel zero
DmaChnOpen(DMAchn1, DMApri0, DMA_OPEN_DEFAULT);
// setup system wide interrupts ///
INTEnableSystemMultiVectoredInt();
#ifndef NOBACKGROUND
iptr = (int*)cgatrick_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;
}
#endif
// generate color burst
p = screen_buffer;
for(y=0;y<200;y++)
{
p[0] = 0x00000CCC;
p[1] = 0xCCCCCC00;
p += PITCH;
}
// generate color chart
for(y=0;y<192;y++)
{
if((y%12)==11) continue; // black lines between colors
p = xolinedirect(y+4);
j = y/12;
for(x=0;x<16;x++)
{
if(y&1)
p[x+4] = (x<<28)|(x<<24)|(x<<20)|(x<<16)|(x<<12)|(x<<8)|(x<<4);//|x;
else p[x+4] = (j<<28)|(j<<24)|(j<<20)|(j<<16)|(j<<12)|(j<<8)|(j<<4);//|j;
}
}
#if 1
// 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
#endif
// Draw a title
video_string(0,0,"nedoPC-32-A");
seconds = stored = n = x = y = 0;
c = 32;
k = 0;
srand(11111);
while(1)
{
n++;
#if 0
// random characters
k = rand();
video_putchar(k%80,2+((k>>6)&15),++c);
if(c==255) c=31;
#endif
#if 0
// random lines
k = rand();
c = k&511; k=8+((k>>8)%184);
video_line(x,y,c,k,2);
x = c; y = k;
#endif
#if 0
// random pixels
k = rand();
video_pt(k%640,8+((k>>9)%184),2);
#endif
if(time != time_tick_60_hz)
{
time = time_tick_60_hz ;
stored = n;
if(n>maxstored) maxstored=n;
n = 0;
if(seconds != time_seconds)
{
seconds = time_seconds;
sprintf(time_string, "%d:%d",seconds,stored);
video_string(0, 24, time_string);
if(seconds&1)
mPORTBClearBits(BIT_2);
else
mPORTBSetBits(BIT_2);
if(seconds&2)
mPORTBClearBits(BIT_3);
else
mPORTBSetBits(BIT_3);
}
}
}
}