SPI transmission errors

Go To Last Post
8 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi everyone,

Working with a project which involves 2 Landrovers. Landrover_1 gets instructions via a DTMF decoder and accordingly turns the appropriate motors. It Also transmits the DTMF data to Landrover_2 via SPI. on receiving this data Landrover_2 is required to turn the appropriate motors.

I am having a lot of difficulties with SPI, its the first time i have worked with SPI and im not really sure i am doing the right stuff. Let me explain the problem.

with all motors disconnected, SPI communications seems to be somewhat working. I still get errors with the data received. I can monitor the data received on landrover_2 by dumping the received SPI data on to the UART bus. But the moment motors are connected on Landrover_1 or/and landrover_2. Complete garbage is received on Landrover_2. I am using 4 wires for SPI communication(ie. MOSI, MISO, SLK and GND)

I have done the following to troubleshoot for errors

1) checked for voltage drops across the board when the motors are connected. - No problems found

2) added more decoupling capacitors across the board. - No effect

3) Reduced the SPI clock to 125Khz -> No changes noted.

Thanks Everyone

Rodney Almeida

Codes for Landrover1 (master) below:


#include  
#include  
#include  
#include  
#include 
#include  
#include 
#include 
#include 
#include 
#include 


void motor(char, char);


void spi_master_init(void) 
{   
  DDRB=0xBF;//spi pins (mosi, sck, ss) configure as output  and miso as input,B0-B3 output for motors.
  DDRD=0xFF;	// motor drivers also on port D
  SPCR = 0x73;//spe=1(spi enable),DORD=1(LSB first),mstr=1(master) fclock/128  
} 


void spi_master_transmit(unsigned char data) 
{  
  SPDR=data;					//place data on spi data register
  while((SPSR&0x80)==0);		//wait for the spi transmittion n check SPIF(bit 7) in SPI status register   
_delay_ms(500);
} 

int main(void) 
{ 
  
unsigned char A;
sei();


spi_master_init(); 
PORTB=0x00; 	
PORTD=0x00;
DDRA=0x00;//input from DTMF module

while(1)
	{
		//motor movements depending on the connection
		if((PINA & 0x0f)==1)//lift arms
		{			
			
			motor(1,0);
			motor(2,0);
			motor(3,1);
			A=1;
		}
		if((PINA & 0x0f)==2)//forward
		{			
			motor(1,2);
			motor(2,1);
			motor(3,0);
			A=2;
		}
		if((PINA & 0x0f)==3)//lower arms
		{			
			motor(1,0);
			motor(2,0);
			motor(3,2);
			A=3;
		}
		if((PINA & 0x0f)==4)//left
		{	
			motor(1,2);
			motor(2,2);
			motor(3,0);
			A=4;
		}
		if((PINA & 0x0f)==5)//stop all
		{
			motor(1,0);
			motor(2,0);
			motor(3,0);
			A=5;
		}		
		if((PINA & 0x0f)==6)//right
		{	
			motor(1,1);
			motor(2,1);
			motor(3,0);
			A=6;
		}
		if((PINA & 0x0f)==7)//forward and lift arms
		{	
			motor(1,2);
			motor(2,1);
			motor(3,1);
			A=7;
		}
		if((PINA & 0x0f)==8)//backward
		{	
			motor(1,1);
			motor(2,2);
			motor(3,0);			
			A=8;
		}
		if((PINA & 0x0f)==9)//backward and lower arms
		{
			motor(1,1);
			motor(2,2);
			motor(3,2);
			A=9;
		}
spi_master_transmit(A); 
_delay_ms(100);
}
}

