Forum: Mikrocontroller und Digitale Elektronik Zeitmessung und printf


von Martin (Gast)


Lesenswert?

Guten Morgen,

anknüpfend an meinen letzten Thread: 
Beitrag "Zeitmessung in µC oder im PC" brauche ich immer noch 
eure Hilfe.

Ich habe zwar das Problem mit der unregelmäßigen Zeit zwischen den 
Messungen gelöst, jedoch habe ich festgestellt, dass ich ja nur jede 5. 
Messung erfasse.

Hier mal die relevanten Code-Ausschnitte:
1
#ifndef F_CPU
2
#define F_CPU 18432000UL
3
#endif
4
#define BAUD 115200UL
5
#define MYUBRR ((F_CPU+BAUD*8)/(BAUD*16)-1)
6
7
#include <avr/interrupt.h>
8
#include <avr/io.h>
9
#include <stdio.h>
10
#include "BMA180_neu.h"
11
12
...
13
14
volatile uint16_t ZeitstempelNeu;
15
volatile uint8_t ZeitstempelH;
16
volatile uint8_t ZeitstempelL; 
17
18
...
19
20
void ioinit(void);
21
void timerinit(void);
22
static int uart_putchar(char c, FILE *stream);
23
uint8_t uart_getchar(void);
24
static FILE mystdout=FDEV_SETUP_STREAM(uart_putchar, NULL, _FDEV_SETUP_WRITE);
25
26
...
27
28
//===Input Capture Interrupt===//
29
ISR(TIMER1_CAPT_vect)
30
{
31
32
  ZeitstempelL=ICR1L;
33
  ZeitstempelH=ICR1H;
34
  ZeitstempelNeu=(ZeitstempelH<<8);
35
  ZeitstempelNeu|=ZeitstempelL;
36
    
37
}  
38
39
...
40
41
int main (void)
42
{
43
44
         char temp;
45
         signed short tempX,tempY,tempZ;
46
            
47
         uint16_t Messzeit=0;
48
         uint16_t Timefactor=35; //benötigt für Festkommaarithm.-> 3,5µs
49
         uint16_t ZeitstempelAlt=0;
50
         uint16_t Timestamp;
51
52
         while (1)      // Mainloop
53
         {
54
              temp=0;
55
56
              while(temp!=1)
57
              {
58
                 temp=read(ACCXLSB)&0x01; //Abfrage von new_data_X Bit
59
              }                            //Gibt an das X_Wert da ist
60
61
              temp=read(ACCXMSB);  //speichern des 14 Bit X-Wertes
62
              tempX=temp<<8;          
63
              tempX|=read(ACCXLSB);
64
              tempX=tempX>>2;    
65
66
              while(temp!=1)
67
              {
68
                 temp=read(ACCYLSB)&0x01; //Abfrage von new_data_Y Bit
69
              }                            //Gibt an das Y_Wert da ist
70
71
              temp=read(ACCYMSB);  //speichern des 14 Bit Y-Wertes
72
              tempY=temp<<8;          
73
              tempY|=read(ACCYLSB);
74
              tempY=tempY>>2;    
75
76
              while(temp!=1)
77
              {
78
                 temp=read(ACCZLSB)&0x01; //Abfrage von new_data_Z Bit
79
              }                            //Gibt an das Z_Wert da ist
80
81
              temp=read(ACCZMSB);  //speichern des 14 Bit Z-Wertes
82
              tempZ=temp<<8;          
83
              tempZ|=read(ACCZLSB);
84
              tempZ=tempZ>>2;  
85
86
              //Berechnung Zeitwert zwischen zwei Messungen
87
88
        while (ZeitstempelNeu==ZeitstempelAlt)  
89
        {
90
          
91
        }       //warten bis Zeitstempel aktuell  
92
93
              Timestamp=ZeitstempelNeu-ZeitstempelAlt; 
94
95
              Messzeit=Timestamp*Timefactor; 
96
97
              printf("%d\t%d\t%d\t%u\n",tempX,tempY,tempZ,Messzeit);
98
99
              ZeitstempelAlt=Zeitstempel;
100
         }
101
}
102
103
...
104
105
static int uart_putchar(char c, FILE *stream)
106
{
107
  if(c=='\n')
108
  
109
    uart_putchar('\r',stream);
110
    
111
  loop_until_bit_is_set(UCSR0A,UDRE0);
112
  UDR0=c;
113
  
114
  return 0;
115
}
116
117
uint8_t uart_getchar(void)
118
{
119
  while(!(UCSR0A&(1<<RXC0)));
120
  return(UDR0);
121
}
122
123
...
124
125
void ioinit(void)
126
{
127
  //1=output, 0=input
128
129
  DDRB=0b11101110; //MISO input, PB0 input
130
  DDRC=0b11111111; //All outputs
131
  DDRD=0b11111110; //PORTD(RX on PD0)
132
  
133
  stdout=&mystdout;  //required for printf init
134
  
135
  UBRR0H = (MYUBRR) >> 8;
136
  UBRR0L = MYUBRR;
137
  UCSR0B = (1<<RXEN0)|(1<<TXEN0);
138
  UCSR0C = (3<<UCSZ00);
139
    
140
  TCCR2B = (1<<CS21);
141
}
142
143
...
144
145
void timerinit(void)
146
{
147
  TCCR1B|=(1<<ICES1)|(1<<CS11)|(1<<CS10);
148
  TIMSK1|=(1<<ICIE1);
149
}

