DSPIC33 SPI-Master-Chipauswahlleitung funktioniert nicht

Ich habe mich sehr bemüht, den SPI-Bus auf einem DSPIC33EP128GM604 zum Laufen zu bringen. Ich habe SPI3 als Standard-8-Bit-Master-Modul konfiguriert und einen Chip-Select-Pin festgelegt, damit ich Slave-Geräte umschalten kann.

Ich bekomme die Chip-Select-Leitung nicht richtig zum Laufen, es scheint, als ob der DSPIC nicht auf den Abschluss der Übertragung wartet, bevor er die Chip-Select-Leitung von LOW auf HIGH umschaltet. Ich habe die Erratas überprüft und nichts scheint für dieses Problem relevant zu sein. Außerdem habe ich versucht, die CS-Leitung auf einen anderen Pin zu tauschen. Ich habe auch SPI-Modul 1 ausprobiert, aber das Problem besteht weiterhin.

Hier ist mein Code:

SPI 3-Konfiguration:

void Init_SPI3 ( void )
{
IFS5bits.SPI3IF = 0; // Clear the Interrupt flag
IEC5bits.SPI3IE = 0; // Disable the interrupt
// SPI1CON1 Register Settings
SPI3CON1bits.DISSCK = 0; // Internal serial clock is enabled
SPI3CON1bits.DISSDO = 0; // SDOx pin is controlled by the module
SPI3CON1bits.MODE16 = 0; // Communication is word-wide (16 bits)
SPI3CON1bits.SSEN = 0; //No CS
SPI3CON1bits.MSTEN = 1; // Master mode disabled
SPI3CON1bits.PPRE = 2; //Pre-scaler
SPI3CON1bits.SPRE = 8;//Pre-scaler 2
SPI3CON1bits.SMP =1; // Input data is sampled at the middle of data output time
SPI3CON1bits.CKE =0; // Serial output data changes on transition from
SPI3CON1bits.CKP = 0; // Idle state for clock is a low level;
SPI3STATbits.SPIEN = 1; // Enable SPI module
// Interrupt Controller Settings
IFS5bits.SPI3IF = 0; // Clear the Interrupt flag
IEC5bits.SPI3IE = 1; // Enable the interrupt
}

SPI 3-Pin-Konfiguration:

TRISBbits.TRISB10 = 0; //Set SPI3CLK as output
TRISBbits.TRISB11 = 0; //Set SPI3DATAOUT as output

RPOR4bits.RP42R=32;  //SPI3_CLK out
RPOR4bits.RP43R=31;  //SPI3_DATA out

Pin-Konfiguration der Chipauswahlleitung:

TRISBbits.TRISB4 = 0; //SPI3 CS is on Pin B4, set it to Output

Einfache Hauptroutine:

    while(1==1){

    PORTBbits.RB4 = 0;//Set CS line to low  
   WriteSPI3(0xaa); //This is from the peripheral library to be safe....
  PORTBbits.RB4 = 1; //Set CS line to high
                    }

Aber hier ist die Ausgabe:

Geben Sie hier die Bildbeschreibung ein

Was ist speziell das Problem, das ich auf dem Scope-Bild sehen sollte?
Wo stellen Sie den SPI-Modus mit CPOL und CPHA ein
die Bits .CKE und .CKP?
Entschuldigung - es ist lange her, dass ich einen dsPIC verwendet habe, und ich bin an STM32F-CMSIS-Anrufe gewöhnt. Sind Sie sicher, dass diese Werte für das Gerät, mit dem Sie sprechen möchten, richtig sind?
Das habe ich noch gar nicht probiert. Das Problem ist, dass die CS-Leitung nicht richtig umgeschaltet wird. Wenn Sie sich die violette Spur (CS) ansehen, ändert sie ihren Zustand vorzeitig. Ich habe DSPIC tbh ziemlich satt, ich gebe ihm noch einen Tag, bevor ich zu den STM32s wechsle ....
Warum können Sie das CS-Bit nicht niedrig lassen und auf die andere Seite umschalten, kurz bevor Sie es hoch setzen? Steht etwas im Weg? Stellen Sie das entsprechende CS direkt vor der Kommunikation ein und lassen Sie es dort bis zur nächsten Schleife.
Afaik, das ist ein Teil mit erweitertem Modus. Die Bedeutung der Flags ändert sich im erweiterten Modus. Stellen Sie sicher, dass Sie den erweiterten Modus ausschalten (siehe spi<x>con2.SPIBEN-Bit)

Antworten (2)

