Forum: Mikrocontroller und Digitale Elektronik 2 ATMega8 über SPI kommunizieren lassen


von Erich (Gast)


Lesenswert?

Hallo,
also der erste µC soll periodisch dem zweiten eine 1 und eine 0 senden. 
Der zweite µC soll bei einer empfangenen 1 eine am PB0 angebrachte LED 
zum leuchten bringen und bei 0 diese wieder aus machen. Simpel, klappt 
aber dennoch nicht. Die Initialisierung und Sende/Empfangsmethoden hab 
ich direkt aus dem Datenblatt. SS ist bei Master als Output konfiguriert 
und beim Slave auf Masse gesetzt.

Code des Masters:
1
#include <avr/io.h>
2
#ifndef F_CPU
3
/* Definiere F_CPU, wenn F_CPU nicht bereits vorher definiert 
4
   (z.B. durch Übergabe als Parameter zum Compiler innerhalb 
5
   des Makefiles). Zusätzlich Ausgabe einer Warnung, die auf die
6
   "nachträgliche" Definition hinweist */
7
#warning "F_CPU war noch nicht definiert, wird nun mit 8000000 definiert"
8
#define F_CPU 8000000UL     /* Quarz mit 3.6864 Mhz */
9
#endif
10
#include <util/delay.h>
11
12
void SPI_MasterInit(void)
13
{
14
  /* Set MOSI, SCK and SS output, all others input */
15
  DDRB = (1<<PB3)|(1<<PB5)|(1<<PB2);
16
  /* Enable SPI, Master, set clock rate fck/16 */
17
  SPCR = (1<<SPE)|(1<<MSTR)|(1<<SPR0);
18
}
19
20
void SPI_MasterTransmit(char cData)
21
{
22
  /* Start transmission */
23
  SPDR = cData;
24
  /* Wait for transmission complete */
25
  while(!(SPSR & (1<<SPIF)));
26
}
27
 
28
int main( void )
29
{
30
SPI_MasterInit();
31
while (1)
32
  {
33
  SPI_MasterTransmit(1);
34
  _delay_ms(700);
35
  SPI_MasterTransmit(0);
36
  _delay_ms(300);
37
  }  
38
  
39
    return 0;
40
}

Slave:
1
#include <avr/io.h>
2
#ifndef F_CPU
3
/* Definiere F_CPU, wenn F_CPU nicht bereits vorher definiert 
4
   (z.B. durch Übergabe als Parameter zum Compiler innerhalb 
5
   des Makefiles). Zusätzlich Ausgabe einer Warnung, die auf die
6
   "nachträgliche" Definition hinweist */
7
#warning "F_CPU war noch nicht definiert, wird nun mit 8000000 definiert"
8
#define F_CPU 8000000UL     /* Quarz mit 3.6864 Mhz */
9
#endif
10
#include <util/delay.h>
11
12
void SPI_SlaveInit(void)
13
{
14
/* Set MISO output, all others input */
15
DDRB = (1<<PB4)|(1<<PB0);
16
/* Enable SPI */
17
SPCR = (1<<SPE);
18
}
19
20
char SPI_SlaveReceive(void)
21
{
22
/* Wait for reception complete */
23
while(!(SPSR & (1<<SPIF)))
24
;
25
/* Return data register */
26
return SPDR;
27
}
28
 
29
int main( void )
30
{
31
  while(1)
32
  {
33
    if (SPI_SlaveReceive() == 1) PORTB |= (1 << PB0);
34
    if (SPI_SlaveReceive() == 0) PORTB |= (0 << PB0); 
35
  }
36
    
37
    return 0;
38
}

was ist da noch falsch?

von Jean P. (fubu1000)


Lesenswert?

Also als erstes initialisierst du nicht den SPI_SLAVE !
Zweitens dein delay_ms geht nicht.
Ausschnitt aus der delay.cpp :
The maximal possible delay is 262.14 ms / F_CPU in MHz.

Gruß

von Jean P. (fubu1000)


Lesenswert?

Ausserdem würde ich beim Slave lieber:

int main( void )
{
  SpiSlaveInit();
  unsigned char rec;
  while(1)
  {
    rec = SPI_SlaveReceive();
    if (rec == 0x01){ PORTB |= (1 << PB0);}
    else { PORTB |= (0 << PB0); }
  }

    return 0;
}


Ansonsten kanns passieren und er empfängt die 1 und vergöeicht aber mit 
0,......


Gruss

von Erich (Gast)


Lesenswert?

stimmt da hab ich vergessen die Init beim Slave aufzurufen, ändert aber 
nichts daran das nichts geht.
Also die Delay Funktion geht bei mir schon, auch eine ganze Sekunde 
funktioniert tadellos.

Ich hab mal beim Oszi SCK angeschaut, wenn ich flashe ist da deutlich 
was zu sehn, beim Senden vom Master aber nichts. Also müsste schon beim 
code vom Master was falsch sein. Reicht es das SS als Output 
konfiguriert ist oder muss der zusätzlich auch auf high gesetzt werden?

von Marius W. (mw1987)


Lesenswert?

Fabian Ostner wrote:
> Zweitens dein delay_ms geht nicht.
> Ausschnitt aus der delay.cpp :
> The maximal possible delay is 262.14 ms / F_CPU in MHz.

Das war früher mal so. Inzwischen kann die avr-libc (seit Version 1.6) 
auch mit sehr langen delays umgehen. Die sind zwar nicht mehr so genau, 
aber mehr als ausreichend.

MfG
Marius

von nixversteh (Gast)


Lesenswert?

> Reicht es das SS als Output
> konfiguriert ist oder muss der zusätzlich auch auf high gesetzt werden?
Datenblatt, S.129
If SS is configured as an output, the pin is a general output pin which 
does not affect the
SPI system. Typically, the pin will be driving the SS pin of the SPI 
Slave.
If SS is configured as an input, it must be held high to ensure Master 
SPI operation. If
the SS pin is driven low by peripheral circuitry when the SPI is 
configured as a Master
with the SS pin defined as an input, the SPI system interprets this as 
another Master
selecting the SPI as a Slave and starting to send data to it.

von Jean P. (fubu1000)


Lesenswert?

Hallo,
also wenn dein _delay funzt ist das reines Glück, weil in der _delay 
wird mit einem uint16 gearbeitet, kann also wie schon oben gepostet nit 
gehen.
Schau selber in der _delay_ms nach, oder besser glaub mir einfach.

Das SS brauchste jetzt erstmal nit, erst bei mehreren Slaves oder wenn 
du später mal den Slave schlafen schickst.

Mach mal zum Test statt deiner _delay_ms(700):
int i;
for(i=0 ; i<=140; i++)
{
  _delay_ms(5);
}

und bei deiner _delay_ms(300):
for(i=0 ; i<=60; i++)
{
   _delay_ms(5);
}


Später nimmste besser Timer und Interrupts.

Gruß

von Jean P. (fubu1000)


Lesenswert?

@MArius:
Hmm na gut will ich glauben, bei mir gehts nit. Aber auch keine Ahnung 
welche Version ich habe.

Gruß

von Marcel K. (viewer)


Lesenswert?

Hallo Erich,
vielleicht hilft dir das hier:
///////////////////////////////////
#define SS        PORTB_Bit4
#define DDR_SS    DDRB_Bit4
#define DDR_MOSI  DDRB_Bit5
#define DDR_MISO  DDRB_Bit6

#define ACTIVATE_SLAVE(){   SS        = LOW;}
#define DEACTIVATE_SLAVE(){ SS        = HIGH;}

#define PIN_INIT_MASTER(){  DDR_SS    = 1;\
                            DDR_MOSI  = 1;\
                            DDR_SCK   = 1;}


#define PIN_INIT_SLAVE(){   DDR_MISO  = 1;}



void init_spi_master_mode(void)
{
  PIN_INIT_MASTER();
  ACTIVATE_SLAVE();
}
void init_spi_slave_mode(void)
{
  PIN_INIT_SLAVE();
}
///////////////////////////////////

Ist ein Codeschnippsel aus meiner include. (Schreibe mit IAR)

Bitte vergewissere dich auch, dass der SS Pin den richtigen Wert hat. 
(input/output; high/low) Sonnst funzt das ganze nicht. es gibt auch ein 
Application Note bei Atmel. Dort findet man auch einiges.

Gruße, Marcel

von Jean P. (fubu1000)


Lesenswert?

Ich hoffe dein Progger hängt nicht am SPI ^^.

Gruß

von Erich (Gast)


Lesenswert?

>Ich hoffe dein Progger hängt nicht am SPI ^^.
hehe, nein, das nicht.

@Marcel
warum hast du im Init_Master_Mode ein Activate_Slave? SS soll doch beim 
Master auf high gehalten werden.

von Erich (Gast)


Lesenswert?

also meinen Master hab ich zum laufen gebracht indem ich
1
tmp = SPSR;
2
tmp = SPDR;
hinzugefügt habe.

aber der Slave will noch nicht, obwohl ich hier selbiges hinzugefügt 
habe.
Hier nochmal der code:
1
uint8_t tmp;
2
3
void SPI_SlaveInit(void)
4
{
5
/* Set MISO output, all others input */
6
DDRB = (1<<PB4)|(1<<PB0)|(1<<PB1);
7
/* Enable SPI */
8
SPCR = (1<<SPE);
9
}
10
11
char SPI_SlaveReceive(void)
12
{
13
tmp = SPSR;
14
tmp = SPDR;
15
/* Wait for reception complete */
16
while(!(SPSR & (1<<SPIF)));
17
/* Return data register */
18
return SPDR;
19
}
20
 
