Forum: Mikrocontroller und Digitale Elektronik Fifo über SPI verschicken


von Hugo Zahnfee (Gast)


Lesenswert?

Hallo,

ich möchte gerne einen Fifo per SPI von einem Slave auf einen Master 
übertragen. Der Fifo auf dem Slave soll slavefifo hier heissen. Die 
Werte in dem slavefifo sind 16bit breit.

Per SPI kann ich nur 8bit Daten versenden. Hat jemand von Euch einen 
Link zu einer fertigen Routine, wie man größere Datenmengen darüber 
versenden kann?

von Hugo Zahnfee (Gast)


Lesenswert?

Mein eigenes C-Programm auf dem Slave sieht bisher so aus:
1
#include "mbed.h"
2
#include "ringbuffer.h"
3
4
SPISlave device(p11, p12, p13, p14);            // mosi, miso, sclk, ssel 
5
Serial pc(USBTX, USBRX);
6
7
RingBuffer mybuffer;
8
9
#define order_getfifolength  220
10
#define order_hole_elemente  221
11
12
void SendeElemente(uint16_t Anzahl) {
13
    uint16_t Zaehler,
14
             Element_16bit;
15
    uint8_t  Element1_8bit,
16
             Element2_8bit;
17
    
18
    for (Zaehler=0; Zaehler<Anzahl; Zaehler++) {
19
        Element_16bit = mybuffer.get();
20
        Element1_8bit = Element_16bit>>8;
21
        Element2_8bit = Element_16bit & 0xFF;
22
        device.reply(Element1_8bit);
23
        device.reply(Element2_8bit);
24
    }
25
}
26
27
int main() {
28
   pc.baud (115200); 
29
   
30
   device.frequency(1000000);
31
   
32
   pc.format   (8, SerialBase::None, 1);
33
   uint8_t i, befehl, elem1, elem2;
34
   
35
   uint16_t Anz_Elements=0;
36
   bool DatenSenden;
37
    
38
   pc.printf("\n\r  Programm xxx wurde neu gestartet... ");
39
   
40
   // Fifo mit Werten fuellen:
41
   for (i=0; i<30; i++) {
42
        mybuffer.put(0xF00+i);
43
        pc.printf("\n\r  (%i)     ", 0xF00+i);
44
   }
45
   
46
   // for (i=0; i<20; i++) { device.reply(i); }
47
   
48
   while (1) {   // Hauptschleife
49
    if (device.receive()) {  // Liegen Daten am SPI-Port an?, if(0)
50
        befehl = device.read();
51
        
52
        switch (befehl) {
53
            case (order_getfifolength):   
54
                Anz_Elements = mybuffer.Anzahl();
55
                elem1 = Anz_Elements>>8;
56
                elem2 = Anz_Elements & 0xFF;
57
                pc.printf ("\n\r fifo-laenge (%i),  elem1(%i), elem2(%i)", Anz_Elements, elem1, elem2);
58
                elem1=70;
59
                device.reply(elem1);
60
                device.reply(elem2);
61
                DatenSenden = true;
62
                while (DatenSenden) {
63
                    if (device.receive()) {
64
                        device.reply(elem2);
65
                        while (DatenSenden) {
66
                            // Warten, bis letztes Bit gesendet. 
67
                            if (device.receive()) {DatenSenden = false;}
68
                        }
69
                    } // ende if(device_receive)
70
                    
71
                } // Ende while(DatenSenden)
72
                
73
74
                // device.reply(Anz_Elements & 0xFF);
75
                break;
76
            case (order_hole_elemente):
77
                pc.printf ("\n\r hole data aus fifo ...");
78
                break;
79
            
80
        } // Ende switch
81
    } // Ende if(0)
82
   
83
   
84
   } // Ende Hauptschleife
85
  
86
  
87
}