void motor(char Motor_ID, char Function)	 //function -> if 1, motor turns left; if 2, motor turns right; if 0, motor off
											 //Motor_ID -> select Motor 1 thru 4
											 //e.g. motor(2,2); starts motor 2 turning right direction
{
if ((Motor_ID==1) && (Function==0)) 	{PORTB &= ~(1<<0); 	PORTB &= ~(1<<1);}
if ((Motor_ID==1) && (Function==1)) 	{PORTB |= (1<<0); 		PORTB &= ~(1<<1);}
if ((Motor_ID==1) && (Function==2)) 	{PORTB &= ~(1<<0); 	PORTB |= (1<<1); }

if ((Motor_ID==2) && (Function==0)) 	{PORTB &= ~(1<<2); 	PORTB &= ~(1<<3);}
if ((Motor_ID==2) && (Function==1))  	{PORTB |= (1<<2); 		PORTB &= ~(1<<3);}
if ((Motor_ID==2) && (Function==2)) 	{PORTB &= ~(1<<2); 	PORTB |= (1<<3); }

if ((Motor_ID==3) && (Function==0)) 	{PORTD &= ~(1<<4); 	PORTD &= ~(1<<5);}
if ((Motor_ID==3) && (Function==1)) 	{PORTD |= (1<<4); 		PORTD &= ~(1<<5);}
if ((Motor_ID==3) && (Function==2)) 	{PORTD &= ~(1<<4); 	PORTD |= (1<<5); }
	
if ((Motor_ID==4) && (Function==0)) 	{PORTD &= ~(1<<6); 	PORTD &= ~(1<<7);}
if ((Motor_ID==4) && (Function==1)) 	{PORTD |= (1<<6); 		PORTD &= ~(1<<7);}
if ((Motor_ID==4) && (Function==2)) 	{PORTD &= ~(1<<6); 	PORTD |= (1<<7); }
}

code for Landrover_2


#include  
#include  
#include  
#include  
#include 
#include  
#include 
#include 
#include 
#include 
#include 

void motor(char, char);


void spi_slave_init(void) 
{ 
  DDRD=0xFF;//output for motors
  PORTD=0x00;  
  DDRB = 0x4F;//sck,mosi,ss input,miso as output,B0-B3 output for motors
  PORTB=0x00;				
  //SPCR = 0x60;	//spe=1,DORD=1,MSTR=0			
} 

unsigned char spi_slave_receive(void) 
{ 
  while((SPSR&0x80)==0);//receives from the master till SPIF is set(bit 7 in SPI status register)
  return SPDR; //return SPI data register
}

int main(void)
{
	unsigned char D;
	sei();
	UART_RBUF_CLEAR();

	spi_slave_init();
	
	while(1)
	{
	D=spi_slave_receive();//slave value received
	UART_putc(0x30+D);
	_delay_ms(200);
	//motor movements based on connections
		if(D==1)//lift arms
		{			
			motor(1,0);
			motor(2,0);
			motor(3,1);
	}
		if(D==2)//forward
		{			
			motor(1,2);
			motor(2,1);
			motor(3,0);
		}
		if(D==3)//lower arms
		{			
			motor(1,0);
			motor(2,0);
			motor(3,2);
			
		}
		if(D==4)//left
		{			
			motor(1,2);
			motor(2,2);
			motor(3,0);
		}
		if(D==5)//stop all
		{
			motor(1,0);
			motor(2,0);
			motor(3,0);
			}		
		if(D==6)//right
		{			
			motor(1,1);
			motor(2,1);
			motor(3,0);
		}
		if(D==7)//forward and lift arms
		{			
			motor(1,2);
			motor(2,1);
			motor(3,1);
		}
		if(D==8)//backward
		{
			motor(1,1);
			motor(2,2);
			motor(3,0);
		}
		if(D==9)//backward and lower arms
		{
		motor(1,1);
			motor(2,2);
			motor(3,2);
		}
		

	}


}


void motor(char Motor_ID, char Function)	 //function -> if 1, motor turns left; if 2, motor turns right; if 0, motor off
											 //Motor_ID -> select Motor 1 thru 4
											 //e.g. motor(2,2); starts motor 2 turning right direction
{
if ((Motor_ID==1) && (Function==0)) 	{PORTB &= ~(1<<0); 	PORTB &= ~(1<<1);}
if ((Motor_ID==1) && (Function==1)) 	{PORTB |= (1<<0); 		PORTB &= ~(1<<1);}
if ((Motor_ID==1) && (Function==2)) 	{PORTB &= ~(1<<0); 	PORTB |= (1<<1); }

if ((Motor_ID==2) && (Function==0)) 	{PORTB &= ~(1<<2); 	PORTB &= ~(1<<3);}
if ((Motor_ID==2) && (Function==1))  	{PORTB |= (1<<2); 		PORTB &= ~(1<<3);}
if ((Motor_ID==2) && (Function==2)) 	{PORTB &= ~(1<<2); 	PORTB |= (1<<3); }

if ((Motor_ID==3) && (Function==0)) 	{PORTD &= ~(1<<4); 	PORTD &= ~(1<<5);}
if ((Motor_ID==3) && (Function==1)) 	{PORTD |= (1<<4); 		PORTD &= ~(1<<5);}
if ((Motor_ID==3) && (Function==2)) 	{PORTD &= ~(1<<4); 	PORTD |= (1<<5); }
	
if ((Motor_ID==4) && (Function==0)) 	{PORTD &= ~(1<<6); 	PORTD &= ~(1<<7);}
if ((Motor_ID==4) && (Function==1)) 	{PORTD |= (1<<6); 		PORTD &= ~(1<<7);}
if ((Motor_ID==4) && (Function==2)) 	{PORTD &= ~(1<<6); 	PORTD |= (1<<7); }
}
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You talk about SPI between two LandRovers. Assuming that they are parked next to each other, you have at least 3m of SPI cable.