Aufgrund der AD-Wandlung im Sensor ergibt sich eine Update-Zeit der 
Messwerte von 417µs, also 0,42ms (laut Datenblatt BMA180). Es werden XYZ 
und der Temperaturwert in diesen 0,42ms aktualisiert.

Wenn ich jetzt das Programm so in Hterm teste, bekomme ich eine Zeit von 
2,08ms zwischen den Messungen.

Lösche ich ein %d aus der printf-Übertragung, dann komme ich auf 1,67ms.
Lösche ich ein weiteres %d, komme ich auf 1,25ms. Lasse ich nur noch das 
%u für die Zeit stehen, dann bekomme ich 0,83ms.

Das sind alles Vielfache der Update-Zeit. Mir kommts so vor, als wenn 
ich mir aus jeder Update-Sequenz (Aktualisierung der 4 Werte) immer nur 
einen Wert rauspicke, z.B. X, und ich für den nächsten Wert Y wieder 
eine Update-Sequenz brauche. Das würde aber heißen das meine 
X-,Y-,Z-Werte gar nicht zueinander passen, sondern immer zu dem 
jeweiligen nächsten Update.

Liegt das Problem an der printf-Übertragung? Falls es an der "zu 
langsamen" Übertragung liegt, könnte ich mir eine Art Ringpuffer 
anlegen, der mir die Werte von 5 Update-Sequenzen zwischenspeichert und 
ich diese dann auf einmal übertrage?

Könnt ihr mir weiterhelfen?? Ich hoffe, dass war jetzt nicht zu viel 
Text.

Danke.

Gruß Martin R.

von Willi (Gast)


Lesenswert?

Martin schrieb:
> ZeitstempelL=ICR1L;
> ZeitstempelH=ICR1H;

Hier habe ich aufgehört, noch weiter zu lesen.

von Martin (Gast)


Lesenswert?

Ergänzend muss ich noch dazu schreiben, dass das Input Capture Interrupt 
durch den Sensor ausgelöst wird, und zwar nur dann wenn ein neues Update 
verfügbar ist, also wenn alle 4 Messwerte wieder aktuell zum auslesen 
bereit stehen.. (INT erfolgt alle 417µs)

@Willi:

Hm...nach deiner Antwort, vermute ich, dass es unnötig ist. Sollte ich 
besser schreiben:
1
ZeitstempelNeu = ICR1

Ich wollte in den beiden Variablen halt erst mal die ICR1 Register 
sichern.
Das sollte doch aber nicht die Lösung für mein Problem sein, oder?

Gruß Martin

von Martin (Gast)


Lesenswert?

Danke für deinen Hinweis Willi, hab es ausprobiert. Es Funktioniert. 
Aber mein eigentliches Problem besteht immer noch.

Gruß Martin

von Markus W. (Firma: guloshop.de) (m-w)


Lesenswert?

Zusammenfassend sagt das ja nur aus, dass printf() sehr langsam ist, 
oder? Dem kann ich nicht widersprechen. Deswegen ersetze ich es meistens 
durch andere Funktionen bzw. eigene Routinen. Das würde sicher auch in 
diesem Fall einen Geschwindigkeitsvorteil bringen.

Bei solch zeitkritischen Berechnungen hilft wahrscheinlich zusätzlich 
ein Umstieg auf Assembler.

von Willi (Gast)


Lesenswert?

Bei 115kBd dauert die Übertragung eines Zeichens ca. 0,1 ms.
Damit kannst Du abschätzen, wie schnell die Ausgabe überhaupt sein kann. 
Printf() an sich muß nicht langsam sein, aber die ser. Ausgabe bremst 
auf jeden Fall.
Eine Ausgabe mit FIFO (Ringpuffer mit 32 byte) per UART-Interrupt 
beschleunigt insofern, dass printf() nicht warten muß und beide Abläufe 
parallel passieren können.

