Forum: Mikrocontroller und Digitale Elektronik ATtiny10 und ICP - Fehlmessung?


von Michael (Gast)


Lesenswert?

Hallo an alle.

Ich möchte die Pulsbreite eines PPM Signals mittels ICP Pin an einem 
Tiny10 messen.

Den Timer initialisiere ich mit 125kHz
1
TCCR0A = 0x00;                // Normal Mode. Zählt bis 0xFFFF und fängt wieder von vorne an
2
  BITSET(TCCR0B, ICES0);            // ICP-Pin hört auf steigende Flanke
3
  BITSET(TCCR0B, ICNC0);            // Noise Canceler an
4
  BITSET(TIMSK0, ICIE0);            // ICP Interrupt aktivieren
5
  TCNT0 = 0x00;                // Zählerstand resetten
6
  ICR0 = 0x00;                // Zählerstand für Events resetten
7
  BITSET(TCCR0B, CS01);            // Timer starten mit Prescaler = 8

Die ISR Routine
1
ISR (TIM0_CAPT_vect)
2
{
3
  
4
  switch(TCCR0B & 64)  
5
  {
6
    case 0:                // fallende Flanke aufgetreten
7
      stamp_falling_edge = ICR0;    // Zählerstand sichern
8
      pulse_time = stamp_falling_edge - stamp_rising_edge;    // Pulsbreite ermitteln
9
      BITSET(TCCR0B, ICES0);      // jetzt auf steigende Flanke horchen
10
      BITSET(TIFR0, ICF0);
11
      BITSET(OUT_PORT, LED);
12
      break;
13
    case 64:        // steigende Flanke aufgetreten
14
      stamp_rising_edge = ICR0;    // Zählerstand sichern
15
      BITCLEAR(TCCR0B, ICES0);    // jetzt auf fallende Flanke horchen
16
      BITSET(TIFR0, ICF0);
17
      BITCLEAR(OUT_PORT, LED);
18
      
19
      break;
20
      
21
    default: break;
22
  }
23
  ICR0 = 0x0000;
24
  TCNT0 = 0x0000;              // Zähler zurücksetzen
25
  
26
}

Mein Problem ist, dass in pulse_time scheinbar nicht korrekt berechnet 
wird. Bei einem PPM-Signal zwischen 1ms und 2ms müssten in pulse_time 
die Werte 125 bis 250 zu finden sein. Da ich nicht debuggen kann, bleibt 
nur die Abfrage ob der Wert einen bestimmten Bereich überschreitet und 
dann eine LED schalten. Danach ist pulse_time immer größer als 250 bzw. 
2ms da die LED (nicht die die oben in der gezeigten ISR getoggelt wird) 
dauerhaft an.

Die ISR an sich scheint aber zu funktionieren da diese LED (die in der 
ISR) ihre Helligkeit in Abhängigkeit der Pulsbreite am ICP änert.

Wo liegt mein Fehler? Lese ich das ICR Register doch falsch aus? Hatte 
auch schon versucht erst das ICR0L, dann ICR0H auszulesen und 
zusammenzuschieben. Brachte aber keine Verbesserung.

Gruß, Michael

von c-hater (Gast)


Lesenswert?

Michael schrieb:

> Wo liegt mein Fehler?

Vermutlich irgendwo hier (Originalzitat DB):

11.5.3 Using the Input Capture Unit
The main challenge when using the Input Capture unit is to assign enough 
processor capacity
for handling the incoming events. The time between two events is 
critical. If the processor has
not read the captured value in the ICR0 Register before the next event 
occurs, the ICR0 will be
overwritten with a new value. In this case the result of the capture 
will be incorrect.
When using the Input Capture interrupt, the ICR0 Register should be read 
as early in the inter-
rupt handler routine as possible. Even though the Input Capture 
interrupt has relatively high
priority, the maximum interrupt response time is dependent on the 
maximum number of clock
cycles it takes to handle any of the other interrupt requests.

