Yahoo Groups archive

AVR-Chat

Index last updated: 2026-04-28 22:41 UTC

Thread

Help with bit banged spi

Help with bit banged spi

2013-11-26 by Philippe Habib

I'm needing to do a bit banged SPI master using an 8051 variant from Silabs and I don't think I'm doing the best thing I could be to get a bit out the port.  I don't think I'm doing the best job to figure out if a bit is high or low, then send it out the port.  I'd appreciate it if someone could suggest a faster way.

I can wiggle the clock at 12.5Mhz, but it takes me 1.1us to do the shift/mask/set to get the next bit of data out, which limits my SPI rate to less than 1 MHz.  I'd like to do 10x that, but I'd settle for 5x.

Here is my code:

	for(j=0;j<Num;j++)
	{
    	for(i=0;i<8;i++)
		{
			Tmp = (((SPIOutBuf[j]<<i) & 0x80)>>7);
			//Tmp = ((SPIOutBuf[j]>>i) & 0x01);
			if(Tmp == 1)
				P4 |= Tmp;	
			else
				P4 &= 0;
			SPICLK = 1;
			SPICLK = 0;
		}
	}


Thank you.

Re: [AVR-Chat] Help with bit banged spi

2013-11-26 by Philippe Habib

I got it to 1.1MHz from 900k by doing this instead.

	for(j=0;j<Num;j++)
	{
    	for(i=0;i<8;i++)
		{
			if(SPIOutBuf[j] & 0x80)
			   P4 |= 1;
 			else
			   P4 &= 0;
			SPIOutBuf[j] <<=1;
			SPICLK = 1;
			SPICLK = 0;
		}
	}

Does anyone see any big improvement I've missed?
Show quoted textHide quoted text
----- Original Message -----
From: "Philippe Habib" <phabib@well.com>
To: avr-chat@yahoogroups.com
Sent: Monday, November 25, 2013 9:48:31 PM
Subject: [AVR-Chat] Help with bit banged spi

I'm needing to do a bit banged SPI master using an 8051 variant from Silabs and I don't think I'm doing the best thing I could be to get a bit out the port.  I don't think I'm doing the best job to figure out if a bit is high or low, then send it out the port.  I'd appreciate it if someone could suggest a faster way.

I can wiggle the clock at 12.5Mhz, but it takes me 1.1us to do the shift/mask/set to get the next bit of data out, which limits my SPI rate to less than 1 MHz.  I'd like to do 10x that, but I'd settle for 5x.

Here is my code:

	for(j=0;j<Num;j++)
	{
    	for(i=0;i<8;i++)
		{
			Tmp = (((SPIOutBuf[j]<<i) & 0x80)>>7);
			//Tmp = ((SPIOutBuf[j]>>i) & 0x01);
			if(Tmp == 1)
				P4 |= Tmp;	
			else
				P4 &= 0;
			SPICLK = 1;
			SPICLK = 0;
		}
	}


Thank you.


------------------------------------

Yahoo Groups Links

Re: [AVR-Chat] Help with bit banged spi

2013-11-26 by Cagri Tanriover

Hi Philippe,

Can you try the following and see if it gives you a bit more speed?

j = 0;
while( j < Num ) 
{
     i = 0;    
    while( i < 8 )
     {

         P4 = ( *(SPIOutBuf + j) & 0x80 ? P4 |1 : 0 );
         *(SPIOutBuf + j ) <<=1;
         SPICLK = 1;
         SPICLK = 0;

        ++i;
    }

    ++j;
}


If the above gives a bit more speed gain, there is one more trick you can apply in the inner while loop. Simply get rid of the loop and write the process 8 times. That will cost you a bit more in terms of code but because the loop overhead will be eliminated, you should see some significant speed gains.

Let me know how it goes. Good luck!


Regards,
Cagri




________________________________
Show quoted textHide quoted text
 From: Philippe Habib <phabib@well.com>
