ATtiny10 und ICP - Fehlmessung?

Gast #6376182
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
Gast #6376198
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.
Gast #6376229
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...
Gast #6376254
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_ */
Gast #6376271
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.
Gast #6376573
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
}
Gast #6377178
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
}
Gast #6377848
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
Gast #6377862
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.
Gast #6377943
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.
Gast #6378081
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
Gast #6378403
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.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren