NerdKits - electronics education for a digital generation

You are not logged in. [log in]

NEW: Learning electronics? Ask your questions on the new Electronics Questions & Answers site hosted by CircuitLab.

Microcontroller Programming » SPI problem

May 25, 2012
by denis1981
denis1981's Avatar

Guys, I have a little problem. I diligently searched the previous topics and could not find the solution to my problem. I’m trying to learn SPI protocol and so trying to complete a very simple project. Two micro-controllers (both ATmega168), one as a slave, second as a master. Master receives an input from the user (push of a button), transmits the data (logical 1) through SPI protocol to the slave and slave turns on an LED. The problem: I did a lot of trouble shooting and it appears that the problem is at the line 62 of the master controller. It seems like this while loops runs forever waiting for the SPIF flag that never signals the end of the transmission. So master code never passes line number 62. Circuitry and codes for master and slave are attached. Clearly I’m just doing something plain stupid as I triple checked all the settings and all looks like per data sheet . I’m stuck on it for nearly a week. Please advise. Denis.

http://i.imgur.com/eiXU5.jpg

Slave:

define F_CPU 14745600

include <stdio.h>

include <math.h>

include <avr/io.h>

include <avr/interrupt.h>

include <avr/pgmspace.h>

include <inttypes.h>

include "../libnerdkits/delay.h"

include "../libnerdkits/lcd.h"

include "../libnerdkits/uart.h"

int main() {

DDRC|=(1<<PC5); DDRC|=(1<<PC4); DDRC|=(1<<PC3); DDRC|=(1<<PC2); DDRB|=(1<<PB4); DDRB &= ~(1<<PB5);
DDRB &= ~(1<<PB3); DDRB &= ~(1<<PB2); SPCR = (1<<SPE)|(1<<CPOL)|(1<<CPHA); uint16_t B;

PORTC|=(1<<PC4);

while(1) {

PORTC|=(1<<PC3);

while(!(SPSR & (1<<SPIF)));

B=SPDR;

if (B==1){ PORTC|=(1<<PC5); } else { PORTC &= ~(1<<PC5); }

PORTC|=(1<<PC2);

}

return 0; }

Master:

define F_CPU 14745600

include <stdio.h>

include <math.h>

include <avr/io.h>

include <avr/interrupt.h>

include <avr/pgmspace.h>

include <inttypes.h>

include "../libnerdkits/delay.h"

include "../libnerdkits/lcd.h"

include "../libnerdkits/uart.h"

uint16_t SPI(uint16_t dataout) { uint16_t datain; SPDR=dataout; while(!(SPSR & (1<<SPIF))); datain=SPDR; PORTB|=(1<<PB2); delay_us(1); PORTB&= ~(1<<PB2); return datain; }

uint16_t A=0;

int main() {

DDRC &= ~(1<<PC5); DDRB &= ~(1<<PB4); DDRB |= (1<<PB3); DDRB |= (1<<PB5);
DDRB |= (1<<PB2);

SPCR = (1<<SPE)|(1<<MSTR)|(1<<CPOL)|(1<<CPHA);

lcd_init(); FILE lcd_stream=FDEV_SETUP_STREAM(lcd_putchar, 0, _FDEV_SETUP_WRITE); lcd_home();

while(1) {

if (PINC&(1<<PC5)){ A=1; } else { A=0; }

SPI(A);
while(!(SPSR & (1<<SPIF))); fprintf_P(&lcd_stream, PSTR("readout: %d"), A); delay_ms(1000);

}

return 0; }

May 26, 2012
by Ralphxyz
Ralphxyz's Avatar

denis1981, I used the "Indent Selection as Code Block" button at the bottom of the page to make this readable.

Slave:    
define F_CPU 14745600
include <stdio.h>
include <math.h>
include <avr/io.h>
include <avr/interrupt.h>
include <avr/pgmspace.h>
include <inttypes.h>
include "../libnerdkits/delay.h"
include "../libnerdkits/lcd.h"
include "../libnerdkits/uart.h"

int main() {

DDRC|=(1<<PC5); DDRC|=(1<<PC4); DDRC|=(1<<PC3); DDRC|=(1<<PC2); DDRB|=(1<<PB4); DDRB &= ~(1<<PB5);
DDRB &= ~(1<<PB3); DDRB &= ~(1<<PB2); SPCR = (1<<SPE)|(1<<CPOL)|(1<<CPHA); uint16_t B;

PORTC|=(1<<PC4);

while(1) {

PORTC|=(1<<PC3);

while(!(SPSR & (1<<SPIF)));

B=SPDR;

if (B==1){ PORTC|=(1<<PC5); } else { PORTC &= ~(1<<PC5); }

PORTC|=(1<<PC2);

}

return 0; }

Master:
define F_CPU 14745600
include <stdio.h>
include <math.h>
include <avr/io.h>
include <avr/interrupt.h>
include <avr/pgmspace.h>
include <inttypes.h>
include "../libnerdkits/delay.h"
include "../libnerdkits/lcd.h"
include "../libnerdkits/uart.h"

uint16_t SPI(uint16_t dataout) { uint16_t datain; SPDR=dataout; while(!(SPSR & (1<<SPIF))); datain=SPDR; PORTB|=(1<<PB2); delay_us(1); PORTB&= ~(1<<PB2); return datain; }

uint16_t A=0;