von Martin (Gast)


Lesenswert?

Hm um auf Assembler umzusteigen habe ich nicht genügend Zeit. Das 
kuriose ist ja, dass der Sensor für die AD-Wandlung 417µs braucht. Nach 
den 417µs brauche ich 0,42ms für die Übertragung des Zeitwertes. Danach 
wieder rund 0,42ms für den ersten Messwert und dann wieder 0,42ms und so 
weiter bis alle 4 Werte übertragen wurden.

Mich wundert ja nicht, dass es so lange dauert, sondern das es Vielfache 
dieser 417µs sind.

Mit Matlab habe ich die Daten dargestellt und bekomme auch logische 
Kennlinien, wenn ich das Zeitintervall = 1,67ms setze.

Dadurch das ich den Zeitwert mit übertrage komme ich auf 2,08ms.

Meine Idee ist nun,
1.
...dass ich den Zeitwert einfach nicht mitübertrage. Dann komme ich auf 
1,67ms. Habe jedoch keine Kontrolle mehr über die wirkliche Zeit.

2.
...oder ich übertrage den Zeitwert und ziehe danach in Matlab 0,42ms von 
diesem ab.

Das ist dann aber bestimmt nicht die eleganteste Art.


Habe gehofft, dass jemand ein ähnliches Problem hatte.

Gruß Martin

von Martin (Gast)


Lesenswert?

Ok danke Willi für deine Zeitangabe. Das würde ich ungefähr hinkommen.

Meine Werte sehen z.B. so aus:

X     Y    Z    Time
-23  134  5534  20755

D.h. die Anzahl der Zeichen ist unterschiedlich, aber angenommen es sind 
5 Zeichen, dann wären es nach deiner Angabe ca. 0,5ms und das deckt sich 
ja in etwa mit meinen Erkenntnissen.

Ist es vielleicht nur Zufall, dass die Zeiten Vielfache von der 
Update-Zeit des Sensors sind?

von Willi (Gast)


Lesenswert?

Martin schrieb:
> Ist es vielleicht nur Zufall, dass die Zeiten Vielfache von der
> Update-Zeit des Sensors sind?

Wenn ich es richtig verstanden habe, synchronisiert doch der Sensor (ich 
kenne ihn nicht) Deine Messung. Folglich muß die Ausgabe im 417µs-Raster 
erfolgen.

von Lehrmann M. (ubimbo)


Lesenswert?

Willi schrieb:
> Martin schrieb:
>> Ist es vielleicht nur Zufall, dass die Zeiten Vielfache von der
>> Update-Zeit des Sensors sind?
>
> Wenn ich es richtig verstanden habe, synchronisiert doch der Sensor (ich
> kenne ihn nicht) Deine Messung. Folglich muß die Ausgabe im 417µs-Raster
> erfolgen.

Die verwendeten Libraries sperren aber alle Interrupts aus, so lange bis 
sie ihrer Ansicht nach fertig sind. Erst dann wird der global Interrupt 
wieder aktiviert. Daraus erklärt sich auch das Vielfache von besagten 
417µs, da erst nach dieser Zeitspanne wieder ein Sync initiiert wird.

von Willi (Gast)


Lesenswert?

Lehrmann Michael schrieb:
> Die verwendeten Libraries sperren aber alle Interrupts aus, so lange bis
> sie ihrer Ansicht nach fertig sind.

Dann schmeiß die Libs weg!

von Martin (Gast)


Lesenswert?

Hm..

@Willi: Der Sensor ist ein 3 Achsen Beschleunigungssensor Bosch BMA180. 
Und du hast Recht damit, dass er nur in dem 417µs-Raster einen Wert 
bringen kann. Und liegt die Messung dazwischen, dann wird auf das 
nächste Vielfache gewartet:-( Also kann ich doch so gar nicht genauer 
messen, als in diesen 417µs-Schritten? Oder?

@Michael: Wenn ich die Libraries rausnehme und die Übertragung anders 
realisiere, werden zwar die Interrupts zugelassen, aber ich kann die 
Messwerte, die während der Übertragung eintrudeln doch dann nicht 
parallel speichern?

Mein Timer gibt einen Wert von 2,08ms aus, also ignoriere ich ja drei 
Messungen, weil die Interrupts während der Übertragung deaktiviert 
werden:-(

Muss ich mal überlegen ob ich das auch anders hinkriege.

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.