To: AVR-Chat@yahoogroups.com 
Cc: avr-chat@yahoogroups.com 
Sent: Tuesday, 26 November 2013, 8:02
Subject: Re: [AVR-Chat] Help with bit banged spi
 


  
I got it to 1.1MHz from 900k by doing this instead.

for(j=0;j<Num;j++)
{
for(i=0;i<8;i++)
{
if(SPIOutBuf[j] & 0x80)
P4 |= 1;
else
P4 &= 0;
SPIOutBuf[j] <<=1;
SPICLK = 1;
SPICLK = 0;
}
}

Does anyone see any big improvement I've missed?

----- Original Message -----
From: "Philippe Habib" <phabib@well.com>
To: avr-chat@yahoogroups.com
Sent: Monday, November 25, 2013 9:48:31 PM
Subject: [AVR-Chat] Help with bit banged spi

I'm needing to do a bit banged SPI master using an 8051 variant from Silabs and I don't think I'm doing the best thing I could be to get a bit out the port.  I don't think I'm doing the best job to figure out if a bit is high or low, then send it out the port.  I'd appreciate it if someone could suggest a faster way.

I can wiggle the clock at 12.5Mhz, but it takes me 1.1us to do the shift/mask/set to get the next bit of data out, which limits my SPI rate to less than 1 MHz.  I'd like to do 10x that, but I'd settle for 5x.

Here is my code:

for(j=0;j<Num;j++)
{
for(i=0;i<8;i++)
{
Tmp = (((SPIOutBuf[j]<<i) & 0x80)>>7);
//Tmp = ((SPIOutBuf[j]>>i) & 0x01);
if(Tmp == 1)
P4 |= Tmp; 
else
P4 &= 0;
SPICLK = 1;
SPICLK = 0;
}
}

Thank you.

------------------------------------

Yahoo Groups Links

Re: [AVR-Chat] Help with bit banged spi

2013-11-26 by David Kelly

On Nov 26, 2013, at 4:46 AM, Cagri Tanriover <dr_cagri_tanriover@yahoo.co.uk> wrote:

>          P4 = ( *(SPIOutBuf + j) & 0x80 ? P4 | 1 : 0 );

I suggest looking at the compiler output listing in assembly language.

Philippe is using an 8051 variant so I doubt he is using gcc, but in my observation with optimization enabled gcc does better with SPIOutBuf[j] than with *(SPIOutBuf + j). Other compilers appreciate having the pointers broken out manually, namely classic 68k compilers under Unix.

8051's have single-bit types and perhaps P4 has been defined as an output bit. If so AND-ing and OR-ing is useless. Hopefully the compiler optimized them out.

While we are at it this doesn't look right: P4 &= 0; That clears all bits in P4. How about P4 &= ~1; which is 0b1111 1110 and only clears the zeroth bit and is exactly as fast at run time? Of course that only matters if P4 is larger than 1 bit wide. But if P4 is a bit variable then use P4 = 1; and P4 = 0;

I think it might be best to unroll the bit shifting. This also has the possible advantage of not destroying the original buffer:

for( j=0 ; j<Num ; j++ ) {
        Tmp = SPIOutBuf[j];

        if( Tmp & (1<<7) )
            P4 |= 1;	
        else
            P4 &= ~1;
        SPICLK = 1;
        PICLK = 0;

        if( Tmp & (1<<6) )
            P4 |= 1;	
        else
            P4 &= ~1;
        SPICLK = 1;
        PICLK = 0;

        if( Tmp & (1<<5) )
            P4 |= 1;	
        else
            P4 &= ~1;
        SPICLK = 1;
        PICLK = 0;

        if( Tmp & (1<<4) )
            P4 |= 1;	
        else
            P4 &= ~1;
        SPICLK = 1;
        PICLK = 0;

        if( Tmp & (1<<3) )
            P4 |= 1;	
        else
            P4 &= ~1;
        SPICLK = 1;
        PICLK = 0;

        if( Tmp & (1<<2) )
            P4 |= 1;	
        else
            P4 &= ~1;
        SPICLK = 1;
        PICLK = 0;

        if( Tmp & (1<<1) )
            P4 |= 1;	
        else
            P4 &= ~1;
        SPICLK = 1;
        PICLK = 0;

        if( Tmp & (1<<0) )
            P4 |= 1;	
        else
            P4 &= ~1;
        SPICLK = 1;
        PICLK = 0;
}