von S. Landolt (Gast)


Lesenswert?

Ist das Programm zu komplex, um es in Gänze zu zeigen?
Da in der ISR unnötigerweise das ICF0 gelöscht wird, könnte ich mir auch 
einen Anfängerfehler wie fehlendes volatile vorstellen.

von Michael (Gast)


Lesenswert?

1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
#include "defines.h"
4
5
volatile unsigned int stamp_rising_edge = 0;            // Zählerstand bei steigender Flanke
6
volatile unsigned int stamp_rising_edgeH = 0;            // Zählerstand bei steigender Flanke
7
volatile unsigned int stamp_rising_edgeL = 0;            // Zählerstand bei steigender Flanke
8
volatile unsigned int stamp_falling_edge = 0;            // Zählerstand bei fallender Flanke
9
volatile unsigned int stamp_falling_edgeH = 0;            // Zählerstand bei fallender Flanke
10
volatile unsigned int stamp_falling_edgeL = 0;            // Zählerstand bei fallender Flanke
11
12
13
volatile unsigned int pulse_time = 0;                // hier wird die aktuell gemessene pulsdauer gespeichert
14
volatile unsigned int min_pulse = PPM_WIDTH_OFF;                // hier die minimalste der gemessenen
15
volatile unsigned int max_pulse = PPM_WIDTH_ON;                // hier wird die maximalste der gemessenen Pulse gespeichert
16
17
ISR (TIM0_CAPT_vect)
18
{
19
  
20
  switch(TCCR0B & 64)  
21
  {
22
    case 0:                // fallende Flanke aufgetreten
23
      stamp_falling_edge = ICR0;    // Zählerstand sichern
24
      //stamp_falling_edgeL = (int)ICR0L;
25
      /*stamp_falling_edgeH = ICR0H; */
26
      //stamp_falling_edge = ((int)(ICR0H << 8) | stamp_falling_edgeL);
27
      pulse_time = stamp_falling_edge - stamp_rising_edge;    // Pulsbreite ermitteln
28
      BITSET(TCCR0B, ICES0);      // jetzt auf steigende Flanke horchen
29
      BITSET(TIFR0, ICF0);
30
      BITSET(OUT_PORT, LED);
31
      break;
32
    case 64:        // steigende Flanke aufgetreten
33
      stamp_rising_edge = ICR0;    // Zählerstand sichern
34
      //stamp_rising_edgeL = (int)ICR0L;
35
      /*stamp_rising_edgeH = ICR0H;*/
36
      //stamp_rising_edge = ((int)(ICR0H << 8) | ICR0L);
37
      BITCLEAR(TCCR0B, ICES0);    // jetzt auf fallende Flanke horchen
38
      BITSET(TIFR0, ICF0);
39
      BITCLEAR(OUT_PORT, LED);
40
      
41
      break;
42
      
43
    default: break;
44
  }
45
  ICR0 = 0x0000;
46
  TCNT0 = 0x0000;              // Zähler zurücksetzen
47
  
48
}
49
50
unsigned char initHW()
51
{
52
  BITSET(PPM_PORT, PPM);          // Pullup für INT0
53
  BITCLEAR(PPM_DDR, PPM);          // INT0 auf Eingang
54
55
  BITSET(DDR, LED);
56
  BITSET(OUT_PORT, LED);          // Status LED
57
  
58
  BITSET(DDR, FET);
59
  BITSET(OUT_PORT, FET);        // vor der Kalibrierung Mosfet ausschalten
60
  
61
62
  //PORTB = 0x0F;
63
  //BITCLEAR(OUT_PORT, LED);
64
  //BITCLEAR(OUT_PORT, FET);
65
66
  // muss das sein  _delay_ms(50);                //warten da nach Anschluss der Empfängerbatterie quatsch gemessenw ird.
67
  
68
  // Timer für PPM Messung
69
  TCCR0A = 0x00;                // Normal Mode. Zählt bis 0xFFFF und fängt wieder von vorne an
70
  BITSET(TCCR0B, ICES0);            // ICP-Pin hört auf steigende Flanke
71
  BITSET(TCCR0B, ICNC0);            // Noise Canceler an
72
  BITSET(TIMSK0, ICIE0);            // ICP Interrupt aktivieren
73
  TCNT0 = 0x00;                // Zählerstand resetten
74
  ICR0 = 0x00;                // Zählerstand für Events resetten
75
  BITSET(TCCR0B, CS01);            // Timer starten mit Prescaler = 8
76
  
77
  return OK;
78
}
79
80
unsigned char switchFET(unsigned char on_off)
81
{
82
  switch (on_off)
83
  {
84
    case OFF:
85
      BITSET(OUT_PORT, FET);
86
      break;
87
    case ON:
88
      BITCLEAR(OUT_PORT,FET);
89
      break;
90
  }
91
  return OK;
92
}
93
94
int main(void)
95
{
96
    initHW();
97
  sei();
98
  //BITSET(FET_PORT,FET);
99
    while (1) 
100
    {
101
    //if ((pulse_time < (PPM_WIDTH_OFF + TOLERANCE)) )          // unterer Grenzwert
102
    if ((pulse_time < (min_pulse + TOLERANCE)) )          // unterer Grenzwert
103
    {
104
      switchFET(OFF);
105
      //BITSET(LED_PORT, LED);
106
    }
107
    
108
    //if (pulse_time > (PPM_WIDTH_ON - TOLERANCE))                  // oberer Grenzwert
109
    if (pulse_time > (max_pulse - TOLERANCE))                  // oberer Grenzwert
110
    {
111
      switchFET(ON);
112
      //BITCLEAR(LED_PORT, LED);
113
    }
114
    }
115
116
  return 0;
117
}

