Hardware ISP speed compared to direct port manipulation?

Hi there!

I’m building a pinball machine and am currently designing the display system.

You can follow the progress on my blog at http://poormanspinball.blogspot.com, if you’d like. :slight_smile:

Now for my question(s):

Using a Arduino Mega2560, my current revision is using direct port manipulation to send the bits to the system at about 170 us per 128 bit row.

For a total of 32 rows, it summons up to around 5.5 ms for a full frame (4096 bit), which becomes increasingly slow when more colors (frames) are being added.

I can probably shave of a few microseconds by unrolling the three nested for-loops.

Can I reach even higher values with SPI?

Is it faster than direct port manipulation? If so - how?

The commonly described " while(!(SPSR & (I << SPIF)));" , must it be used? Or can I simply push 128 bit data and hope it arrives?

The reason I’m simply not testing away is that there’s a lot of stuff to rewire and reconfigure for SPI to be used, so I’d take a chance to check with you guys first. :slight_smile:

The data is stored in a “byte frame[32][128]” variable which must be compared during runtime to see which shade it has. I.e a simple:

if (frame[y][x] > CURRENT_COLOR_LAYER) writePin(COLUMN_DATA, HIGH); else writePin(COLUMN_DATA, LOW);

Can this be speeded up in any way?

I figure that it should be possible (somehow) to get the performance up to around 56 us based on the “Staggering SPI” document. But how?

Thanks for your time, looking forward to some input on this matter! :slight_smile:

Using the SPI hardware will be a lot faster, see the data sheet.

Hi there!

Thanks for your reply, but which datasheet are you referring to? [this?](Staggering spi performance for arduino | PPT)

The data sheet for the Mega2650.

I’ve written what I believe to be a reasonable SPI implementation for my needs. Let me know if you see anything that looks way off! :slight_smile:

... code was removed. See below for updated code.

Since there will be only 16 shades I figure I could also use the high/low 4bit of the byte to save memory.

Another question is : Do I have to disable the SPI begin/end after each row, or can I do the row pulsing etc with SPI still enabled?

Hi there!

I got a nice 8+1 color pinball DMD going now at approx. 52 frames/sec using the following code (forgive the formatting…) :