--
David Kelly N4HHE, dkelly@HiWAAY.net
============================================================
Whom computers would destroy, they must first drive mad.

RE: [AVR-Chat] Help with bit banged spi

2013-11-26 by Larry Viesse

I agree with David on the points he raised.  It would be very helpful if
Philippe could give us a little more detail as to how he has implemented his
bit banged SPI port.  Knowing what P4 and SPIOutBuf are used for and how
they are defined.

 

I once used this code to add an SPI port to an 8051
http://www.maximintegrated.com/app-notes/index.mvp/id/3524 .  Read the
application note carefully, especially the last sentence in the first
paragraph following the abstract.  It has been a while, but I believe that
it was quite fast.

 

Larry Viesse W7PAN
Show quoted textHide quoted text
From: AVR-Chat@yahoogroups.com [mailto:AVR-Chat@yahoogroups.com] On Behalf
Of David Kelly
Sent: Tuesday, November 26, 2013 7:32 AM
To: AVR-Chat@yahoogroups.com
Subject: Re: [AVR-Chat] Help with bit banged spi

 

  


On Nov 26, 2013, at 4:46 AM, Cagri Tanriover <dr_cagri_tanriover@yahoo.co.uk
<mailto:dr_cagri_tanriover@yahoo.co.uk> > wrote:

> P4 = ( *(SPIOutBuf + j) & 0x80 ? P4 | 1 : 0 );

I suggest looking at the compiler output listing in assembly language.

Philippe is using an 8051 variant so I doubt he is using gcc, but in my
observation with optimization enabled gcc does better with SPIOutBuf[j] than
with *(SPIOutBuf + j). Other compilers appreciate having the pointers broken
out manually, namely classic 68k compilers under Unix.

8051's have single-bit types and perhaps P4 has been defined as an output
bit. If so AND-ing and OR-ing is useless. Hopefully the compiler optimized
them out.

While we are at it this doesn't look right: P4 &= 0; That clears all bits in
P4. How about P4 &= ~1; which is 0b1111 1110 and only clears the zeroth bit
and is exactly as fast at run time? Of course that only matters if P4 is
larger than 1 bit wide. But if P4 is a bit variable then use P4 = 1; and P4
= 0;

I think it might be best to unroll the bit shifting. This also has the
possible advantage of not destroying the original buffer:

for( j=0 ; j<Num ; j++ ) {
Tmp = SPIOutBuf[j];

if( Tmp & (1<<7) )
P4 |= 1; 
else
P4 &= ~1;
SPICLK = 1;
PICLK = 0;

if( Tmp & (1<<6) )
P4 |= 1; 
else
P4 &= ~1;
SPICLK = 1;
PICLK = 0;

if( Tmp & (1<<5) )
P4 |= 1; 
else
P4 &= ~1;
SPICLK = 1;
PICLK = 0;

if( Tmp & (1<<4) )
P4 |= 1; 
else
P4 &= ~1;
SPICLK = 1;
PICLK = 0;

if( Tmp & (1<<3) )
P4 |= 1; 
else
P4 &= ~1;
SPICLK = 1;
PICLK = 0;

if( Tmp & (1<<2) )
P4 |= 1; 
else
P4 &= ~1;
SPICLK = 1;
PICLK = 0;

if( Tmp & (1<<1) )
P4 |= 1; 
else
P4 &= ~1;
SPICLK = 1;
PICLK = 0;

if( Tmp & (1<<0) )
P4 |= 1; 
else
P4 &= ~1;
SPICLK = 1;
PICLK = 0;
}

