Плата центрального недопроцессора nedoCPU-32

8-битные микроконтроллеры PICmicro (ПИКи) от Microchip и совместимые, а также 16-битные PIC24 и 32-битные PIC32

Moderator: Shaos

Post Reply
User avatar
Shaos
Admin
Posts: 25261
Joined: 09 Jan 2003 06:22
Location: Silicon Valley
Contact:

Re: Плата центрального недопроцессора nedoCPU-32

Post by Shaos »

Stan wrote:
Shaos wrote:Интересной особенностью параллельного мастер порта (сигналы PMxx) является то, что на него можно натравить DMA, которое может брать последовательность байтов из памяти и колбасить их в порт - это можно использовать для генерации видео...
На фотографиях это точечная графика через PIC32 или всё же псевдографический режим?
Если точечная графика, то какой размер растра?
По вышеприведённому линку всё написано: http://hackaday.io/project/2032-pic32-oscilloscope

Точечная графика 256x200

Или мы по ангельски не разумеем?...
User avatar
Shaos
Admin
Posts: 25261
Joined: 09 Jan 2003 06:22
Location: Silicon Valley
Contact:

Re: Плата центрального недопроцессора nedoCPU-32

Post by Shaos »

Подменил фонт на свой, который юзался в NedoText и тоже 5x7:
Attachments

Shaos font used
Shaos font used
Photo0137.jpg (105.44 KiB) Viewed 14007 times

User avatar
Shaos
Admin
Posts: 25261
Joined: 09 Jan 2003 06:22
Location: Silicon Valley
Contact:

Re: Плата центрального недопроцессора nedoCPU-32

Post by Shaos »

А вот недофонт 8x8, использованный в Circuits.CC - теперь можно строить nedoPC-32 :idea:
Attachments

nedoPC font used
nedoPC font used
Photo0138.jpg (107.74 KiB) Viewed 14004 times

User avatar
Shaos
Admin
Posts: 25261
Joined: 09 Jan 2003 06:22
Location: Silicon Valley
Contact:

Re: Плата центрального недопроцессора nedoCPU-32

Post by Shaos »

Вот ещё немного экспериментов с недофонтом (как видно на большом ТВ картинка вылазит за пределы экрана по горизонтали):
Attachments

nedoPC font used on big TV
nedoPC font used on big TV
Photo0140.jpg (38.59 KiB) Viewed 14001 times

nedoPC font used again
nedoPC font used again
Photo0139.jpg (47.15 KiB) Viewed 14001 times

User avatar
Shaos
Admin
Posts: 25261
Joined: 09 Jan 2003 06:22
Location: Silicon Valley
Contact:

Re: Плата центрального недопроцессора nedoCPU-32

Post by Shaos »

Пересчитал частоту на 64 МГц и сдвинул кадр чуть вправо, оставив колбасню DMA без изменений - всё влезло:
Attachments

nedoPC-31
nedoPC-31
Photo0141.jpg (29.62 KiB) Viewed 14001 times

User avatar
Shaos
Admin
Posts: 25261
Joined: 09 Jan 2003 06:22
Location: Silicon Valley
Contact:

Re: Плата центрального недопроцессора nedoCPU-32

Post by Shaos »

Вот исходник под 64 МГц, из которого выкинуто всё лишнее, а также оптимизировано:

 исходник тут

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
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 *
 * SCK1 is pin 25
 * SDO1 is PPS group 2, map to RPA1 (pin 3)
 * SDI1 is PPS group 2, map to RPB8 (pin 17)
 *
 * VIDEO is PortA.1 
 * SYNC  is PortB.5
 *
 */

#include <plib.h>
#include <xc.h> // need for pps
#include <stdio.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

// The SPI channel
volatile SpiChannel spiChn = SPI_CHANNEL1 ;	// the SPI channel to use
volatile int spiClkDiv = 6 ;//5.333 MHz pixel rate => 256 points in 48 us

#define DX 256
#define DY 200

// main screen buffer array 256 wide x 200 high = 51200 pixels
// 51200/32 = 1600 integer words
int screen_buffer[1600] ;
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 us_5_cycles  160 // 5 uSec at 30 MHz Fpb, prescaler=1
#define us_11_cycles 352 // beginning of the video DMA burst -- use for centering

// 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 ;

#include "nedofont.h"

