Forum: Mikrocontroller und Digitale Elektronik Probleme mit SOFT-SPI - Welchen Wald sehe ich nicht?


von User_Y (Gast)


Lesenswert?

Hallo,

ich stehe irgendwie total auf dem Schlauch! Möchte gerne in einer Klasse 
eine SPI durch eine Soft-SPI ersetzen.

Mit HW-SPI funktioniert alles tadellos und die Sendefunktion ist 
definiert als:
1
static void spiSend(uint8_t b) 
2
{
3
  SPDR = b;
4
  while (!(SPSR & (1 << SPIF)));
5
}

Initialisiert wird die HW-SPI durch:
1
SPCR = (1 << SPE) | (1 << MSTR) | (1 << SPR1) | (1 << SPR0);

MOSI, SCLK und SS sind als Ausgänge deklariert und MISO als Eingang.

Nun will ich mit den gleichen PINs, also ohne etwas an der HW zu ändern 
Soft-SPI machen. Das klappt aber leider nicht. Dabei habe ich nur die 
Funktion "spiSend" geändert in:
1
static void spiSend(uint8_t b)
2
{    
3
    for(uint8_t i=7;i<=0;i--)
4
    {
5
        if( b & (1<<i) )
6
        {
7
            PORTB |= (1<<3);     // B3:MOSI
8
        }
9
        else 
10
        {
11
            PORTB &= ~(1<<3);    // B3:MOSI
12
        }
13
                
14
        _delay_us(1);
15
        
16
        PORTB |= (1<<5);         // B5:CLK  high
17
        _delay_us(50);
18
        
19
        PORTB &= ~(1<<5);        // B5:CLK  low
20
        _delay_us(50);
21
    }
22
}

Also entweder sehe ich den Wald vor lauter Bäumen nicht oder ich habe 
'nen prinzipiellen Denkfehler!? Die HW würde ich aus ausschließen, da 
ich an der ja nix mache.

Besten Dank für sachdienliche Hinweise!! (Und bitte keine Vorschläge das 
ich viell. doch die HW-SPI nutzen sollte, weil die ja ein BUS ist usw.)

Danke!

von Karl H. (kbuchegg)


Lesenswert?

für einen uint8_t gilt

>     for(uint8_t i=7;i<=0;i--)


mit Ausnahme von 0 kann es kein i geben welches kleiner/gleich 0 ist 
(wegen unsigned). Und 7 ist ganz sicher nie kleiner/gleich 0. D.h. die 
Schleife wird überhaupt nicht betreten.

von Karl H. (kbuchegg)


Lesenswert?

Machs nicht komplizierter als notwendig.
Zumal du die Operation
    1 << i
sowieso nicht im Programm haben willst
1
#define MOSI_PIN  3
2
#define CLK_PIN   5
3
4
static void spiSend(uint8_t b)
5
{
6
  uint8_t mask = 0x80;
7
8
  for(uint8_t i = 0; i < 8; i++)
9
  {
10
    if( b & mask )
11
    {
12
      PORTB |= (1<<MOSI_PIN);
13
    }
14
    else 
15
    {
16
      PORTB &= ~(1<<MOSI_PIN);
17
    }
18
    mask >>= 1;
19
                
20
    _delay_us(1);
21
        
22
    PORTB |= (1<<CLK_PIN);
23
    _delay_us(50);
24
       
25
    PORTB &= ~(1<<CLK_PIN);
26
    _delay_us(50);
27
  }
28
}

von User_Y (Gast)


Lesenswert?

Hallo Karl,

danke für die super schnelle Antwort! Das war schon mal der erste 
"Wald". Leider scheint es nicht der einzige / letzte zu sein, denn es 
geht immernoch nicht :-/

Habe die Funktion nun so dastehen:
1
static void spiSend(uint8_t b)
2
{    
3
    for(uint8_t i=0;i<8;i++)
4
    {
5
        if( b & (1<<(7-i)) )
6
        {
7
            PORTB |= (1<<3);     // B3:MOSI
8
        }
9
        else 
10
        {
11
            PORTB &= ~(1<<3);    // B3:MOSI
12
        }
13
                
14
        _delay_us(1);
15
        
16
        PORTB |= (1<<5);         // B5:CLK  high
17
        _delay_us(50);
18
        
19
        PORTB &= ~(1<<5);        // B5:CLK  low
20
        _delay_us(50);
21
    }
22
}

von Karl H. (kbuchegg)


Lesenswert?

User_Y schrieb:

>         if( b & (1<<(7-i)) )

Du willst zwar diese OPeration nicht haben und wie man das anders machen 
kann, zeigt das Posting vor deinem, allerdings: funktionieren sollte es.

Die Funktion an sich ist IMHO in Ordnung, da muss es also noch was 
anderes geben, was hier nicht sichtbar ist.


Was steuerst du denn an?
Kannst du die delays mal länger machen (so im Sekundenbereich) und 2 LED 
an die Ausgänge hängen. Die müsstest du dann schön blinken sehen.

von hele (Gast)


Lesenswert?

nichts an der hw geändert? Hast du die ios richtig konfiguriert 
(Digitaler Ausgang statt spi Pin), das fehlt vielleicht. immer auch eine 
gute Idee ist es ein oszi anzuhängen und zu schauen ob auch auf allen 
pins das richtige Signal kommt

von User_Y (Gast)


Lesenswert?

Es ist eine SD-Card-Library. Das Auslesen der SD-Karte funktioniert 
tadellos mit der originalen Funktion "sendSPI".

Sobald ich sie wie oben beschrieben ändere kann die SD-Karte nicht mehr 
initialisiert werden.