--
David Kelly N4HHE, dkelly@HiWAAY.net <mailto:dkelly@HiWAAY.net> 
============================================================
Whom computers would destroy, they must first drive mad.

Re: [AVR-Chat] Help with bit banged spi

2013-11-26 by David Kelly

On Nov 26, 2013, at 1:04 PM, Larry Viesse <larryvc@hotmail.com> wrote:

> I once used this code to add an SPI port to an 8051 http://www.maximintegrated.com/app-notes/index.mvp/id/3524 .  Read the application note carefully, especially the last sentence in the first paragraph following the abstract.  It has been a while, but I believe that it was quite fast.

Thats a cool bit-banged SPI written by someone who knows 8051 C. Elegantly handles CPOL and CPHA, and is read/write just the way "real" SPI operates.

I'll pat myself on the back in observation Maxim unrolled the loop pretty much the same as I did, only they dressed it up nicer with macros.

--
David Kelly N4HHE, dkelly@HiWAAY.net
============================================================
Whom computers would destroy, they must first drive mad.

Re: [AVR-Chat] Help with bit banged spi

2013-11-27 by dspic@cat12.com

On Mon, 25 Nov 2013 22:02:36 -0800 (PST), Philippe Habib
<phabib@well.com> wrote:

>			if(SPIOutBuf[j] & 0x80)
>			   P4 |= 1;
> 			else
>			   P4 &= 0;

Perhaps prefer
			   P4 &= 0xFE;

Re: [AVR-Chat] Help with bit banged spi

2013-11-27 by bobgardner@aol.com

//---external mc3204 12 bit spi a/d converter
#define CLKHI() PORTB |=  0x80
#define CLKLO() PORTB &= ~0x80
#define CLKPULSE() CLKHI(); CLKLO()

#define DATHI() PORTB |=  0x40
#define DATLO() PORTB &= ~0x40

#define STARTBIT() DATHI(); CLKPULSE()
#define  STOPBIT() DATLO(); CLKPULSE()

#define DDRB6OUT() DDRB |=  0x40
#define DDRB6IN()  DDRB &= ~0x40

#define CSHI() PORTB |=  0x10
#define CSLO() PORTB &= ~0x10

//-----------------------------------------
void bbout8(unsigned char dat){
//shift 8 bits of dat out pb6 lsb 1st
//clk=pb7 dat=pb6 cs=pb4
//seems to be about half as fast as the double speed spi version
unsigned char i,msk,bit;

  DDRB6OUT(); 
	STARTBIT();            
	msk=0x01; 
	for(i=0; i<8; i++){              //for n bits
  bit=(dat & msk) != 0;          //extract bit from dat
		  if(bit) DATHI(); else DATLO(); //put it in the port
		  CLKPULSE();                    //shake the clock
		  msk <<= 1;                     //ready for next bit
	}
	STOPBIT();
}

Re: [AVR-Chat] Help with bit banged spi

2013-11-28 by David Kelly

On Nov 26, 2013, at 8:23 PM, dspic@cat12.com wrote:

> On Mon, 25 Nov 2013 22:02:36 -0800 (PST), Philippe Habib
> <phabib@well.com> wrote:
> 
>> 			if(SPIOutBuf[j] & 0x80)
>> 			   P4 |= 1;
>> 			else
>> 			   P4 &= 0;
> 
> Perhaps prefer
> 			   P4 &= 0xFE;

I prefer to write it:  P4 &= ~1; or P4 &= ~(1<<0); to better indicate the bit position I am clearing.

--
David Kelly N4HHE, dkelly@HiWAAY.net
============================================================
Whom computers would destroy, they must first drive mad.



[Non-text portions of this message have been removed]

Re: [AVR-Chat] Help with bit banged spi

2013-12-09 by Philippe Habib

