SPI Bus Kommunikation in C: Funktionen und Anwendungsbeispiele
Gesendet von Javi und eingeordnet in Informatik
Geschrieben am in Deutsch mit einer Größe von 6,96 KB
Einführung in den SPI-Bus
Der SPI-Bus (Serial Peripheral Interface) ist ein weit verbreiteter Kommunikationsstandard, der hauptsächlich für den Datentransfer zwischen integrierten Schaltkreisen in elektronischen Geräten eingesetzt wird. Es ist ein serieller, synchroner Bus, der im Vollduplex-Modus arbeitet. Er verfügt über eine Taktleitung (SCK), eine Leitung für eingehende Daten (MISO), eine für ausgehende Daten (MOSI) und einen Chip-Select-Pin (SS), der den Betrieb des Geräts, mit dem kommuniziert werden soll, aktiviert oder deaktiviert.
SPI-Bus-Verwaltung in C
Es gibt vier C-Funktionen, die mit dem SPI-Modul zusammenhängen. Diese sind jedoch nur nutzbar, wenn das Mikrocontroller-Hardware-Modul (SSP) ausgewählt ist. Es gibt auch Software-Implementierungen des SPI-Moduls. Die genannten Funktionen sind:
setup_spi()
void setup_spi(const mode);
Diese Funktion wird verwendet, um die serielle Schnittstelle (SPI) zu initialisieren und zu konfigurieren sowie den Betriebsmodus dieser Kommunikation zu definieren.
In der Header-Datei des Mikrocontrollers (.h
) sind eine Reihe von Tags definiert, die mit Modi verbunden sind und in drei Gruppen unterteilt werden:
a) Modus
SPI_MASTER
: Konfiguriert das Gerät als Master.SPI_SLAVE
: Konfiguriert das Gerät als Slave.SPI_SS_DISABLED
: Deaktiviert den SS-Pin (Chip Select).
b) Taktflanken
Konfiguriert die SPI-Übertragungsmodi in Abhängigkeit von den folgenden Kombinationen:
SPI_L_TO_H
: Taktflanke von niedrig nach hoch (steigende Flanke).SPI_H_TO_L
: Taktflanke von hoch nach niedrig (fallende Flanke).
// MOTOROLA | MICROCHIP | CCS
//---------------------------------------------------------------------------------------------------
// SPI Mode 0,0 | CKP = 0, CKE = 1 | SPI_L_TO_H | SPI_XMIT_L_TO_H
// SPI Mode 0,1 | CKP = 0, CKE = 0 | SPI_L_TO_H
// SPI Mode 1,0 | CKP = 1, CKE = 1 | SPI_H_TO_L
// SPI Mode 1,1 | CKP = 1, CKE = 0 | SPI_H_TO_L | SPI_XMIT_L_TO_H
CKP
= Clock Polarity Select bitCKE
= Clock Edge Select bit
c) Frequenz
Bestimmt die Frequenz, mit der der Takt schwingt. Dies wird nur verwendet, wenn das Gerät als Master konfiguriert ist, da dieser das Taktsignal generiert.
SPI_CLK_DIV_4
SPI_CLK_DIV_16
SPI_CLK_DIV_64
SPI_CLK_T2
Die oben genannten Tags werden mit dem Bit-Operator OR
(|
) verknüpft. Einige Beispiele:
- Konfiguration des Geräts als Master, mit Modus B (0,1) und einer bestimmten Taktfrequenz:
setup_spi(SPI_MASTER | SPI_L_TO_H | SPI_CLK_DIV_16);
- Konfiguration des Geräts als Slave, mit Modus C (1,0) und deaktiviertem Chip-Select-Pin:
setup_spi(SPI_SLAVE | SPI_H_TO_L | SPI_SS_DISABLED);
spi_write()
void spi_write(char c);
Diese Funktion schreibt das nächste Zeichen an den SPI-Port. Wenn das Gerät als Master konfiguriert ist, generiert es ein Taktsignal und sendet den Wert sofort. Ist das Gerät ein Slave, wird der Wert erst gesendet, wenn der Master ein Taktsignal sendet.
spi_read()
char spi_read([char c]);
Diese Funktion hat eine unterschiedliche Wirkung, je nachdem, ob das Gerät als Master oder Slave konfiguriert ist. Die Funktion liest Daten über den SPI-Bus und kann optional einen Parameter übergeben bekommen.
Als Master-Gerät:
Wenn das SPI-Gerät als Master konfiguriert ist, sendet die Funktion den Wert c
, während gleichzeitig ein neuer Wert empfangen wird (z.B. d = spi_read(c)
). Wenn keine Daten gesendet werden sollen, aber ein Lesevorgang erforderlich ist, wird 0
an die Funktion übergeben (z.B. d = spi_read(0)
). Wenn Sie zuvor die Funktion spi_write()
verwendet haben und Daten während des Schreibvorgangs lesen möchten, können Sie diese Funktion ohne Parameter aufrufen (z.B. d = spi_read()
).
Als Slave-Gerät:
Wenn das Gerät als Slave konfiguriert ist, wartet die Funktion spi_read()
darauf, dass das Master-Gerät das Taktsignal und die Daten an den SPI-Port sendet. Während die neuen Daten empfangen werden, sendet der Slave die Daten c
an das Master-Gerät. Um zu überprüfen, ob Daten über die SPI-Schnittstelle empfangen wurden, kann die Funktion spi_data_is_in()
verwendet werden.
spi_data_is_in()
char spi_data_is_in();
Diese Funktion liefert TRUE
, wenn Daten über die SPI-Schnittstelle empfangen wurden.
Anwendungsbeispiel: Master-Slave-Kommunikation
Hier ist ein Beispiel für die Verwendung der erklärten Funktionen. Das Beispiel zeigt die Kommunikation zwischen einem Master- und einem Slave-Gerät. Um den Master-Code zu kompilieren, entfernen Sie die Kommentarzeichen vor #define MASTER_DEVICE
.
#include <16f877.h>
#fuses HS, NOWDT // Für einen Microchip PIC16F877
#use delay(clock=10000000)
#use rs232(baud=9600, parity=N, xmit=PIN_C6, rcv=PIN_C7, bits=8)
#use standard_IO(D)
//#define MASTER_DEVICE
#ifdef MASTER_DEVICE
// Master-Gerät
void main() {
int8 c, d;
setup_spi(SPI_MASTER | SPI_L_TO_H | SPI_CLK_DIV_16);
printf("Press any key to begin\r\n"); // Added \r\n for better console output
while (1) {
c = getchar();
output_D(0xFF ^ c); // Schaltet die entsprechenden LEDs an PORTD um
// Methode 1: Senden und Empfangen in zwei Schritten
spi_write(c); // Sendet Daten
d = spi_read(); // Empfängt Daten
// Alternative Methode: Senden und Empfangen in einem Schritt
// d = spi_read(c); // Schreibt Daten und liest gleichzeitig neue Daten
putchar(c);
}
}
#else
// Slave-Gerät
void main() {
int8 c;
setup_spi(SPI_SLAVE | SPI_L_TO_H | SPI_CLK_DIV_16);
while (1) {
// PIN_C2 blinkt, um Aktivität anzuzeigen
output_low(PIN_C2);
delay_ms(100); // 100 ms Verzögerung
output_high(PIN_C2);
delay_ms(100);
// Methode 1: Prüfen, ob Daten bereit sind, bevor gelesen wird
if (spi_data_is_in()) {
c = spi_read(); // Liest Daten
output_D(0xFF ^ c); // Schaltet die entsprechenden LEDs an PORTD um
spi_write(c); // Sendet Daten zurück an den Master
}
// Alternative Methode:
// c = spi_read(c); // Schreibt alte Daten und liest neue
// output_D(0xFF ^ c); // Schaltet die LEDs an PORTD um
}
}
#endif
Es gibt viele weitere komplexe Bibliotheken, die den Einsatz spezifischer Funktionen zur Verwaltung des SPI-Busses ermöglichen.