// == OC3 ISR ============================================
// VECTOR 14 is OC3 vector -- set up of ipl 3 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
}

// == 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++ ;

    // 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
        time_tick_60_hz++;        
    }
    // == 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 i,j = (x>>2)+(y<<6);
    int shf = (3-(x&3))<<3;
    for (i=0;i<7;i++) {
        screen_buffer[j]=(screen_buffer[j]&(~(255<<shf)))|(font8x8[c-32][i]<<shf);
        j += 8;
    }
}

//==================================
// 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 frame,seconds;
    unsigned long n;
    // global time
    int time ;
    char cu1[]="nedoPC-32-A v1.0";
    char cu2[]="March 2015";
    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
    // at 40 MHz PB clock 63.5 microSec is
    // 2540 ticks
    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 RPB5 (pin 14)
    PPSOutput(2, RPB5, 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 , us_11_cycles+2, us_11_cycles); //
    // 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

    // 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_1 | BIT_5);
    mPORTBSetBits(BIT_0);

    // divide Fpb by spiClkDiv, configure the I/O ports.
    // 32 bit transfer
    SpiChnOpen(spiChn, SPI_OPEN_ON | SPI_OPEN_MODE32 | SPI_OPEN_MSTEN , spiClkDiv ) ;

    //=== DMA Channel 1 ================================
    // Open DMA Chan1 and chain from channel zero
    DmaChnOpen(DMAchn1, DMApri0, DMA_OPEN_DEFAULT);

    // setup system wide interrupts  ///
    INTEnableSystemMultiVectoredInt();

    // 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,cu1);
    video_string(21,1,cu2);
   
    frame = seconds = 0;
	while(1)
	{
            n = 0;
            // wait until the next 1/60 second
            while(time == time_tick_60_hz){n++;}
            // use ISR time-tick to update time stamp
            mPORTBSetBits(BIT_1); // for profiling execution time
            time = time_tick_60_hz ;
            if(++frame==60)
            {
                frame = 0;
                sprintf(time_string, "%d:%u ",++seconds,n);
                video_string(1, 23, time_string);
            }
            mPORTBClearBits(BIT_1); // for profiling execution time
 	}

}

P.S. В пределах одного кадра (60 раз в секунду) цикл while(time == time_tick_60_hz){n++;} выполняется 110936 раз - не густо...
Attachments

nedoPC-32-A тест while
nedoPC-32-A тест while
Photo0142.jpg (68.87 KiB) Viewed 13990 times

User avatar
Shaos
Admin
Posts: 25261
Joined: 09 Jan 2003 06:22
Location: Silicon Valley
Contact:

Re: Плата центрального недопроцессора nedoCPU-32

Post by Shaos »

Вот ещё более оптимизированная программка (подсчёт секунд выведен в прерывание и развёрнут цикл печати символа) - в пределах кадра (1/60 секунды) успевает вывестись 2670 случайных символов в верхние 16 строк (а всего одновременно на экран влезают 32*25=800 символов):

 исходник тут

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
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 *
 * SCK1 is pin 25
 * SDO1 is PPS group 2, map to RPA1 (pin 3)
 * SDI1 is PPS group 2, map to RPB8 (pin 17)
 *
 * VIDEO is PortA.1 
 * SYNC  is PortB.5
 *
 */

#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

// The SPI channel
volatile SpiChannel spiChn = SPI_CHANNEL1 ;	// the SPI channel to use
volatile int spiClkDiv = 6 ;//5.333 MHz pixel rate => 256 points in 48 us

#define DX 256
#define DY 200

// main screen buffer array 256 wide x 200 high = 51200 pixels
// 51200/32 = 1600 integer words
int screen_buffer[1600] ;
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 us_5_cycles  160 // 5 uSec at 30 MHz Fpb, prescaler=1
#define us_11_cycles 352 // beginning of the video DMA burst -- use for centering

// 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"

// == OC3 ISR ============================================
// VECTOR 14 is OC3 vector -- set up of ipl 3 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
}