First I want to offer a late thank you for everyone who took the time to help me out.  Even though this wasn't an AVR problem, I knew that this group's great experience and willingness to help would be what I needed.

Because of shifting project priorities and a death in the family, I wasn't able to get back to the messages or to my project for a few days.

David caught my bug where I was clearing out the entire register instead of only the bit I was interested in.  He was also right about single bit types.  It happened that my particular part has 4 ports, and port 1-3 are bit selectable, while port 4 is not so I had to do the ANDing and ORing for that port.

In the end, the hardware I was controlling was a much larger contributor to the overall time consumed from command to completion that optimizing the SPI to pick up a microsecond or two didn't turn out to be worthwhile.

I got all of the firmware features done in plenty of time and the HW designer now has a whole week to tune his stuff prior to delivery so we can all live happily ever after.

Thanks again for the assistance, this is the the best group I'm on.
Show quoted textHide quoted text
----- Original Message -----
From: "David Kelly" <dkelly@hiwaay.net>
To: AVR-Chat@yahoogroups.com
Sent: Tuesday, November 26, 2013 7:32:10 AM
Subject: Re: [AVR-Chat] Help with bit banged spi


On Nov 26, 2013, at 4:46 AM, Cagri Tanriover <dr_cagri_tanriover@yahoo.co.uk> wrote:

>          P4 = ( *(SPIOutBuf + j) & 0x80 ? P4 | 1 : 0 );

I suggest looking at the compiler output listing in assembly language.

Philippe is using an 8051 variant so I doubt he is using gcc, but in my observation with optimization enabled gcc does better with SPIOutBuf[j] than with *(SPIOutBuf + j). Other compilers appreciate having the pointers broken out manually, namely classic 68k compilers under Unix.

8051's have single-bit types and perhaps P4 has been defined as an output bit. If so AND-ing and OR-ing is useless. Hopefully the compiler optimized them out.

While we are at it this doesn't look right: P4 &= 0; That clears all bits in P4. How about P4 &= ~1; which is 0b1111 1110 and only clears the zeroth bit and is exactly as fast at run time? Of course that only matters if P4 is larger than 1 bit wide. But if P4 is a bit variable then use P4 = 1; and P4 = 0;

I think it might be best to unroll the bit shifting. This also has the possible advantage of not destroying the original buffer:

for( j=0 ; j<Num ; j++ ) {
        Tmp = SPIOutBuf[j];

        if( Tmp & (1<<7) )
            P4 |= 1;	
        else
            P4 &= ~1;
        SPICLK = 1;
        PICLK = 0;

        if( Tmp & (1<<6) )
            P4 |= 1;	
        else
            P4 &= ~1;
        SPICLK = 1;
        PICLK = 0;

        if( Tmp & (1<<5) )
            P4 |= 1;	
        else
            P4 &= ~1;
        SPICLK = 1;
        PICLK = 0;

        if( Tmp & (1<<4) )
            P4 |= 1;	
        else
            P4 &= ~1;
        SPICLK = 1;
        PICLK = 0;

        if( Tmp & (1<<3) )
            P4 |= 1;	
        else
            P4 &= ~1;
        SPICLK = 1;
        PICLK = 0;

        if( Tmp & (1<<2) )
            P4 |= 1;	
        else
            P4 &= ~1;
        SPICLK = 1;
        PICLK = 0;

        if( Tmp & (1<<1) )
            P4 |= 1;	
        else
            P4 &= ~1;
        SPICLK = 1;
        PICLK = 0;

        if( Tmp & (1<<0) )
            P4 |= 1;	
        else
            P4 &= ~1;
        SPICLK = 1;
        PICLK = 0;
}

--
David Kelly N4HHE, dkelly@HiWAAY.net
============================================================
Whom computers would destroy, they must first drive mad.



------------------------------------

Yahoo Groups Links

Move to quarantaine

This moves the raw source file on disk only. The archive index is not changed automatically, so you still need to run a manual refresh afterward.