void writeDisplay()
{
	/*
		Write all shades in one go. (i.e around 2.3 ms per color)
	*/

	register byte x,y,lx,b;
    register byte *f;

	cli();						 //Disable interrupts 
	
	for (lx=1; lx < DISPLAY_MAX_BRIGHTNESS+1;lx++) //repeat for each color/shade
	{   
	    f = &frame[0][0];

		//row 1  -------------
		
		bitClear(PORTF, 2);          //set 4th bit of PORTF to LOW					          		
		for (x=0; x < 16; x++)   //send full 128 bit row using SPI
		{
			if ( *f++ >= lx) 	b  = B10000000; else b=0;
		 	if ( *f++ >= lx) 	b |= B01000000;
		 	if ( *f++ >= lx) 	b |= B00100000;
		 	if ( *f++ >= lx) 	b |= B00010000;
		 	if ( *f++ >= lx) 	b |= B00001000;
		 	if ( *f++ >= lx) 	b |= B00000100;
		 	if ( *f++ >= lx) 	b |= B00000010;
		 	if ( *f++ >= lx) 	b |= B00000001;
		 	
			SPI.transfer(b);
		}   
	    bitSet(PORTF, 2);            //set 4th bit of PORTF to HIGH		//Column latch
   	  	bitSet(PORTF, 4);          //set 5th bit of PORTF to HIGH (mark first row)   	
		bitClear(PORTF, 5);          //set 6th bit of PORTF to LOW					     
        bitClear(PORTF, 3);          //set 4th bit of PORTF to LOW	
       	bitSet(PORTF, 3);            //set 4th bit of PORTF to HIGH		       	
		bitSet(PORTF, 5);            //set 6th bit of PORTF to HIGH		
		 		
		 		
		 //row 2-31      ------------
		 for (y=0; y < DISPLAY_MAX_ROWS-2; y++)
		 {
		 	
		    bitClear(PORTF, 2);          //set 4th bit of PORTF to LOW					          	
			for (x=0; x < 16; x++)   //send full 128 bit row using SPI
			{
				if ( *f++ >= lx) 	b  = B10000000; else b=0;
			 	if ( *f++ >= lx) 	b |= B01000000;
			 	if ( *f++ >= lx) 	b |= B00100000;
			 	if ( *f++ >= lx) 	b |= B00010000;
			 	if ( *f++ >= lx) 	b |= B00001000;
			 	if ( *f++ >= lx) 	b |= B00000100;
			 	if ( *f++ >= lx) 	b |= B00000010;
			 	if ( *f++ >= lx) 	b |= B00000001;
			 	
				SPI.transfer(b);
			}   
		   	bitSet(PORTF, 2);            //set 4th bit of PORTF to HIGH		//Column latch
	   	  	bitClear(PORTF, 4);          //set 5th bit of PORTF to LOW (the rest of the rows are not leading)
	  		bitClear(PORTF, 5);          //set 6th bit of PORTF to LOW					   	  	
	        bitClear(PORTF, 3);          //set 4th bit of PORTF to LOW	
	       	bitSet(PORTF, 3);            //set 4th bit of PORTF to HIGH		
			bitSet(PORTF, 5);            //set 6th bit of PORTF to HIGH		
		 }
		 
		 
		//row 32	 ---------------
	    bitClear(PORTF, 2);          //set 4th bit of PORTF to LOW					          	
		for (x=0; x < 15; x++)   //send full 128 bit row using SPI
		{
			if ( *f++ >= lx) 	b  = B10000000; else b=0;
		 	if ( *f++ >= lx) 	b |= B01000000;
		 	if ( *f++ >= lx) 	b |= B00100000;
		 	if ( *f++ >= lx) 	b |= B00010000;
		 	if ( *f++ >= lx) 	b |= B00001000;
		 	if ( *f++ >= lx) 	b |= B00000100;
		 	if ( *f++ >= lx) 	b |= B00000010;
		 	if ( *f++ >= lx) 	b |= B00000001;		 	
			SPI.transfer(b);
		}   
		if ( *f++ >= lx) 	b  = B10000000; else b=0;
	 	if ( *f++ >= lx) 	b |= B01000000;
	 	if ( *f++ >= lx) 	b |= B00100000;
	 	if ( *f++ >= lx) 	b |= B00010000;
	 	if ( *f++ >= lx) 	b |= B00001000;
	 	if ( *f++ >= lx) 	b |= B00000100;
	 	if ( *f++ >= lx) 	b |= B00000010;
	 	if ( *f	  >= lx) 	b |= B00000001;		 	
		SPI.transfer(b);
	   	bitSet(PORTF, 2);            //set 4th bit of PORTF to HIGH		//Column latch
   	  	bitClear(PORTF, 4);          //set 5th bit of PORTF to LOW (the rest of the rows are not leading)
		bitClear(PORTF, 5);          //set 6th bit of PORTF to LOW					
        bitClear(PORTF, 3);          //set 4th bit of PORTF to LOW	
       	bitSet(PORTF, 3);            //set 4th bit of PORTF to HIGH		
		bitSet(PORTF, 5);            //set 6th bit of PORTF to HIGH		
	}	//End of color/shade
	
	sei(); 						//enable interrupts.

	//    delayMicroseconds(20);  //this should match the time it takes to update a full row to keep the last row from shining too brightly.
	bitClear(PORTF, 5);          //set 6th bit of PORTF to LOW		
}

This could probably be improved a lot, but for now I’m very satisfied. :slight_smile:

The main time thief is the “if ( *f++ >= lx)” part, the code runs at about 128 frames/sec with a fixed value instead of “b”.

Feel free to improve the code! :slight_smile: