Forum: Compiler & IDEs Pointer oder was?


von RTC (Gast)


Lesenswert?

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
extern int *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
      CanTxMsg TxMessage;
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
void ur_sh_get_rtc(){
4
  RTC_TimeTypeDef RTC_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?"

: Verschoben durch User
von ttl (Gast)


Lesenswert?

extern int *TimeStamp[];

nicht 2mal initialisieren

von Stefan F. (Gast)


Lesenswert?

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.

von RTC (Gast)


Lesenswert?

includieren tue ich nur die dateien die ich brauche von der 
stdperiphlib.
Alles andere trage ich immer händisch ein, zb so in der main:
1
#include "main.h"
2
#include "stm32f4xx.h"
3
#include "stm32f4xx_gpio.h"
4
#include "stm32f4xx_tim.h"
5
#include "misc.h"
6
#include "stm32f4xx_it.h"
7
#include "stm32f4xx_eeprom.h"
8
#include "stm32f4xx_flash.h"
9
10
uint32_t deviceserial0; 
11
void led_init(void);
12
void button_init(void);
13
void pwm_init(void);
14
void LED_An(void);
15
void LED_Aus(void);
16
void pwm_activate(void);
17
void pwm_deactivate(void);
18
void enable_on(void);
19
void enable_off(void);
20
void can_init(void);
21
void blac(void);
22
void delay_init(void);
23
int getid();
24
void delid(void);
25
void rtc_init(void);
26
void set_rtc();
27
int ur_sh_get_rtc();
28
int Done=0;
29
int Count=0;
30
int reset=0;
31
32
int main usw usw...

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

RTC schrieb:
> Alles andere trage ich immer händisch ein, zb so in der main:

Das solltest Du Dir wieder abgewöhnen.

von Stefan F. (Gast)


Lesenswert?

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];

von RTC (Gast)


Lesenswert?

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_TimeTypeDef RTC_TimeStructure;
4
      CanTxMsg TxMessage;
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...

von holger (Gast)


Lesenswert?

>Ich schätze mal das liegt an dem komischen BCD Format.

Dann nimm halt RTC_Format_BIN.

von Karl H. (kbuchegg)


Lesenswert?

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.

von DirkB (Gast)


Lesenswert?

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)

von RTC (Gast)


Lesenswert?

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?

von Karl H. (kbuchegg)


Lesenswert?

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.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

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
void ur_sh_get_rtc(){
4
  RTC_TimeTypeDef RTC_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
  int   dieVariable = 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
  int   dieVariable = 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
  int   dieVariable = 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
  int   dieVariable = 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.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

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!

: Bearbeitet durch User
von RTC (Gast)


Lesenswert?

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.
1
void ur_sh_rtc_init(void){
2
3
  RTC_InitTypeDef  RTC_InitStructure;
4
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
5
6
  PWR_BackupAccessCmd(ENABLE);
7
8
  RCC_BackupResetCmd(ENABLE);
9
  RCC_BackupResetCmd(DISABLE);
10
11
  RCC_LSICmd(ENABLE);
12
13
  while(RCC_GetFlagStatus(RCC_FLAG_LSIRDY) == RESET)  { }
14
15
  RCC_RTCCLKConfig(RCC_RTCCLKSource_LSI);
16
17
  RCC_RTCCLKCmd(ENABLE);
18
19
  RTC_WaitForSynchro();
20
21
  //Prescaler wählen
22
  //LSI = 32 kHz    127      249      1Hz
23
  RTC_InitStructure.RTC_AsynchPrediv = 127;
24
  RTC_InitStructure.RTC_SynchPrediv  = 249;    // (32KHz / 128) - 1 = 249
25
  RTC_InitStructure.RTC_HourFormat   = RTC_HourFormat_24;
26
  RTC_Init(&RTC_InitStructure);
27
28
}

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

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.

von RTC (Gast)


Lesenswert?

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.

von RTC (Gast)


Lesenswert?

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.

von ter oder (Gast)


Lesenswert?

>Da ich nur Hex Werte über meine CAN Schnittstelle jagen kann, ist es

Das ist grober Unsinn.

"Hex Werte" gibt es nur in der String-Darstellung!

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

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.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.