int main() {

DDRC &= ~(1<<PC5); DDRB &= ~(1<<PB4); DDRB |= (1<<PB3); DDRB |= (1<<PB5);
DDRB |= (1<<PB2);

SPCR = (1<<SPE)|(1<<MSTR)|(1<<CPOL)|(1<<CPHA);

lcd_init(); FILE lcd_stream=FDEV_SETUP_STREAM(lcd_putchar, 0, _FDEV_SETUP_WRITE); lcd_home();

while(1) {

if (PINC&(1<<PC5)){ A=1; } else { A=0; }

SPI(A);
while(!(SPSR & (1<<SPIF))); fprintf_P(&lcd_stream, PSTR("readout: %d"), A); delay_ms(1000);

}

return 0; }

Ralph

May 26, 2012
by denis1981
denis1981's Avatar

Thank you. I tried to do it when posted originally but it wouldn't work for me then... Any idea why it doesn't work?

May 27, 2012
by Ralphxyz
Ralphxyz's Avatar

Any idea why it doesn't work?

Oh, "it" works.

There have been a number of post about posting code.

Originally "we" had to put four spaces before a line to indent it as code.

In fact you can still put in four spaces which is what the "Indent Selection as Code Block" button does.

Since we do not have a practice forum where new users can experiment before posting.

But we do have the "Preview" so you have to see what you are posting before actually posting so you always can go back and try something else.

Of course there are certain assumptions made about posters.

Like doing a "Selection" it is assumed everybody knows how to do a Selection but for some a "Highlight" might make more sense.

So you copy your code to the "Body" window (where you are composing your post).

And then you "Select" it by left clicking and holding your mouse button at the start of your code and dragging your mouse to the end of your code.

There now your code is "Highlighted" in blue as in being "Selected".

Then you just click the "Indent Selection as Code Block".

When you "Preview" you will see you correctly formatted code block.

Now hopefully someone will help you with your SPI problem.

Have you compared your code to the Servo Squirter code?

Also are you doing anything about "button debounce"? I do not know if that would effect your setup but it does mess up things.

I just quickly looked at your code but did not notice any debounce.

Ralph

May 27, 2012
by sask55
sask55's Avatar

Hi denis1981

slave code

You do not have to set the CPOL or the CPHA bits in the SPCR register. You will have to set the SPE and the SPIE bits.

   SPCR |= (1<<SPE) | (1<<SPIE);

Enable global interrupts.

    sei();

Set up the SPI interrupt code that will run when the SPIF interrupt flag in the SPSR register is set after the SPI transfer is complete.

    ISR(SPI_STC_vect){  
B = SPDR;
if (B==1){ PORTC|=(1<<PC5); } else { PORTC &= ~(1<<PC5); }
    }

Master code

    PORTC |= (1<<PC5); // turn on internal pull up resistor for pin 5 on the C port

You do not have to set the CPOL or the CPHA bits You should select a SPI clock speed by setting one or both of the SPR1 and SPR0 bits. Table 18-5 in the data sheet. Without setting ether of these bits the SPI will default to f osc/4 which is alright.

For example to set the SPI speed to 1/64 of the oscillator speed

   SPCR |= (1<<SPE) | (1<<MSTR) || (1<<SPR1) ;  or SPCR |= (1<<SPE) | (1<<MSTR) |2;

Inside the while loop

send a 0 out on the SPI if pin 5 on the C port is high or else send a 1 out on the SPI if (ie button is pushed and pin is shorted to ground)

 if (PINC&(1<<PC5)){
 SPI(0); 
   }else{
 SPI(1);

    }

  while(!(SPSR & (1<<SPIF)));

I hope this helps a bit.

May 27, 2012
by sask55
sask55's Avatar

I think a example of the least amount of code required to do what you described may be more clear.

 //Slave code:

define F_CPU 14745600
include <stdio.h>
include <math.h>
include <avr/io.h>
include <avr/interrupt.h>
include <avr/pgmspace.h>
include <inttypes.h>
include "../libnerdkits/delay.h"
include "../libnerdkits/lcd.h"
include "../libnerdkits/uart.h"

 ISR(SPI_STC_vect) {// SPI interupt code will run after SPI complete
    B = SPDR;
    if (B==1){
    PORTC|=(1<<PC5);    
    } else {
    PORTC &= ~(1<<PC5); }  
    }

int main() {

DDRC|=(1<<PC5);// pin c5 as output
 DDRB|=(1<<PB4); DDRB &= ~(1<<PB5);
DDRB &= ~(1<<PB3); DDRB &= ~(1<<PB2); 
 uint16_t B;
 sei(); // enable global interrupts
  SPCR |= (1<<SPE) | (1<<SPIE);// enable SPI, enable spi interputs

while(1) {
    }

return 0; }

//Master code:

define F_CPU 14745600
include <stdio.h>
include <math.h>
include <avr/io.h>
include <avr/interrupt.h>
include <avr/pgmspace.h>
include <inttypes.h>
include "../libnerdkits/delay.h"
include "../libnerdkits/lcd.h"
include "../libnerdkits/uart.h"

int main() {

DDRC &= ~(1<<PC5);
DDRB &= ~(1<<PB4); DDRB |= (1<<PB3);
DDRB |= (1<<PB5);  // set PC5 as input.
PORTC |= (1<<PC5); // turn on internal pull up resistor for PC5
DDRB |= (1<<PB2);

SPCR |= (1<<SPE) | (1<<MSTR) | 2;// enable master SPI at 1/64 Ocs freq.

while(1) {
if (PINC&(1<<PC5)){
        SPI(0);    
    }else{
        SPI(1); 
        }  
 while(!(SPSR & (1<<SPIF)));

}

return 0; }

If I have not made any syntax errors this code should do what you have described.

May 27, 2012
by sask55
sask55's Avatar

Noticed an error line 27 uint16_t B; must be moved to the top of the SPI interupt.

Post a Reply

Please log in to post a reply.

Did you know that reading a double floating point variable with scanf requires "%lf" for "long float"? Learn more...