Aufgrund eines anderen Beitrags
(Beitrag "RTC Inbetriebnahme STM32F4") der etwas abgeschweift
ist, verfasse ich mal einen neuen mit passenderem Betreff.
"Nun verzweifel ich eher an der Rückgabe der Werte die per CAN versendet
werden sollen....
da ich mit Pointern arbeiten muss ist mir das alles noch nicht so ganz
klar."
Aufrufendes C File:
1
externint*TimeStamp[3]={0,0,0};
2
3
if(RxMessage.StdId==ID&&d[0]==0x54&&d[1]==0x47){//Anfrage mit ID und TG im Datenfeld Ruft Zeitstempel ab
4
5
CanTxMsgTxMessage;
6
7
ur_sh_get_rtc();
8
9
10
TxMessage.StdId=ID;//Standart Identifier
11
TxMessage.RTR=CAN_RTR_DATA;//Data frame
12
TxMessage.IDE=CAN_ID_STD;//Standart Identifier
13
TxMessage.DLC=3;//length of the frame
14
15
TxMessage.Data[0]=*TimeStamp[0];
16
TxMessage.Data[1]=*TimeStamp[1];
17
TxMessage.Data[2]=*TimeStamp[2];
18
19
CAN_Transmit(CAN2,&TxMessage);
20
}
Aufgerufenes C File:
1
int*TimeStamp[3]={0,0,0};
2
3
voidur_sh_get_rtc(){
4
RTC_TimeTypeDefRTC_TimeStructure;
5
6
RTC_GetTime(RTC_Format_BCD,&RTC_TimeStructure);
7
*TimeStamp[0]=RTC_TimeStructure.RTC_Hours;
8
*TimeStamp[1]=RTC_TimeStructure.RTC_Minutes;
9
*TimeStamp[2]=RTC_TimeStructure.RTC_Seconds;
10
11
//RTC_GetTime(RTC_Format_BCD,);
12
13
}
"Leider weigert sich der Compiler mit der Antwort das "TimeStamp"
doppelt
deklariert sei.
Aber das ist es doch normalerweise durch "extern" nicht oder habe ich da
was falsch verstanden?"
Wie genau stehen die beiden C File in Beziehung zueinander?
Ich frage, weil man nur Funktionen aufrufen kann, nicht Dateien. Die
Beziehung zwischen der Datei mit der aufrufenden Funktion und der Datei
mit der aufgerufenen Funktion stellt man üblicherweise mit Header
Dateien her. Und darin befinden sich üblicherweise #ifdef Ausdrücke, die
doppelte Definition verhindern.
Also wäre jetzt ganz interessant, die Include Statements in den
betroffenen *.c dateien zu sehen, sowie den Inhalt der relevanten *.h
Dateien.
Versuche mal, die Initialisierung weg zu nehmen, so das sie nicht
doppelt vorkommt:
vorher: extern int *TimeStamp[3]={0,0,0};
nachher: extern int *TimeStamp[3];
Hab es jetzt umgebaut und direkt reingestopft, da die Funktion nicht so
wollte wie ich, auch die änderung der doppelten initialisierung hat
leider nicht weitergeholfen.
Mittlerweile sieht es so aus:
1
if(RxMessage.StdId==ID&&d[0]==0x54&&d[1]==0x47){//Anfrage mit ID und TG im Datenfeld Ruft Zeitstempel ab
2
3
RTC_TimeTypeDefRTC_TimeStructure;
4
CanTxMsgTxMessage;
5
6
TxMessage.StdId=ID;//Standart Identifier
7
TxMessage.RTR=CAN_RTR_DATA;//Data frame
8
TxMessage.IDE=CAN_ID_STD;//Standart Identifier
9
TxMessage.DLC=3;//length of the frame
10
11
RTC_GetTime(RTC_Format_BCD,&RTC_TimeStructure);
12
TxMessage.Data[0]=RTC_TimeStructure.RTC_Hours;
13
TxMessage.Data[1]=RTC_TimeStructure.RTC_Minutes;
14
TxMessage.Data[2]=RTC_TimeStructure.RTC_Seconds;
15
16
CAN_Transmit(CAN2,&TxMessage);
17
}
Leider ändert das nichts daran das die Uhr nur kurze Zeit richtig läuft
und dann plötzlich um 20 Minuten hinterherhinkt und dann auf einmal
wieder eine halbe stunde voraus....
Ich schätze mal das liegt an dem komischen BCD Format.
Ich rechne die Zeit immer vom Hexadezimalen ins Dezimale System um.
Bin ich da auf dem Holzweg?
Leider komme ich der Funktionalität der RTC noch nicht ganz auf die
Schliche...
RTC schrieb:> Ich schätze mal das liegt an dem komischen BCD Format.> Ich rechne die Zeit immer vom Hexadezimalen ins Dezimale System um.> Bin ich da auf dem Holzweg?
Wenn die Umrechnung richtig programmiert ist, dann nicht.
Aber ohne Code lässt sich das schwer sagen.
http://cdecl.org/ sagt zu int *TimeStamp[3] : declare TimeStamp as array
3 of pointer to int
Willst du wirklich drei Zeiger auf int haben?
Und warum initialisierst du diese mit 0? (und nicht mit NULL)
Gegenfrage (da ich im Programmieren noch nicht allzusehr bewandert bin
:) )
Warum nicht als int?
Und 0 ist doch dasselbe wie NULL, ich dachte ob man das mit Buchstaben
oder Zahlen macht ist ja eigentlich wurschtegal, bedeuten tut es ja
dasselbe oder nicht?
RTC schrieb:> Und 0 ist doch dasselbe wie NULL, ich dachte ob man das mit Buchstaben> oder Zahlen macht ist ja eigentlich wurschtegal, bedeuten tut es ja> dasselbe oder nicht?
Ja, es bedeutet dasselbe.
Aber die Verwendung von NULL ist ein Hinweis an den geneigten Leser des
Codes, dass man es hier mit einem Pointer zu tun hat. Die Verwendung von
NULL anstelle von 0 (und auch von '\0' anstelle von 0 in einem
char-Kontext) ist eine Lesehilfe für diejenigen, die dann die Programme
derjenigen korrigieren sollen, die sie selber mangels Überblick nicht
mehr gebacken bekommen.
Merke: Ja, man kann in C optisch sehr dicht programmieren. Die Sprache
lässt das zu. Aber ob das programmierstrategisch schlau ist, ist eine
ganz andere Frage.
Zeit bei der Programmentwicklung spart man nicht dadurch, dass man sich
3 Tastendrücke einspart. Jede Programmzeile wird ein mal geschrieben und
vielleicht noch ein paar mal verändert. Aber sie wird im Laufe eines
Programmlebens viele Zig-mal gelesen. Es ist daher schlauer, sich Code
so zurecht zu legen, dass er gut gelesen werden kann. Und zwar so, dass
man nicht ständig 300 Millionen Querverweise im Kopf haben muss und auch
nicht so, dass man ständig im Code rumscrollen muss. Sondern so, dass
man möglichst viel Information aus dem Code selber entnehmen kann. Je
einfacher und schneller man eine Codezeile erfassen kann (in möglichst
vielen Details), desto schneller und zuverlässiger geht das.
RTC schrieb:> Gegenfrage (da ich im Programmieren noch nicht allzusehr bewandert bin> :) )> Warum nicht als int?
Die Frage ist nicht 'int oder nicht int'. Die Frage ist: 'Pointer oder
nicht Pointer'!
Man verwendet keine Pointer, wenn man nicht muss. Du hast zwar im OT nur
sehr wenig Umgebung gezeigt, aber aus dem Gezeigten erschliesst sich
erst mal nicht, warum man hier Pointer einsetzen müsste. Ganz im
Gegenteil sieht das hier
1
int*TimeStamp[3]={0,0,0};
2
3
voidur_sh_get_rtc(){
4
RTC_TimeTypeDefRTC_TimeStructure;
5
6
RTC_GetTime(RTC_Format_BCD,&RTC_TimeStructure);
7
*TimeStamp[0]=RTC_TimeStructure.RTC_Hours;
8
*TimeStamp[1]=RTC_TimeStructure.RTC_Minutes;
9
*TimeStamp[2]=RTC_TimeStructure.RTC_Seconds;
10
...
erst mal nach einem Fehler aus. Denn die 3 Pointer zeigen ins nirgendwo.
Dort kann man so erst mal keine int speichern.
Grundsätzlich: Eine Pointer Variable wie zb
1
int*pI;
ist erst mal nur eine Variable, in der man die Adresse ablegen kann, wo
man einen int speichern könnte. Aber der Speicher, in dem dann der int
tatsächlich gespeichert werden kann, der existiert dadurch noch nicht.
Das ist ein bischen so, wie der Unterschied zwischen deinem
Telefon-Adressbuch und der realen Person. Im Adressbuch steht, wo die
Person wohnt. Aber das ist was anderes, wie das Haus in dem die Person
dann tatsächlich wohnt.
1
pI
2
+---------+
3
| NULL |
4
+---------+
Bis jetzt existiert nur die Pointer Variable. Dort wird eine Adresse
gespeichert.
1
int*pI=NULL;
2
intdieVariable=5;
1
pI
2
+---------+
3
| NULL |
4
+---------+
5
6
dieVariable
7
+----------+
8
| 5 |
9
+----------+
Zum Beispiel die Adresse einer int Variablen
1
int*pI=NULL;
2
intdieVariable=5;
3
4
pI=&dieVariable;
1
pI
2
+---------+
3
| o |
4
+---|-----+
5
|
6
| dieVariable
7
| +----------+
8
+------------------>| 5 |
9
+----------+
in der dann der gewünschte Wert tatsächlich abgelegt werden kann
1
int*pI=NULL;
2
intdieVariable=5;
3
4
pI=&dieVariable;
5
*pI=8;
1
pI
2
+---------+
3
| o |
4
+---|-----+
5
|
6
| dieVariable
7
| +----------+
8
+------------------>| 8 |
9
+----------+
Gibt es aber keine Speicherfläche, in der ein int abgelegt wird, zeigt
der Pointer daher irgendwohin in den Speicher
1
int*pI;
2
intdieVariable=5;
3
4
*pI=8;
1
pI
2
+---------+
3
| o |
4
+---|-----+
5
+-------------------------------------> 8
6
dieVariable
7
+----------+
8
| 5 |
9
+----------+
dann schreibst du irgendwo in den Speicher. Wo genau hängt vom Zufall
und dem genauen Programmablauf ab. Aber auf jeden Fall ist das kein
Speicher, der in irgendeiner Form zur gezielten Verwendung für dein
Programm reserviert wurde. Dort im Speicher kann alles mögliche liegen.
zb. auch andere Variablen, die dann magisch urplötzlich ihren Wert
ändern. zb. auch wichtige Systeminformationen, wie zb Stackwerte, die
das Programm benötigt, um aus Funktionen wieder zum Aufrufer
zurückzufinden. Was genau da im Speicher überbügelt wird, kann man nur
mittels aufwändigen Analysen herausfinden. Es ist im Grunde aber egal,
denn Fehler ist Fehler und so etwas ist nun mal ein schwerwiegender
Fehler.
Karl Heinz schrieb:> Zeit bei der Programmentwicklung spart man nicht dadurch, dass man sich> 3 Tastendrücke einspart. Jede Programmzeile wird ein mal geschrieben und> vielleicht noch ein paar mal verändert. Aber sie wird im Laufe eines> Programmlebens viele Zig-mal gelesen. Es ist daher schlauer, sich Code> so zurecht zu legen, dass er gut gelesen werden kann. Und zwar so, dass> man nicht ständig 300 Millionen Querverweise im Kopf haben muss und auch> nicht so, dass man ständig im Code rumscrollen muss. Sondern so, dass> man möglichst viel Information aus dem Code selber entnehmen kann. Je> einfacher und schneller man eine Codezeile erfassen kann (in möglichst> vielen Details), desto schneller und zuverlässiger geht das.
Aus dem gleichen Grund ist auch
1
if(RxMessage.StdId==ID&&
2
d[0]=='T'&&d[1]=='G'){
3
...
um Welten besser als
1
if(RxMessage.StdId==ID&&d[0]==0x54&&d[1]==0x47){//Anfrage mit ID und TG im Datenfeld Ruft Zeitstempel ab
Dass dieses 'TG' in deinem Kommentar ernst zu nehmen ist und was es
bedeutet, hab ich erst rausgefunden, als ich probehalber 0x54 und 0x47
in einer ASCII Tabelle nachgeschlagen habe.
Völlig sinnlos.
Wenn du 'T' meinst, dann schreib auch 'T' und nicht 0x54!
Hallo Karl Heinz,
vielen Dank für die Super Erklärung!
Endlich hab auch ich das mal mit den Pointern kapiert! :)
1
if(RxMessage.StdId==ID&&
2
d[0]=='T'&&d[1]=='G'){
3
...
Wäre mir eigentilch auch lieber, nur leider reagiert der Mikrocontroller
dann nicht wenn ich ihm die entsprechenden Datenpakete über CAN schicke,
da ich in der Software nur HEX Werte versenden kann.
Mittlerweile habe ich dank der Tipps die RTC auch zum laufen gebracht
mit plausiblen Werten, nur leider läuft sie etwas zu schnell, obwohl die
Prescaler laut Datenblatt richtig gesetzt wurden.
Ist die Abweichung erfahrungsgemäß wirklich so groß, dass bei der Wahl
des LSI Taktes die RTC nach einer Minute schon 4 Sekunden zu schnell
ist?
Da ich das erste mal mit der RTC arbeite, kann ich die Abweichungen noch
nicht wirklich einschätzen.
RTC schrieb:> wäre mir eigentilch auch lieber, nur leider reagiert der Mikrocontroller> dann nicht wenn ich ihm die entsprechenden Datenpakete über CAN schicke,> da ich in der Software nur HEX Werte versenden kann.
Du hast da, so glaube ich, was grundlegendes noch nicht verstanden.
0x54 ist dasselbe wie 'T'.
Der Compiler erzeugt exakt denselben Binärcode, ob da in Deinem
Quelltext nun das eine oder das andere steht.
Doch habe ich, vielleicht habe ich mich falsch ausgedrückt.
Da ich nur Hex Werte über meine CAN Schnittstelle jagen kann, ist es
(zumindest momentan) übersichtlicher wenn man sieht was man gerade
versendet und wo es im Code sitzt.
Sorry dafür.
Aber ihr habt recht, der Übersichtlichkeit halber werde ich das später
mit den Buchstaben trotzdem umsetzen.
Hat noch jemand einen Tipp zwecks der Einstellung des Prescalers, denn
die Uhr läuft immernoch zu schnell und was ich bemerkt habe, sie startet
urplötzlich wieder von 00:00:00 ab, ohne das eine veränderung
meinerseits erfolgt ist.
Vermutlich (hoffentlich) meint er eine Einschränkung der Software, mit
denen er die CAN-Telegramme erzeugt, vielleicht ist das ja so etwas wie
das "Terminalprogramm" hTerm.