PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : PIC18F SPI über MSSP



Torrentula
02.09.2012, 16:27
Hallo PIC Experten!

Im Moment bin ich dabei einen 74HC595 mit einem PIC18F2520 anzusteuern, genauer gesagt mit Hilfe des MSSP-Moduls. Ich möchte es im SPI Modus betreiben allerdings bekomme ich keinerlei Clock/Datensignal.

Leider kann mir hier die wirklich tolle Seite von Sprut auch nicht weiterhelfen, deshalb hier mal mein code:


void spi_init(void){

SSPSTATbits.SMP = 0; // Input data sampled at middle of data output time
SSPSTATbits.CKE = 0; // Transmit occurs on transition from Idle to active clock state

SSPCON1bits.CKP = 0; // Idle state for clock is a low level

SSPCON1bits.SSPM3 = 0; // F_SPI = F_OSC / 4
SSPCON1bits.SSPM2 = 0;
SSPCON1bits.SSPM1 = 0;
SSPCON1bits.SSPM0 = 0;

// enable MSSP
SSPCON1bits.SSPEN = 1;

// set SDO (RC5) to output
TRISCbits.RC5 = 0;
// set SCK (RC3) to output
TRISCbits.RC3 = 0;
// set SS (RA5) to input
//TRISAbits.RA5 = 1;
}

void spi_send(unsigned char data){
// wait until the previous byte has been transmitted
while( PIR1bits.SSPIF == 0 );
PIR1bits.SSPIF = 0; // reset SSPIF bit
SSPBUF = data; // write the data byte to be transmitted to SSPBUF
}


Der Teil in der init, bei dem Bits auf low gesetzt werden ist nur testweise und macht keinen Unterschied zu der init ohne diese Sequenz.

Ich hoffe es gibt hier jemanden der einen möglicherweise simplen Fehler sieht ;)

Siro
06.09.2012, 15:21
Ich vermute :
Nach einem Reset (Neustart des Prozessors) ist das Bit SSPIF gelöscht = 0.
Das wird esvermutlich auch bleiben, solange Du noch nichts raus geschoben hast.
Du must zumindest erstmal ein Byte rausschieben, damit dieses Bit gesetzt wird.
Quasi den ganzen Vorgang erstmal starten mit einem Dummy Byte.
Also direkt in SSPBUF was reinscheiben ohne Beachtung des SSPIF Flags.

Torrentula
06.09.2012, 15:39
Danke für den Input, das Problem war fast wie du sagst:
Das SSBUF Register muss erst mit dem zu sendenden Byte geladen werden und dann muss man auf das SSPIF Bit warten.

Ich habe das jetzt so gelöst:


void spi_send(unsigned char data){
LATCbits.LATC2 = 0; // set CS low
SSPBUF = data; // write the data byte to be transmitted to SSPBUF
while(!PIR1bits.SSPIF); // wait for SSPIF bit to be set
while(!SSPSTATbits.BF); // wait for BF bit to be set --> both bits set = transfer complete (-> double buffered)
LATCbits.LATC2 = 1; // set CS high
}


Bei mir tut sich jetzt eine weitere Frage auf: Wenn ich nicht zusätzlich auf das BF Bit warte, geht CS viel zu früh (ca. ab der hälfte der Übertragung) wieder auf high. Laut Datenblatt wird das SSPIF Bit erst gesetzt, nachdem das Byte vollständig gesendet wurde. Meine Vermutung ist das das Double Buffering durch das SSPBUF Register etwas damit zu tun haben könnte.

Mit der jetzigen Lösung bin ich allerdings auch nicht recht zufrieden, da CS relativ früh (8 takte vorher) auf high und auch relativ spät (auch ca. 8 takte) wieder auf high, was natürlich die Übertragungsgeschwindigkeit mindert.

Hat jemand dazu noch eine Idee?

Siro
06.09.2012, 20:05
Du must das SSPIF Bit auch wieder löschen, sonst ist deine erste while Schleife für die Katz, da das SSPIF-Bit ja noch von der letzen fertigen Übertragung gesetzt ist. Das ist auch der Grund warum dein CS gleich wieder hoch geht.
Das gleichst Du jetzt quasi aus, indem Du auf das BF-Flag wartest.

Also lösch mal erst das SSPIF Bit
dann schreib den neuen Wert ins SSPBUF
und dann warte auf das IF Bit