Zu komplex ist das wohl eher weniger. An das volatile habe ich sehr wohl 
gedacht. Und die Sache mit den fehlenden Ressourcen habe ich gelesen, 
allerdings handelt es sich hierbei um keine zeitkritische Anwendung von 
daher fällt das glaube ich weniger ins Gewicht. So meine Meinung...

von S. Landolt (Gast)


Lesenswert?

Das "defines.h"?

von Michael (Gast)


Lesenswert?

Oh....
1
#ifndef HW_DEFINES_H_
2
#define HW_DEFINES_H_
3
4
// Hardwaredefines
5
#define PPM_DDR    DDRB
6
#define PPM_PORT  PORTB
7
#define PPM_PIN    PINB
8
#define PPM      PB1    // für ATTiny25 und 45 identisch
9
10
#define DDR      DDRB
11
#define OUT_PORT  PORTB
12
#define IN_PIN    PINB
13
#define LED      PB0    // für ATTiny25 = PB3
14
#define FET      PB2    // für ATTiny25 = PB4
15
16
// Softwaredefines
17
#define TOLERANCE    15
18
19
// -- Werte für Timer mit Prescaler = 8
20
// 375 = 3ms
21
// 312 = 2,5ms
22
#define MAX_PPM_HIGH_TIME  375  // wenn PPM High länger als 3ms dann in Failsafe gehen (Berechnung:  0,003s/(1/125000)  )
23
#define PPM_WIDTH_OFF    125
24
#define PPM_WIDTH_ON    250
25
//-------------------
26
27
#define ERROR      1
28
#define OK        0
29
#define ON        1
30
#define OFF        0
31
#define BUTTOM      0
32
#define TOP        3
33
34
35
// Macros
36
#define BITSET(port, pin)  port |= (1<<pin)
37
#define BITCLEAR(port, pin)  port &= ~(1<<pin)
38
#define BITTOGGLE(port, pin) port ^= (1<<pin)
39
#define BITTST(port, pin) port & pin
40
41
unsigned char initHW();
42
unsigned char switchFET(unsigned char on_off);
43
44
#endif /* HW_DEFINES_H_ */

von S. Landolt (Gast)


Lesenswert?