// == 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++ ;

    // 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)
{
    unsigned long n, stored;
    // global time
    int time, seconds, k, c;
    char cu1[]="nedoPC-32-A v1.0";
    char cu2[]="March 2015";
    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
    // at 40 MHz PB clock 63.5 microSec is
    // 2540 ticks
    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 RPB5 (pin 14)
    PPSOutput(2, RPB5, 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 , us_11_cycles+2, us_11_cycles); //
    // 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

    // 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_1 | BIT_5);
    mPORTBSetBits(BIT_0);

    // divide Fpb by spiClkDiv, configure the I/O ports.
    // 32 bit transfer
    SpiChnOpen(spiChn, SPI_OPEN_ON | SPI_OPEN_MODE32 | SPI_OPEN_MSTEN , spiClkDiv ) ;

    //=== DMA Channel 1 ================================
    // Open DMA Chan1 and chain from channel zero
    DmaChnOpen(DMAchn1, DMApri0, DMA_OPEN_DEFAULT);

    // setup system wide interrupts  ///
    INTEnableSystemMultiVectoredInt();

    // 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,cu1);
    video_string(21,1,cu2);

    seconds = stored = n = 0;
    c = 32;
    srand(11111);
	while(1)
	{       
            n++;
            k = rand();
            video_putchar(k&31,(k>>5)&15,++c);
            if(c==255) c=31;
            // wait until the next 1/60 second
            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:%u ",seconds,stored);
                video_string(1, 23, time_string);
            }
 	}

}

Attachments

nedoCPU-32-A putchar test
nedoCPU-32-A putchar test
Photo0143.jpg (71.83 KiB) Viewed 13988 times

User avatar
Stan
Banned
Posts: 397
Joined: 04 Jan 2013 17:09
Location: 95.24.178.158

Re: Плата центрального недопроцессора nedoCPU-32

Post by Stan »

Shaos wrote:
Stan wrote:Если точечная графика, то какой размер растра?
По вышеприведённому линку всё написано: http://hackaday.io/project/2032-pic32-oscilloscope

Точечная графика 256x200

Или мы по ангельски не разумеем?...
Трудно мне сказать, разумеете вы "по ангельски" или нет, но короткого ответа по-русски: "Точечная графика 256x200" мне вполне достаточно. :idea:
На прочие "мовы", можете не трудиться переводить. :dj:
User avatar
Shaos
Admin
Posts: 25261
Joined: 09 Jan 2003 06:22
Location: Silicon Valley
Contact:

Re: Плата центрального недопроцессора nedoCPU-32

Post by Shaos »

А вот рисование случайных линий - порядка 100 линий за кадр получается (а если закомментить линии и раскомментить пикселы, то будет около 7000 случайных пикселов за кадр):

 исходник тут

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
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 *
 * SCK1 is pin 25
 * SDO1 is PPS group 2, map to RPA1 (pin 3)
 * SDI1 is PPS group 2, map to RPB8 (pin 17)
 *
 * VIDEO is PortA.1 
 * SYNC  is PortB.5
 *
 */

#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

// The SPI channel
volatile SpiChannel spiChn = SPI_CHANNEL1 ;	// the SPI channel to use
volatile int spiClkDiv = 6 ;//5.333 MHz pixel rate => 256 points in 48 us

#define DX 256
#define DY 200

// main screen buffer array 256 wide x 200 high = 51200 pixels
// 51200/32 = 1600 integer words
int screen_buffer[1600] ;
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 us_5_cycles  160 // 5 uSec at 30 MHz Fpb, prescaler=1
#define us_11_cycles 352 // beginning of the video DMA burst -- use for centering

// 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"

// == OC3 ISR ============================================
// VECTOR 14 is OC3 vector -- set up of ipl 3 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
}

// == 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++ ;

    // 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)
{
    unsigned long n, stored;
    // global time
    int time, seconds, x, y, k, c;
    char cu1[]="nedoPC-32-A v1.0";
    char cu2[]="March 2015";
    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
    // at 40 MHz PB clock 63.5 microSec is
    // 2540 ticks
    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 RPB5 (pin 14)
    PPSOutput(2, RPB5, 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 , us_11_cycles+2, us_11_cycles); //
    // 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

    // 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_1 | BIT_5);
    mPORTBSetBits(BIT_0);

    // divide Fpb by spiClkDiv, configure the I/O ports.
    // 32 bit transfer
    SpiChnOpen(spiChn, SPI_OPEN_ON | SPI_OPEN_MODE32 | SPI_OPEN_MSTEN , spiClkDiv ) ;

    //=== DMA Channel 1 ================================
    // Open DMA Chan1 and chain from channel zero
    DmaChnOpen(DMAchn1, DMApri0, DMA_OPEN_DEFAULT);

    // setup system wide interrupts  ///
    INTEnableSystemMultiVectoredInt();

    // 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,cu1);
    video_string(21,1,cu2);

    seconds = stored = n = x = y = 0;
    c = 32;
    srand(11111);
	while(1)
	{       
            n++;
            k = rand();
#if 0
            // random characters
            video_putchar(k&31,2+((k>>5)&15),++c);
            if(c==255) c=31;
#else
            // random lines
            c = k&255; k=16+((k>>8)%168);
            video_line(x,y,c,k,2);
            x = c; y = k;
            // random pixels
//            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:%u ",seconds,stored);
                video_string(1, 23, time_string);
            }
 	}

}