Das BF-Bit ist für deine Anwenung, nur rausschieben, eigentlich irrelevant.

Das Double Buffering hat damit nichts zu tun.
Denn das ist nur beim Empfang aktiv. Beim Senden jedoch nicht.

Ich hoffe, ich liege nicht völlig falsch, hab das auch noch nicht programmiert.

Torrentula
07.09.2012, 16:01
Der folgende code funktioniert leider auch nicht zufriedenstellend, egal ob ich das SSPIF bit vor dem laden von SSPBUF oder nach dem setzen von SSPIF wieder lösche, die CS Leitung ist jetzt gut doppelt so lange low wie sie sein sollte.



void spi_send(unsigned char data){
LATCbits.LATC2 = 0; // set CS low
SSPBUF = data; // write the data byte to be transmitted to SSPBUF
while(!PIR1bits.SSPIF); // wait for SSPIF bit to be set
PIR1bits.SSPIF = 0; // reset SSPIF bit
LATCbits.LATC2 = 1; // set CS high
}

Siro
07.09.2012, 18:49
Nun müssen wir mal über die Zeiten reden.
Wieviele Bytes schiebst Du denn raus pro Sekunde, bzw. was hast Du für eine Clockrequenz ?
und wie lang ist denn dein CS-Signal auf Low.
Wenn das sooooo Zeitkritisch bei Dir ist, solltest Du Dir mal den Assemblercode ansehen, den der Compiler gemacht hat.
Im Zweifelsfalle kannst Du mit Assembler noch die eine oder andere Nano/Mikro-sekunde rausholen.
Kann ich mir kaum vorstellen, dass deine Anwenung dies erfordert.
Was hast Du denn dran an dem74595 ???

Torrentula
07.09.2012, 19:00
Also im Moment sende ich einfach 0xAA (10101010) alle 100ms bei einer clock Frequenz von 8MHz (ein 7,81MHz), an den uC Leitungen hängt bei den Messungen jetzt nichts dran (es funktioniert ja auch mit dem 595). Die CS-Leitung ist 2.52µs low und das Byte wird in 1µs übertragen.
Besonders zeitkritisch ist es jetzt nicht, allerdings wundert mich es schon ein wenig.
Die SPI Frequenz ist jetzt gleich der Frequenz der CPU, möglicherweise dauert das setzen des Ausgangsbits für CS einfach ein paar Taktzyklen und bei geringerer SPI Frequenz ist der Effekt dann nicht so gravierend.

Ich möchte im Endeffekt das Display einer Binäruhr multiplexen (also so um die 24 LEDs).

Siro
07.09.2012, 19:24
Na da mach Dir mal über die paar Takzyklen keine Sorgen, das ist völlig okay und spielt nicht annähernd eine Rolle bei deiner Anwendung.
Und richtig, der PIC braucht ja für die Befehle entsprechend Zeit.
Das würde in Assembler ungefär so aussehen.


movf data,W ; Daten ins W Register laden
bcf LATC,0 ; Chip Select Low
movwf SSPBUF ; Daten ins Schieberegister
bcf PIR1,SSPIF ; Interrupt Flag löschen
wait:
btfss PIR1,SSPIF ; scip, wenn SSPIF Bit gesetzt ist
goto wait ; ansonsten weiter warten
bsf LATC,0 ; Chip Select wieder High

Was dein Compiler draus macht weis ich ja nicht. Aber den erzeugten Assembler Code kannst Du Dir sicher auch irgendwie ansehen.
Dann schaust Du in das Instruction Set von dem PIC und dort sind die Taktzyklen pro Befehl zu finden und kannst, wenns Dich interessiert, exakt ausrechnen wie lange er also braucht für den erzeugten Code.

Also selbst wenn er 10 Mikro Sekunden brauchen würde um ein Byte rauszuschicken, braucht er für 3 Bytes (24 LEDs) grad mal 30 Mikrosekunden. Du könntest also theoretisch rund 3333 mal pro Sekunde komplett neue Daten zu deinen LEDs schicken. Ich denke da brauchst Du Dir wirklich keinen Kopf drüber machen. Geniess das Wochenende udn programmier einfach weiter ;)

-------------------------------------------------
Instruction Set : Seite 270 im Datenblatt. Tabelle TABLE 24-2