Mein Pythonprogramm auf dem Raspberry Pi bisher so:
1
import RPi.GPIO as GPIO
2
3
import spidev
4
import time
5
6
val16 = 0b1100001101010101
7
val16 = 0b0000000100000000
8
9
Anzahl_Werte_in_fifo = 30
10
11
order_getfifolength = 220
12
order_hole_elemente = 221
13
14
15
spi = spidev.SpiDev()
16
spi.open (0, 1)
17
spi.max_speed_hz = 1000000
18
19
#antwort = spi.xfer2([0xFF])
20
#print (antwort)
21
#print ("44544")
22
a = val16>>8
23
b = val16 & 0xFF
24
c = (a<<8) + b
25
#print (bin(val16))
26
#print (bin(a))
27
#print (bin(b))
28
#print (bin(c))
29
30
print ("\n spi-readbytes-test:")
31
#result = spi.readbytes(25)
32
#print (result)
33
34
# Anzahl holende Werte senden:
35
36
37
# Sende Befehl, um Anzahl Elemente in Fifo zu ermitteln:
38
spi.xfer2([order_getfifolength])
39
time.sleep(0.01)
40
result=[]
41
result.append (spi.readbytes(1)[0])
42
result.append (spi.readbytes(1)[0])
43
print (result)
44
print (spi.readbytes(1))
45
anzahl = (result[0]<<8)+result[1]
46
print ("\n Die Anzahl der Elemente im Fifo ist: %s, result(%s)" %(str(anzahl), str(result)) )
47
time.sleep(1)
48
spi.xfer2([order_hole_elemente])
49
50
# Lese Anzahl der Werte (16bit)
51
result=[]
52
result = spi.xfer2([0, 0])
53
result=[]
54
result.append(12)
55
result.append(10)
56
print (result)
57
# 2x 8bit-Werte zu 16-bit Zahl zusammenfassen...
58
Anzahl_Elemente = (result[0]<<8)+result[1]
59
60
Anzahl_Elemente=30
61
62
Ergebnisse_8bit=[]
63
Ergebnisse_8bit = spi.readbytes(Anzahl_Elemente*2)
64
#print (Ergebnisse_8bit)
65
66
Ergebnisse_16bit=[]
67
for j in range (0, Anzahl_Elemente*2, 2):
68
    Ergebnisse_16bit.append( (Ergebnisse_8bit[j]<<8)+Ergebnisse_8bit[j+1] )
69
70
#print (Ergebnisse_16bit)
71
72
spi.close()

von Stefan S. (chiefeinherjar)


Lesenswert?

Warum nicht einfach 2x 8 Bit versenden und dann beim Empfänger wieder 
zusammensetzen? Bzw. im Zweifelsfalle eben nicht den Hardware-SS-Pin zu 
nehmen sondern eben einen beliebigen anderen Pin und dann eben eben zu 
Beginn der Übertragung diesen Pin auf LOW ziehen, erst die ersten 8 Bit 
und dann die zweiten 8 Bit übertragen und dann diesen Pin eben auf HIGH 
ziehen.

Wo liegt das Problem?

von Hugo Zahnfee (Gast)


Lesenswert?

Was ich gut hinbekommen habe, das ist die Zerlegung der 16bit-Werte in 
zwei 8bit-Werte mit dem Shifen >>8 und dem Unden & 0xFF - und das 
Zusammenbauen auf der Master-Seite.

Jetzt geht es darum, eine Möglichkeit zu finden, viele Werte 
hintereinander gut zu übertragen.

Mein Problem ist die Sache mit dem SPI-Port. Ich kann mir die Länge der 
zu übertragenden Daten ermitteln - nehmen wir mal an, ich möchte 30 
Stück aus meinem Fifo lesen.

Also muss der Master 60 mal 8bit-Werte holen (30 mal 16bit aus dem 
Fifo).

Ein einfacher schöner Befehl wäre, wenn ich auf dem Raspberry Pi unter 
Python das machen könnte:

spi.readbytes(Anzahl*2).

Dann bekomme ich eine Liste in Python mit 60 Elementen. Leider 
funktioniert das auf der Slave-Seite nicht, die Daten dann so 
bereitzustellen, dass ich den spi.readbytes-Befehl ausführen kann. Es 
kommt dann nur ein Wert an.

Auf der Slave-Seite muss ich mit dem Befehl

.reply(value); den Wert an den SPI-Port schreiben, damit er bei der 
nächsten Leseoperation ausgewertet werden kann.

Ich komme halt momentan auf keine neue Idee, wie ich das hinkriegen 
kann.

von Hugo Zahnfee (Gast)


Lesenswert?

Stefan Schmidt schrieb:
> Warum nicht einfach 2x 8 Bit versenden und dann beim Empfänger wieder
> zusammensetzen?

das klappt ja schon.

Stefan Schmidt schrieb:
> Bzw. im Zweifelsfalle eben nicht den Hardware-SS-Pin zu
> nehmen sondern eben einen beliebigen ...

Meinst Du, ich muss mir einen Software-SPI-Port entwickeln und nicht den 
Hardware-SPI benutzen?

von H. D. (lactobacillus)


Lesenswert?

Hugo Zahnfee schrieb:
> Stefan Schmidt schrieb:
>> Bzw. im Zweifelsfalle eben nicht den Hardware-SS-Pin zu
>> nehmen sondern eben einen beliebigen ...
>
> Meinst Du, ich muss mir einen Software-SPI-Port entwickeln und nicht den
> Hardware-SPI benutzen?

Entweder du bleibst bei 8-Bit oder du benutzt ein eigenes 
16-BIT-Software-SPI.

--> Zerleg es is 2 Teile und nimm Hardware-SPI

von Hugo Zahnfee (Gast)


Lesenswert?

Die Kombination funktioniert:
1
Microcontroller (C):
2
3
for (i=0; i<30; i++) { device.reply(i); }
4
5
Master (Python):
6
7
for kkkk in range (0, 30):
8
    result.append(spi.readbytes(1)[0])

Was nicht geht ist das hier:

Microcontroller(C):

for (i=0; i<30; i++) { device.reply(i); }

Master (Python):

result = spi.readbytes(30)

[/c]


