Forum: Mikrocontroller und Digitale Elektronik SPI-Bus verliert Werte


von Hugo Gruffy Duck (Gast)


Lesenswert?

Hi,

ich habe einen Raspberry Pi und einen mbed Nucleo ST32F411RE miteinander 
verbunden. Mittels SPI-Bus möchte ich vom Raspberry Pi Daten an den mbed 
senden und empfangen.

Das ist das Pythonprogramm auf dem Raspberry Pi:
1
try:
2
    import tkinter as tk
3
except:
4
    import Tkinter as tk
5
    
6
import spidev
7
    
8
spi = spidev.SpiDev()
9
spi.open (0, 0)
10
import time
11
# spi.max_speed_hz = 1000000
12
    
13
14
button_width = 40
15
16
def bit_8_to_16(value=[]):    
17
    return ( (value[0]<<8)+value[1] )
18
    
19
def bit_16_to_8(value=0):
20
    return_value = []
21
    return_value.append(value>>8)
22
    return_value.append(value&0x00FF)
23
    return return_value
24
25
def main():
26
    root = tk.Tk()
27
    
28
    def do_exit():
29
        print ("PROGRAMM BEENDET")
30
        root.quit()
31
    
32
    def do_adc_task_isrunning():
33
        print ("ADC TASK RUNNING")
34
        spi.writebytes([0x00, 0xA0])
35
                
36
    def do_adc_task_start():
37
        print ("ADC TASK START")
38
        spi.writebytes([0x00, 0xA1])
39
                
40
    def do_adc_task_stopp():
41
        print ("ADC TASK STOPP")
42
        spi.writebytes([0x00, 0xA2])
43
                
44
    def do_adc_task_setfrequency():
45
        print ("ADC TASK SETFREQUENCY")
46
        spi.writebytes([0x00, 0xA3])
47
        time.sleep(0.1)  
48
        spi.writebytes([0x00, 100])      
49
        
50
    def do_fifo_anzahl_elements():
51
        print ("FIFO ANZAHL ELEMENTS")
52
        spi.writebytes([0x00, 0xB0])
53
        
54
        
55
    def do_fifo_get_one_element():
56
        print ("FIFO GET ONE ELEMENT")
57
        spi.writebytes([0x00, 0xB1])
58
        print (spi.readbytes(2))
59
        
60
    def do_fifo_get_elements():
61
        print ("FIFO GET ELEMENTS")        
62
        spi.writebytes([0x00, 0xB2])    
63
    
64
    #ADC_TASK_ISRUNNING      = 0x00A0,
65
    #ADC_TASK_START          = 0x00A1,
66
    #ADC_TASK_STOPP          = 0x00A2,
67
    #ADC_TASK_SETFREQUENCY   = 0x00A3,
68
    
69
    #FIFO_ANZAHL_ELEMENTS    = 0x00B0,
70
    #FIFO_GET_ONE_ELEMENT    = 0x00B1,
71
    #FIFO_GET_ELEMENTS       = 0x00B2
72
    
73
    button_1 = tk.Button(text="ADC_TASK_START", width=button_width, command=do_adc_task_start )
74
    button_1.pack()
75
    button_2 = tk.Button(text="ADC_TASK_STOPP", width=button_width, command=do_adc_task_stopp )
76
    button_2.pack()
77
    button_3 = tk.Button(text="ADC_TASK_SETFREQUENCY", width=button_width, command=do_adc_task_setfrequency )
78
    button_3.pack()
79
    button_4 = tk.Button(text="ADC_TASK_ISRUNNING", width=button_width, command=do_adc_task_isrunning )
80
    button_4.pack()
81
    button_5 = tk.Button(text="FIFO_ANZAHL_ELEMENTS", width=button_width, command=do_fifo_anzahl_elements )
82
    button_5.pack()
83
    button_6 = tk.Button(text="FIFO_GET_ONE_ELEMENT", width=button_width, command=do_fifo_get_one_element )
84
    button_6.pack()
85
    button_7 = tk.Button(text="FIFO_GET_ELEMENTS", width=button_width, command=do_fifo_get_elements )
86
    button_7.pack()    
87
    
88
    exit_button = tk.Button (text="Exit", command=do_exit, width=button_width)
89
    exit_button.pack()    
90
    
91
    root.mainloop()
92
93
if __name__ == "__main__":
94
    main()
95
    spi.close()

Das ist das C-Programm auf dem mbed:
1
#include "mbed.h"
2
#include "read_adc.h"
3
#include "ringbuffer.h"
4
#include "spi_commands.h"
5
6
#ifdef TARGET_NUCLEO_F411RE
7
    SPISlave spidevice(PA_7, PA_6, PA_5, PA_15);            // mosi, miso, sclk, ssel 
8
#endif
9
10
#ifdef TARGET_LPC1768
11
    SPISlave spidevice(p11, p12, p13, p14);            // mosi, miso, sclk, ssel 
12
#endif
13
14
// Ringbuffer initialisieren:
15
    RingBuffer ADC_Fifo;
16
    RingBuffer Error_Fifo;
17
    
18
// ADC_Interrupt initialisieren
19
    ReadADCValues read_ADC_values;
20
21
Serial pc(USBTX, USBRX);
22
23
uint32_t help, get_element=0;
24
25
26
void spi_evaluation(uint16_t command) {
27
    bool abort = false;
28
    switch (command) {
29
        case ADC_TASK_ISRUNNING: 
30
            pc.printf ("\n\r ADC_TASK_ISRUNNING (%d)", read_ADC_values.is_active);
31
            break;
32
33
        case ADC_TASK_START:
34
            pc.printf ("\n\r ADC_TASK_START");
35
            read_ADC_values.start();
36
            break;
37
38
        case ADC_TASK_STOPP:
39
            pc.printf ("\n\r ADC_TASK_STOPP");
40
            read_ADC_values.stopp();
41
            break;
42
43
        case ADC_TASK_SETFREQUENCY:
44
            pc.printf ("\n\r ADC_TASK_FREQUENCY");
45
            abort=true;
46
            while (abort) {
47
                if (spidevice.receive()) {
48
                    abort = false;
49
                    help=spidevice.read();
50
//                    read_ADC_values.set_sampling_rate( spidevice.read() );                    
51
                }
52
            }  // Ende while
53
            
54
            pc.printf ("\n\r NEW_FREQUENCY (%d)", help);
55
            
56
            break;            
57
58
        case FIFO_ANZAHL_ELEMENTS:
59
            pc.printf ("\n\r FIFO ANZAHL ELEMENTS");
60
            break;            
61
        
62
        case FIFO_GET_ONE_ELEMENT:
63
            // pc.printf ("\n\r FIFO GET ONE ELEMENT");
64
            spidevice.reply(get_element);
65
            get_element++;
66
            break;            
67
        
68
        case FIFO_GET_ELEMENTS:
69
            pc.printf ("\n\r FIFO GET ELEMENTS");
70
            break; 
71
    } // Switch Ende           
72
}
73
74
int main () {
75
    // uart konfigurieren
76
    pc.baud (921600);
77
    pc.printf ("\n\r Programm wurde gestartet");
78
    
79
    // spi konfigurieren
80
    spidevice.format (16, 0);
81
    // spidevice.frequency(1000000);
82
    
83
    // ADC-Interrupt konfigurieren
84
    read_ADC_values.init();
85
 
86
    while (1) {
87
        if (spidevice.receive()) {
88
             spi_evaluation (spidevice.read());
89
        }  // Ende if
90
91
     } // Hauptschleife Programm
92
}

Hier ist noch die Datei SPI_COMMANDS.h
1
/*
2
In dieser Datei werden die Hexwerte für die SPI-Befehle, die vom 
3
SPI-Master kommen, mit lesbaren Worten codiert. 
4
*/
5
6
#ifndef SPI_COMMANDS_H
7
#define SPI_COMMANDS_H
8
9
enum SPICommands {
10
    ADC_TASK_ISRUNNING      = 0x00A0,
11
    ADC_TASK_START          = 0x00A1,
12
    ADC_TASK_STOPP          = 0x00A2,
13
    ADC_TASK_SETFREQUENCY   = 0x00A3,
14
    
15
    FIFO_ANZAHL_ELEMENTS    = 0x00B0,
16
    FIFO_GET_ONE_ELEMENT    = 0x00B1,
17
    FIFO_GET_ELEMENTS       = 0x00B2
18
};
19
#endif

Die anderen Headerdateien wie der Fifo werden in dem aktuellen Programm 
noch nicht benutzt. Die mbed.h wird vom Internetcompiler bereitgestellt.

Mein Problem ist folgendes:

Drücke ich mehrmals hintereinander schnell auf dem Raspberry den Button 
"FIFO GET ONE ELEMENT", dan bekomme ich einen Wert zurück, der bei jedem 
Aufruf um einen Wert nach oben addiert wird. Manchmal bekomme ich ein 
paar mal hintereinander den gleichen Wert - woran liegt das?

