Hallo,
ich habe ein seltsames Verhalten des RTC RV3149-C3 von Microchrystal.
Der hier gezeigte Code funktioniert.
Aber nur mit dem zusätzlichen Schreiben/Lesen eines Bytes zwischen Byte Befehl und Byte Daten Sekunden.
Wenn ich die beiden Zeilen weglassen, so wie es im Datenblatt auch steht, dann sind alle Werte um 1 Byte versetzt, also z.B. die cnt_sec werden dann nur jede Minute hochgezählt.
Aber nur mit dem zusätzlichen Schreiben/Lesen eines Bytes zwischen Byte
Befehl und Byte Daten Sekunden.
Magst Du uns auch den MC nennen?
Vielleicht hat der ja noch Bytes vom vorherigen Befehl im Empfangspuffer.
Typisch gibt es ein Flag, was angibt, ob der Puffer auch leer ist.
Wird nicht beim Schreiben (SPI2PUT) bei SPI auch gleichzeitig ein Byte
gelesen?
Immer. Weil SPI ja nur gekoppelte Schieberegister sind, kommt mit jedem Bit, das vom Master gesendet wird, auch ein Bit vom Slave zurück. Und deshalb wird immer ein SPI2Put() gemacht und danach gelesen.
Wie sehen denn die fraglichen beiden SPI-Funktionen aus, in denen der Fehler passiert? Besonders SPI2Get() ist interessant. Ist da ein zusätzlicher Buffer drin? Was passiert, wenn du den Puffer mit SPI2Get() zweimal hintereinander ausliest?
Und ganz interessant ist auch: was siehst du mit dem Oszi/LA auf den Busleitungen? Verhalten die sich so wie in der Appnote angegeben?
while(!SPI2STATbits.SPITBE); // Warten bis Sendepuffer leer
Vielleicht solltest du da vor dem Lesen warten, bis der Empfangspuffer voll ist. Denn je nach SPI-Implementation kann das Einlesen eines Bits durchaus einen halben SPI-Takt-Zyklus später passieren.
Hmm. Das ist aber nur die Transmitt buffer empty. Ich würde hier noch auf den SPIRXIF warten, damit man sicher ist dass da etwas gibt.
Ausserdem würde ich da nach dem Zugriff auf Registers immer inen _sync() machen. Ich habe da immer Angst dass eventuell etwas nicht herausgeschrieben wird, und irgendwo noch wartet.
Und ja eigentlich brauchst du nur:
unsigned char SPI2PutGet(unsigned char data) {
SPI2BUF = wert; // Schreiben
_sync();
while(!SPI2STATbits.SPITBE); // Warten bis Sendepuffer leer
while(!SPI2STATbits.SPIRXIF); // Warten bis Sendepuffer leer
return SPI2BUF;
}
Vielleicht solltest du da vor dem Lesen warten, bis der
Empfangspuffer voll ist.
Ich möchte ja immer nur 1 Byte übertragen und empfangen,
Der SPI Puffer hat wohl 16 Byte.
Da vermute ich, dass das Bit für Empfangspuffer voll nur gesetzt wird, wenn alle 16 Byte gefüllt sind... Oder ?
while(!SPI2STATbits.SPIRXIF); // Warten bis Sendepuffer leer
Ein SPIRXIF kann ich nicht finden, nur ein SPIRBF.
Register 23-3:SPIxSTAT: SPI Status Register (Continued)
bit 5
SPIRBE: RX FIFO Empty bit (valid only when ENHBUF = 1)
1 = RX FIFO is empty (CRPTR = SWPTR)
0 = RX FIFO is not empty (CRPTR < SWPTR)
Ich hätte schon lang einen Draht an den SPI-Clock und den MISO gelötet und einen an irgendeinen einen IO-Pin des µC. Und dann diesen IO-Pin in der Routine SPI2Get() gesetzt und wieder zurückgesetzt:
1
unsignedcharSPI2Get(void)
2
{
3
set_debugpin();
4
while(!SPI2STATbits.SPITBE);// Warten bis Sendepuffer leer
5
clear_debugpin();
6
returnSPI2BUF;
7
}
Und dann gemessen, ob an dieser Stelle tatsächlich schon das letzte Bit angekommen ist.
Wieso musst du das vermuten? Es gibt doch ein Datenblatt.
Oder ?
Wäre ja schon arg ungeschickt, wenn erst der ganze Puffer gefüllt würde, und dann per Flag signalisiert würde, dass er schnell geleert werden muss. aber keine Sorge: man kann verschiedene "Ansprechschwellen" einstellen.
Und die Wortlänge kann mit 8, 16 und 32 Bit eingestellt werden.
Der hat was mit der Speicherverwaltung im µC zu tun und besänftigt hier lediglich "die Angst dass eventuell etwas nicht herausgeschrieben wird, und irgendwo noch wartet."
Wenn die Register aber anständig volatile deklariert sind, dann geht das auch ohne solche Placebobefehle.
Der Witz daran: wenn der Befehl ausreichend viel Zeit braucht, dann könnte der hier evtl. sogar helfen. Und wenn das zufälligerweise so hingeworden ist, merkt sich der Programmierer: wenns klemmt einfach mal diesen Befehl einstreuen.
Ich würde diese Zeitinformationen auch nicht in zig globale Variabeln cnt_xxx packen, sondern in 1 einzigen Struct, und einen Pointer darauf der Funktion RTCread() als Parameter übergeben.
Da beides eh nie getrennt ausgeführt werden kann und darf, würde ich
daraus eine Funktion machen.
Das sehe ich anderes.
Beim RTC OK, aber es wird eine zeitkritische SPI Abfrage geben.
Daher in 2 Funktionen aufgeteilt, die dann in einer State-Maschine aufgerufen werden.
bei 1 MHZ SPI Takt dauert ja die Zeit zwischen senden/Empfangen immerhin
8 us.
Das sehe ich anderes.
Beim RTC OK, aber es wird eine zeitkritische SPI Abfrage geben.
Daher in 2 Funktionen aufgeteilt, die dann in einer State-Maschine
aufgerufen werden.
Das kann dann zu lustigen Effekten führen, denn während des kompletten Lesevorgangs ist der SPI ja blockiert, weil der CE aktiv gehalten werden muss. Du musst also die Hardware für andere Teilnehmer, die den SPI i ihrers FSM ebenfalls verwenden möchten, sperren, damit die nicht ihren SS# auch noch aktivieren und ihre Daten gleich mal hinterherschicken...
Daher in 2 Funktionen aufgeteilt
Dann solltst du aber angesichts des 16 Worte tiefen Sendepuffers auch nicht warten, bis der Sendepuffer leer ist, sondern du kannst alle 8 Bytes sofort hintereinander senden und dann die Daten nach und nach abholen.
1
voidSPI2Put(unsignedcharwert)
2
{
3
SPI2BUF=wert;// Schreiben
4
}
5
unsignedcharSPI2Get(void)
6
{
7
while(SPI2STATbits.SPIRBE);// Warten wenn Empfangspuffer leer
8
returnSPI2BUF;
9
}
10
voidRTCread(void)// Datum und Uhrzeit aus RTC lesen
Ja, wenn man nicht zu jedem Sendebyte auch immer ein Empfangsbyte abholt.
Und will man mal ein Byte zuviel empfangen, hängt die CPU.
Man muß also genau mitzählen.
Man sollte daher eine static Zählvariable mitlaufen lassen.
Das kann dann zu lustigen Effekten führen, denn während des kompletten
Lesevorgangs ist der SPI ja blockiert, weil der CE aktiv gehalten werden
muss. Du musst also die Hardware für andere Teilnehmer, die den SPI i
ihrers FSM ebenfalls verwenden möchten, sperren, damit die nicht ihren
SS# auch noch aktivieren und ihre Daten gleich mal hinterherschicken...
Stimmt.
Aber bei meiner zeitkritische SPI Anwendung wird über SPI4 an nur ein Teilnehmer verbunden.