Aus dem Datenblatt und den Beispielcodes reicht das Receive Buffer Flag aus, um uns mitzuteilen, wann das TX-Ereignis abgeschlossen ist und der CS somit umschalten kann. Es ist etwas seltsam, warum es für diesen Chip kein Flag gibt, das uns sagt, wann das SPI-Schieberegister alle seine Daten übertragen hat. Der gesamte Beispielcode überprüft nur das Empfangspuffer-Flag.

Nach viel Kopfkratzen, Versuch und Irrtum und schließlich den Mikrochip-Foren kann ich bestätigen, dass dieser Code funktioniert :

   uint8_t Write_SPI1(uint8_t command) { //Does work    
    SPI1BUF = command; // send data over SPI
    while(!SPI1STATbits.SPIRBF) ; //wait until SPIRBF goes high  
    return SPI1BUF; //KEY STATEMENT THAT MADE A DIFFERENCE, Return the actual data received
    }

Wenn Ihr Peripheriegerät von der Peripheral Pin Select (SPI2, SPI3 usw.) abhängt, ist es neben diesem Code wichtig, nicht nur einen SPI-Clock-Out, sondern auch einen SPI-Clock-In zu definieren, selbst wenn Sie sich im Master-Modus befinden

Was mich jedoch ziemlich verblüfft, ist, dass dieser Code (von dem ich denke, dass er dasselbe tun sollte) nicht funktioniert:

  void Write_SPI1(uint8_t command) { //Does not work, premature toggle of CS, tried this code initially....
    uint8_t garbage;
    SPI1BUF = command; // send data over SPI
    while(!SPI1STATbits.SPIRBF) ; //wait until SPIRBF goes high 
    garbage = SPI1BUF; // read dummy data, does not seem to clear the flag as expected
    }
Seltsam. Nur ein Gedanke - haben Sie versucht, die Optimierung vor dem Kompilieren auszuschalten? Sieht so aus, als würde Ihr Compiler Verknüpfungen verwenden, die Sie nicht verwenden möchten, und das Deaktivieren der Optimierung könnte die Dinge verbessern.
Tatsächlich könnte der Compiler "Müll" als nie verwendet sehen und die gesamte Zeile optimieren. Es kann einen Rückgabewert nicht wegoptimieren.
... oder versuchen Sie, der Garbage-Deklaration volatile hinzuzufügen.
Volatile hinzufügen funktioniert nicht. Aber das Zurückgeben von Müll anstelle von SPI1BUF funktioniert auch. Hmmm, klingt nach einem Compiler-Perk.

Warten Sie, bis die SPI-Übertragung abgeschlossen ist, bevor Sie die Chipauswahl mit so etwas wie deaktivieren

 while(SPI3_Tx_Buf_Full);  //wait till completion of transmission

Sehen Sie sich das Beispiel mit SPI1 unter http://pat.cybersites.ca/docs/PIC24F/SPI_Example1.html an

Ich nahm an, dass sich die periphere Bibliothek darum kümmern würde. Trotzdem habe ich das entsprechende Flag für meinen Chip gefunden: while(SPI3STATbits.SPITBF){};Macht keinen Unterschied, wird das Flag vom Modul irgendwie falsch gesetzt?
SPITBFzeigt nur an, ob sich Bits im Puffer befinden oder nicht, nicht, ob noch gesendet wird. Versuchen Sie nach der whilehinzugefügten Anweisung while(!SPI3STATbits.SPIRBF)zu warten, bis die Übertragung beendet ist und etwas in den Empfangspuffer gestellt wurde (auch wenn Sie nichts damit tun).
SPI3_Tx_Buf_Fullsollte ein Makro sein, das das richtige Bit testet. Sie behaupten CS selbst mit PORTBbits.RB4 = 0, also müssen Sie es auch selbst zurücknehmen. Sie müssen nur warten, bis das Peripheriegerät die Bits herausgeschoben hat. Wenn die Bibliothek es selbst NACH dem Starten einer Übertragung macht, würde das nur CPU-Zyklen verschwenden, die Sie vielleicht auf andere Weise verwenden möchten. Es kann einen eingebauten Test geben, bevor der nächste Wert in geschrieben wird WriteSPI3. Aber das ist vor dem Starten des Peripheriegeräts, nicht danach
Ich habe versucht hinzuzufügen: while(!SPI3STATbits.SPIRBF): Keine Änderung, außer der Tatsache, dass der CS etwas länger behauptet wird und ich nur eine Übertragung erhalte, nach der das Bild nichts tut, so dass es bei dieser While-Anweisung eindeutig eine Schleife durchläuft.
@AdilMalik Sie verwechseln den TX-Puffer mit dem TX-Schieberegister. Aus dem Schieberegister werden die Bits tatsächlich gesendet, während Ihr Code sie aus dem Puffer lädt. Das Modul überträgt Ihr Datenbyte intern aus dem Puffer in das Schieberegister, während es bereit ist, es zu senden (was Ihnen eine SPITBF-Flag-Anzeige gibt), aber zu diesem Zeitpunkt befinden sich die Bits noch im Schieberegister und wurden nicht gesendet noch. Wenn Sie SPIRBF als Flag verwenden, um Ihnen mitzuteilen, wann die Transaktion abgeschlossen ist, müssen Sie sicherstellen, dass Sie zuerst den RX-Puffer leeren, damit das SPIRBF-Flag gelöscht beginnt.
Vielleicht könnte SPIxSTAT.SPIBUSY funktionieren, wenn SPITBE es nicht tut.
Oder setzen Sie SPIxSTAT.SISEL auf Modus 5 (0b101) und verwenden Sie den SPI-Interrupt als Flag, um anzuzeigen, wann das letzte Bit gesendet wurde.
Alle Vorschläge ausprobiert, nichts scheint zu funktionieren: 1) SPIxSTAT.SPIBUSY ist kein gültiges Bit für mein Gerät 2) Erstellt eine Interrupt-Funktion: void __attribute__((interrupt,auto_psv)) _SPI3Interrupt(void) { PORTBbits.RB4 = 1; }und setze .SISEL auf 5. Immer noch keine Änderung. Ich fange an zu denken, dass dies ein möglicher Fehler ist?
Ich nehme an, Sie haben daran gedacht , RB4=1 in Ihrer Hauptschleife auszukommentieren ...? Obwohl ich eigentlich keine Interrupt-Funktion verwenden würde - warten Sie einfach in Ihrer Hauptschleife darauf, dass das Interrupt-Flag mit while (!whatever_the_irq_flag_bit_is) gesetzt wird;
Und wenn Sie die SPIxRBF-Methode ausprobieren, stellen Sie sicher, dass das RBF-Flag gelöscht ist, bevor Sie mit der Übertragung beginnen.
@AdilMalik - "Ist das ein möglicher Fehler?" Angenommen, Ihr -CS-Signal ist die violette Linie auf Ihrem Scope-Foto, dann würde ich nicht nach einem DSPIC33-Fehler suchen. Das liegt daran, dass ein -CS-Signal wie diese lila Linie niemals für einen normalen SPI-Slave funktionieren würde, und ein so schwerwiegender Fehler wie dieser wäre bekannt. Ich verwende diese MCU nicht, aber wenn ich an Ihrer Stelle wäre, würde ich zusätzlich zu den hilfreichen Ratschlägen, die Sie erhalten, damit beginnen, nach einem funktionierenden Beispiel-SPI-Code zu suchen. Hier ist ein EE.SE-Thread, der ein angeblich funktionierendes SPI-Master-Beispiel enthält .
Ich habe mir viele von Microchip bereitgestellte SPI-Beispiele angesehen. Zunächst einmal hat der von ihnen bereitgestellte Code widersprüchliche Werte dafür, wann sich das Flag im TX-Zustand befindet usw. Die Kernmethode ist jedoch genauso, wie viele der oben genannten Personen vorgeschlagen haben. Hier ist einer aus dem Abschnitt „Beispielcode“ des DSPIC.
void Write_SPI1 ( int16_t command ) { int16_t temp; PORTBbits.RB7 = 0; // lower the slave select line temp = SPI1BUF; // dummy read of the SPI1BUF register to clear the SPIRBF flag SPI1BUF = command; // write the data out to the SPI peripheral while( !SPI1STATbits.SPITBF ) // wait for the data to be sent out ; PORTBbits.RB7 = 1; // raise the slave select line }
void Write_SPI2 ( int16_t command ) { int16_t temp; PORTBbits.RB6 = 0; // lower the slave select line temp = SPI1BUF; // dummy read of the SPI1BUF register to clear the SPIRBF flag SPI1BUF = command; // write the data out to the SPI peripheral while( !SPI1STATbits.SPIRBF ) // wait for the data to be sent out ; PORTBbits.RB6 = 1; // raise the slave select line }
Für die funktional gleiche Routine prüfen sie also 2 unterschiedliche Flags, die Idee ist jedoch dieselbe. Es funktioniert in beiden Fällen nicht. Aber wenn ich ein paar Zyklen mit einer for-Schleife verschwende, schaltet der CS-Pin wie erwartet korrekt um