21
int main( void )
22
{
23
  SPI_SlaveInit();
24
  while(1)
25
  {
26
    tmp = SPI_SlaveReceive();
27
    if (tmp == 0) PORTB |= (1 << PB0);
28
    else PORTB |= (0 << PB0); 
29
  }
30
    
31
    return 0;
32
}
Die Pin Vebindung hab ich jetzt so oft überprüft, die müsste stimmen. 
Aber was nun im code noch fehlt/falsch ist....keine Ahnung.
Ich hoffe mir kann da jemand weiter helfen.

von Marcel K. (viewer)


Lesenswert?

€Erich:
Du hast Recht. SO NICHT!! :o) Ist wohl beim koieren etwas nicht so 
richtig gelaufen. Muss ich dann noch mal da Heim prüfen!!
Ich glaube Dir das Du die Pinverbindung geprüft hast. Hast Du aber auch 
mit einem Messgerät mal am /CS gemessen? Auch mal den Master am /CS des 
Slaves getrennt und den /CS des Slaves fest auf Masse gelegt? Da habe 
ich nämlich gemerkt dass bei mir etwas nicht gestimmt hat!!

Grüße, Marcel

von Jean P. (fubu1000)


Lesenswert?

Hallo,
der Master sollte auch gehen ohne dieses tmp = .... Zeugs.
Ansonsten häng doch einfach mal den SS Pin vom MAster an einen PortPin 
von deinem Slave. Frage diesen in der while Schleife ab und falls Low 
dann springe in den Receive Teil.

Gruß.

von Erich (Gast)


Lesenswert?

wenn du mit CS SlaveSelect meinst, dann ja, den habe ich die ganze Zeit 
auf GND gelegt gehabt.

Fabian, als ich das Auslesen von SPSR und SPDR hinzugefügt habe, habe 
ich ein Ausgangssignal bekommen, davor nicht.

von Jean P. (fubu1000)


Lesenswert?

Hallo,
ja mein Fehler meinte den SS-Pin.
Hmm das mit den SPDR und SPSR auslesen sollte überflüssig sein, wenn du 
Lust hast, dann kompiliere doch mal deinen Code ohne die Register 
auszulesen und stell mal die LSS-Datei hier rein. Mal schauen, ob da was 
wegoptimiert wird oder so.
Gruß

von Erich (Gast)


Angehängte Dateien:

Lesenswert?

hallo,
so jetzt hab ich das mal gemacht. Allerdings hat das ja nichts mit 
meinem eigentlichen Problem zu tun, dem Empfangen von den SPI Signalen.

Hatte mir das einfacher vorgestellt eine 1 bzw. 0 über SPI zu versenden. 
Dass ich da schon 2 Wochen dran rum mach ist schon zum verzweifeln.

von Erich (Gast)


Lesenswert?

Ich hab die Empfangsroutine des Slaves mal für den Interrupt-Betrieb 
umgeschrieben. Und hier empfange ich tatsächlich etwas. Die ISR wird zu 
den richtigen Zeitpunkten aufgerufen:
1
#include <avr/io.h>
2
#ifndef F_CPU
3
#warning "F_CPU war noch nicht definiert, wird nun mit 8000000 definiert"
4
#define F_CPU 8000000UL     /* Quarz mit 8 Mhz */
5
#endif
6
#include <util/delay.h>
7
#include <avr/interrupt.h>
8
9
char tmp;
10
11
void SPI_SlaveInit(void)
12
{
13
/* Set MISO output, all others input */
14
DDRB = (1<<PB4)|(1<<PB0)|(1<<PB1);
15
/* Enable SPI und SPI-Interrupt*/
16
SPCR = (1<<SPE)|(1<<SPIE);
17
}
18
19
ISR(SPI_STC_vect)
20
{
21
PORTB ^= ( 1 << PB0 );  // Toggle PB0 z.B. angeschlossene LED
22
}
23
 
24
int main( void )
25
{
26
  SPI_SlaveInit();
27
  sei();
28
  while(1)
29
  {
30
  }
31
    
32
    return 0;
33
}
Ersetze ich aber innerhalb der ISR die Zeile
1
PORTB ^= ( 1 << PB0 );
durch
1
tmp = SPDR;
2
if (tmp == 0) PORTB &= ~(1<<PB0); //LED aus
3
if (tmp == 1) PORTB |= (1<<PB0); //LED an
dann bleibt die LED aus (PB0 auf low). Sprich es wird etwas übertragen, 
aber es kommt etwas falsches an. Bei einem Leitungsweg von 10cm dürfte 
das doch aber nicht der Fall sein.
Kann doch nicht sein dass das so schwierig ist.

von Erich (Gast)


Lesenswert?

ich habe keine Ahnung warum es auf einmal funktioniert, aber es 
funktioniert. Zig mal mit den Geschwindigkeiten rum gespielt, dutzende 
male geflasht und auf einmal gehts mit dem Code wie er ein Posting 
drüber da steht. Fragt mich nicht warum.

Btw. Fabian Ostner...
>Ich hoffe dein Progger hängt nicht am SPI ^^.
geht trotzdem

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.