Da im Original die SPI zur Initialisierung auf f_osc / 128 gesetzt wird 
(f_osc = 16MHz) dachte ich, ich wäre mit meinen 2x50µs Delay ganz gut im 
Rennen. Ich habe aber auch schon 2x100µs oder 2x10µs probiert, leider 
erfolglos.

Das Ganze mit Sekunden-Takt und LEDs kann ich erst heut Abend testen 
wenn ich zu Hause bin.

Bis dahin, vielen Dank!

von User_Y (Gast)


Lesenswert?

@hele

ich ändere ja in der Software nur die Funktion "sendSPI". Alles 
andere, d.h. insbesondere auch die Definition von Ein- und Ausgängen 
bleibt erhalten.

Schon komisch :-)

von Karl H. (kbuchegg)


Lesenswert?

Ja Moment. Und wo wird im Gegenzug von MISO eingelesen?

Sieh dir mal die Aufrufstellen von sendSPI an. Ich wette da werden ein 
paar dabei sein, bei denen nach dem Aufruf der Funktion auf SPDR 
zugegriffen wird. SPI ist bidirektional und von einer SD-Library wird 
man erwarten, dass sie auch etwas von der Karte lesen muss. Und sei es 
nur in der Initialisierung die Bestätigung von der Karte.

von ... (Gast)


Lesenswert?

User_Y schrieb:
> Alles
>
> andere, d.h. insbesondere auch die Definition von Ein- und Ausgängen
>
> bleibt erhalten.

und

User_Y schrieb:
> Initialisiert wird die HW-SPI durch:
>
> SPCR = (1 << SPE) | (1 << MSTR) | (1 << SPR1) | (1 << SPR0);

Schön das deine Hardware-SPI noch eingeschaltet ist.....

von User_Y (Gast)


Lesenswert?

@Karl
meinst Du etwa etwas in der Art:
1
static  uint8_t spiRec(void) {
2
  spiSend(0XFF);
3
  return SPDR;
4
}

Ups, wenn ich "spiSend()" auf SOFT umstelle wird SPDR wohl nicht mehr 
viel ausgeben ... :-)

@gast
"Schön das deine Hardware-SPI noch eingeschaltet ist....."

Ups, die zweite :-)


Danke euch beiden, werde mal biss basteln und später berichten!

von User_Y (Gast)


Lesenswert?

Hey,

nur kurze Info das nun alles läuft, es waren wie zu erwarten die 
beiden oberen Hinweise!

Vielen lieben Dank noch einmal!

von User_Y (Gast)


Lesenswert?

@Karl

Auch wenn jetzt alles läuft möchte ich mich doch immer ein bisschen 
weiterbilden und frage mich wie Du das hier gemeint hast:

"Zumal du die Operation
    1 << i
sowieso nicht im Programm haben willst"

Rein rechentechnisch passiert doch das Gleiche wie bei Deinem Vorschlag 
mit der Maskierung!?

Ist das einfach der besseren Code-Lesbarkeit wegen? Kommt der Compiler 
damit besser zurecht? Oder hat das andere Gründe?

LG und vielen Dank!

von Walter S. (avatar)


Lesenswert?

es dauert halt unnötig lange und auch verschieden lang, dein Takt ist 
also nicht besonders genau

von Karl H. (kbuchegg)


Lesenswert?

User_Y schrieb:
> @Karl
>
> Auch wenn jetzt alles läuft möchte ich mich doch immer ein bisschen
> weiterbilden und frage mich wie Du das hier gemeint hast:
>
> "Zumal du die Operation
>     1 << i
> sowieso nicht im Programm haben willst"
>
> Rein rechentechnisch passiert doch das Gleiche wie bei Deinem Vorschlag
> mit der Maskierung!?

konzeptionell: ja
Rechenzeitmässig: nein

Dein AVR hat keinen Barrelshifter! D.h. der Compiler muss diese 
Operation so implementieren


   uint8_t result = 0x01;
   for( uint8_t j = 0; j < i; j++ )
     result <<= 1;

Das ist das, was der Compiler aus 1<<i machen muss.


und jetzt vergleiche nochmal, ob die beiden Vorgehensweisen in alle 
Belangen gleich sind.

Links/Rechts schieben ist gut. Aber achte drauf, dass du immer um eine 
konstante Anzahl an Bits schiebst. Keine Variable. Denn so ein Konstrukt 
muss der Compiler mit einer Schleife auflösen.

von User_Y (Gast)


Lesenswert?

Super,

wieder was dazu gelernt!

Danke für die schnelle und kompetente Antwort!

von Falk B. (falk)


Lesenswert?


von eProfi (Gast)


Lesenswert?

Und so würde ich es schreiben:
1
#define MOSIPORT PORTB
2
#define MOSIPIN  3
3
#define CLK_PORT PORTB
4
#define CLK_PIN  5
5
void spiSend(uint8_t b){    
6
  uint8_t mask=0x80;do{ //mask ist zugleich der Bitzähler
7
    if(b&mask)MOSIPORT |= (1<<MOSI_PIN); // B3:MOSI
8
    else      MOSIPORT &=~(1<<MOSI_PIN); // B3:MOSI
9
    _delay_us(1);
10
11
    CLK_PORT |= (1<<CLK_PIN);_delay_us(50);// B5:CLK  high
12
    //***
13
    CLK_PORT &=~(1<<CLK_PIN);_delay_us(50);// B5:CLK  low
14
    }while((mask>>=1)!=0);
15
  }

***
Dazwischen gleich noch die Data-In reinquetschen.

von MCUA (Gast)


Lesenswert?

>(Und bitte keine Vorschläge das
>ich viell. doch die HW-SPI nutzen sollte, weil die ja ein BUS ist usw.)
Aber den Vorschlag, das in ASM zu machen (da weiss man (ohne Umwege) 
taktgenau was passiert).

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.