C Software SPI

Janiiix3

Aktives Mitglied
28. Sep. 2013
1.333
10
38
Hannover
Sprachen
  1. ANSI C
  2. C#
Naaabend,



CodeBox C
uint8_t spiSoftRead( uint8_t byte )
{
   uint8_t n = 0;
   uint8_t ret = 0;
   uint8_t tmp = byte;

   for( ; n < 8 ; n++ )
   {
      //SCK KOMMT NOCH!
       ret>>=1;       
       ret |= PINA & 1<<PA3; // Wenn Bit gesetzt dann speichern
       tmp<<=1;       
   }

   return ret;
}


Versuche gerade via. SPI ein paar Bits einzulesen. Erstmal geht es nur um das speichern der eingebenden Daten.
Ich würde gerne eine "if Abfrage" umgehen und es so wie im Kodebeispiel zu sehen ist realisieren. Geht das überhaupt?
 
Hi Jan,

ich weiß nun nicht, warum du "if" nicht verwenden möchtest :hmmmm: ...

Dein Code funktioniert so nicht, da bereits nach 4 mal rechts shiften von "ret" die ersten Bits verloren gehen.
Das ganze ist auch abhängig von der Pin-Position, in deinem Fall PA3.
Bei PA7 würde es funktionieren, aber ob das so sinnvoll ist.
 
Wollte es mal anders probieren. Wenn ich das ganze um die aktuelle Bit Position nach rechts verschiebe, sollte das doch klappen?
Wenn Ich jetzt z.B (1<<5) abfragen möchte und dann (1<<5) >> 5 verschiebe, sollte das doch funktionieren?



CodeBox C
result |= PINA & 1<<5 >> 5;
result<<=1;
 
Ich jetzt z.B (1<<5) abfragen möchte und dann (1<<5) >> 5 verschiebe, sollte das doch funktionieren?
Ja, ich denke schon, wenn du das Ergebnis der & Operation shiftest. Die & Operation würde ich noch Klammern.

Ansonsten uberlege mal, was der Compiler hiermit macht
(1<<5) >> 5

Allerdings muss der Mikrocontroller ja einige Male shiften. Das ist ein bisschen wie, wenn du neben der Fahrertür deines Autos stehst, läufst um das Auto rum, steigst auf der Beifahrerseite ein und krabbelst zur Fahrerseite, weil du die if-Fahrertür nicht nutzen oder es einfach mal anders probieren möchtest ;):D Ok, sowas habe ich auch schon mal gemacht. Es gibt auch noch viel mehr Wege ;)
 


CodeBox C
result |= (PINA & PA3_bm) ? 1 : 0;
result <<= 1;

Ohne if :D
;)
:p
 
Hi Mikro,

kannst Du das mal in Assembler (=Maschinencode) übersetzen (lassen)?
Ich würde (in ASM) den Zustand des Eingangspins ins C-Flag übertragen (also C löschen, C in Abhängigkeit des Beins mit Hilfe von SBIC uns SEC setzen), und anschließend ins Result rollen.
"PINA & PA3_bm" liest doch erst das Pinregister, und verANDet anschließend mit einer Konstante - was macht das "? 1 : 0" dahinter?
 
Ansonsten uberlege mal, was der Compiler hiermit macht
(1<<5) >> 5
Er würde erst nach links verschieben und dann das 5 te Bit setzen?.

Allerdings muss der Mikrocontroller ja einige Male shiften. Das ist ein bisschen wie, wenn du neben der Fahrertür deines Autos stehst, läufst um das Auto rum, steigst auf der Beifahrerseite ein und krabbelst zur Fahrerseite, weil du die if-Fahrertür nicht nutzen oder es einfach mal anders probieren möchtest ;):D Ok, sowas habe ich auch schon mal gemacht. Es gibt auch noch viel mehr Wege ;)
Das ist so als wenn Ich 5 x um das Haus renne bis Ich in die Eingangstüre gehe? Willst du mir das damit sagen? :D Also viel zu viel Aufwand?
 
Hi LotadaC,

das ist, wie Dirk ganz richtig sagte, nur eine verkürzte Schreibweise für einen if-else-Block. ;)
Der GCC übersetzt das so:

CodeBox Assembler
       result |= (PINA & PA3_bm) ? 1 : 0;
00000056  IN R24,0x19     In from I/O location
00000057  BST R24,3       Bit store from register to T
00000058  CLR R24         Clear Register
00000059  BLD R24,0       Bit load from T to register
0000005A  OR R28,R24      Logical OR
       result <<= 1;
0000005B  LSL R28         Logical Shift Left
 
Da nach einer Lösung ohne konditionalem Sprung gesucht werden soll (warum auch immer... mein Weg mit SBIC übers Carry rollen zählt ja auch dazu...), hier folgender Vorschlag:
  1. Pinregister lesen
  2. entsprechenden Pin ins T-Flag speichern
  3. Result schieben
  4. T in Result laden
Mit dem Lesen und Schreiben von Result hat man hier drei I/O-Zugriffe, ein Logical Shift, und zwei T-Operationen. Außerdem werden so zwei Rechenregister benutzt.

Edit: War ja quasi zeitgleich mit Deinem Beitrag - interessanterweise fast dasselbe...
nur eine verkürzte Schreibweise für einen if-else-Block.
Wobei die IF-Abfrage rausoptimiert wurde - nicht schlecht.

Trotzdem steig ich noch nicht so richtig durch, was konkret gemacht werden soll... Ich dachte, er will den Pin lesen, und in eine Variable reinschieben...
Warum dann erst das gesicherte Bit ins gelöschte Register lesen, und danach verORren, und dann schieben?
 
Zuletzt bearbeitet:
Da nach einer Lösung ohne konditionalem Sprung gesucht werden soll (warum auch immer... mein Weg mit SBIC übers Carry rollen zählt ja auch dazu...), hier folgender Vorschlag:
  1. Pinregister lesen
  2. entsprechenden Pin ins T-Flag speichern
  3. Result schieben
  4. T in Result laden
Mit dem Lesen und Schreiben von Result hat man hier drei I/O-Zugriffe, ein Logical Shift, und zwei T-Operationen. Außerdem werden so zwei Rechenregister benutzt.
Ich wollte einfach mal eine andere Lösung suchen als mit "if" anscheind ist das aber nicht sinnvoll.
 
Die ausführliche Schreibweise
if (PINA & PA3_bm)
...result |= 1;
else
...result |= 0; (entfällt)
Wird sogar noch kürzer übersetzt:

CodeBox Assembler
       if (PINA & PA3_bm)
00000056  SBIC 0x19,3       Skip if bit in I/O register cleared
           result |= 1;
00000057  ORI R28,0x01      Logical OR with immediate
       result <<= 1;
00000058  LSL R28           Logical Shift Left
 
Das ist meinem Vorschlag mit SBIC ähnlich, aber müßte nicht eigentlich erst geschoben, und danach verORrt werden? So befindet sich das zuletzt eingeschobene Bit doch nicht mehr im LSB, sondern schon eins weiter...?

also (Laden mal wieder weggelassen):
  1. Result schieben
  2. Bein mit SBIC "prüfen"
  3. 'ne "1" ins LSB von Result (ob mit ORI oder SUBI ist Jacke wie Hose)
Aber SBIC ist eine konditionale Verzweigung - das will Jan ja aus irgendwelchen Gründen nicht...

Edit: Schritt 3 mit INC - das verbiegt das Carry nicht. Dann brauchts auch keinen Bitzähler mehr. Vor dem Empfang 0x01 ins Result. Wenn beim Schieben das Carry kommt (bei Schritt 1), bist Du nach Schritt 3 fertig. Da 2 und 3 das Carry nicht antasten, kannst Du es auch nach 3 noch auswerten...

@Mikro23 : mach mal C draus...
 
Zuletzt bearbeitet:
Also könnte ich die Spi Empfangsroutine so implementieren?


CodeBox C
uint8_t spiSoftRead( uint8_t byte )
{
   uint8_t n = 0;
   uint8_t ret = 0;

   /*
   *   Vorher Adresse des zu lesenden Kommandos senden..
   */


   for( n = 0 ; n < 8 ; n++ )
   {
       //CLK_HIGH
       ret |= (SPI_PORT & SPI_PIN) 1 : 0;
       ret<<=1;
       //CLK_LOW
   }
   
   return ret;
}
 
