Warum muss ich beim 4-Draht-SPI-Lesevorgang bei jedem Sendevorgang die MISO-Leitung lesen?

Ich möchte ein SPI-Slave-Gerät auslesen. So habe ich mir den Ablauf vorgestellt:

CS_LOW

  1. SPI_Write (Slave_Adresse)
  2. SPI_Write(register)
  3. SPI_Write (0x00) // Dummy-Byte
  4. Daten = SPI_Read()

CS_HIGH

Dies funktioniert jedoch nicht. Damit es funktioniert, muss ich Folgendes befolgen:

CS_LOW

  1. SPI_Write (Slave_Adresse)
  2. temp = SPI_Read()
  3. SPI_Write(register)
  4. temp = SPI_Read()
  5. SPI_Write (0x00) // Dummy-Byte
  6. Daten = SPI_Read()

CS_HIGH

Warum muss ich die Daten auf der MISO-Leitung lesen, auch wenn ich weiß, dass nichts wünschenswert ist?

PS: Die SPI-Funktionen Read() und Write() sind nur meine eigenen Implementierungen, nichts Besonderes für eine einzelne MCU.

Bearbeiten: Wie gewünscht, liefere ich einige Details zu den MCUs.

Ich verwende zwei MCUs - STM32F303VCT und TI TIVA123GH6PM . Beide MCUs verlangen, dass ich das genannte Verfahren befolge.

Hier ist meine Implementierung der SPI-Sende- und Empfangsfunktion auf STM32

uint8_t SPI_rdwr(uint8_t data)
{
    
    *(__IO uint8_t *)&SPI1->DR = data;              //send data
    
    while(!(SPI1->SR & SPI_SR_TXE));                // wait till TX buffer is empty
    
    while(SPI1->SR & SPI_SR_BSY);                  // wait if SPI line is busy
    
    while(!(SPI1->SR & SPI_SR_RXNE));               //wait till RX buffe is NOT empty                   

    return *(__IO uint8_t *)&SPI1->DR;          //return data if data received on MISO line
    
}
Es liegt einfach in der Natur der Vollduplex-Datenübertragung.
Ob es funktioniert oder nicht, hängt von der MCU und der SPI-Implementierung ab, die es hat, und dem Code, den Sie geschrieben haben, um es zu verwenden. Würden Sie offenlegen, welche MCU Sie verwenden, um dies zu beantworten?
Es kann sein, dass SPI_Read die READ-Daten in die CPU taktet ... und dadurch auch die WRITE-Daten an das Peripheriegerät austaktet. Wenn ja, wird es nicht funktionieren, es wegzulassen.
Oder die SPI-Hardware hat einen Lesepuffer, der zB ein Byte empfangen kann und nicht vom Schieberegister aktualisiert wird, wenn es nicht gelesen wird. Wir wissen es nicht, es sei denn, @Luffy gibt uns die genaue MCU-Marke / das genaue Modell und den Code, um zu sehen, was falsch ist.
Da es sich um Ihre eigene Implementierung handelt, teilen Sie uns bitte mit, was die Funktion SPI_Read() macht
@Justme Ich habe einige Details als Bearbeitung hinzugefügt.
@ user253751 Ich habe jetzt einige Details hinzugefügt. Bitte schauen Sie hinein. In meinem Unterprogramm habe ich sowohl Sende- als auch Empfangsprozeduren zu einer kombiniert. Ich habe eine separate Sendefunktion, bei der ich nur 1. in das Datenregister schreibe und 2. warte, bis der TX-Puffer leer ist. Ich verwende die Sendefunktion, wenn ich nur in ein bestimmtes Register auf dem Slave-Gerät schreiben möchte.
Dann ist meine Vermutung: Wenn der Empfangspuffer voll ist, erhalten Sie einen Fehlerzustand, der SPI stoppt.
@Luffy Aber dein spi_rdwr stimmt nicht mit dem überein, was du zuerst gefragt hast. Es liegt am Slave, wie es funktioniert, aber Ihr rdwr sollte für den Vollduplex-Betrieb einwandfrei funktionieren, und tatsächlich erfordert STM32 SPI, dass Sie den Empfangspuffer lesen, oder er wird nicht mit den empfangenen Daten aktualisiert. Der rdwr sendet und empfängt nun ein Byte. Und Sie erwähnen nicht, mit welchem ​​Sklaven Sie kommunizieren, also kann ich Ihnen nicht sagen, ob das Protokoll korrekt ist oder nicht.
@Justme, es ist ein MCP23S17 IO-Expander. Die meisten Slave-Geräte erfordern das Senden von 3 Bytes in einem Chipauswahlzyklus, wie ich mir vorstellen würde. Der rdwr im Code integriert nur die beiden erwähnten SPI_Write- und SPI_Receive-Funktionen als Algorithmus, da dem Schreiben sowieso ein Lesen vorausgeht
@Luffy Nein, Sie stellen sich das falsch vor, jedes Gerät kann frei wählen, welches Protokoll die Designer gewählt haben. MCP23S17 ist etwas seltsam. Es erfordert das Schreiben des Opcodes, das Registrieren und dann das Schreiben oder Lesen von Daten. Es gibt keine Dummy-Bytes, daher ist die von Ihnen dargestellte Sequenz nicht korrekt.
@Ruffy Justme hat recht. SPI ist nur eine Möglichkeit, Bytes zu verschieben. Was diese Bytes bedeuten und logischerweise, wer schreibt und wer liest, ist völlig willkürlich, und Vollduplexbetrieb ist ebenfalls möglich, und einige Geräte unterstützen ihn möglicherweise gut, während andere nur mittelmäßige Arbeit leisten.

Antworten (1)

Warum muss ich beim 4-Draht-SPI-Lesevorgang bei jedem Sendevorgang die MISO-Leitung lesen?

Weil:

  1. Aus Leistungsgründen haben die HAL-Implementierer SPI_Write ohne die eingebaute Schleife "Warten auf leeres Senderegister" geschrieben, SPI_Readhaben aber eine solche Schleife. In diesem Fall würde das Ausgeben von Schreibvorgängen ohne Lesevorgänge den Puffer überlaufen lassen. Ihre Implementierung verschwendet auch Zeit in dieser Schleife - sobald Sie lesen können, können Sie auch schreiben; oder

  2. Die von Ihnen verwendete Hardware-Abstraktionsbibliothek (HAL) konfiguriert das Silizium so, dass es so funktioniert, wenn das Silizium dies nicht erfordert.

  3. und vielleicht konfigurieren Sie die HAL nicht so, wie Sie es möchten, falls die HAL dies unterstützt (und notwendigerweise auch das Silizium); oder

  4. Vielleicht konfigurieren Sie das Silizium nicht, um das "Richtige" zu tun, wenn das Silizium dies zulässt. oder

  5. Das Silizium ist so konzipiert, dass es so funktioniert und nicht anders.

Ich erinnere mich nicht an die Details der SPI-Implementierung auf den von Ihnen verwendeten Chips, aber:

  1. Bei einigen SPI-Implementierungen kann der Empfänger abgeschaltet werden, sodass Lesevorgänge nicht erforderlich sind, da diese Funktion deaktiviert wird.

  2. SPI kann wahrscheinlich mit DMA verwendet werden, sodass Sie sich nicht zu sehr um den Overhead kümmern, der durch das Lesen entsteht.

  3. Selbst wenn Sie es in Software tun, können die Lesevorgänge billig sein. Die Verwendung der HAL würde wahrscheinlich auch dabei helfen, obwohl Sie den Code in einer optimierten Versionskonfiguration kompilieren und die Assembly prüfen, um zu bestätigen, dass sie nichts Unnötiges tut. Sogar Ihr handgeschriebener Code macht unnötige Dinge.