Attachments

nedoPC-32-A random pixels test
nedoPC-32-A random pixels test
Photo0145.jpg (66.15 KiB) Viewed 13987 times

nedoPC-32-A random lines test
nedoPC-32-A random lines test
Photo0144.jpg (64.63 KiB) Viewed 13987 times

User avatar
Shaos
Admin
Posts: 25261
Joined: 09 Jan 2003 06:22
Location: Silicon Valley
Contact:

Re: Плата центрального недопроцессора nedoCPU-32

Post by Shaos »

Ну и наконец показывание вкомпилированных в код XBM-картинок :)

P.S. Это моя старшая дочка в Magic Kingdom (Disney World)

P.P.S. 19575 - это количество вызовов rand(), умещающихся в один кадр (1/60 секунды)

 исходник тут

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
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 *
 * SCK1 is pin 25
 * SDO1 is PPS group 2, map to RPA1 (pin 3)
 * SDI1 is PPS group 2, map to RPB8 (pin 17)
 *
 * VIDEO is PortA.1 
 * SYNC  is PortB.5
 *
 */

#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

// The SPI channel
volatile SpiChannel spiChn = SPI_CHANNEL1 ;	// the SPI channel to use
volatile int spiClkDiv = 6 ;//5.333 MHz pixel rate => 256 points in 48 us

#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 us_5_cycles  160 // 5 uSec at 30 MHz Fpb, prescaler=1
#define us_11_cycles 352 // beginning of the video DMA burst -- use for centering

// 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 ipl 3 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
}

// == 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++ ;

    // 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
    // at 40 MHz PB clock 63.5 microSec is
    // 2540 ticks
    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 RPB5 (pin 14)
    PPSOutput(2, RPB5, 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 , us_11_cycles+2, us_11_cycles); //
    // 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

    // 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_1 | BIT_5);
    mPORTBSetBits(BIT_0);

    // divide Fpb by spiClkDiv, configure the I/O ports.
    // 32 bit transfer
    SpiChnOpen(spiChn, SPI_OPEN_ON | SPI_OPEN_MODE32 | SPI_OPEN_MSTEN , spiClkDiv ) ;

    //=== 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++;
            k = rand();
#if 0
            // random characters
            video_putchar(k&31,2+((k>>5)&15),++c);
            if(c==255) c=31;
#endif
#if 0
            // random lines
            c = k&255; k=16+((k>>8)%168);
            video_line(x,y,c,k,2);
            x = c; y = k;
#endif
#if 0
            // random pixels
            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:%u ",seconds,stored);
                video_string(1, 23, time_string);
            }
 	}

}

Attachments

nedoPC-32-A XBM-image test
nedoPC-32-A XBM-image test
Photo0146.jpg (96.78 KiB) Viewed 13983 times

User avatar
Shaos
Admin
Posts: 25261
Joined: 09 Jan 2003 06:22
Location: Silicon Valley
Contact:

Re: Плата центрального недопроцессора nedoCPU-32

Post by Shaos »

Ещё фоточка, выведенная через PIC32 (если в исходнике раскомментить dolphin_bits):
Attachments

nedoCPU-32 XBM-image test 2
nedoCPU-32 XBM-image test 2
Photo0147.jpg (125.76 KiB) Viewed 13982 times

User avatar
Shaos
Admin
Posts: 25261
Joined: 09 Jan 2003 06:22
Location: Silicon Valley
Contact:

Re: Плата центрального недопроцессора nedoCPU-32

Post by Shaos »

