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.
Wer findet den Fehler im Code ?
Dirk F. schrieb:> 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.
Dirk B. schrieb:> 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.
Dirk F. schrieb:> RV3149-C3 von Microchrystal
Die Firma hat kein h im Namen.
> Wer findet den Fehler im Code ?
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?
-
https://www.microcrystal.com/fileadmin/Media/Products/RTC/App.Manual/RV-3149-C3_App-Manual.pdf
Lothar M. schrieb:> Und ganz interessant ist auch: was siehst du mit dem Oszi/LA auf den> Busleitungen?
Geht leider nicht. Bekomme aufgrund der geringen Größe (0,4 mm
Pinabstand) keine Testpins fürs Oszi angelötet
Dirk F. schrieb:> Die Funktion ist im o.g Quellcode gezeigt...
Ah, jetzt ja, eine Insel!
> 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.
Dirk F. schrieb:> Geht leider nicht. Bekomme aufgrund der geringen Größe (0,4 mm> Pinabstand) keine Testpins fürs Oszi angelötet
Die Uhr hat doch 0,8 mm Pinabstand. Mit einer passenden Lupe sollte das
gehen...
unsigned char SPI2Get (void)
{
while(!SPI2STATbits.SPITBE); // Warten bis Sendepuffer leer
return SPI2BUF;
}
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;
}
Lothar M. schrieb:> 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 ?
Andras H. schrieb:> 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)
Dirk F. schrieb:> Oder ?
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.
Dirk F. schrieb:> Der SPI Puffer hat wohl 16 Byte.> Da vermute ich
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.
Peter D. schrieb:> SPIRBF
Das würde ich auch probieren...
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.
Peter D. schrieb:> Da beides eh nie getrennt ausgeführt werden kann und darfWie ich schrieb:>> Weil SPI ja nur gekoppelte Schieberegister sind, kommt mit jedem Bit,>> das vom Master gesendet wird, auch ein Bit vom Slave zurück.
Das macht den Code dann irgendwie auch leserlicher:
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.
Peter D. schrieb:> 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.
Dirk F. schrieb:> 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
Lothar M. schrieb:> Das kann dann zu lustigen Effekten führen
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.
Lothar M. schrieb:> 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.