If they are both moving, you need two very good drivers!

SPI is not a wise choice for comms in a noisy environment. It could work with RS485 driver chips or similar. I suggest that you might be better off using USARTs with RS232 over 5m or RS485 over 100m.

I would also suggest that you examine your programming style. Take a pencil and paper, and you could make everything a lot clearer.

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

How about a schematic ? Provide links to the chips you're using and tell us WHICH MCU you're using . These are things you should do on EVERY thread you start.

Your spi_transmit() is wrong. Open your MCU datasheet and read how it's done in the spi section ( and notice it DOESN'T have a delay in it. Why would you want to SLOW it down ? ) . While in the 'sheet double-check your spi_init() and that spi pins are connected right . Also read about how to use the SPI_CS in master mode. You can't use a comm. I/F if you don't study how to use it.

NEVER write so much code at once, you should write a bit, debug, etc.

1) Studio 4.18 build 716 (SP3)
2) WinAvr 20100110
3) PN, all on Doze XP... For Now
A) Avr Dragon ver. 1
B) Avr MKII ISP, 2009 model
C) MKII JTAGICE ver. 1

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The spi_transmit() would work, but I cannot see the point in waiting for 0.5 second for every byte. You would normally read the SPDR for any reply from a Slave.

Slowing down the SPI has no great point. The Slave reacts entirely to what SCK pulses it receives. The slower the transmit clock, the more chance of noise spikes going through to the Slave as extra SCK pulses. e.g. what goes wrong with parallel port ISP programmers.

You have an inherently noisy environment, you need some signal conditioning. And I would be happier with faster transmission with an error correction protocol.

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks guys,

Here is the thing. i used UART for communications and had no problem whatsoever(UART TTL). since i was wanted to learn SPI, i thought i might just try SPI communications instead of UART.

should i use co-axial wire for the signal cables, cause i know its a very noisy environment?

The cable between the landrovers is about 1.5m. I decrease the spi clk cause i was under the assumption that i would have more attenuation at higher frequencies.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

sorry i did not mention the MCU... its a ATMEGA32 clocked at 16Mhz.. will try to provide a schematic soon.

Rodney

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Using an overall screen could be helpful.
Using balanced lines with twisted pairs is very effective.

Get out that pencil and paper. Draw a UART signal.
Do the same with an SPI signal.

With your pencil, add in some noise to your signals. Depending on where you add the noise to the UART signal, the glitches are ignored, averaged out, detected and registered as parity or framing errors.

For SPI, every glitch counts on the SCK line!
On the MISO or MOSI line, it depends where the glitch occurs.

Simply adding a low-pass filter and schmidt trigger will take out a lot of short spikes.

All the same, one misconstrued signal and your LandRovers will crash.

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Rodney, it's better to use names instead of "magic numbers".

while( (SPSR & ( 1<< SPIF ))==0 )

instead of

while( (SPSR & 0x80)== 0 )

It makes code more readable / self-documenting . Something to think about for future apps.

Ma bad, yeah the transmit() is correct. Thanks David. Still, Rodney you need to check out how to handle SPI CS and spi generally. :wink:

1) Studio 4.18 build 716 (SP3)
2) WinAvr 20100110
3) PN, all on Doze XP... For Now
A) Avr Dragon ver. 1
B) Avr MKII ISP, 2009 model
C) MKII JTAGICE ver. 1