Если верить автору, то такой способ работы с графикой отнимает только 8% процессорного времени, а это значит, что при наличии языка Си, 64МГц 32-битного проца, 256Кб флеша и 64Кб ОЗУ, можно много прикольных штук понаделать, не смотря на ограничения графики - ну что, делаем чёрно-белую игровую приставку? :idea:

P.S. Кстати я тут прикинул, что несложно будет цвет снаружи добавить как опциональную первую палитру CGA с её олдскульными 4 цветами (black, cyan, magenta, white), правда разрешение тогда станет не 256x200, а 128x200...

P.P.S. Хотя судя по всему код в лёгкую перенастраивается на более длинные строки, однако при этом проц будет жраться интенсивнее...

P.P.P.S. Ради эксперимента перенастроил на 640 пикселов в строке (и соответственно на более частую работу DMA) - ухудшений в производительности главного цикла не заметил...
User avatar
Shaos
Admin
Posts: 25261
Joined: 09 Jan 2003 06:22
Location: Silicon Valley
Contact:

Re: Плата центрального недопроцессора nedoCPU-32

Post by Shaos »

Можно примерно подсчитать стоимость минимального игрового компьютера на PIC32, если бы пользователь собирал его сам на бредборде (по ценам DigiKey):

IC1: PIC32MX170F256B-I/SP - $4.49
LED1: красный светодиод 5 мм - $0.15
C1,C2: ненужны, если питаемся от батареек
C3,C4,C6: 0.1uF (ceramic) - 3*$0.24 = $0.72
C5: 10uF (ceramic) - $0.49
C7: ненужен, если питаемся от батареек
C8,C9: ненужны
R1: 10 Ohm - $0.10
R2: 10 kOhm - $0.10
R3,R4: 470 Ohm - 2*$0.10 = $0.20
X5: хедер для PICkit3 - примерно $0.20
S1-S7: кнопка ресета и игровые кнопки - 7*$0.12 = $0.72
Резисторы для кнопок - 6*$0.10 = $0.60
RCA разъёмы (звук и видео) - 2*$1 = $2 (примерно)
Резисторы для звука и видео - 5*$0.10 = $0.50
Держатель для двух пальчиковых батареек - $1 (плюс-минус)
Две пальчиковые батарейки - 2*$1 = $2 (примерно)

Всего $13.27 плюс бредборда и проводочки (ну скажем ещё $5) - по моему очень неплохо...
User avatar
Shaos
Admin
Posts: 25261
Joined: 09 Jan 2003 06:22
Location: Silicon Valley
Contact:

Re: Плата центрального недопроцессора nedoCPU-32

Post by Shaos »

Shaos wrote:P.S. Кстати я тут прикинул, что несложно будет цвет снаружи добавить как опциональную первую палитру CGA с её олдскульными 4 цветами (black, cyan, magenta, white), правда разрешение тогда станет не 256x200, а 128x200...
Суть в том, что пикселы выдаются наружу через SPI, у которого есть клок на отдельной ноге (проверил осциллографом, что действительно есть, причём только когда выводятся пикселы изображения), т.е. мы можем сохранять чётный пиксел, чтобы потом с нечётным сформировать 2-битный цвет с 2 раза меньшим горизонтальным разрешением, например на палке-галке...

P.S. Переставил синхру с PortB.5 на PortB.1, освободив тем самым 8 контактов подряд для кнопок - от PortB.4 до PortB.11

P.P.S. А на PortB.2 и PortB.3 можно выдавать 2-битный звук с частотой дискретизации 15734.2 Гц (частота строчной развёртки NTSC)...

 исходник тут

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 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 ipl 3 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
}

// == 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++ ;

    // 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
    // at 40 MHz PB clock 63.5 microSec is
    // 2540 ticks
    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, 354, 352); // 354,352
    // 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

    // 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);
            }
    }

}

User avatar
Shaos
Admin
Posts: 25261
Joined: 09 Jan 2003 06:22
Location: Silicon Valley
Contact:

Re: Плата центрального недопроцессора nedoCPU-32

Post by Shaos »

С другой стороны зачем 2-битный звук, если можно сделать ШИМ на блоке Compare, которых ещё осталось?...

P.S. Подцепил 2 Compare - один будет ШИМ на звук, а второй - на подсветку отдельных строк ;)

 исходник тут

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);
            }
    }

}

Attachments

more compares
more compares
Photo0148.jpg (103.12 KiB) Viewed 13933 times

Post Reply