Ich verstehe einfach nicht, warum das mit dem spi.readbytes(30) nicht in 
einem Rutsch funktionieren tut :-(

von Stefan S. (chiefeinherjar)


Lesenswert?

In Python kenne ich mich nicht wirklich aus, aber prinzipiell habe ich 
es wie folgt implementiert gehabt:
Ich habe einen beliebigen freien Pin (beispielsweise Pin PC1) mit dem 
SS-Pin vom Slave verbunden. Die restlichen Hardware-SPI-Pins habe ich 
beibehalten und alles entsprechend verkabelt.

Dann habe ich unmittelbar vor dem Aktivieren der Übertragung mittels SPI 
einfach den Pin PC1 auf Low gezogen und die ersten 8 Bit ganz normal 
übertragen.
Nach dem die Übertragung fertig ist wird ja der eigentliche 
Hardware-SS-Pin wieder auf High gezogen. Dies juckt den Slave ja nicht, 
da der Pin PC1 immer noch auf low ist.
Daraufhin habe ich erneut eine SPI-Übertragung mit den zweiten 8 Bit 
gestartet und NACH DEM diese beendet war den Pin PC1 auf High gezogen.

So sieht es für den Slave aus, als ob die 16 Bit am Stück übertragen 
wurden. Hauptsache der SS-Pin VOM SLAVE ist Low während der gesamten 
Übertragung(en).

von Stefan S. (chiefeinherjar)


Lesenswert?

In C würde das, was ich beschrieben habe so aussehen, wobei "command" 
der 16 Bit Befehl/Wert ist, welcher übertragen werden soll.
1
for(int i = 0; i<2; i++){
2
   PORTC &= ~(1<<PC1); //PIN PC1 auf Low ziehen um dem Slave den Start der Übertragung zu signalisieren
3
   uint8_t byte_to_send = (command && 0x00FF); //Die unteren 8 Bit maskieren
4
   spi_send(byte_to_send); //Übertragen der unteren 8 Bit
5
   byte_to_send = command >> 8; //Die oberen 8 Bit maskieren
6
   spi_send(byte_to_send); // Übertragen der oberen 8 Bit
7
   PORTC |= (1<<PC1); //PIN PC1 auf High ziehen um dem Slave die Beendigung der Übertragung zu Signalisieren
8
}

von sushi (Gast)


Lesenswert?

Hugo Zahnfee schrieb:
> Die Kombination funktioniert:
> Microcontroller (C):
>
> for (i=0; i<30; i++) { device.reply(i); }
>
> Master (Python):
>
> for kkkk in range (0, 30):
>     result.append(spi.readbytes(1)[0])
>
> Was nicht geht ist das hier:
>
> Microcontroller(C):
>
> for (i=0; i<30; i++) { device.reply(i); }
>
> Master (Python):
>
> result = spi.readbytes(30)


Also es scheint daran zu liegen, dass dein uC die Daten nicht 
rechtzeitig in den Buffer legen kann. Mit der funktionierenden Variante 
wird vermutlich für jedes Byte die SS Leitung auf Low gezogen und nach 
dem einen Byte wieder losgelassen. Das passiert 30 mal. Dabei hat der uC 
genug Zeit seinen Buffer zu füllen.
Bei der nicht funktionieren Variante wird die SS Leitung nur einmal Low 
gezogen, dann werden schnell 30 Bytes versendet und danach wird die SS 
Leitung losgelassen.

von Stefan S. (chiefeinherjar)


Lesenswert?

sushi schrieb:
> Bei der nicht funktionieren Variante wird die SS Leitung nur einmal Low
> gezogen, dann werden schnell 30 Bytes versendet und danach wird die SS
> Leitung losgelassen.

Das wird das Problem sein. Ich habe mir den Quelltext nicht genau 
angesehen, aber ausgehend davon, dass der µC nur einen Puffer von 8 Bit 
hat muss natürlich JEDES MAL die SS-Leitung losgelassen werden.

Ich habe ursprünglich nur auf die Anforderung geantwortet gehabt, mehr 
als 8 Bit auf einmal zu versenden. Habe angenommen, dass ein IC/Slave 
angesprochen werden sollte, welches eben einen (beispielsweise) 16 Bit 
langen Befehl braucht bzw. dessen Puffer groß genug ist.

von sushi (Gast)


Lesenswert?

Es kann aber auch sein, dass dein Chip dir per Interrupt Bescheid gibt, 
dass dein Puffer sich leert, und du dann dort schnell weitere Bits 
hineinschieben kannst...

von isidor (Gast)


Lesenswert?

Stefan Schmidt schrieb:
> Das wird das Problem sein. Ich habe mir den Quelltext nicht genau
> angesehen, aber ausgehend davon, dass der µC nur einen Puffer von 8 Bit
> hat muss natürlich JEDES MAL die SS-Leitung losgelassen werden.

So ist es. Ein SPI Slave hat keine andere (sichere) Möglichkeit
signalisiert zu bekommen dass ein Datum eintrifft. Es sei denn
er zählt die Clocks einzeln mit.

von Hugo Zahnfee (Gast)


Lesenswert?

sushi schrieb:
> Bei der nicht funktionieren Variante wird die SS Leitung nur einmal Low
> gezogen, dann werden schnell 30 Bytes versendet und danach wird die SS
> Leitung losgelassen.

Ach so - Du meinst, der Buffer kann erst dann befüllt werden, wenn CS 
seinen Pegel ändert und "fertig" ist?

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.