Tut mir leid, ich muss passen; so auf Anhieb kann ich nichts erkennen 
(aber C ist nicht meine Muttersprache), ausprobieren kann ich es auch 
nicht, da kein ATtiny10 vorhanden ist, nur ATtiny85.

von Michael (Gast)


Lesenswert?

trotzdem Danke für den Versuch. Der nächste bitte! ;-)

von c-hater (Gast)


Lesenswert?

Michael schrieb:

> // Macros
> #define BITSET(port, pin)  port |= (1<<pin)
> #define BITCLEAR(port, pin)  port &= ~(1<<pin)
> #define BITTOGGLE(port, pin) port ^= (1<<pin)
> #define BITTST(port, pin) port & pin

BITTST ist falsch.

von S. Landolt (Gast)


Lesenswert?

Tja, Michael, es war gestern schon spät für meine Verhältnisse, 
Entschuldigung, also neuer Versuch:
  dieses Konstrukt in der ISR ist doch Unfug, oder? Versuchen Sie mal 
dies:
1
ISR (TIM0_CAPT_vect)
2
{
3
  switch(TCCR0B & 64)  
4
  {
5
    case 0:                // fallende Flanke aufgetreten
6
      pulse_time = ICR0;
7
      BITSET(TCCR0B, ICES0);      // jetzt auf steigende Flanke horchen
8
      BITSET(OUT_PORT, LED);
9
      break;
10
    case 64:        // steigende Flanke aufgetreten
11
      TCNT0 = 0;
12
      BITCLEAR(TCCR0B, ICES0);    // jetzt auf fallende Flanke horchen
13
      BITCLEAR(OUT_PORT, LED);
14
      break;
15
    default: break;
16
  }
17
}

von S. Landolt (Gast)


Lesenswert?

War keine gute Idee, nochmal: die ursprüngliche ISR ist eigentlich okay, 
ausgenommen eine winzige Kleinigkeit, TCNT0 muss natürlich durchlaufen:
1
ISR (TIM0_CAPT_vect)
2
{
3
  switch(TCCR0B & 64)  
4
  {
5
    case 0:                // fallende Flanke aufgetreten
6
      stamp_falling_edge = ICR0;    // Zählerstand sichern
7
      pulse_time = stamp_falling_edge - stamp_rising_edge;
8
      BITSET(TCCR0B, ICES0);      // jetzt auf steigende Flanke horchen
9
      BITSET(OUT_PORT, LED);
10
      break;
11
    case 64:        // steigende Flanke aufgetreten
12
      stamp_rising_edge = ICR0;    // Zählerstand sichern
13
      BITCLEAR(TCCR0B, ICES0);    // jetzt auf fallende Flanke horchen
14
      BITCLEAR(OUT_PORT, LED);
15
      break;
16
    default: break;
17
  }
18
}

von Michael (Gast)


Lesenswert?

Ja, das macht Sinn dass TCNT0 durchlaufen muss.
Ich wollte mir das Handling der Overflows sparen da dieser alle 0,52428 
Sekunden auftreten würde. Meine Signale werden aber nie länger als ca. 
2,7ms. Von daher wäre das Overflow Handling unnötig wenn ich den Timer 
bei der erkanntne FALLENDEN Flanke zurücksetze. Ja? Sollte so gehen oder 
?


@c-hater:
Warum sollte BITTST falsch sein? Ist das Bit gesetzt, gibt das Makro 
einen Wert > 0 aus. Also Zweck ist doch erfüllt

von Peter D. (peda)


Lesenswert?

Michael schrieb:
> Warum sollte BITTST falsch sein?

Es paßt nicht zur Nomenklatur der anderen 3 Macros. Schau nochmal 
richtig hin.

von S. Landolt (Gast)


Lesenswert?

"Overflow Handling"? Wie bereits erwähnt, ist C nicht meine 
Muttersprache, aber ich dachte, durch das 'unsigned int pulse_time' geht 
das automatisch. Zumindest sieht es hier auf meinem ATtiny84 so aus.