Mit dem Button "ADC TASK FREQUENCY" möchte ich später die Zeit eines 
Timers einstellen. Dazu möchte ich einen Wert an den mbed senden. Nicht 
immer wird der Wert empfangen und dann wartet das Programm in der 
Schleife:
1
        case ADC_TASK_SETFREQUENCY:
2
            ...
3
            while (abort) {
4
                if (spidevice.receive()) {
5
             ...

obwohl vom Raspberry Pi gleich danach noch ein Wert gesendet wird:
1
    def do_adc_task_setfrequency():
2
        print ("ADC TASK SETFREQUENCY")
3
        spi.writebytes([0x00, 0xA3])
4
        time.sleep(0.1)  
5
        spi.writebytes([0x00, 100])

Es ist sogar extra noch ein sleep-Befehl dadrin, weil ich dachte, dass 
der mbed vielleicht nicht schnell genug ist und etwas Zeit braucht, bis 
die while-Schleife etc. verarbeitet ist.

Die Kommunikation funktioniert also nicht fehlerfrei zwischen dem 
Raspberry Pi und dem mbed und ich habe keine Idee, wie ich das Problem 
in den Griff kriegen kann.

Ich möchte später z.B. in einem Rutsch hintereinander 100 Messwerte vom 
mbed an den Raspberry Pi senden - aber wenn es schon bei einem Wert 
nicht richtig funktioniert und manche Werte doppelt empfangen werden, 
dann ist das problematisch. Woran könnte das liegen?

von (prx) A. K. (prx)


Lesenswert?

Kann es sein, dass sich SPI und UART beim Zeitverhalten in die Quere 
kommen? Eine nicht tief gepufferte UART Ausgabe blockiert den µC.

von Hugo Gruffy Duck (Gast)


Lesenswert?

A. K. schrieb:
> Kann es sein, dass sich SPI und UART beim Zeitverhalten in die Quere
> kommen? Eine nicht tief gepufferte UART Ausgabe blockiert den µC.
1
        case FIFO_GET_ONE_ELEMENT:
2
            // pc.printf ("\n\r FIFO GET ONE ELEMENT");
3
            spidevice.reply(get_element);
4
            get_element++;
5
            break;

Ich hab die printf-Anweisung schon herausgenommen. Oder meinst Du, die 
Printf-Anweisung auf Seiten des Raspberry Pis kann auch problematisch 
sein?

von Hugo Gruffy Duck (Gast)


Lesenswert?

Ich hab das Programm jetzt etwas abgeändert. Auf dem mbed:
1
        case FIFO_GET_ONE_ELEMENT:
2
            // pc.printf ("\n\r FIFO GET ONE ELEMENT");
3
            spidevice.reply(get_element);
4
            get_element++;
5
            break;

Auf dem Raspberry Pi:
1
    def do_fifo_get_one_element():
2
        print ("FIFO GET ONE ELEMENT")
3
        elements=[]
4
        for lalala in range (0, 10):
5
            spi.writebytes([0x00, 0xB1])
6
            time.sleep(0.00001)
7
            elements.append(spi.readbytes(2)[1])
8
            # print (spi.readbytes(2))
9
            time.sleep(0.00001)
10
        print (elements)

Während der SPI-Kommunikation sind nun keine print-Anweisungen mehr 
vorhanden. Dennoch kommt es zum Datenverlust - nicht immer werden die 
10ner-Blöcke sauber übertragen.

von dunno.. (Gast)


Lesenswert?

Hugo Gruffy Duck schrieb:
> time.sleep(0.00001)

Ich bezweifle dass du es schaffst in ner scriptsprache auf nem raspi 
10µS zu warten. Und selbst wenn, ist denn der MBED in der lage in den 
10µS:
-das SPI Kommando in die Main-Loop zu bekommen
-auszuwerten
-und das ergebnis wieder in den spi-buffer zu schreiben?

spendiere zum test mal deutlich mehr zeit. wenns dann noch nicht 
funktioniert, ist der fehler woanders.

von Hugo Gruffy Duck (Gast)


Lesenswert?

dunno.. schrieb:
> Ich bezweifle dass du es schaffst in ner scriptsprache auf nem raspi
> 10µS zu warten.

Warum bezweifelst Du das? Meinst Du, der Raspi ist schneller oder eher 
langsamer?

Ich hatte auch schon vorher längere Intervalle angegeben, sogar mal eine 
ganze Sekunde, was ja eigentlich ne Ewigkeit für den mbed und den 
Raspberry sein müsste.

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.