mach mal C draus...
Da ja 8 Bit empfangen werden, würde ich das in C so schreiben:
(Es wird nicht das Ergebnis geschoben, sondern der Schleifenzähler ;))

CodeBox C
for (uint8_t i = 1; i; i <<= 1)
   if (PINA & PA3_bm)
       result |= i;
GCC macht das daraus:

CodeBox Assembler
       for (uint8_t i = 1; i; i <<= 1)
00000058  LDI R18,0x01       Load immediate
           if (PINA & (1<<PA3))
00000059  SBIC 0x19,3        Skip if bit in I/O register cleared
               result |= i;
0000005A  OR R28,R18         Logical OR
0000005B  LSL R18            Logical Shift Left
0000005C  SBIW R24,0x01      Subtract immediate from word
0000005D  BRNE PC-0x04       Branch if not equal
 
Wann wird "i" unwahr?
Wenn die "1" rausgeschoben worden ist, ist i = 0, d.h. unwahr.
Wahr ist in C alles, was ungleich null ist.

i sieht in diesem Fall also nacheinander so aus:
...i......// Schleifendurchlaufanzahl (entspricht Bitnr.)
0x01 // 0
0x02 // 1
0x04 // 2
0x08 // 3
0x10 // 4
0x20 // 5
0x40 // 6
0x80 // 7
0x00 // hier wird die Schleife beendet
 
Wann wird "i" unwahr? Wenn die Variable überläuft?
Nein, wenn das Doppelregister R24:R25 null wird. An welcher Stelle dieses Register beladen wird, steht nicht im Code. In Zeile 8 wird von diesem Doppelregister (16Bit-Zahl) eine eins abgezogen, wenn das Ergebnis nicht null ist, wird in Zeile 9 zu Zeile 4 zurückgesprungen. Wenn das Ergebnis null war, wird Zeile 9 übersprungen. Wo dieser 24:25-Zähler initialisiert wird, steh nicht im vorliegendem Abschnitt...
nicht das Ergebnis geschoben, sondern der Schleifenzähler ;)
???
"i" scheint R18 zu sein, die Schleife scheint über R24:R25 zu laufen.
"result" ist dann R28. Wären vier Register...

Mein Vorschlag war:


CodeBox Assembler
einsprunglabel:
LSL R28
SBIC 0x19, 3
INC R28
BRCC einsprunglabel

Result (R28) wird vor dem Start mit 0x01 beladen, nach achtmal schieben fällt die eins ins Carry, was sich in Zeile 5 auswirkt.
Letztendlich willst Du aber nicht im Kreis springen sondern stattdessen auf die Clock-Flanken reagieren...
 

Über uns

  • Makerconnect ist ein Forum, welches wir ausschließlich für einen Gedankenaustausch und als Diskussionsplattform für Interessierte bereitstellen, welche sich privat, durch das Studium oder beruflich mit Mikrocontroller- und Kleinstrechnersystemen beschäftigen wollen oder müssen ;-)
  • Dirk
  • Du bist noch kein Mitglied in unserer freundlichen Community? Werde Teil von uns und registriere dich in unserem Forum.
  •  Registriere dich

User Menu

 Kaffeezeit

  • Wir arbeiten hart daran sicherzustellen, dass unser Forum permanent online und schnell erreichbar ist, unsere Forensoftware auf dem aktuellsten Stand ist und der Server regelmäßig gewartet wird. Auch die Themen Datensicherheit und Datenschutz sind uns wichtig und hier sind wir auch ständig aktiv. Alles in allem, sorgen wir uns darum, dass alles Drumherum stimmt :-)

    Dir gefällt das Forum und unsere Arbeit und du möchtest uns unterstützen? Unterstütze uns durch deine Premium-Mitgliedschaft!
    Wir freuen uns auch über eine Spende für unsere Kaffeekasse :-)
    Vielen Dank! :ciao:


     Spende uns! (Paypal)