von Michael (Gast)


Lesenswert?

Mit Overflow Handling meine ich den Timer, nicht die Variable. Wenn der 
Timestamp der steigenden Flanke vor Überlauf des Timers und der 
Timestamp der fallenden Flanke nach Überlauf des Timers genommen wird. 
Dann kann ich nicht einfach timestamp_ende - timestamp_anfang rechnen. 
Das wollte ich mir ersparen.

von S. Landolt (Gast)


Lesenswert?

> Dann kann ich nicht einfach timestamp_ende - timestamp_anfang rechnen.

Tatsächlich? Trotz 'unsigned int' auf AVR8? Na, das soll ein C-Kenner 
beurteilen.

von S. Landolt (Gast)


Lesenswert?

PS:
Impuls mit 2.0 ms, also 0x00FA, einmal bei TCNT0= 0x1000, dann bei 
0xFFF0:
1
 10FA
2
-1000
3
=====
4
 00FA
5
6
 00EA
7
-FFF0
8
=====
9
 00FA
Stelle ich mir zumindest so vor.

von Michael (Gast)


Lesenswert?

hmm... jetzt bringst mich durcheinander.

00EA
-FFF0
=====
 00FA


ist doch das gleiche wie

 234
-65520
======

hier kann doch garnicht das selbe rauskommen wie  bei

10FA
-1000
=====
 00FA

von S. Landolt (Gast)


Lesenswert?

Also wenn ich meinen Taschenrechner HP48 auf 16-Bit einstelle, dann 
kommt dasselbe raus.

von Peter D. (peda)


Lesenswert?

Differenzen stimmen immer, auch bei einem Überlauf.

von S. Landolt (Gast)


Lesenswert?

Ihr Umschreiben ins Dezimalsystem ist insofern irreführend, als bei 
diesem ja kein Überlauf bei 65536 stattfindet. Rechnen wir stattdessen 
komplett im Dezimalsystem mit 3 Stellen, wieder 250, einmal ab 500, dann 
ab 900:
1
 750
2
-500
3
====
4
 250
5
6
 150
7
-900
8
====
9
 250

von S. Landolt (Gast)


Lesenswert?

> Differenzen stimmen immer, auch bei einem Überlauf.
Entscheidend ist hier, dass sowohl 'unsigned int' als auch TCNT0 
dieselbe Breite von 16 bit haben.

von Michael (Gast)


Lesenswert?

Ok. Muss mir das mal nach Feierabend in Ruhe durch den Kopf gehen 
lassen.

von c-hater (Gast)


Lesenswert?

Michael schrieb:

> Warum sollte BITTST falsch sein? Ist das Bit gesetzt, gibt das Makro
> einen Wert > 0 aus. Also Zweck ist doch erfüllt

Nö. Beispiel: Bit 3

Rechnen tut dein Macro dann: Port & 00000011, weil 00000011 halt die 
binäre Repräsentation von 3 ist.

Das Ergebnis davon ist nur dann true, wenn Bit 0 oder 1 in Port gesetzt 
sind, es ist aber false, wenn tatsächlich (nur) Bit 3 gesetzt ist.

Die korrekte Maske für Bit 3 wäre 00001000.

Also entweder du deklarierst

#define BITTST(port, pinmask) port & pinmask

dann wird deutlich, dass hier eine vorher aufbereitete Maske übergeben 
werden muss (mit der dann aber mehrere Bits gleichzeitig getestet werden 
können) oder du schreibst es so, wie bei den anderen drei Makros. Dann 
wird tatsächlich nur Bit 3 getestet, wenn 3 übergeben wird.

von Rush .. (rush)


Lesenswert?

Du hast recht! Muss ich ändern.

Zu dem eigentlichen Problem:

Habe jetzt in der ISR den Timerreset und den Reset des ICP Registers 
entfernt. Funktioniert jetzt wie gewünscht!

Vielen Danke an Euch!

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.