Forum: Mikrocontroller und Digitale Elektronik Multiplex+ICP


von Fan_AT (Gast)


Lesenswert?

Tach liebe Gemeinde,
Ich expirementire grad mit Frequenzmessung per ICP des ATmega88a und 
4xSieben-Segment-Anzeigen. Leider flackert die Anzeige mit steigender 
Frequenz immer mehr zwischen Werten "Null" und der richtigen Frequenz :(

Überschneiden sich da etwa die Interrupts?

LG

von holger (Gast)


Lesenswert?

>Überschneiden sich da etwa die Interrupts?

Woher soll das jemand wissen der deinen Code nicht kennt?

von Falk B. (falk)


Lesenswert?

Zeile 42!

von Fan_AT (Gast)


Lesenswert?

Natürlich, wo bleiben meine Manieren? ;)
1
#define F_CPU 16000000UL // uC läuft mit 16MHz
2
3
#ifndef TRUE
4
#define TRUE 1
5
#define FALSE 0
6
#endif
7
8
9
#include <avr/io.h>
10
#include <avr/interrupt.h>
11
#include <util/atomic.h>
12
13
14
volatile unsigned short z=0;    // Zählvariable des Timer1
15
volatile char UpdateDisplay = TRUE;      // char-1Byte // Einen Wert TRUE definiert 
16
volatile unsigned long Zeit = 0;      // unsigned long-4Bytes
17
18
volatile uint8_t tausender, hunderter, zehner, einer;  // Speicher für die eizelnen Digits
19
volatile uint8_t position = 0;
20
21
volatile unsigned short zaehler1_ovf = 0;    // Anzahl der Überläufe des Timer1, short-2Bytes
22
23
unsigned long zaehlschritte = 0;
24
25
uint8_t bitmaske = 0b00010000; // Funktion: Keinen Strom auf PortD4 geben, da TTL
26
27
uint16_t freq = 0;
28
uint16_t zahl = 0; // Kopie von freq
29
30
volatile unsigned long Startzeit = 0;  
31
32
33
//float korrekturfaktor = 1; // für Abgleich anpassen (z.B. 0.9997)
34
35
36
const int8_t numbers[11] =    // Array zur Darstellung der Zeichen auf der 7-Seg-Anz.
37
{
38
39
  0b01101111,    // 0
40
  0b00000110,    // 1
41
  0b10101011,    // 2
42
  0b10001111,    // 3
43
  0b11000110,    // 4
44
  0b11001101,    // 5
45
  0b11101101,    // 6
46
  0b00000111,    // 7
47
  0b11101111,    // 8
48
  0b11001111,    // 9
49
  0b00000000    // leere Anzeige
50
  
51
};
52
53
54
ISR(TIMER1_OVF_vect) 
55
{
56
  ++z;
57
  //TIFR = (1<<TOV1); //Beim Überlauf von Timer1 und dem anschließenden Interrupt erfolgt eine Zurücksetzung auf Null
58
  //Braucht nicht gelöscht zu werden. Jeder Interrupt löscht automatisch sein eigenes Flag beim Aufruf der ISR.
59
  
60
}
61
62
63
ISR(TIMER1_CAPT_vect)
64
{
65
66
   if(UpdateDisplay)          // Das Display wurde mit den Ergebnissen der vorhergehenden
67
    {                // Messung noch nicht aktualisiert. 
68
      return;        
69
  }                    
70
  
71
    static char ErsteFlanke = TRUE;
72
  static  uint8_t LowByte = 0;
73
  static uint8_t HighByte = 0;
74
    
75
  
76
77
78
 
79
  
80
  LowByte = ICR1L;         // LowByte zuerst, HighByte später gepuffert
81
  HighByte = ICR1H;  
82
   
83
  
84
  if(ErsteFlanke)
85
  {
86
    Startzeit = ICR1;
87
    z = 0;
88
    ErsteFlanke = FALSE;       // Nach der nächsten Flanke beginnt die Ausgabe
89
  
90
  
91
  if ((HighByte < 128) && (TIFR & (1<<TOV1)))
92
    {   
93
      // wartenden Timer Overflow Interrupt vorziehen
94
      ++z;         
95
      TIFR = (1<<TOV1);    // Timer Overflow int. löschen, da schon hier ausgeführt // macht der uC angeblich selbst
96
    }
97
  }
98
  
99
  // das ist die zweite Flanke im Messzyklus. Die Messung wird gestoppt
100
  
101
  
102
  else
103
  {
104
  Zeit = 0;
105
    Zeit = ICR1 - Startzeit;
106
    UpdateDisplay = TRUE;      // Eine vollständige Messung. Sie kann ausgewertet werden
107
    ErsteFlanke = TRUE;        // Bei der nächsten Flanke beginnt der nächste Messzyklus
108
  
109
  
110
  zaehler1_ovf = z;    // Anzahl der Überläufe wird in zaehler1_ovf kopiert
111
  Startzeit = 0;
112
  z=0;  // Nach dem Sichern - annullieren
113
  
114
  }  
115
  
116
  
117
  
118
119
}
120
121
122
ISR (TIMER2_OVF_vect)  // timer0 overflow interrupt
123
{
124
    
125
   if(UpdateDisplay)          // Das Display wurde mit den Ergebnissen der vorhergehenden
126
    {             // Messung noch nicht aktualisiert. Die nächste Messung
127
    return;         // verzögern, bis die Start- und EndTime-Variablen wieder
128
  }                    // gefahrlos beschrieben werden können
129
  
130
  
131
  TCNT2 += 0; // nicht nötig
132
    
133
    PORTC = 0b00000000;    // alle Digits aus
134
    
135
  position++;      // beim nächsten Mal nächstes Digit
136
  if(position == 4)  // wenn alle Digits durch
137
  {
138
    position = 0;  // von vorne beginnen
139
  }
140
}
141
142
int main(void)
143
{
144
  // Definition von Ein- und Ausgängen des uC Boards
145
  DDRB = 0xFE;  // Mit Ausnahme des ICP1-Pins alles als Ausgang
146
  DDRC = 0xFF;  // PORTC als Ausgang - über PORTC werden die jeweiligen Segmente einer 7-Seg. Anzeige gesteuert
147
  DDRD = 0xFF;  // PORTD als Ausgang
148
  PORTC = 0b00000000;
149
  
150
  
151
  TIMSK |= (1<<TICIE1) | (1<<TOIE1); // Interrupts akivieren, Capture und Overflow - das ODER | am Anfang hinzugefügt
152
  TCCR1B |= (1<<ICES1)  | (1<<CS10); // Input Capture Edge, kein Prescaler
153
    
154
  TIMSK |= (1 << TOIE2);           // Interrupt aktivieren, Overflow
155
    TCCR2 |= (1 << CS22)|(1 << CS21);  // Vorteiler auf 256
156
157
  sei();          // Interruptbehandlung ein
158
  
159
160
  
161
  while(1)
162
  {
163
  
164
    if(UpdateDisplay)    // Erst nach zweiter Flanke ausführen
165
      {
166
        
167
        
168
        ATOMIC_BLOCK(ATOMIC_FORCEON)
169
        {
170
          //zaehlschritte = (unsigned long)(0.5+(65536.0*zaehler1_ovf) + Zeitdifferenz);
171
          zaehlschritte = (unsigned long)((65536.0*zaehler1_ovf) + Zeit);   
172
        }
173
        
174
      
175
        if (zaehlschritte==0)
176
          {
177
            freq = 0;
178
          }
179
        else
180
          {
181
            ATOMIC_BLOCK(ATOMIC_FORCEON)
182
            {
183
              // Die abschließende Berechnung der Schritte pro Sekunde
184
              freq = (uint16_t) (F_CPU/(zaehlschritte));
185
            }
186
          
187
      
188
      
189
            zahl = freq;
190
          
191
            tausender =  zahl/1000;  // Tausender Dezimalstelle
192
            zahl = zahl % 1000;
193
    
194
            hunderter = zahl/100;  // Hunderter Dezimalstelle
195
            zahl = zahl % 100;
196
  
197
            zehner = zahl/10;    // Zehner Dezimalstelle
198
            zahl = zahl % 10;
199
    
200
            einer = zahl;      // Einer Dezimalstelle
201
          
202
          
203
            switch (position)    // aktuelle Stelle ausgeben
204
        {
205
          case 0 : if (tausender==0) 
206
            {  PORTD=numbers[10];
207
            PORTC |= (1 << PC0);} 
208
          else
209
            {  PORTD=numbers[tausender];
210
            PORTC |= (1 << PC0);}      
211
          break;
212
    
213
        case 1 : if ((tausender==0)&&(hunderter==0))
214
            {  PORTD=numbers[10];
215
            PORTC |= (1 << PC1);}
216
          else
217
            {  PORTD=numbers[hunderter];
218
            PORTC |= (1 << PC1);}
219
          break;
220
         
221
        case 2 : if ((tausender==0)&&(hunderter==0)&&(zehner==0))
222
            {  PORTD=numbers[10];
223
            PORTC |= (1 << PC2);}
224
          else
225
            {  PORTD=numbers[zehner];
226
            PORTC |= (1 << PC2);}
227
          break;
228
         
229
        case 3 :     PORTD=numbers[einer];
230
            PORTC |= (1 << PC3);
231
          break;
232
        }
233
    
234
          
235
        
236
          UpdateDisplay = FALSE;
237
      }
238
    }
239
      
240
      
241
  }
242
243
return 0;
244
}

von Peter D. (peda)


Lesenswert?

Ich finde nirgends den Timerinterrupt der das Multiplex macht.
Multiplex im Main scheitert immer.

von holger (Gast)


Lesenswert?

Du machst deine LED Matrixausgabe in der Main Loop.
Das kann so nicht gehen. Die wird je nach Interruptlast
mehr oder weniger flackern. Und noch schlimmer, wenn
ein Interrupt komplett blockiert können dir die LEDs
wegen zu hohem Strom kaputt gehen.

Deine LED Matrixausgabe muss in einen Timerinterrupt.
Die Berechnung was ausgegeben werden soll kannst du in der
Main Loop machen.

von Fan_AT (Gast)


Lesenswert?

So war auch die letzte Version - in der aktuellen ist in 
ISR(TIMER2_OVF_vect) ja nur die Positionierung des eingeschalteten 
Segments.

Mein Gedanke war, dass ICP so mehr Priorität bekommt. Völlig unwichtig, 
funktioniert beides nicht.

Aber prinzipiell dürfte ICP+Multiplxing nichts im Wege stehen oder?

von Fan_AT (Gast)


Lesenswert?

Hat also in etwa so ausgesehen:
1
#define F_CPU 16000000UL // uC läuft mit 8MHz
2
3
#ifndef TRUE
4
#define TRUE 1
5
#define FALSE 0
6
#endif
7
8
9
#include <avr/io.h>
10
#include <avr/interrupt.h>
11
#include <util/atomic.h>
12
13
//static inline void digit(uint8_t wert, uint8_t pin);
14
15
volatile unsigned short z=0;    // Zählvariable des Timer1
16
//volatile unsigned short UpdateDisplay;   // Verzögerung
17
volatile char UpdateDisplay = TRUE;      // char-1Byte // Einen Wert TRUE definiert 
18
volatile unsigned long Zeit = 0;      // unsigned long-4Bytes
19
20
volatile uint8_t tausender, hunderter, zehner, einer;  // Speicher für die eizelnen Digits
21
volatile uint8_t position = 0;
22
23
volatile unsigned short zaehler1_ovf = 0;    // Anzahl der Überläufe des Timer1, short-2Bytes
24
//unsigned int zaehler1_ovf = 0;    // Anzahl der Überläufe des Timer1
25
unsigned long zaehlschritte = 0;
26
27
uint8_t bitmaske = 0b00010000; // Funktion: Keinen Strom auf PortD4 geben, da TTL
28
29
uint16_t freq = 0;
30
31
32
volatile unsigned long Startzeit = 0;  
33
34
35
//float korrekturfaktor = 1; // für Abgleich anpassen (z.B. 0.9997)
36
37
38
const int8_t numbers[11] =    // Array zur Darstellung der Zeichen auf der 7-Seg-Anz.
39
{
40
41
  0b01101111,    // 0
42
  0b00000110,    // 1
43
  0b10101011,    // 2
44
  0b10001111,    // 3
45
  0b11000110,    // 4
46
  0b11001101,    // 5
47
  0b11101101,    // 6
48
  0b00000111,    // 7
49
  0b11101111,    // 8
50
  0b11001111,    // 9
51
  0b00000000    // leere Anzeige
52
  
53
};
54
55
56
ISR(TIMER1_OVF_vect) 
57
{
58
  ++z;
59
  //TIFR = (1<<TOV1); //Beim Überlauf von Timer1 und dem anschließenden Interrupt erfolgt eine Zurücksetzung auf Null
60
  //Braucht nicht gelöscht zu werden. Jeder Interrupt löscht automatisch sein eigenes Flag beim Aufruf der ISR.
61
  
62
}
63
64
65
ISR(TIMER1_CAPT_vect)
66
{
67
68
   if(UpdateDisplay)          // Das Display wurde mit den Ergebnissen der vorhergehenden
69
    {             // Messung noch nicht aktualisiert. Die nächste Messung
70
    return;         // verzögern, bis die Start- und EndTime-Variablen wieder
71
  }                    // gefahrlos beschrieben werden können
72
  
73
  
74
75
76
77
  static char ErsteFlanke = TRUE;
78
79
  
80
    static  uint8_t LowByte = 0;
81
  
82
    static uint8_t HighByte = 0;
83
    
84
  
85
86
87
 
88
  
89
  LowByte = ICR1L;         // LowByte zuerst, HighByte später gepuffert
90
  HighByte = ICR1H;  
91
  
92
93
   
94
  
95
  if(ErsteFlanke)
96
  {
97
    Startzeit = ICR1;
98
    z = 0;
99
    ErsteFlanke = FALSE;       // Nach der nächsten Flanke beginnt die Ausgabe
100
  
101
  
102
  //DIe Interruptvorziehung abgeschaltet - 07.08.13 - der Wechsel zu Null ist nun langsamer
103
  
104
  if ((HighByte < 128) && (TIFR & (1<<TOV1)))
105
    {   
106
      // wartenden Timer Overflow Interrupt vorziehen
107
      ++z;         
108
      TIFR = (1<<TOV1);    // Timer Overflow int. löschen, da schon hier ausgeführt // macht der uC angeblich selbst
109
    }
110
  }
111
112
  
113
  
114
  else
115
  {
116
  Zeit = 0;
117
    Zeit = ICR1 - Startzeit;
118
    UpdateDisplay = TRUE;      // Eine vollständige Messung. Sie kann ausgewertet werden
119
    ErsteFlanke = TRUE;        // Bei der nächsten Flanke beginnt der nächste Messzyklus
120
  
121
  
122
  zaehler1_ovf = z;    // Anzahl der Überläufe wird in zaehler1_ovf kopiert
123
  Startzeit = 0;
124
  z=0;  // Nach dem Sichern - annullieren
125
  
126
  }  
127
  
128
  
129
  
130
131
}
132
133
134
ISR (TIMER2_OVF_vect)  // timer0 overflow interrupt
135
{
136
    
137
   if(UpdateDisplay)          // Das Display wurde mit den Ergebnissen der vorhergehenden
138
    {             // Messung noch nicht aktualisiert. Die nächste Messung
139
    return;         // verzögern, bis die Start- und EndTime-Variablen wieder
140
  }                    // gefahrlos beschrieben werden können
141
  
142
  
143
  TCNT2 += 0; // add 6 to the register (our work around)  // bei größeren Frequenzen half TCNT0=190 gegen Flimmern
144
    
145
    PORTC = 0b00000000;    // alle Digits aus
146
    
147
  switch (position)    // aktuelle Stelle ausgeben
148
  {
149
    case 0 : if (tausender==0) 
150
          {  PORTD=numbers[10];
151
            PORTC |= (1 << PC0);} 
152
         else
153
          {  PORTD=numbers[tausender];
154
            PORTC |= (1 << PC0);}      
155
         break;
156
    
157
    case 1 : if ((tausender==0)&&(hunderter==0))
158
          {  PORTD=numbers[10];
159
            PORTC |= (1 << PC1);}
160
         else
161
          {  PORTD=numbers[hunderter];
162
            PORTC |= (1 << PC1);}
163
         break;
164
         
165
    case 2 : if ((tausender==0)&&(hunderter==0)&&(zehner==0))
166
          {  PORTD=numbers[10];
167
            PORTC |= (1 << PC2);}
168
         else
169
          {  PORTD=numbers[zehner];
170
            PORTC |= (1 << PC2);}
171
         break;
172
         
173
    case 3 :     PORTD=numbers[einer];
174
            PORTC |= (1 << PC3);
175
         break;
176
  }
177
    
178
  
179
  position++;      // beim nächsten Mal nächstes Digit
180
  if(position == 4)  // wenn alle Digits durch
181
  {
182
    position = 0;  // von vorne beginnen
183
  }
184
}
185
186
187
void zahl_ausgeben(uint16_t zahl)        // Auch diese Zugriffe atomar gestalten!!!! Können sonst unterbrochen werden
188
{
189
  tausender =  zahl/1000;  // Tausender Dezimalstelle
190
  zahl = zahl % 1000;
191
  
192
  hunderter = zahl/100;  // Hunderter Dezimalstelle
193
  zahl = zahl % 100;
194
  
195
  zehner = zahl/10;    // Zehner Dezimalstelle
196
  zahl = zahl % 10;
197
  
198
  einer = zahl;      // Einer Dezimalstelle
199
}
200
201
202
203
int main(void)
204
{
205
  // Definition von Ein- und Ausgängen des uC Boards
206
  DDRB = 0xFE;  // Mit Ausnahme des ICP1-Pins alles als Ausgang
207
  DDRC = 0xFF;  // PORTC als Ausgang - über PORTC werden die jeweiligen Segmente einer 7-Seg. Anzeige gesteuert
208
  DDRD = 0xFF;  // PORTD als Ausgang
209
  PORTC = 0b00000000;
210
  
211
  
212
  TIMSK |= (1<<TICIE1) | (1<<TOIE1); // Interrupts akivieren, Capture und Overflow - das ODER | am Anfang hinzugefügt
213
  TCCR1B |= (1<<ICES1)  | (1<<CS10); // Input Capture Edge, kein Prescaler
214
  
215
216
  
217
  TIMSK |= (1 << TOIE2);           // Interrupt aktivieren, Overflow
218
    TCCR2 |= (1 << CS22)|(1 << CS21);  // Vorteiler auf 256
219
220
221
  
222
223
  sei();          // Interruptbehandlung ein
224
  
225
226
  
227
  while(1)  // unendliche Schleife
228
  {
229
  
230
    if(UpdateDisplay)    // Erst nach zweiter Flanke ausführen
231
      {
232
        
233
        ATOMIC_BLOCK(ATOMIC_FORCEON)
234
        {
235
          zaehlschritte = (unsigned long)((65536.0*zaehler1_ovf) + Zeit); // sonst am Ende: +Zeitdifferenz;  
236
        }
237
        
238
      
239
        if (zaehlschritte==0)
240
          {
241
            freq = 0;
242
          }
243
        else
244
          {
245
            ATOMIC_BLOCK(ATOMIC_FORCEON)
246
            {
247
              // Die abschließende Berechnung der Schritte pro Sekunde
248
              freq = (uint16_t) (F_CPU/(zaehlschritte));
249
            }
250
      
251
            zahl_ausgeben(freq);
252
          
253
254
          UpdateDisplay = FALSE;
255
      }
256
    }
257
      
258
      
259
  }
260
261
return 0;
262
}

von holger (Gast)


Lesenswert?

>ISR (TIMER2_OVF_vect)  // timer0 overflow interrupt
>{
>
>   if(UpdateDisplay)          // Das Display wurde mit den Ergebnissen der 
>vorhergehenden
>    {             // Messung noch nicht aktualisiert. Die nächste Messung
>    return;         // verzögern, bis die Start- und EndTime-Variablen >wieder
>  }                    // gefahrlos beschrieben werden können

Mach dieses   if(UpdateDisplay)

da weg. Dein Matrixdisplay muss immer upgedatet werden,
egal ob deine Messung noch läuft oder nicht. Zeig dort
halt immer den letzten Wert an. Du kannst das
multiplexen deiner Matrix nicht einfach unterbrechen.
Dann flackert das Viech wie Hölle.

von Fan_AT (Gast)


Lesenswert?

Oh danke, nicht aufgepasst. Das Problem ist eben folgendes: Es gibt kein 
Flimmern wie beim falscheingestellten Multiplex. Die Anzeige flackert 
immer zwischen Null und der richtigen Frequenz. Nur wird das Flackern 
bei steigender Freqenz immer schneller.
Und ich weiß dafür einfach keinen Grund

von Axel S. (a-za-z0-9)


Lesenswert?

Fan_AT schrieb:

> Aber prinzipiell dürfte ICP+Multiplxing nichts im Wege stehen oder?

Nein, natürlich nicht. Funktionierende Schaltung + Code kannst dir hier 
ansehen: Frequenzzählermodul

Da flackert gar nix, obwohl sich die LED-Displays die Controllerpins 
sogar mit dem Vorteiler und den Jumpern teilen.

Ein Vorschlag zur allgemeinen Strukturierung:

1. mach einen Display-Buffer für den darzustellenden Inhalt deines 
Displays. Bei 7-Segment bietet es sich an, hier direkt die Segmente 
abzuspeichern.

2. such dir eine Interruptquelle, die ca. 100Hz * Anzahl Stellen hat. 
Das kann ein Timerüberlauf sein oder ein Compare Match. Wichtig: 
regelmäßig muß es sein und nicht zu langsam (dann flimmert die Anzeige)

3. schreib eine ISR für diesen Interrupt, der den Display-Buffer 
zyklisch ausliest und die jeweils nächste Stelle ausgibt.

Die eigentliche Messung machst du in der Mainloop. Und wenn du ein neues 
Ergebnis hast, dann schreibst du es in den Display-Buffer. Auf diese 
Weise ist es auch trivial, einen Meßwert "einzufrieren".

Wenn es nur kurzzeitig ist, darfst du dann in der Mainloop auch cli/sei 
verwenden. Mit etwas Geschick kann man sogar das Display für ein paar µs 
ausschalten, die Pins für was anderes verwenden (Vorteiler und Jumper 
auslesen) und dann wieder einschalten. Obiger Frequenzzähler macht das 
in main.c Zeile 136 bis 180.


XL

von Karl H. (kbuchegg)


Lesenswert?

und auch den QUatsch schmeisst du raus.
Das hat alles im Multiplex nichts verloren.
1
  switch (position)    // aktuelle Stelle ausgeben
2
  {
3
    case 0 : if (tausender==0) 
4
          {  PORTD=numbers[10];
5
            PORTC |= (1 << PC0);} 
6
         else
7
          {  PORTD=numbers[tausender];
8
            PORTC |= (1 << PC0);}      
9
         break;
10
    
11
    case 1 : if ((tausender==0)&&(hunderter==0))
12
          {  PORTD=numbers[10];
13
            PORTC |= (1 << PC1);}
14
         else
15
          {  PORTD=numbers[hunderter];
16
            PORTC |= (1 << PC1);}
17
         break;
18
         
19
    case 2 : if ((tausender==0)&&(hunderter==0)&&(zehner==0))
20
          {  PORTD=numbers[10];
21
            PORTC |= (1 << PC2);}
22
         else
23
          {  PORTD=numbers[zehner];
24
            PORTC |= (1 << PC2);}
25
         break;
26
         
27
    case 3 :     PORTD=numbers[einer];
28
            PORTC |= (1 << PC3);
29
         break;
30
  }

Die Multiplexroutine kümmert sich nicht mehr um logische Zusammenhänge. 
Das ist alles der Job des Codes, der die auszugebenden Bitmuster für die 
Anzeigen zusammenstellt. Die Multiplexroutine schaufelt nur noch Daten 
raus. Nicht mehr und nicht weniger.

PS: Anstatt Einzelvariablen für Tausender, Hunderter etc. ist ein Array 
eine ganz einfache Sache. Deine Multiplexroutine sieht dann ganz einfach 
so aus
1
volatile uint8_t digits[4];  // Speicher für die eizelnen Digits
2
volatile uint8_t digit_position = 0;
3
uint8_t digitControl[4] = { (1 << PC0), (1 << PC1), (1 << PC2), (1 << PC3) };
4
5
ISR (TIMER2_OVF_vect)  // timer0 overflow interrupt
6
{
7
  PORTC = 0b00000000;    // alle Digits aus
8
9
  digit_position++;
10
  if( digit_position == 4 )
11
    digit_position = 0;
12
13
  PORTD = digits[ digit_position ];
14
  PORTC = digitControl[ digit_position ];
15
}

fertig, Das ist deine ganze Multiplexroutine.
Wenn irgendein andere Code was auf die Anzeige zaubern will, dann hat er 
gefälligst die entsprechenden Bytes in das digit-Array zu stellen.

Ob da jetzt bei Zahlen eine führende 0 angezeigt wird oder nicht, das 
entscheidet die Funktion, die einen int in die einzelnen Digits zerlegt 
und die zugehörigen auszugebenden Bitmuster in das digit Array schreibt. 
Das entscheidet NICHT die Multiplexroutine! Die schaufelt nur Bytes raus 
auf die Anzeige. Und wenn in den digits enthalten ist, dass immer nur 
die oberen Querbalken der einzelnen Segmente leuchten sollen, dann ist 
das eben so - das hat die Multiplexroutine nicht zu kümmern. Ab sofort 
gibt es für den Rest des Programms nur noch dieses digits-Array. Was 
auch immer da an auszugebendem Leuchtmuster in die einzelnen Stellen 
reingeschrieben wird, das wird angezeigt.

1
void zahl_ausgeben(uint16_t zahl)
2
{
3
  uint8_t i;
4
5
  digits[4] = numbers[ zahl % 10 ];
6
  zahl /= 10;
7
8
  for( i = 3; i != 255; i-- )
9
  {
10
    if( zahl > 0 )
11
    {
12
      digits[i] = numbers[ zahl % 10 ];
13
      zahl = zahl / 10;
14
    }
15
    else
16
      digits[i] = numers[10];
17
  }
18
}

und nein, die zahl_ausgeben darf auch durch einen Interrupt unterbrochen 
werden. Du hast dann ganz kurz eine falsche Zahl an der Anzeige. Das ist 
aber so kurz, dass du es nicht sehen wirst. Viel wichtiger ist, dass die 
Multiplex-ISR in gleichen Abständen arbeiten darf. Ob da ein paar 
Millisekunden lang eine zusammengewürfelte Anzeige sichtbar ist oder 
nicht, ist für uns Menschen nicht merkbar.

von Karl H. (kbuchegg)


Lesenswert?

> Die Anzeige flackert immer zwischen Null und der richtigen Frequenz

Nachdem du den Multiplex korrekt gemacht hast, musst du dann eben 
nachsehen, warum in deinen Berechnungen immer abwechselnd 0 und ein 
anderer Wert rauskommt.

von Fan_AT (Gast)


Lesenswert?

Wow, da sitzt man, programmiert - und dann geht das noch tausendfach 
kompakter -.-

Aktuell sieht es so aus, leider noch irgendwo ein kleiner Fehler drin - 
es wird gar nichts angezeigt. Aber vielen Dank schonmal für die Tipps.
1
#define F_CPU 16000000UL // uC läuft mit 16MHz
2
3
#ifndef TRUE
4
#define TRUE 1
5
#define FALSE 0
6
#endif
7
8
9
#include <avr/io.h>
10
#include <avr/interrupt.h>
11
#include <util/atomic.h>
12
13
volatile unsigned short z=0;    // Zählvariable des Timer1
14
volatile char UpdateDisplay = FALSE;      // char-1Byte // Einen Wert TRUE definiert 
15
volatile unsigned long Zeit = 0;      // unsigned long-4Bytes
16
17
//volatile uint8_t tausender, hunderter, zehner, einer;  // Speicher für die eizelnen Digits
18
//volatile uint8_t position = 0;
19
20
volatile unsigned short zaehler1_ovf = 0;    // Anzahl der Überläufe des Timer1, short-2Bytes
21
//unsigned int zaehler1_ovf = 0;    // Anzahl der Überläufe des Timer1
22
unsigned long zaehlschritte = 0;
23
24
uint16_t freq = 0;
25
26
27
volatile unsigned long Startzeit = 0;
28
29
volatile uint8_t digits[4];  // Speicher für die eizelnen Digits
30
volatile uint8_t digit_position = 0;
31
32
uint8_t digitControl[4] = { (1 << PC0), (1 << PC1), (1 << PC2), (1 << PC3) };  
33
34
35
//float korrekturfaktor = 1; // für Abgleich anpassen (z.B. 0.9997)
36
37
38
const int8_t numbers[11] =    // Array zur Darstellung der Zeichen auf der 7-Seg-Anz.
39
{
40
41
  0b01101111,    // 0
42
  0b00000110,    // 1
43
  0b10101011,    // 2
44
  0b10001111,    // 3
45
  0b11000110,    // 4
46
  0b11001101,    // 5
47
  0b11101101,    // 6
48
  0b00000111,    // 7
49
  0b11101111,    // 8
50
  0b11001111,    // 9
51
  0b00000000    // leere Anzeige
52
  
53
};
54
55
56
ISR(TIMER1_OVF_vect) 
57
{
58
  ++z;
59
  //TIFR = (1<<TOV1); //Beim Überlauf von Timer1 und dem anschließenden Interrupt erfolgt eine Zurücksetzung auf Null
60
  //Braucht nicht gelöscht zu werden. Jeder Interrupt löscht automatisch sein eigenes Flag beim Aufruf der ISR.
61
  
62
}
63
64
65
ISR(TIMER1_CAPT_vect)
66
{
67
68
   if(UpdateDisplay)          // Das Display wurde mit den Ergebnissen der vorhergehenden
69
    {             // Messung noch nicht aktualisiert. Die nächste Messung
70
    return;         // verzögern, bis die Start- und EndTime-Variablen wieder
71
  }                    // gefahrlos beschrieben werden können
72
  
73
  
74
  static char ErsteFlanke = TRUE;
75
  static uint8_t LowByte = 0;
76
  static uint8_t HighByte = 0;
77
    
78
  
79
  LowByte = ICR1L;         // LowByte zuerst, HighByte später gepuffert
80
  HighByte = ICR1H;  
81
  
82
83
   
84
  
85
  if(ErsteFlanke)
86
  {
87
    Startzeit = ICR1;
88
    z = 0;
89
    ErsteFlanke = FALSE;       // Nach der nächsten Flanke beginnt die Ausgabe
90
  
91
  }
92
  
93
  
94
  if ((HighByte < 128) && (TIFR & (1<<TOV1)))
95
    {   
96
      // wartenden Timer Overflow Interrupt vorziehen
97
      ++z;         
98
      TIFR = (1<<TOV1);    // Timer Overflow int. löschen, da schon hier ausgeführt // macht der uC angeblich selbst
99
    }
100
101
  
102
  else
103
  {
104
  Zeit = 0;
105
    Zeit = ICR1 - Startzeit;
106
    UpdateDisplay = TRUE;      // Eine vollständige Messung. Sie kann ausgewertet werden
107
    ErsteFlanke = TRUE;        // Bei der nächsten Flanke beginnt der nächste Messzyklus
108
  
109
  
110
  zaehler1_ovf = z;    // Anzahl der Überläufe wird in zaehler1_ovf kopiert
111
  Startzeit = 0;
112
  z=0;  // Nach dem Sichern - annullieren
113
  
114
  }  
115
  
116
117
}
118
119
120
ISR(TIMER2_OVF_vect)  // timer2 overflow interrupt
121
{
122
  PORTC = 0b00000000;    // alle Digits aus
123
  TCNT2 += 0; 
124
125
  digit_position++;
126
  
127
  if( digit_position == 4 )
128
    {digit_position = 0;}
129
130
  PORTD = digits[digit_position];
131
  PORTC = digitControl[digit_position];
132
}
133
134
135
void zahl_ausgeben(uint16_t zahl)
136
{
137
  uint8_t i;
138
139
  digits[3] = numbers[zahl % 10];  // Die letzte Ziffer wird gesichert
140
  zahl /= 10;
141
142
  for( i=2; i>=0 ; i-- ) // das Gleiche wie  for( i = 3; i != 255; i-- )?
143
  {
144
    if( zahl > 0 )
145
    {
146
      digits[i] = numbers[zahl % 10];
147
      zahl /= 10;
148
    }
149
    else
150
      digits[i] = numbers[10];
151
  }
152
  
153
  if(freq==0)
154
  {digits[3] = numbers[0];}
155
}
156
157
158
159
int main(void)
160
{
161
  // Definition von Ein- und Ausgängen des uC Boards
162
  DDRB = 0xFE;  // Mit Ausnahme des ICP1-Pins alles als Ausgang
163
  DDRC = 0xFF;  // PORTC als Ausgang - über PORTC werden die jeweiligen Segmente einer 7-Seg. Anzeige gesteuert
164
  DDRD = 0xFF;  // PORTD als Ausgang
165
  PORTC = 0b00000000;
166
  
167
  
168
  TIMSK |= (1<<TICIE1) | (1<<TOIE1) | (1 << TOIE2); // Interrupts akivieren, Capture und Overflow, Overlow-Interrupt aktivieren
169
  TCCR1B |= (1<<ICES1)  | (1<<CS10); // Input Capture Edge, kein Prescaler
170
  
171
    TCCR2 |= (1 << CS22)|(1 << CS21);    // Vorteiler auf 256
172
173
174
  sei();          // Interruptbehandlung ein
175
  
176
177
  while(1)  // unendliche Schleife
178
  {
179
  
180
    if(UpdateDisplay)    // Erst nach zweiter Flanke ausführen
181
      {
182
        
183
        ATOMIC_BLOCK(ATOMIC_FORCEON)
184
        {
185
          zaehlschritte = (unsigned long)((65536.0*zaehler1_ovf) + Zeit); // sonst am Ende: +Zeitdifferenz;  
186
        }
187
        
188
      
189
        if (zaehlschritte==0)
190
          {
191
            freq = 0;
192
          }
193
        else
194
          {
195
            ATOMIC_BLOCK(ATOMIC_FORCEON)
196
            {
197
              // Die abschließende Berechnung der Schritte pro Sekunde
198
              freq = (uint16_t) (F_CPU/(zaehlschritte));
199
            }
200
      
201
            zahl_ausgeben(freq);
202
          
203
204
          UpdateDisplay = FALSE;
205
      }
206
    }
207
      
208
      
209
  }
210
211
return 0;
212
}

von Karl H. (kbuchegg)


Lesenswert?

Fan_AT schrieb:

> Aktuell sieht es so aus, leider noch irgendwo ein kleiner Fehler drin -
> es wird gar nichts angezeigt.


Mir fehlt da
1
int main(void)
2
{
3
  // Definition von Ein- und Ausgängen des uC Boards
4
  DDRB = 0xFE;  // Mit Ausnahme des ICP1-Pins alles als Ausgang
5
  DDRC = 0xFF;  // PORTC als Ausgang - über PORTC werden die jeweiligen Segmente einer 7-Seg. Anzeige gesteuert
6
  DDRD = 0xFF;  // PORTD als Ausgang
7
  PORTC = 0b00000000;
8
  
9
  
10
  TIMSK |= (1<<TICIE1) | (1<<TOIE1) | (1 << TOIE2); // Interrupts akivieren, Capture und Overflow, Overlow-Interrupt aktivieren
11
  TCCR1B |= (1<<ICES1)  | (1<<CS10); // Input Capture Edge, kein Prescaler
12
  
13
    TCCR2 |= (1 << CS22)|(1 << CS21);    // Vorteiler auf 256
14
15
...

die Freigabe des Overflow Interrupts beim Timer 2.
Dann ist natürlich klar, dass dein Multiplexing nicht anspringt.

von Karl H. (kbuchegg)


Lesenswert?

Nur um sicher zu gehen. Teste mal das Multiplexing ganz alleine. Zuviele 
Änderungen gleichzeitig sind nicht gut in der Entwicklung. D.h. wir 
gehen mal einen Schritt zurück und sehen nach, ob die Basis auch 
wirklich funktioniert.
1
int main(void)
2
{
3
  uitn16_t cnt = 0;
4
5
  DDRC = 0xFF;  // PORTC als Ausgang - über PORTC werden die jeweiligen Segmente einer 7-Seg. Anzeige gesteuert
6
  DDRD = 0xFF;  // PORTD als Ausgang
7
  PORTC = 0b00000000;
8
  
9
  TIMSK |= (1 << TOIE2);
10
  TCCR2 |= (1 << CS22)|(1 << CS21);    // Vorteiler auf 256
11
  
12
  sei();
13
14
  while(1)
15
  {
16
    zahl_ausgeben( cnt++ );
17
    if( cnt == 10000 )
18
      cnt = 0;
19
20
    _delay_ms( 200 );
21
  }
22
23
  return 0;
24
}

Sobald dann von dir die Bestätigung kommt, dass das funktioniert, ist 
damit dann das Thema Multiplexing vom Tisch. Darum brauchst du dich dann 
nicht mehr kümmern.

von Fan_AT (Gast)


Lesenswert?

In deinem Quote, nur eine Zeile später kommt sei(); vor. Damit aktiviere 
ich doch auch global die Interrupts?

Wie dem auch sei - irgendwo habe ich einen Fehler eingebaut - der 
Multiplex funktioniert gar nicht. Die Anzeige ist aber intakt.

Hier der Code für reinen Multiplex-Betrieb:
1
#define F_CPU 16000000UL // uC läuft mit 16MHz
2
3
#ifndef TRUE
4
#define TRUE 1
5
#define FALSE 0
6
#endif
7
8
9
#include <avr/io.h>
10
#include <avr/interrupt.h>
11
#include <util/atomic.h>
12
#include <util/delay.h>
13
14
15
volatile uint8_t digits[4];  // Speicher für die eizelnen Digits
16
volatile uint8_t digit_position = 0;
17
18
uint8_t digitControl[4] = { (1 << PC0), (1 << PC1), (1 << PC2), (1 << PC3) };
19
20
uint16_t cnt = 0;
21
22
const uint8_t numbers[11] =    // Array zur Darstellung der Zeichen auf der 7-Seg-Anz.  // 08.08.13 - int8_t numbers[11], nicht uint8_t
23
{
24
25
  0b01101111,    // 0
26
  0b00000110,    // 1
27
  0b10101011,    // 2
28
  0b10001111,    // 3
29
  0b11000110,    // 4
30
  0b11001101,    // 5
31
  0b11101101,    // 6
32
  0b00000111,    // 7
33
  0b11101111,    // 8
34
  0b11001111,    // 9
35
  0b00000000    // leere Anzeige
36
  
37
};
38
39
void delay_ms(uint16_t ms)
40
{
41
  for(uint16_t t=0; t<=ms; t++)
42
    _delay_ms(1);
43
}
44
    
45
46
ISR(TIMER2_OVF_vect)  // timer2 overflow interrupt
47
{
48
  PORTC = 0b00000000;    // alle Digits aus
49
  TCNT2 += 0; 
50
51
  digit_position++;
52
  
53
  if( digit_position == 4 )
54
    {digit_position = 0;}
55
56
  PORTD = digits[digit_position];
57
  PORTC = digitControl[digit_position];
58
}
59
60
61
void zahl_ausgeben(uint16_t zahl)
62
{
63
  uint8_t i;
64
65
  digits[3] = numbers[ zahl % 10 ];
66
  zahl /= 10;
67
68
 for( i=2; i>=0 ; i-- )
69
  {
70
    if( zahl > 0 )
71
    {
72
      digits[i] = numbers[ zahl % 10 ];
73
      zahl = zahl / 10;
74
    }
75
    else
76
      digits[i] = numbers[10];
77
  }
78
}
79
80
81
int main(void)
82
{
83
  
84
85
  DDRC = 0xFF;  // PORTC als Ausgang - über PORTC werden die jeweiligen Segmente einer 7-Seg. Anzeige gesteuert
86
  DDRD = 0xFF;  // PORTD als Ausgang
87
  PORTC = 0b00000000;
88
  
89
  TIMSK |= (1 << TOIE2);
90
  TCCR2 |= (1 << CS22)|(1 << CS21);    // Vorteiler auf 256
91
  
92
  sei();
93
94
  while(1)
95
  {
96
    zahl_ausgeben( cnt++ );
97
    if( cnt = 10000 )
98
      cnt = 0;
99
100
    delay_ms(200);
101
  }
102
103
  return 0;
104
}

von Fan_AT (Gast)


Lesenswert?

Ich melde mich, sobald ich den Fehler finde, wird vllt. noch etwas 
dauern :D

von Fan_AT (Gast)


Lesenswert?

Gut, digitControl kann man als volatile deklarieren. Ändern tut sich 
aber nichts.

von Karl H. (kbuchegg)


Lesenswert?

Fan_AT schrieb:
> Ich melde mich, sobald ich den Fehler finde, wird vllt. noch etwas
> dauern :D


Moment. Hast du den Overflow Interrupt jetzt freigegeben?
In deinem Programm hat das hier
1
  TIMSK |= (1 << TOIE2);
gefehlt!

> Gut, digitControl kann man als volatile deklarieren. Ändern tut sich aber 
nichts.


Wer hat was von einem fehlenden volatile gesagt? Da fehlt keiner.

von Karl H. (kbuchegg)


Lesenswert?

> In deinem Quote, nur eine Zeile später kommt sei(); vor. Damit aktiviere ich 
doch auch global die Interrupts?

Argh.
sei() ist das Rundumschlag - 'Jetzt gilt es. Alle aktivierten Interrupts 
sind auch aktiv und können Funktionsaufrufe auslösen'. Sozusagen der 
Hauptschalter.


Aber nichts desto trotz musst du auch mal wo festlegen, welche 
Interrupts auch zugelassen sind. Der Mega hat viele mögliche 
INterrupt-Quellen. Die interessieren dich aber alle nicht.
Dich interessiert der Interrupt vom Overflow des Timers 2. Daher
1
  TIMSK |= (1 << TOIE2);

von Karl H. (kbuchegg)


Lesenswert?

Hmm.
Hab ich deine Hardware-Belegung aus dem Original-Code falsch 
rausgelesen?

Am Port-D hängen die Segmentleitungen. Ein 1-bit aktiviert das Segment
Am Port-C, Pins 0 bis 3, hängen die Digit-Leitungen. Ein 1 Bit aktiviert 
das entsprechende Digit.

von Fan_AT (Gast)


Lesenswert?

> TIMSK |= (1<<TICIE1) | (1<<TOIE1) | (1 << TOIE2); // Interrupts akivieren, 
>Capture und Overflow, Overlow-Interrupt aktivieren

Die Freigabe erfolgte nur zusammen in einer Zeile mit dem Rest - aber 
sie war da.

Von 'E' halte ich sehr viel, wenn es funktioneirt. Du wirst wohl lachen, 
aber der Code funktioniert nicht. Hier:
1
#define F_CPU 16000000UL // uC läuft mit 16MHz
2
3
#include <avr/io.h>
4
#include <avr/interrupt.h>
5
#include <util/delay.h>
6
7
8
volatile uint8_t digits[4];  // Speicher für die eizelnen Digits
9
volatile uint8_t digit_position = 0;
10
11
volatile uint8_t digitControl[4] = { (1 << PC0), (1 << PC1), (1 << PC2), (1 << PC3) };
12
13
uint16_t cnt = 0;
14
15
const int8_t numbers[12] =    // Array zur Darstellung der Zeichen auf der 7-Seg-Anz.
16
{
17
18
  0b01101111,    // 0
19
  0b00000110,    // 1
20
  0b10101011,    // 2
21
  0b10001111,    // 3
22
  0b11000110,    // 4
23
  0b11001101,    // 5
24
  0b11101101,    // 6
25
  0b00000111,    // 7
26
  0b11101111,    // 8
27
  0b11001111,    // 9
28
  0b00000000,    // leere Anzeige
29
  0b11101001,    // 'E'
30
};
31
32
void delay_ms(uint16_t ms)
33
{
34
  for(uint16_t t=0; t<=ms; t++)
35
    _delay_ms(1);
36
}
37
    
38
39
ISR(TIMER2_OVF_vect)  // timer2 overflow interrupt
40
{
41
  //PORTC = 0b00000000;    // alle Digits aus
42
43
  digit_position++;
44
  
45
  if( digit_position == 4 )
46
    {digit_position = 0;}
47
48
  PORTD = digits[digit_position];
49
  PORTC = digitControl[digit_position];
50
}
51
52
53
void zahl_ausgeben(uint16_t zahl)
54
{
55
  uint8_t i;
56
57
  if( zahl > 9999 )
58
  {
59
    digits[0] = digits[1] = digits[2] = digits[3] = numbers[11]; //'E'
60
    return; 
61
  }
62
63
  digits[4] = numbers[ zahl % 10 ];
64
  zahl /= 10;
65
66
  for( i = 3; i != 255; i-- )
67
  {
68
    if( zahl > 0 )
69
    {
70
      digits[i] = numbers[ zahl % 10 ];
71
      zahl = zahl / 10;
72
    }
73
    else
74
      digits[i] = numbers[10];
75
  }
76
}
77
78
int main(void)
79
{
80
  
81
82
  DDRC = 0xFF;  // PORTC als Ausgang - über PORTC werden die jeweiligen Segmente einer 7-Seg. Anzeige gesteuert
83
  DDRD = 0xFF;  // PORTD als Ausgang
84
  PORTC = 0b00000000;
85
  
86
  TIMSK |= (1 << TOIE2);
87
  TCCR2 |= (1 << CS22)|(1 << CS21);    // Vorteiler auf 256
88
  
89
  sei();
90
91
  while(1)
92
  {
93
    zahl_ausgeben( cnt++ );
94
    if( cnt = 10000 )
95
      cnt = 0;
96
97
    delay_ms(200);
98
  }
99
100
  return 0;
101
}

Mir ist die Schleifenbedingung allerdings nicht ganz geheuer oO
ginge das nicht mit
1
 for( i=2; i>=0 ; i-- )

von Karl H. (kbuchegg)


Lesenswert?

Hier steckt der Fehler.

Beim Mega88 steckt das entsprechende Bit nicht im TIMSK, sondern im 
TIMSK2.
1
  TIMSK2 |= ( 1 << TOIE2 );

von Fan_AT (Gast)


Lesenswert?

4x 7-Segment Anzeigen mit je gemeinsamen Kathode. Die Steuerleitungen 
für die Digits liegen auf PORTC=0b0000XXXX.
Die einzelnen Segmente liegen auf PORTD.

von Karl H. (kbuchegg)


Lesenswert?

Fan_AT schrieb:

> Von 'E' halte ich sehr viel, wenn es funktioneirt.

Gut ich poste dann den Code nochmal. Hab ihn wieder rausgelöscht, damit 
der Thread kein Durcheinander wird.

> Du wirst wohl lachen,
> aber der Code funktioniert nicht.

Ich lache gar nicht.
Ich sollte eher weinen, weil ich dir aus deinem Urprogramm geglaubt 
habe, dass
1
   TIMSK |= ( 1 << TOIE2 );
richtig ist, wenn es das tatsächlich nicht ist.
1
   TIMSK2 |= ( 1 << TOIE2 );

Ich hätte es eigentlich mit dem Datenblatt kontrollieren sollen. Getreu 
dem Motto: nimm nichts als richtig an, ehe du es nicht überprüft hast.

von Fan_AT (Gast)


Lesenswert?

Nein, keine Sorge - es funktioniert immer noch nicht. Teste es aktuell 
auf 'nem ATmega8 und vergessen, den Codeschnipsel zu ändern. Tut mir 
Leid.

Also: klappt weder auf dem uC, noch auf dem anderen. Ich gehe nochmal 
alle Register durch.

von Karl H. (kbuchegg)


Lesenswert?

Fan_AT schrieb:

> Mir ist die Schleifenbedingung allerdings nicht ganz geheuer oO

Allerdings hast du recht.

Asche auf mein Haupt

> ginge das nicht mit
>
1
>  for( i=2; i>=0 ; i-- )
2
>

So zwar nicht, denn i ist sowieso als unsigned Wert IMMER größer oder 
gleich 0. i kann nicht negativ sein

Allerdings hat ich noch einen kapitalen Bock geschossen. Einen richtigen 
Anfängerfehler.
Hier ist die Korrektur
1
void zahl_ausgeben(uint16_t zahl)
2
{
3
  uint8_t i;
4
5
  if( zahl > 9999 )
6
  {
7
    digits[0] = digits[1] = digits[2] = digits[3] = numbers[11]; //'E'
8
    return; 
9
  }
10
11
  digits[3] = numbers[ zahl % 10 ];
12
  zahl /= 10;
13
14
  for( i = 2; i != 255; i-- )
15
  {
16
    if( zahl > 0 )
17
    {
18
      digits[i] = numbers[ zahl % 10 ];
19
      zahl = zahl / 10;
20
    }
21
    else
22
      digits[i] = numbers[10];
23
  }
24
}

von Karl H. (kbuchegg)


Lesenswert?

Fan_AT schrieb:
> Nein, keine Sorge - es funktioniert immer noch nicht. Teste es aktuell
> auf 'nem ATmega8 und vergessen,


grrrrrrrrr

von Fan_AT (Gast)


Lesenswert?

Hoppla! Das war echt die for-Bedingung :(
Hätte mir, Deppen, auch einfallen können... die ungleich 255 hängt mit 
der max.Größe des uint8_t zusammen?

Vielen, vielen Dank schonmal dafür - ich sah den Wald vor lauter Bäumen 
nicht mehr...

von Karl H. (kbuchegg)


Lesenswert?

Fan_AT schrieb:
> Hoppla! Das war echt die for-Bedingung :(
> Hätte mir, Deppen, auch einfallen können... die ungleich 255 hängt mit
> der max.Größe des uint8_t zusammen?
>
> Vielen, vielen Dank schonmal dafür - ich sah den Wald vor lauter Bäumen
> nicht mehr...

Puh.
Da muss ich mir jetzt erst mal den Schweiss von der Stirn wischen.

Ich seh grad, den anderen Fehler, der mich wie einen absoluten Tölpel 
hat aussehen lassen, hast du selbst schon korrigiert gehabt und 
Stillschweigen darüber bewahrt. Anständig von dir!
1
    digits[4] = numbers[ zahl % 10 ];
könnt mich jetzt noch in den Allerwertesten beissen deswegen.

von Karl H. (kbuchegg)


Lesenswert?

Fan_AT schrieb:
> Hoppla! Das war echt die for-Bedingung :(
> Hätte mir, Deppen, auch einfallen können... die ungleich 255 hängt mit
> der max.Größe des uint8_t zusammen?
>

Jep.
Ist quasi -1 für einen unsigned Wert mit 8 Bit

Ja ich weiss. ist nicht so toll. Aber mir ist auf die Schnelle nichts 
anderes eingefallen :-)

von Fan_AT (Gast)


Lesenswert?

Das ist alles besser, als das von mir Geleistete ;)

Hier nochmal der gesamte Code - der in seiner Gesamtheit - wieder nichts 
anzeigt. Ob du den Fehler auf Anhieb siehst?
Das Ganze läuft jetzt auf ATmega8 - die Register scheinen alle richtig 
zu sein. Überprüfe ich aber grade. Danke dir für die ganze Mühe...
1
#define F_CPU 16000000UL // uC läuft mit 16MHz
2
3
#ifndef TRUE
4
#define TRUE 1
5
#define FALSE 0
6
#endif
7
8
9
#include <avr/io.h>
10
#include <avr/interrupt.h>
11
#include <util/atomic.h>
12
13
volatile unsigned short z=0;    // Zählvariable des Timer1
14
volatile char UpdateDisplay = TRUE;      // char-1Byte // Einen Wert TRUE definiert 
15
volatile unsigned long Zeit = 0;      // unsigned long-4Bytes
16
17
//volatile uint8_t tausender, hunderter, zehner, einer;  // Speicher für die eizelnen Digits
18
//volatile uint8_t position = 0;
19
20
volatile unsigned short zaehler1_ovf = 0;    // Anzahl der Überläufe des Timer1, short-2Bytes
21
//unsigned int zaehler1_ovf = 0;    // Anzahl der Überläufe des Timer1
22
unsigned long zaehlschritte = 0;
23
24
uint16_t freq = 0;
25
26
27
volatile unsigned long Startzeit = 0;
28
29
volatile uint8_t digits[4];  // Speicher für die eizelnen Digits
30
volatile uint8_t digit_position = 0;
31
32
uint8_t digitControl[4] = { (1 << PC0), (1 << PC1), (1 << PC2), (1 << PC3) };  
33
34
35
//float korrekturfaktor = 1; // für Abgleich anpassen (z.B. 0.9997)
36
37
38
const uint8_t numbers[11] =    // Array zur Darstellung der Zeichen auf der 7-Seg-Anz.  // 08.08.13 - int8_t numbers[11], nicht uint8_t
39
{
40
41
  0b01101111,    // 0
42
  0b00000110,    // 1
43
  0b10101011,    // 2
44
  0b10001111,    // 3
45
  0b11000110,    // 4
46
  0b11001101,    // 5
47
  0b11101101,    // 6
48
  0b00000111,    // 7
49
  0b11101111,    // 8
50
  0b11001111,    // 9
51
  0b00000000    // leere Anzeige
52
  
53
};
54
55
56
ISR(TIMER1_OVF_vect) 
57
{
58
  ++z;
59
  //TIFR = (1<<TOV1); //Beim Überlauf von Timer1 und dem anschließenden Interrupt erfolgt eine Zurücksetzung auf Null
60
  //Braucht nicht gelöscht zu werden. Jeder Interrupt löscht automatisch sein eigenes Flag beim Aufruf der ISR.
61
  
62
}
63
64
65
ISR(TIMER1_CAPT_vect)
66
{
67
68
   if(UpdateDisplay)          // Das Display wurde mit den Ergebnissen der vorhergehenden
69
    {             // Messung noch nicht aktualisiert. Die nächste Messung
70
    return;         // verzögern, bis die Start- und EndTime-Variablen wieder
71
  }                    // gefahrlos beschrieben werden können
72
  
73
  
74
  static char ErsteFlanke = TRUE;
75
  static uint8_t LowByte = 0;
76
  static uint8_t HighByte = 0;
77
    
78
  LowByte = ICR1L;         // LowByte zuerst, HighByte später gepuffert
79
  HighByte = ICR1H;  
80
  
81
82
   
83
  
84
  if(ErsteFlanke)
85
  {
86
    Startzeit = ICR1;
87
    z = 0;
88
    ErsteFlanke = FALSE;       // Nach der nächsten Flanke beginnt die Ausgabe
89
  
90
  }
91
  
92
  
93
  if ((HighByte < 128) && (TIFR & (1<<TOV1)))
94
    {   
95
      // wartenden Timer Overflow Interrupt vorziehen
96
      ++z;         
97
      TIFR = (1<<TOV1);    // Timer Overflow int. löschen, da schon hier ausgeführt // macht der uC angeblich selbst
98
    }
99
100
  
101
  else
102
  {
103
  Zeit = 0;
104
    Zeit = ICR1 - Startzeit;
105
    UpdateDisplay = TRUE;      // Eine vollständige Messung. Sie kann ausgewertet werden
106
    ErsteFlanke = TRUE;        // Bei der nächsten Flanke beginnt der nächste Messzyklus
107
  
108
  
109
  zaehler1_ovf = z;    // Anzahl der Überläufe wird in zaehler1_ovf kopiert
110
  Startzeit = 0;
111
  z=0;  // Nach dem Sichern - annullieren
112
  
113
  }  
114
  
115
116
}
117
118
119
ISR(TIMER2_OVF_vect)  // timer2 overflow interrupt
120
{
121
  PORTC = 0b00000000;    // alle Digits aus
122
  TCNT2 += 0; 
123
124
  digit_position++;
125
  
126
  if( digit_position == 4 )
127
    {digit_position = 0;}
128
129
  PORTD = digits[digit_position];
130
  PORTC = digitControl[digit_position];
131
}
132
133
134
void zahl_ausgeben(uint16_t zahl)
135
{
136
  uint8_t i;
137
138
  digits[3] = numbers[zahl % 10];  // Die letzte Ziffer wird gesichert
139
  zahl /= 10;
140
141
  for( i = 2; i != 255; i-- ) 
142
  {
143
    if( zahl > 0 )
144
    {
145
      digits[i] = numbers[zahl % 10];
146
      zahl /= 10;
147
    }
148
    else
149
      digits[i] = numbers[10];
150
  }
151
  
152
  if(freq==0)
153
  {digits[3] = numbers[0];}
154
  
155
}
156
157
158
159
int main(void)
160
{
161
  // Definition von Ein- und Ausgängen des uC Boards
162
  DDRB = 0xFE;  // Mit Ausnahme des ICP1-Pins alles als Ausgang
163
  DDRC = 0xFF;  // PORTC als Ausgang - über PORTC werden die jeweiligen Segmente einer 7-Seg. Anzeige gesteuert
164
  DDRD = 0xFF;  // PORTD als Ausgang
165
  PORTC = 0b00000000;
166
  
167
  
168
  TIMSK |= (1<<TICIE1) | (1<<TOIE1) | (1 << TOIE2); // Interrupts akivieren, Capture und Overflow, Overlow-Interrupt aktivieren
169
  TCCR1B |= (1<<ICES1)  | (1<<CS10); // Input Capture Edge, kein Prescaler
170
  
171
    TCCR2 |= (1 << CS22)|(1 << CS21);    // Vorteiler auf 256
172
173
174
  sei();          // Interruptbehandlung ein
175
  
176
177
  while(1)  // unendliche Schleife
178
  {
179
  
180
    if(UpdateDisplay)    // Erst nach zweiter Flanke ausführen
181
      {
182
        
183
        ATOMIC_BLOCK(ATOMIC_FORCEON)
184
        {
185
          zaehlschritte = (unsigned long)((65536.0*zaehler1_ovf) + Zeit); // sonst am Ende: +Zeitdifferenz;  
186
        }
187
        
188
      
189
        if (zaehlschritte==0)
190
          {
191
            freq = 0;
192
          }
193
        else
194
          {
195
            ATOMIC_BLOCK(ATOMIC_FORCEON)
196
            {
197
              // Die abschließende Berechnung der Schritte pro Sekunde
198
              freq = (uint16_t) (F_CPU/(zaehlschritte));
199
            }
200
      
201
            zahl_ausgeben(freq);
202
          
203
204
          UpdateDisplay = FALSE;
205
      }
206
    }
207
      
208
      
209
  }
210
211
return 0;
212
}

von Karl H. (kbuchegg)


Lesenswert?

Fan_AT schrieb:
> Das ist alles besser, als das von mir Geleistete ;)
>
> Hier nochmal der gesamte Code - der in seiner Gesamtheit - wieder nichts
> anzeigt.

D.h. das Multiplexing, Zahlenausgabe, Überlauferkennung in der 
Zahlenausgabe läuft erst mal?

(Sorry wenn ich nachfrage. Aber ich hab nur das, was du schreibst. Und 
ich brauch klare Bestätigungen was geht und was nicht geht. Ist für mich 
wichtig, damit ich weiß, welche Teile ich aus der Fehlersuche 
ausklammern kann)

von Karl H. (kbuchegg)


Lesenswert?

1
void zahl_ausgeben(uint16_t zahl)
2
{
3
  ....
4
  if(freq==0)
5
  {digits[3] = numbers[0];}
6
  
7
}

Das hat in der Funktion nichts verloren.

Disziplin!

Eine Funktion hat eine Aufgabe.
Die Aufgabe der Funktion zahl_ausgeben ist es, eine Zahl auszugeben. 
Egal welche. Ob freq 0 ist oder nicht, interessiert hier nicht.
Wenn der restliche Code eine Anzeige von 0 haben will, dann soll er 
zahl_ausgeben(0) aufrufen. Aber nicht hier in der Funktion irgendwelche 
Hacks einbauen. Das sind alles Dinge, die dir früher oder später auf den 
SChädel fallen.
Frag dich immer:
Welche Aufgabe hat eine Funktion? Was gehört zu dieser Aufgabe? Was 
gehört NICHT zu dieser Aufgabe?

von Fan_AT (Gast)


Lesenswert?

Gut, Disziplin, gemerkt. Und gelöscht.

Gut, also... Nein, eher schlecht. Unser letztes Programm, reines 
Multiplxing zeigt zwar eine Null an - aber dabei bleibt's auch. Ich bin 
noch nicht fündig geworden.

von Karl H. (kbuchegg)


Lesenswert?

Lass uns den Teil in main mal korrekt einrücken. Dann sieht man schon 
mal mehr
1
  while(1)  // unendliche Schleife
2
  {
3
  
4
    if(UpdateDisplay)    // Erst nach zweiter Flanke ausführen
5
    {
6
      ATOMIC_BLOCK(ATOMIC_FORCEON)
7
      {
8
        zaehlschritte = (unsigned long)((65536.0*zaehler1_ovf) + Zeit); // sonst am Ende: +Zeitdifferenz;  
9
      }
10
        
11
      
12
      if (zaehlschritte==0)
13
      {
14
        freq = 0;
15
      }
16
      else
17
      {
18
        ATOMIC_BLOCK(ATOMIC_FORCEON)
19
        {
20
          // Die abschließende Berechnung der Schritte pro Sekunde
21
          freq = (uint16_t) (F_CPU/(zaehlschritte));
22
        }
23
        zahl_ausgeben(freq);
24
          
25
26
        UpdateDisplay = FALSE;
27
      }
28
    }
29
  }
30
31
  return 0;
32
}

Hä?
Da stimmt doch in der logischen Struktur was nicht.
Damit zahl_ausgeben aufgerufen werden kann, müssen ein paar 
Vorbedingungen gültig sein.

Aber noch viel schlimmer.
Damit UpdateDisplay je FALSE werden kann, müssen ebenfalls ein paar 
Vorbedingungen gültig sein. Bei einem 0 Ergebnis in zaehlschritte, wird 
UpdateDisplay nicht FALSE. Was dann wiederrum im Gegenzug dazu führt, 
das in der ISR
1
ISR(TIMER1_CAPT_vect)
2
{
3
4
   if(UpdateDisplay)          // Das Display wurde mit den Ergebnissen der vorhergehenden
5
    {             // Messung noch nicht aktualisiert. Die nächste Messung
6
    return;         // verzögern, bis die Start- und EndTime-Variablen wieder
7
  }                    // gefahrlos beschrieben werden können
8
  ...
keine weitere Messung mehr durchgeführt wird, die dafür sorgen könnte, 
dass zaehlschritte ungleich 0 werden könnte.


Das muss in main anders angeordnet werden!
Ein 0 Ergebnis ist kein fehlerhaftes Ergebnis, sondern einfach nur 0.
Auch das wird angezeigt und UpdateDisplay auf FALSE gesetzt, damit eine 
weitere Messung durchgeführt werden kann.


Saubere Einrückungen sind nicht einfach nur eine optische Sache!
Sie helfen dir die Codestruktur zu überblicken!

von Fan_AT (Gast)


Lesenswert?

Hm, UpdateDisplay muss außerhalb der if-Abfrage stehen.

Struktur ist sehr gut, man muss aber auch die Sprache beherrschen :) Ist 
jetzt auf mich bezogen.

Mein letzter Post bezog sich auf das reine Multiplex-Programm, was 
lediglich eine Null anzeigt und nicht weiterzählt.

von Karl H. (kbuchegg)


Lesenswert?

Der Kommentar hier
1
    if(UpdateDisplay)    // Erst nach zweiter Flanke ausführen

sollte anders aussehen.
Das ist nicht 'wenn zweite Flanke'. D.h. technisch ist es das schon. 
Aber an dieser Stelle ist es besser zu lesen als:

Wenn eine Messung fertig ist.

Dieses Flag hat 2 Aufgaben:
Zum einen ist es die Benachrichtigung aus der ISR in die main() "Ich 
habe eine Messung fertig". Zum anderen ist es die Verriegelung der ISR 
mit isch selbst, so lange keine weitere Messung zu machen, bis die 
main() das Ergebnis verarbeitet hat.
D.h. wenn main() die Messergebnisse der ISR ausgewertet hat, dann muss 
sie UpdateDisplay wieder auf FALSE setzen. Egal was dann bei der 
Auswertung rauskommt. Das FALSE-setzen von UpdateDisplay ist quasi die 
Rückmeldung von main() in die ISR "OK, ich hab mir deinen Messwert 
geholt, du kannst wieder loslegen und die nächste Messung machen". Das 
aber, muss IMMER passieren. Egal was dann bei der Auswertung rauskommt

von Karl H. (kbuchegg)


Lesenswert?

Fan_AT schrieb:

> Mein letzter Post bezog sich auf das reine Multiplex-Programm, was
> lediglich eine Null anzeigt und nicht weiterzählt.


Was jetzt?
Wir reden aneinander vorbei.

Können wir uns erst mal auf eine Sache, und nur auf eine Sache 
konzetrieren?

1
int main(void)
2
{
3
  
4
5
  DDRC = 0xFF;  // PORTC als Ausgang - über PORTC werden die jeweiligen Segmente einer 7-Seg. Anzeige gesteuert
6
  DDRD = 0xFF;  // PORTD als Ausgang
7
  PORTC = 0b00000000;
8
  
9
  TIMSK |= (1 << TOIE2);
10
  TCCR2 |= (1 << CS22)|(1 << CS21);    // Vorteiler auf 256
11
  
12
  sei();
13
14
  while(1)
15
  {
16
    zahl_ausgeben( cnt++ );
17
    if( cnt = 10000 )         //   <------- *** das ist kein Vergleich ***
18
      cnt = 0;
19
20
    delay_ms(200);
21
  }
22
23
  return 0;
24
}

läuft das (mit der Korrektur): ja oder nein.
Zeigt die Anzeige hochzählende Zahlen: ja oder nein.

von Fan_AT (Gast)


Lesenswert?

Gut, bleiben wir erstmal beim reinen Multiplex.

Ja, das Programm läuft und zeigt eine Null an, ohne Flimmer, Flackern.

Nein, das Programm zählt nicht, sondern bleibt konstant bei Null.

von Karl H. (kbuchegg)


Lesenswert?

Fan_AT schrieb:
> Gut, bleiben wir erstmal beim reinen Multiplex.
>
> Ja, das Programm läuft und zeigt eine Null an, ohne Flimmer, Flackern.
>
> Nein, das Programm zählt nicht, sondern bleibt konstant bei Null.

Ok. Dann korrigier mal
1
    if( cnt = 10000 )

zu
1
    if( cnt == 10000 )

von Fan_AT (Gast)


Lesenswert?

Genau, mit dem Vergleich läuft es nun durch und beginnt von neu.

von Karl H. (kbuchegg)


Lesenswert?

Fan_AT schrieb:
> Genau, mit dem Vergleich läuft es nun durch und beginnt von neu.

Gut. Wie willst du weitermachen?
Mit dem Overflow-E bei der Zahlausgabe?
Wär vernünftig, damit zu große Rechenergebnisse in der 
Frequenzberechnung nicht alles durcheinander schmeissen.

von Karl H. (kbuchegg)


Lesenswert?

Ist schnell gemacht und schnell getestet (Ich weiß nämlich nicht, ob ich 
die 7-Seg Darstellung fürs 'E' richtig gemacht habe)
1
const int8_t numbers[11] =    // Array zur Darstellung der Zeichen auf der 7-Seg-Anz.
2
{
3
4
  0b01101111,    // 0
5
  0b00000110,    // 1
6
  0b10101011,    // 2
7
  0b10001111,    // 3
8
  0b11000110,    // 4
9
  0b11001101,    // 5
10
  0b11101101,    // 6
11
  0b00000111,    // 7
12
  0b11101111,    // 8
13
  0b11001111,    // 9
14
  0b00000000,    // leere Anzeige
15
  0b11101001,    // 'E'
16
};
17
18
void zahl_ausgeben(uint16_t zahl)
19
{
20
  uint8_t i;
21
22
  if( zahl > 9999 )
23
  {
24
    digits[0] = digits[1] = digits[2] = digits[3] = numbers[11]; 'E'
25
    return; 
26
  }
27
28
  digits[3] = numbers[zahl % 10];  // Die letzte Ziffer wird gesichert
29
  zahl /= 10;
30
31
  for( i = 2; i != 255; i-- ) 
32
  {
33
    if( zahl > 0 )
34
    {
35
      digits[i] = numbers[zahl % 10];
36
      zahl /= 10;
37
    }
38
    else
39
      digits[i] = numbers[10];
40
  }
41
}


und getestet
1
int main()
2
{
3
  DDRC = 0xFF;  // PORTC als Ausgang - über PORTC werden die jeweiligen Segmente einer 7-Seg. Anzeige gesteuert
4
  DDRD = 0xFF;  // PORTD als Ausgang
5
  PORTC = 0b00000000;
6
  
7
  TIMSK |= (1 << TOIE2);
8
  TCCR2 |= (1 << CS22)|(1 << CS21);    // Vorteiler auf 256
9
  
10
  sei();
11
12
  while(1)
13
  {
14
    zahl_ausgeben( 500 );
15
    delay_ms(1000);
16
    zahl_ausgeben( 10000 );
17
    delay_ms(1000);
18
  }
19
20
  return 0;
21
}

offensichtlich sollte die Anzeige zwischen 500 und lauter E im 
Sekundentakt wechseln.

von Karl H. (kbuchegg)


Lesenswert?

Wenn die Anzeige 'wild' aussieht, dann hab ich mich wahrscheinlich hier
1
    0b11101001,    // 'E'

bei den Bits vertan.

von Fan_AT (Gast)


Lesenswert?

Die Frequenzbegrenzung ist bereits fertig und wird noch vor den 
Frequenzzähler geschaltet. Aber das "E"-Programm funktioniert 
einwandfrei.

Jetzt das Programm in seiner Gesamtheit. UpdateDisplay ist nun außerhalb 
der if-Abfrage. Es wird nichts angezeigt.

Hier der Code:
1
#define F_CPU 16000000UL // uC läuft mit 16MHz
2
3
#ifndef TRUE
4
#define TRUE 1
5
#define FALSE 0
6
#endif
7
8
9
#include <avr/io.h>
10
#include <avr/interrupt.h>
11
#include <util/atomic.h>
12
13
volatile unsigned short z=0;    // Zählvariable des Timer1
14
volatile char UpdateDisplay = TRUE;      // char-1Byte // Einen Wert TRUE definiert 
15
volatile unsigned long Zeit = 0;      // unsigned long-4Bytes
16
17
//volatile uint8_t tausender, hunderter, zehner, einer;  // Speicher für die eizelnen Digits
18
//volatile uint8_t position = 0;
19
20
volatile unsigned short zaehler1_ovf = 0;    // Anzahl der Überläufe des Timer1, short-2Bytes
21
//unsigned int zaehler1_ovf = 0;    // Anzahl der Überläufe des Timer1
22
unsigned long zaehlschritte = 0;
23
24
uint16_t freq = 0;
25
26
27
volatile unsigned long Startzeit = 0;
28
29
volatile uint8_t digits[4];  // Speicher für die eizelnen Digits
30
volatile uint8_t digit_position = 0;
31
32
uint8_t digitControl[4] = { (1 << PC0), (1 << PC1), (1 << PC2), (1 << PC3) };  
33
34
35
//float korrekturfaktor = 1; // für Abgleich anpassen (z.B. 0.9997)
36
37
38
const int8_t numbers[11] =    // Array zur Darstellung der Zeichen auf der 7-Seg-Anz.  // 08.08.13 - int8_t numbers[11], nicht uint8_t
39
{
40
41
  0b01101111,    // 0
42
  0b00000110,    // 1
43
  0b10101011,    // 2
44
  0b10001111,    // 3
45
  0b11000110,    // 4
46
  0b11001101,    // 5
47
  0b11101101,    // 6
48
  0b00000111,    // 7
49
  0b11101111,    // 8
50
  0b11001111,    // 9
51
  0b00000000    // leere Anzeige
52
  
53
};
54
55
56
ISR(TIMER1_OVF_vect) 
57
{
58
  ++z;
59
  //TIFR = (1<<TOV1); //Beim Überlauf von Timer1 und dem anschließenden Interrupt erfolgt eine Zurücksetzung auf Null
60
  //Braucht nicht gelöscht zu werden. Jeder Interrupt löscht automatisch sein eigenes Flag beim Aufruf der ISR.
61
  
62
}
63
64
65
ISR(TIMER1_CAPT_vect)
66
{
67
68
   if(UpdateDisplay)          // Das Display wurde mit den Ergebnissen der vorhergehenden
69
    {             // Messung noch nicht aktualisiert. Die nächste Messung
70
    return;         // verzögern, bis die Start- und EndTime-Variablen wieder
71
  }                    // gefahrlos beschrieben werden können
72
  
73
  
74
  static char ErsteFlanke = TRUE;
75
  static uint8_t LowByte = 0;
76
  static uint8_t HighByte = 0;
77
    
78
  LowByte = ICR1L;         // LowByte zuerst, HighByte später gepuffert
79
  HighByte = ICR1H;  
80
  
81
82
   
83
  
84
  if(ErsteFlanke)
85
  {
86
    Startzeit = ICR1;
87
    z = 0;
88
    ErsteFlanke = FALSE;       // Nach der nächsten Flanke beginnt die Ausgabe
89
  
90
  }
91
  
92
  
93
  if ((HighByte < 128) && (TIFR & (1<<TOV1)))
94
    {   
95
      // wartenden Timer Overflow Interrupt vorziehen
96
      ++z;         
97
      TIFR = (1<<TOV1);    // Timer Overflow int. löschen, da schon hier ausgeführt // macht der uC angeblich selbst
98
    }
99
100
  
101
  else
102
  {
103
  Zeit = 0;
104
    Zeit = ICR1 - Startzeit;
105
    UpdateDisplay = TRUE;      // Eine vollständige Messung. Sie kann ausgewertet werden
106
    ErsteFlanke = TRUE;        // Bei der nächsten Flanke beginnt der nächste Messzyklus
107
  
108
  
109
  zaehler1_ovf = z;    // Anzahl der Überläufe wird in zaehler1_ovf kopiert
110
  Startzeit = 0;
111
  z=0;  // Nach dem Sichern - annullieren
112
  
113
  }  
114
  
115
116
}
117
118
119
ISR(TIMER2_OVF_vect)  // timer2 overflow interrupt
120
{
121
  PORTC = 0b00000000;    // alle Digits aus
122
  TCNT2 += 0; 
123
124
  digit_position++;
125
  
126
  if( digit_position == 4 )
127
    {digit_position = 0;}
128
129
  PORTD = digits[digit_position];
130
  PORTC = digitControl[digit_position];
131
}
132
133
134
void zahl_ausgeben(uint16_t zahl)
135
{
136
  uint8_t i;
137
138
  digits[3] = numbers[zahl % 10];  // Die letzte Ziffer wird gesichert
139
  zahl /= 10;
140
141
  for( i = 2; i != 255; i-- ) 
142
  {
143
    if( zahl > 0 )
144
    {
145
      digits[i] = numbers[zahl % 10];
146
      zahl /= 10;
147
    }
148
    else
149
      digits[i] = numbers[10];
150
  }
151
  
152
  
153
}
154
155
156
157
int main(void)
158
{
159
  // Definition von Ein- und Ausgängen des uC Boards
160
  DDRB = 0xFE;  // Mit Ausnahme des ICP1-Pins alles als Ausgang
161
  DDRC = 0xFF;  // PORTC als Ausgang - über PORTC werden die jeweiligen Segmente einer 7-Seg. Anzeige gesteuert
162
  DDRD = 0xFF;  // PORTD als Ausgang
163
  PORTC = 0b00000000;
164
  
165
  
166
  TIMSK |= (1<<TICIE1) | (1<<TOIE1) | (1 << TOIE2); // Interrupts akivieren, Capture und Overflow, Overlow-Interrupt aktivieren
167
  TCCR1B |= (1<<ICES1)  | (1<<CS10); // Input Capture Edge, kein Prescaler
168
  
169
    TCCR2 |= (1 << CS22)|(1 << CS21);    // Vorteiler auf 256
170
171
172
  sei();          // Interruptbehandlung ein
173
  
174
175
  while(1)  // unendliche Schleife
176
  {
177
  
178
    if(UpdateDisplay)    // Erst nach zweiter Flanke ausführen
179
      {
180
        
181
        ATOMIC_BLOCK(ATOMIC_FORCEON)
182
        {
183
          zaehlschritte = (unsigned long)((65536.0*zaehler1_ovf) + Zeit); // sonst am Ende: +Zeitdifferenz;  
184
        }
185
        
186
      
187
        if (zaehlschritte==0)
188
          {
189
            freq = 0;
190
          }
191
        else
192
          {
193
            ATOMIC_BLOCK(ATOMIC_FORCEON)
194
            {
195
              // Die abschließende Berechnung der Schritte pro Sekunde
196
              freq = (uint16_t) (F_CPU/(zaehlschritte));
197
            }
198
      
199
            zahl_ausgeben(freq);
200
          
201
          }
202
          
203
          UpdateDisplay = FALSE;
204
      }
205
    
206
      
207
      
208
  }
209
210
return 0;
211
}

von Fan_AT (Gast)


Lesenswert?

Ups, mein Fehler:
1
while(1)  // unendliche Schleife
2
  {
3
  
4
    if(UpdateDisplay)    // Erst nach zweiter Flanke ausführen
5
      {
6
        
7
        ATOMIC_BLOCK(ATOMIC_FORCEON)
8
        {
9
          zaehlschritte = (unsigned long)((65536.0*zaehler1_ovf) + Zeit); // sonst am Ende: +Zeitdifferenz;  
10
        }
11
        
12
      
13
        if (zaehlschritte==0)
14
          {
15
            freq = 0;
16
            zahl_ausgeben(0);
17
          }
18
        else
19
          {
20
            ATOMIC_BLOCK(ATOMIC_FORCEON)
21
            {
22
              // Die abschließende Berechnung der Schritte pro Sekunde
23
              freq = (uint16_t) (F_CPU/(zaehlschritte));
24
            }
25
      
26
            zahl_ausgeben(freq);
27
          
28
          }
29
          
30
          UpdateDisplay = FALSE;
31
      }
32
    
33
      
34
      
35
  }

von Fan_AT (Gast)


Lesenswert?

Bei dem aktuellen Stand bleibt imemr die Null leuchten - unabhängig, ob 
ein Signal anliegt oder nicht.

von Karl H. (kbuchegg)


Lesenswert?

Ok. Sieht wohl so aus, als ob die Ausgabe damit zuverlässig wäre. Auch 
wenn ich nicht verstehe, warum du schon wieder Code präsentierst, in dem 
die Overflow-Behandlung in zahl_ausgabe nicht enthalten ist.


Konzentrieren wir uns mal auf die Capture ISR und schreiben die mal 
übersichtlicher. Dein Klammern Einrückschema macht mich nämlich 
wahnsinnig.
1
ISR(TIMER1_CAPT_vect)
2
{
3
  if(UpdateDisplay)          // Das Display wurde mit den Ergebnissen der vorhergehenden
4
  {             // Messung noch nicht aktualisiert. Die nächste Messung
5
    return;         // verzögern, bis die Start- und EndTime-Variablen wieder
6
  }                    // gefahrlos beschrieben werden können
7
  
8
  static char ErsteFlanke = TRUE;
9
  static uint8_t LowByte = 0;
10
  static uint8_t HighByte = 0;
11
    
12
  LowByte = ICR1L;         // LowByte zuerst, HighByte später gepuffert
13
  HighByte = ICR1H;  
14
15
  if(ErsteFlanke)
16
  {
17
    Startzeit = ICR1;
18
    z = 0;
19
    ErsteFlanke = FALSE;       // Nach der nächsten Flanke beginnt die Ausgabe
20
  }
21
22
  if ((HighByte < 128) && (TIFR & (1<<TOV1)))
23
  {   
24
    // wartenden Timer Overflow Interrupt vorziehen
25
    ++z;         
26
    TIFR = (1<<TOV1);    // Timer Overflow int. löschen, da schon hier ausgeführt // macht der uC angeblich selbst
27
  }
28
  else
29
  {
30
    Zeit = 0;
31
    Zeit = ICR1 - Startzeit;
32
    UpdateDisplay = TRUE;      // Eine vollständige Messung. Sie kann ausgewertet werden
33
    ErsteFlanke = TRUE;        // Bei der nächsten Flanke beginnt der nächste Messzyklus
34
  
35
  
36
    zaehler1_ovf = z;    // Anzahl der Überläufe wird in zaehler1_ovf kopiert
37
    Startzeit = 0;
38
    z=0;  // Nach dem Sichern - annullieren
39
  }  
40
}


Hmm. Das kann nicht stimmen. Das 'else' in dem der UpdateDisplay=TRUE 
sitzt, gehört zum
1
  if ((HighByte < 128) && (TIFR & (1<<TOV1)))

das ist aber ganz sicher nicht die Bedingung, die einen Update 
verhindern sollte. Das else sollte zur Abfrage der ersten Flanke 
gehören. Zweck des ganzen ist es doch, abwechselnd den Capture Wert dem 
Beginn eines Pulses bis zum Beginn des nächsten Pulses zuzuordnen.

von Karl H. (kbuchegg)


Lesenswert?

PS: mit welcher Eingangsfrequenz testest du eigentlich? Welche Frequenz 
wirfst du dem Teil vor?
Nur damit man mal ein bischen nachrechnen kann, was da so an 
Zählerwerten rauskommen müsste.

von Karl H. (kbuchegg)


Lesenswert?

Nope. Das sieht nicht richtig aus.
Können wir uns darauf einigen, die Overflowbehandlung erst mal beiseite 
zu lassen und du gibst dem Tei lnur Frequenzen zu schlucken, die keinen 
zu berücksichtigenden Overflow auslösen werden?

IMOH müsste das ganze zumindest so aussehen
1
ISR(TIMER1_CAPT_vect)
2
{
3
  if(UpdateDisplay)          // Das Display wurde mit den Ergebnissen der vorhergehenden
4
  {             // Messung noch nicht aktualisiert. Die nächste Messung
5
    return;         // verzögern, bis die Start- und EndTime-Variablen wieder
6
  }                    // gefahrlos beschrieben werden können
7
  
8
  static char ErsteFlanke = TRUE;
9
  static uint8_t LowByte = 0;
10
  static uint8_t HighByte = 0;
11
    
12
  LowByte = ICR1L;         // LowByte zuerst, HighByte später gepuffert
13
  HighByte = ICR1H;  
14
15
  if(ErsteFlanke)
16
  {
17
    Startzeit = ICR1;
18
    z = 0;
19
    ErsteFlanke = FALSE;       // Nach der nächsten Flanke beginnt die Ausgabe
20
  }
21
  else
22
  {
23
    Zeit = ICR1 - Startzeit;
24
    UpdateDisplay = TRUE;      // Eine vollständige Messung. Sie kann ausgewertet werden
25
    ErsteFlanke = TRUE;        // Bei der nächsten Flanke beginnt der nächste Messzyklus
26
  
27
  
28
    zaehler1_ovf = z;    // Anzahl der Überläufe wird in zaehler1_ovf kopiert
29
    Startzeit = 0;
30
    z=0;  // Nach dem Sichern - annullieren
31
  }  
32
}

und in main dann:
1
        ....
2
        ATOMIC_BLOCK(ATOMIC_FORCEON)
3
        {
4
          zaehlschritte = Zeit;
5
        }
6
        ....

von Fan_AT (Gast)


Lesenswert?

Wie ich bereits schrieb - die Frequenzbegrenzung ist bereits fertig und 
wird noch vor den Frequenzzähler geschaltet. Darum habe ich mich bereits 
gekümmert. Die genaue Frequenz kann ich an der Skala des 
Funktionsgenerators ablesen, die ich zuvor mit 'nem Oszilloskop 
eingezeichnet habe.
Ist also nicht das Problem, das Testen.
Der Frequenzbereich ist mit 1-1400 Hz klein gehalten und einige 
Variablen könnten noch kleiner deklariert werden.
1
ISR(TIMER1_CAPT_vect)
2
{
3
4
   if(UpdateDisplay)          // Das Display wurde mit den Ergebnissen der vorhergehenden
5
    {             // Messung noch nicht aktualisiert. Die nächste Messung
6
    return;         // verzögern, bis die Start- und EndTime-Variablen wieder
7
  }                    // gefahrlos beschrieben werden können
8
  
9
  
10
  static char ErsteFlanke = TRUE;
11
  static uint8_t LowByte = 0;
12
  static uint8_t HighByte = 0;
13
    
14
  LowByte = ICR1L;         // LowByte zuerst, HighByte später gepuffert
15
  HighByte = ICR1H; 
16
  
17
  
18
  if ((HighByte < 128) && (TIFR & (1<<TOV1)))
19
  {   
20
      // wartenden Timer Overflow Interrupt vorziehen
21
      ++z;         
22
      TIFR = (1<<TOV1);    // Timer Overflow int. löschen, da schon hier ausgeführt // macht der uC angeblich selbst
23
  } 
24
  
25
26
   
27
  
28
  if(ErsteFlanke)
29
  {
30
    Startzeit = ICR1;
31
    z = 0;
32
    ErsteFlanke = FALSE;       // Nach der nächsten Flanke beginnt die Ausgabe
33
  
34
  }
35
   
36
  else
37
  {
38
  Zeit = 0;
39
    Zeit = ICR1 - Startzeit;
40
    UpdateDisplay = TRUE;      // Eine vollständige Messung. Sie kann ausgewertet werden
41
    ErsteFlanke = TRUE;        // Bei der nächsten Flanke beginnt der nächste Messzyklus
42
  
43
  
44
  zaehler1_ovf = z;    // Anzahl der Überläufe wird in zaehler1_ovf kopiert
45
  Startzeit = 0;
46
  z=0;  // Nach dem Sichern - annullieren
47
  
48
  }  
49
  
50
51
}

Du hast Recht, es ist die Bedingung, die einen verpassten Interrupt 
wieder miteinbezieht.

von Karl H. (kbuchegg)


Lesenswert?

Karl Heinz Buchegger schrieb:
> PS: mit welcher Eingangsfrequenz testest du eigentlich? Welche Frequenz
> wirfst du dem Teil vor?
> Nur damit man mal ein bischen nachrechnen kann, was da so an
> Zählerwerten rauskommen müsste.


Was hast du an Werten.
Du hast 16Mhz und Prescaler 1.

D.h. der Timer1 mit seinen 16 Bit wird in 1 Sekunde ...
1
  16000000 / 65536 = 244.1
... 244 mal überlaufen.

D.h. jede Eingangsfrequenz, die höher als 244Hz ist, dürfte dann 
eigentlich keinen zu berücksichtigenden Overflow während des Captures 
verursachen. Sicherheitshalber noch höher gehen. 1kHz, wenn du hast, 
sollte eine gute Basis sein, um damit zu testen.

von Fan_AT (Gast)


Lesenswert?

Klar, dann arbeiten wir erstmal ohne einen Overflow.

von Karl H. (kbuchegg)


Lesenswert?

> Der Frequenzbereich ist mit 1-1400 Hz

1 ist zuwenig!

Du brauchst erst mal eine Eingangsfrequenz, so dass dir der Timer keine 
Differenz liefern müsste, die größer als 65535 ist! Also keinen zu 
berücksichtigenden Overflow!

von Fan_AT (Gast)


Lesenswert?

Ich meinte den gesamten Bereich. Nehmen wir als Referenzfrequenz dann 
die 1,400Khz

von Karl H. (kbuchegg)


Lesenswert?

Stell mal 1kHz ein. Rechnet sich leichter als 1.4kHz :-)

Was ergibt sich daraus?
1kHz bedeutet, dass der Capture Interrupt alle 1ms aufgerufen werden 
müsste.

Wie weit kann der Timer in 1ms zählen?
In 1 Sekunde würde er bis 16000000 zählen. In 1ms (also 1/1000) Sekunde 
kommt er logischerweise nur 1/1000 so weit. Also bis 16000.

D.h.
Aus
1
ISR(TIMER1_CAPT_vect)
2
{
3
  if(UpdateDisplay)          // Das Display wurde mit den Ergebnissen der vorhergehenden
4
  {             // Messung noch nicht aktualisiert. Die nächste Messung
5
    return;         // verzögern, bis die Start- und EndTime-Variablen wieder
6
  }                    // gefahrlos beschrieben werden können
7
  
8
  static char ErsteFlanke = TRUE;
9
  static uint8_t LowByte = 0;
10
  static uint8_t HighByte = 0;
11
    
12
  LowByte = ICR1L;         // LowByte zuerst, HighByte später gepuffert
13
  HighByte = ICR1H;  
14
15
  if(ErsteFlanke)
16
  {
17
    Startzeit = ICR1;
18
    z = 0;
19
    ErsteFlanke = FALSE;       // Nach der nächsten Flanke beginnt die Ausgabe
20
  }
21
  else
22
  {
23
    Zeit = ICR1 - Startzeit;
24
    UpdateDisplay = TRUE;      // Eine vollständige Messung. Sie kann ausgewertet werden
25
    ErsteFlanke = TRUE;        // Bei der nächsten Flanke beginnt der nächste Messzyklus
26
  
27
  
28
    zaehler1_ovf = z;    // Anzahl der Überläufe wird in zaehler1_ovf kopiert
29
    Startzeit = 0;
30
    z=0;  // Nach dem Sichern - annullieren
31
  }  
32
}


müssten sich Werte für 'Zeit' von um die 16000 ergeben.
16000 kannst du auf einer 4 stelligen 7_seg nicht anzeigen. ABer 1/10 
davon.

Also
1
int main()
2
{
3
  ...
4
5
  while(1)  // unendliche Schleife
6
  {
7
    if(UpdateDisplay)    // Erst nach zweiter Flanke ausführen
8
    {
9
      ATOMIC_BLOCK(ATOMIC_FORCEON)
10
      {
11
        zaehlschritte = Zeit / 10;
12
      }
13
      zahl_ausgeben( zaehlschritte );
14
      UpdateDisplay = FALSE;
15
    }
16
  }
17
  return 0;
18
}

Hast du bei 1kHz die erwarteten 1600 (+- ein paar Zerquetschte) auf der 
Anzeige?

von Karl H. (kbuchegg)


Lesenswert?

:-)
Und du siehst hoffentlich jetzt auch, warum ich so drauf gedrängt habe, 
die Zahlenausgabe erst mal fehlerfrei zu machen.
Denn die brauchen wir jetzt! Die muss zum jetzigen Zeitpunkt über jeden 
Fehler erhaben sein.
Denn was ich jetzt nicht brauchen kann, das ist, das ich nicht weiß ob 
ein Fehler in der Messung oder in der Ausgabe passiert. Die Ausgabe und 
Anzeige MUSS zum jetzigen Zeitpunkt etwas sein, um das ich mich nicht 
mehr kümmern brauchen darf.
Daher die ganzen Vortests um sicher zu stellen, dass die Zahlenausgabe 
auf jeden Fall korrekt funktioniert.
Wenn jetzt auf der Anzeige 832 steht, dann muss auch zahl_ausgeben mit 
832 aufgerufen worden sein. Oder eben mit 0, wenn in der Anzeige 0 
steht. Da darf es keinerlei Unsicherheiten mehr geben.

von Fan_AT (Gast)


Lesenswert?

So... Noch einbisschen geändert.
Jap, du hast absolut Recht. Knapp über 1600.

Ich habe es jetzt mal mit dem Gesamtcode getestet: die Messung war ja 
auch davor schon korrekt, aber die Anzeige flimmerte.

Nun ist es folgendermaßen:
Bei Frequenzen kleiner 500Hz sieht man den schnellen Wechsel zwischen 
der richtigen Frequenz und Null
Bei Frequenzen kleiner 1000Hz flimmern die letzten 1-2 Ziffern.
Bei größeren Frequenzen flimmert nur manchmal die letzte Ziffer, was 
vllt. auch nur ein Rundungsfehler ist.

von Fan_AT (Gast)


Lesenswert?

Ich glaube, deine Arbeitsweise muss ich mir mal angewöhnen, gehe etwas 
chaotisch vor udn versuche über alles einen gleichzeitigen Überblick zu 
behalten ;)

von Karl H. (kbuchegg)


Lesenswert?

Fan_AT schrieb:
> So... Noch einbisschen geändert.
> Jap, du hast absolut Recht. Knapp über 1600.

gut.

> Ich habe es jetzt mal mit dem Gesamtcode getestet:

Ich gebs auf.
Vergiss den Gesamtcode.
Immer noch nicht begriffen? Wir bauen das ganze systematisch neu auf!

von Fan_AT (Gast)


Lesenswert?

Entschuldige bitte. Also - die zaehlschritte ergeben irgendwas mit 1600.

Wie ist das weitere Vorgehen?

von Karl H. (kbuchegg)


Lesenswert?

> gehe etwas chaotisch vor

Du wärst schon längst fertig, wenn du nicht so am bestehenden 
Originalcode hängen würdest, sondern das ganze Programm systematisch neu 
aufbauen würdest

von Karl H. (kbuchegg)


Lesenswert?

Fan_AT schrieb:
> Entschuldige bitte. Also - die zaehlschritte ergeben irgendwas mit 1600.
>
> Wie ist das weitere Vorgehen?

OK. wenn du die Frequenz variierst, ändert sich der Wert.
Wie muss er sich verändern? Je schneller die Frequenz, desto weniger 
weit kann der Zähler zählen.

Wieder etwas rechnen:
1.4kHz, macht eine Periodendauer von 1/1400 = 0.7ms.
0.0007 * 16000000 = 11428
Ein Zehtnel davon in der Anzeige. Die Anzeige muss 1142 anzeigen


In der anderen Richtung: 500Hz
500Hz sind 2ms. 0.002 * 16Mio = 32000. 1/10 davon in der Anzeige, macht 
3200
Mit 3200 ist noch Spielraum. (6535 ist unsere Grenze)

300Hz
300Hz sind 0.33 ms. 0.0033 * 16Mio = 52800. 1/10 davon in der Anzeige 
macht 5280

D.h. du hast
1
  eingestellte Frequenz      Anzeige
2
3
 1.4kHz                      1142
4
 1.0kHz                      1600
5
 500Hz                       3200
6
 300Hz                       5280


Wenn dein Programm tatsächlich diese Werte, bei den eingestellten 
Frequenzen auf die 7-Segment zaubert, dann geb ich mich erst mal 
zufrieden. Das akzeptiere ich als Funktionstest der grundlegenden 
Messung.


Ich hab jetzt absichtlich noch nicht auf die Frequenz in der Anzeige 
umgerechnet, auch wenns trivial wäre.
Aber unser Problem wird der Zählbereich ab 65535 sein. D.h. da ist der 
Zählerwert der interessante.

von Karl H. (kbuchegg)


Lesenswert?

Ach ja. Flimmern.

Ganz exakt wird deine Anzeige wahrscheinlich nicht ruhig stehen. Die 
letzte Stelle, schätze ich mal, wird ein wenig verwaschen sein. Aber der 
Rest muss ruhig stehen, ohne irgendwas. Da darf auch kein Wechsel 
zwischen 0 und einer anderen Zahl sein.

Wenn dein Frequenzgenerator es zulässt exakt 1Khz einzustellen, dann 
muss da auf der Anzeige 1600 rock solid dort stehen. Vielleicht mal 
1599, vielleicht mal 1601. Aber mehr darf das Ding da nicht rumzappeln.
Wenns zu sehr (zu schnell) zappelt, denn machen wir uns da mal eine 
Pause zwischen die einzelnen Messzyklen rein um zu sehen, zwischen 
welchen Werten es zappelt.

von Fan_AT (Gast)


Lesenswert?

Also, alles nachgemessen:

1400Hz - 1083-1082
1000Hz - 1865-1686
500Hz - flimmert schnell zwischen 3146 und 59xx
300Hz - flimmert stark, nichts zu erkennen

Das ist auch mein ursprüngliches Problem, dem wir wohl näher kommen :)

von Fan_AT (Gast)


Lesenswert?

bei 1000Hz sind es 1685-1686, Tippfehler

von Fan_AT (Gast)


Lesenswert?

1400 werden bis auf die letzte Stelle auch genau registriert. Aber da 
schwankt es um eine Ziffer. Das Problem ist wohl beim Überlauf, bei 
kleineren Frequenzen

von Karl H. (kbuchegg)


Lesenswert?

Fan_AT schrieb:
> Also, alles nachgemessen:
>
> 1400Hz - 1083-1082
> 1000Hz - 1685-1686
> 500Hz - flimmert schnell zwischen 3146 und 59xx
> 300Hz - flimmert stark, nichts zu erkennen

Hmm.
Sollte eigentlich nicht sein.
Wie sehr vertraust du deinem Frequenzgenerator?


Probier mal
1
  while(1)  // unendliche Schleife
2
  {
3
    if(UpdateDisplay)    // Erst nach zweiter Flanke ausführen
4
    {
5
      ATOMIC_BLOCK(ATOMIC_FORCEON)
6
      {
7
        zaehlschritte = Zeit / 10;
8
      }
9
      zahl_ausgeben( zaehlschritte );
10
      UpdateDisplay = FALSE;
11
    }
12
13
    _delay_ms( 500 );
14
  }


Ziel der Sache ist es, den Display Refresh zu verlangsamen um einen 
Überblick über die Zahlenwechsel zu bekommen. Kannst auch mal ein wenig 
mit den Delay Zeiten variieren, so dass du noch einigermassen gut 
ablesen kannst, was gemessen wird.

> Das Problem ist wohl beim Überlauf, bei kleineren Frequenzen

Bei den Frequenzen sollte sich ein Timerüberlauf noch nicht bemerkbar 
machen. Die unsigned Rechnung gleicht diesen einen Überlauf aus, so dass 
man sich nicht drum kümmern braucht.

von Fan_AT (Gast)


Lesenswert?

Das ist auch genau mein Problem. Auf eine andere Weise bin ich eben auch 
auf den Wechsel der long-Variable gekommen, aber kenne keine Lösung.

Der Generator ist genau. Zumindest war er das bei dem letzten 
Oszilloskop-Test. Ich gehe jetzt einfach mal vom Fehler meinerseits aus, 
da ich nicht annehmen kann, dass der Generator PLUS Oszilloskop kaputt 
sind ;)

Das Dealay-Programm ist nun ausprobiert worden. in den unteren beiden 
Bereichen (300,500) ist ein stärkeres Schwanken zu sehen.
300Hz - 5446,7989, 8117

Bei 1400 Hz ist mal konstant bei 1086, aber zwischendurch springt es auf 
3870. Und ich verstehe beim besten Willen nicht den Grund.

von Karl H. (kbuchegg)


Lesenswert?

Fan_AT schrieb:

> Der Generator ist genau.
ok, zur Kenntnis genommen.

> Bei 1400 Hz ist mal konstant bei 1086, aber zwischendurch springt es auf
> 3870. Und ich verstehe beim besten Willen nicht den Grund.

Versteh ich auch noch nicht.
Dem müssen wir aber auf den Grund gehen. Solange das nicht geklärt ist, 
macht es keinen Sinn, darauf aufbauend das Programm weiter zu treiben. 
Die Zwischenziele müssen korrekte Werte ergeben. Sonst ist alles darauf 
aufbauende automatisch falsch.


Zeig nochmal das Programm in seiner vollen Pracht.

von Fan_AT (Gast)


Lesenswert?

Das komplette Paket oder das Delay-Programm aus dem letzten Schritt?

Hier ist das Delay-Programm:
1
#define F_CPU 16000000UL // uC läuft mit 16MHz
2
3
#ifndef TRUE
4
#define TRUE 1
5
#define FALSE 0
6
#endif
7
8
9
#include <avr/io.h>
10
#include <avr/interrupt.h>
11
#include <util/atomic.h>
12
#include <util/delay.h>
13
14
volatile unsigned short z=0;    // Zählvariable des Timer1
15
volatile char UpdateDisplay = TRUE;      // char-1Byte // Einen Wert TRUE definiert 
16
volatile unsigned long Zeit = 0;      // unsigned long-4Bytes
17
18
//volatile uint8_t tausender, hunderter, zehner, einer;  // Speicher für die eizelnen Digits
19
//volatile uint8_t position = 0;
20
21
volatile unsigned short zaehler1_ovf = 0;    // Anzahl der Überläufe des Timer1, short-2Bytes
22
//unsigned int zaehler1_ovf = 0;    // Anzahl der Überläufe des Timer1
23
unsigned long zaehlschritte = 0;
24
25
uint16_t freq = 0;
26
27
28
volatile unsigned long Startzeit = 0;
29
30
volatile uint8_t digits[4];  // Speicher für die eizelnen Digits
31
volatile uint8_t digit_position = 0;
32
33
uint8_t digitControl[4] = { (1 << PC0), (1 << PC1), (1 << PC2), (1 << PC3) };  
34
35
36
//float korrekturfaktor = 1; // für Abgleich anpassen (z.B. 0.9997)
37
38
39
const int8_t numbers[11] =    // Array zur Darstellung der Zeichen auf der 7-Seg-Anz.  // 08.08.13 - int8_t numbers[11], nicht uint8_t
40
{
41
42
  0b01101111,    // 0
43
  0b00000110,    // 1
44
  0b10101011,    // 2
45
  0b10001111,    // 3
46
  0b11000110,    // 4
47
  0b11001101,    // 5
48
  0b11101101,    // 6
49
  0b00000111,    // 7
50
  0b11101111,    // 8
51
  0b11001111,    // 9
52
  0b00000000    // leere Anzeige
53
  
54
};
55
56
57
ISR(TIMER1_OVF_vect) 
58
{
59
  ++z;
60
  //TIFR = (1<<TOV1); //Beim Überlauf von Timer1 und dem anschließenden Interrupt erfolgt eine Zurücksetzung auf Null
61
  //Braucht nicht gelöscht zu werden. Jeder Interrupt löscht automatisch sein eigenes Flag beim Aufruf der ISR.
62
  
63
}
64
65
66
ISR(TIMER1_CAPT_vect)
67
{
68
69
   if(UpdateDisplay)          // Das Display wurde mit den Ergebnissen der vorhergehenden
70
    {             // Messung noch nicht aktualisiert. Die nächste Messung
71
    return;         // verzögern, bis die Start- und EndTime-Variablen wieder
72
  }                    // gefahrlos beschrieben werden können
73
  
74
  
75
  static char ErsteFlanke = TRUE;
76
  static uint8_t LowByte = 0;
77
  static uint8_t HighByte = 0;
78
    
79
  LowByte = ICR1L;         // LowByte zuerst, HighByte später gepuffert
80
  HighByte = ICR1H; 
81
  
82
  
83
  if ((HighByte < 128) && (TIFR & (1<<TOV1)))
84
  {   
85
      // wartenden Timer Overflow Interrupt vorziehen
86
      ++z;         
87
      TIFR = (1<<TOV1);    // Timer Overflow int. löschen, da schon hier ausgeführt // macht der uC angeblich selbst
88
  } 
89
  
90
91
   
92
  
93
  if(ErsteFlanke)
94
  {
95
    Startzeit = ICR1;
96
    z = 0;
97
    ErsteFlanke = FALSE;       // Nach der nächsten Flanke beginnt die Ausgabe
98
  
99
  }
100
   
101
  else
102
  {
103
  Zeit = 0;
104
    Zeit = ICR1 - Startzeit;
105
    UpdateDisplay = TRUE;      // Eine vollständige Messung. Sie kann ausgewertet werden
106
    ErsteFlanke = TRUE;        // Bei der nächsten Flanke beginnt der nächste Messzyklus
107
  
108
  
109
  zaehler1_ovf = z;    // Anzahl der Überläufe wird in zaehler1_ovf kopiert
110
  Startzeit = 0;
111
  z=0;  // Nach dem Sichern - annullieren
112
  
113
  }  
114
  
115
116
}
117
118
119
ISR(TIMER2_OVF_vect)  // timer2 overflow interrupt
120
{
121
  PORTC = 0b00000000;    // alle Digits aus
122
  TCNT2 += 0; 
123
124
  digit_position++;
125
  
126
  if( digit_position == 4 )
127
    {digit_position = 0;}
128
129
  PORTD = digits[digit_position];
130
  PORTC = digitControl[digit_position];
131
}
132
133
134
void zahl_ausgeben(uint16_t zahl)
135
{
136
  uint8_t i;
137
138
  digits[3] = numbers[zahl % 10];  // Die letzte Ziffer wird gesichert
139
  zahl /= 10;
140
141
  for( i = 2; i != 255; i-- ) 
142
  {
143
    if( zahl > 0 )
144
    {
145
      digits[i] = numbers[zahl % 10];
146
      zahl /= 10;
147
    }
148
    else
149
      digits[i] = numbers[10];
150
  }
151
  
152
  
153
}
154
155
156
157
int main(void)
158
{
159
  // Definition von Ein- und Ausgängen des uC Boards
160
  DDRB = 0xFE;  // Mit Ausnahme des ICP1-Pins alles als Ausgang
161
  DDRC = 0xFF;  // PORTC als Ausgang - über PORTC werden die jeweiligen Segmente einer 7-Seg. Anzeige gesteuert
162
  DDRD = 0xFF;  // PORTD als Ausgang
163
  PORTC = 0b00000000;
164
  
165
  
166
  TIMSK |= (1<<TICIE1) | (1<<TOIE1) | (1 << TOIE2); // Interrupts akivieren, Capture und Overflow, Overlow-Interrupt aktivieren
167
  TCCR1B |= (1<<ICES1)  | (1<<CS10); // Input Capture Edge, kein Prescaler
168
  
169
    TCCR2 |= (1 << CS22)|(1 << CS21);    // Vorteiler auf 256
170
171
172
  sei();          // Interruptbehandlung ein
173
  
174
175
  /*while(1)  // unendliche Schleife
176
  {
177
  
178
    if(UpdateDisplay)    // Erst nach zweiter Flanke ausführen
179
      {
180
        
181
        ATOMIC_BLOCK(ATOMIC_FORCEON)
182
        {
183
          zaehlschritte = (unsigned long)((65536.0*zaehler1_ovf) + Zeit); // sonst am Ende: +Zeitdifferenz;  
184
        }
185
        
186
      
187
        if (zaehlschritte==0)
188
          {
189
            freq = 0;
190
            zahl_ausgeben(freq);
191
          }
192
        else
193
          {
194
            ATOMIC_BLOCK(ATOMIC_FORCEON)
195
            {
196
              // Die abschließende Berechnung der Schritte pro Sekunde
197
              freq = (uint16_t) (F_CPU/(zaehlschritte));
198
              
199
            }
200
      
201
            zahl_ausgeben(freq);
202
          
203
          }
204
          
205
          UpdateDisplay = FALSE;
206
      }*/
207
      
208
     while(1)  // unendliche Schleife
209
     
210
  {
211
    if(UpdateDisplay)    // Erst nach zweiter Flanke ausführen
212
    {  
213
    
214
      ATOMIC_BLOCK(ATOMIC_FORCEON)
215
      {
216
      zaehlschritte = Zeit / 10;
217
      }
218
    
219
      zahl_ausgeben( zaehlschritte );
220
      UpdateDisplay = FALSE;
221
    }
222
223
    _delay_ms( 500 );
224
  }
225
  return 0;
226
}

von Karl H. (kbuchegg)


Lesenswert?

Fan_AT schrieb:
> Das komplette Paket oder das Delay-Programm aus dem letzten Schritt?

Immer die letzte Version. Die dir wir gerade entwicklen.

von Fan_AT (Gast)


Lesenswert?

Gut, das ist sie. Ausgeklammert sind die Programmleichen.

von Georg G. (df2au)


Lesenswert?

Einwurf eines Mitlesers: wie groß ist numbers[]? Wirklich nur 11 
Elemente? Eigentlich sollte der Compiler das aber merken...

von Karl H. (kbuchegg)


Lesenswert?

Hmm. Lass mich mal überlegen.
Könnte es sein, das wir ein Problem mit pending Interrupts haben?
Ich denke nicht, denn genau dazu gibt es den Capture Interrupt. Auf der 
anderen Seite, wenn wir einen Capture verpassen ...
Das muss ich mir durch den Kopf gehen lassen. Moment

von Fan_AT (Gast)


Lesenswert?

@Georg.G.
Danke für's Mitlesen und für die Anmerkung. Bei der Deklaration wird in 
der eckigen Klammer die Gesamtanzahl der beinhaltenden Elemente 
angegeben. Und aktuell sind es doch 11 oder irre ich mich?

@Karl Heinz Buchegger
Die einzige Idee von mir wäre vllt. noch die Überprüfung, ob der ICF1 
Flag gesetzt ist...

von Fan_AT (Gast)


Lesenswert?

@Georg.G.
Danke für's Mitlesen und für die Anmerkung. Bei der Deklaration wird in 
der eckigen Klammer die Gesamtanzahl der beinhaltenden Elemente 
angegeben. Und aktuell sind es doch 11 oder irre ich mich?

@Karl Heinz Buchegger
Die einzige Idee von mir wäre vllt. noch die Überprüfung, ob der ICF1 
Flag gesetzt ist... Denn die Überprüfung auf einen verpassten Interrupt 
wird ist doch bereits implementiert und High-/LowBytes werden in 
richtiger Reihenfolge ausgelesen.

von Karl H. (kbuchegg)


Lesenswert?

Fan_AT schrieb:

> @Karl Heinz Buchegger
> Die einzige Idee von mir wäre vllt. noch die Überprüfung, ob der ICF1
> Flag gesetzt ist... Denn die Überprüfung auf einen verpassten Interrupt
> wird ist doch bereits implementiert und High-/LowBytes werden in
> richtiger Reihenfolge ausgelesen.

Ja ich weiß.
Das sollte eigentlich alles stimmen.
Nur tut es das nicht. Verblüffender Weise.

von Georg G. (df2au)


Lesenswert?

Fan_AT schrieb:
> Und aktuell sind es doch 11 oder irre ich mich?

Ich zähle 0...9, Leerstelle und das E. Meine Enkeltochter meint, das 
wären 12 Elemente.

von Fan_AT (Gast)


Lesenswert?

Die Enkeltochter ist sehr gut :)
In der aktuellen Version ist das 'E' nicht vorhanden, da keine 
Frequenzbegrenzung benötgt wird und somit auch kein zu großer Wert 
angezeigt werden kann.

Aber habe grad gesehen, dass der Timer2 höhere Priorität besitzt, als 
CaptureEvent. Wage einen kurzen Versuch.

von Fan_AT (Gast)


Lesenswert?

So... Der Timer0 hat eine niedrigere Priorität als das Capture Event.

Folgendermaßen sieht das jetzt aus:
Bei TCNT0 flackert die ganze Anzeige. Doch umgerechnet wird bis 700Hz 
wird die völlig korrekte Frequenz dargestellt.
Ab da wird wieder die zwischendurch erscheinende Null erkennbar.

von Fan_AT (Gast)


Lesenswert?

Ich meinte natürlich TCNT0=0.

Frequenzen größer 700Hz werden korrekt gemessen, ein Flimmern des 
Multiplex ist wahrnehmbar.

von Karl H. (kbuchegg)


Lesenswert?

Hast du neue Ideen?

Ich denke nicht, dass es irgendwas mit den 'Prioritäten' zu tun hat. 
Dazu sind deine Abweichungen einfach zu groß. Die ISR sind allesamt 
klein, die sind in kürzester Zeit, in der Größenordnung von 1 bis 2 µs 
(maximal) abgearbeitet. Im Capture darf man das überhaupt nicht sehen, 
dazu hat man ja den Hardware-Capture. Und auf den Anzeigen ist dieser 
Jitter zu kurz, dals das er sich bemerkbar machen kann. So gut sind 
deine Augen nicht.


Ich denke in eine ganz andere Richtung: Kann es sein, dass dein µC 
zwischendurch ein paar Reset macht?

von Fan_AT (Gast)


Angehängte Dateien:

Lesenswert?

Guten Tag,

Ideen... Habe noch einpaar Versuche gestartet, wie atomare Zugriffe auf 
Variablen größer 8Bit, doch ohne Erfolg.
Wenn nichts Besseres einfällt, werde ich wohl den uC auswechseln, 
Treiber.
Ich packe mal den Schaltplan hier rein - den nehrlich gesagt weiß ich 
nicht, wieso ein Reset durchgeführt werden sollte.
Aber Gott sei Dank gibt es ja auch erfahrene Leute ;)

von Fan_AT (Gast)


Lesenswert?

die Idee ist aber nicht schlecht, habe erstmal Brown-out detection level 
auf VCC=2,7V gemacht - keine Änderung.
Dann werde ich mal die Spannung nicht aus dem Generator beziehen, 
sondern extern.

von Georg G. (df2au)


Lesenswert?

Hat nichts mit deinem Fehler zu tun: Die Referenzspannung des AD sollte
nicht hart an VCC gebunden sein. Das ist u.U. ein Ausgang. Offen lassen.

von Fan_AT (Gast)


Lesenswert?

So, habe es nun mit stabilisierter Spannung durchgezogen - minimale 
positive Änderung zu sehen, aber immer noch der gleiche Fehler mit 
schnellem Wechseln zu Null und zurück.
Ja, danke für die Anmerkung - wird das nächste Mal beachtet :)

Was könnte noch einen ständigen Reset auslösen?

von holger (Gast)


Lesenswert?

>Was könnte noch einen ständigen Reset auslösen?

Watchdog?

von Fan_AT (Gast)


Lesenswert?

Ist zwar eien Idee, aber dafür muss ja das Programm in einer 
unpassierbaren Schleife stecken o.ä., nicht wahr?

Habe ihn zwar nicht mal angemacht, aber das Deaktivieren ginge doch 
unter Verwendung von <avr/wdt.h> mit dem Befehl
1
wdt_disable();

möglich?

von Fan_AT (Gast)


Lesenswert?

Zumindest macht sich kein Fortschritt bemerkbar mit diesem Befehl...

von Peter D. (peda)


Lesenswert?

Sende mal das komplette compilierbare Programm als Anhang.

von Fan_AT (Gast)


Angehängte Dateien:

Lesenswert?

Hier - die .c Datei.

von Peter D. (peda)


Lesenswert?

ATOMIC_BLOCK(ATOMIC_FORCEON)
haben im Interrupt nichts zu suchen, die sind nur für Main-Tasks.

von Fan_AT (Gast)


Lesenswert?

Hast Recht, war mein letzter Versuch irgendwas zu ändern :)

Siehst du irgendwelche groben Fehler?

von Peter D. (peda)


Lesenswert?

1
       
2
        ATOMIC_BLOCK(ATOMIC_FORCEON)
3
        {
4
          zaehlschritte = (unsigned long)((65536.0*zaehler1_ovf) + Zeit); // sonst am Ende: +Zeitdifferenz;  
5
        }

Nur die Interruptvariable atomar in einer lokalen speichern,
Nicht für einen riesen Salm an Berechnungen sperren.
1
            ATOMIC_BLOCK(ATOMIC_FORCEON)
2
            {
3
              // Die abschließende Berechnung der Schritte pro Sekunde
4
              freq = (uint16_t) (F_CPU/(zaehlschritte));
5
              
6
            }

Wozu hier?

von Fan_AT (Gast)


Lesenswert?

Beim ersten wird die Unterbrechung eigentlich auch nur bedingt benötigt 
- zaehler1_ovf wird bereits in ISR gesichert.

Wie gesagt, ich habe vosichtshalber bei jeder Schreiboperation, die mehr 
als 1 Taktzyklus benötigt die Interrupts pausiert.

Habe soeben alle  ATOMIC_BLOCK(ATOMIC_FORCEON) entfernt - kein Erfolg. 
Es muss doch was geben, was ich übersehe?

von Fan_AT (Gast)


Lesenswert?

Gut, vielleicht ist es etwas Grundlegendes, nicht die Software selbst.
Den uC habe ich bereits ausgetauscht. Eine andere Spannungsquelle 
benutzt.

Bei den Fuses setze ich Ext.Crystal Resonator High Freq.; Start-Time 
16CK+64ms. Hinzu kommt noch das CKOPT fuse.

Das Ganze schreiben tue ich mithilfe des mySmartUSB MK2.

Watchdog-Timer ist von vornherein nicht aktiviert.

von Peter D. (peda)


Lesenswert?

1
   if(UpdateDisplay)          // Das Display wurde mit den Ergebnissen der vorhergehenden
2
    {             // Messung noch nicht aktualisiert. Die nächste Messung
3
    return;         // verzögern, bis die Start- und EndTime-Variablen wieder
4
  }                    // gefahrlos beschrieben werden können

Was dieses "UpdateDisplay" macht, ist mir noch nicht klar. Es könnte 
Probleme machen.
Ich würde einfach jeden Timestamp abspeichern, mehr nicht.
Und das Main nimmt sich einfach 2 aufeinanderfolgende Timestamps, wenn 
es Lust dazu hat.
1
ISR(TIMER1_CAPT_vect)
2
{
3
  last_time = current_time;
4
  current_time = ICR1;
5
}
6
// main:
7
  uint16_t delta_time;
8
  ATOMIC_BLOCK(ATOMIC_FORCEON)
9
    delta_time = current_time - last_time;
10
  machwas( delta_time );

Beschreib dochmal in Prosa, wie das Programm funktionieren sol.
Wie ist überhaupt der gewünschte Meßbereich?

von Fan_AT (Gast)


Lesenswert?

Karl Heinz Buchegger, warum hast du mich verlassen? :D

@Peter Dannegger:
[QUOTE]
Dieses Flag hat 2 Aufgaben:
Zum einen ist es die Benachrichtigung aus der ISR in die main() "Ich
habe eine Messung fertig". Zum anderen ist es die Verriegelung der ISR
mit isch selbst, so lange keine weitere Messung zu machen, bis die
main() das Ergebnis verarbeitet hat.
D.h. wenn main() die Messergebnisse der ISR ausgewertet hat, dann muss
sie UpdateDisplay wieder auf FALSE setzen. Egal was dann bei der
Auswertung rauskommt. Das FALSE-setzen von UpdateDisplay ist quasi die
Rückmeldung von main() in die ISR "OK, ich hab mir deinen Messwert
geholt, du kannst wieder loslegen und die nächste Messung machen". Das
aber, muss IMMER passieren. Egal was dann bei der Auswertung rauskommt
[/QUOTE]

Der gewünschte Messbreich ist 1-1400Hz, was eigentlich kein Problem 
darstellen sollte, aber trotzdem nicht per Capture-Event klappt.

Die aktuelle Version des Programms kann man eben in zwei Prozeduren 
gliedern: Capture, Multiplex.

Mittlerweile werden alle Messvariablen gleich in den ISR gesichert - das 
erspart das Ausschalten von Interrupts zwischendurch, mit evtl. Ausnahme 
von unsigned long Berechnungen. (zwischenschritte in main() )

Bei steigender Flanke des eingehenden TTL-Signals erfolgt das 
Capture-Event, bei dem der Zählerstand gesichert wird - unter 
Voraussetzung, dass die Anzeige (zahl_ausgeben) abgearbeitet wurde 
(UpdateDisplay = FALSE)

Erst bei zweiter Flanke wird die Differenz der Start-Und End...

OMFG....LEUTE....
1
 Zeit =  ICR1 - Startzeit;

Und was, wenn nach einem Timerüberlauf ICR1 kleiner ist als Startzeit? 
Dann ergbit dann... genau richtig: Null!

Folglich muss es heißen:

[c]
Zeit = zaehler1_ovf*65536.0 + ICR1 - Startzeit;
/c]

Ich laufe udn teste es ausgiebig, wünscht mir Glück oO

von Fan_AT (Gast)


Lesenswert?

Es hat funktioniert! Der Fehler mit dem ständigen Flimmern zu Null und 
zurück ist weg! Ich müsste mir jetzt paar Gedanken zur 
Optimierungsroutine machen - Einer-, manchmal Zehnerstelle flackert ab 
und zu ganz unschön, so dass man die Zahl nicht ablesen kann.

von Ulrich (Gast)


Lesenswert?

Das flackern der Anzeige kann verschiedene Ursachen haben. Ein Problem 
ist das wohl ein Update der Anzeige sehr schnell erfolgt wenn die 
Frequenz hoch ist. Damit kann die letzte Stelle immer leicht flackern.

Da ist auch noch ein kleiner Fehler mit drin: Die Zahl der Überläufe 
sollte aus der Variable z kommen, nicht der alten  Kopie von der letzten 
Periode. Dazu sollte man Fließkomma Zahlen vermeiden, also:
[c]
Zeit = z*65536UL + ICR1 - Startzeit;
/c]

Die Multiplikation mit 65536 ließe sich ggf. über den Zugriff als union 
auch noch einsparen - es könnte sein das der Compiler das auch schon hin 
bekommt, ich fürchte aber nicht.

von Fan_AT (Gast)


Lesenswert?

Hallo Ulrich,

danke für die Rückmeldung, korrigiert - auch bei Kleinigkeiten spart man 
sich schon paar Taktzyklen.
Interessant wird dann die Behebung des Problems, denn das ist wirklich 
ein Ableseproblem - an manchen Stellen kann man die Zahl bei Zehner-und 
Einerstellen nicht richtig einstellen. Du hast 'ne auf 8 flimmernde 7, 
doch eher du die 8 flimmerfrei machen kannst, kriegst du schon die 9! oO

Also meinst du, dass man bei den Frequenzen, wo das Problem anfängt, in 
meinem Fall ab 70-80Hz, kurzzeitig den Multiplex... einfrieren lässt?

von Fan_AT (Gast)


Lesenswert?

Mal den Timer1 Vorteiler und Noise Canceler ausprobiert - ohne Erfolg, 
beim Vorteiler sogar schlimmer.
Beim Vorteiler dachte ich, es wäre so, als ob ich die eingehende 
Frequenz teilen würde, was mir somit einen langsameren Multiplex 
beschert hätte...
Gibt es Alternativen, etwa in Richtung D-FlipFlops, der die Frequenz 
halbiert oder Methoden, wie man das Flackern bekämpft?

von Ulrich (Gast)


Lesenswert?

Für eine Stabilere Anzeige könnte man eine neue Messung erst nach einer 
gewissen zeit (z.B. 0,5 s) starten, oder besser erst dann übernehmen.

Wenn man mag könnte man auch die Zeit für mehr als eine Periode Messen, 
so dass man z.B. auf eine "Torzeit" von z.B. 0,5-1 s kommt. Der Start 
geht so wie jetzt, aber das Ende wird erst nach etwa 0,5 s (wohl ein 
gewisser Wert für z) genommen und davor werden nur die Perioden gezählt. 
Bei z.B. 1 kHz wird dann nicht die Zeit für eine Periode gemessen, 
sondern die Zeit für z.B. 505 Perioden und dann durch die Zahl geteilt. 
Das gibt ein höhere Auflösung und auch gleich eine langsamere Messung.

Der Vorteiler beim Timer reduziert die Auflösung der Messung. Den 
braucht man bei der Messung über ICP mit zählen der Überläufe nicht. 
Sinnvoll wäre ggf. ein Vorteiler für das Signal, wenn die Frequenz zu 
hoch (so ab 50-500 kHz) wird. Für niedriger Frequenzen geht es aber 
besser ohne Vorteiler.

von Fan_AT (Gast)


Lesenswert?

Naja, das Ding ist, ich hatte die Schaltung bereits mit Torzeiten auf 
T0, da war mir jedoch die Wartezeit/Offset viel zu groß. Mit ICP habe 
ich mir eben viel schnellere Messungen erhofft.
Bei solchen kleinen Frequenzen habe ich erwartet, dass es funktionieren 
würde.

von Ulrich (Gast)


Lesenswert?

Die Messung geht ja auch schneller, nur halt jetzt zu schnell um das 
noch auf der Anzeige noch gut zu erkennen. Vor allem bei den höheren 
Frequenzen (so ab etwa 100 Hz) muss man für mehr Auflösung dann halt die 
Zeit für mehr Perioden messen. Der Unterschied zum Zählen mit fester 
Torzeit ist, dass man über ICP eine etwa von der Frequenz unabhängige 
relative Auflösung bekommt.

von Fan_AT (Gast)


Lesenswert?

Gut, dann bleibt mir nichts anderes übrig. Ich versuche es erstmal mit 
insgesamt zwei Messungen ab 80Hz.
Vielen Dank für die Hilfe - falls neue Ideen kommen, würde ich sie 
selbstverständlich wissen wollen :)

von m.n. (Gast)


Lesenswert?

Fan_AT schrieb:
> falls neue Ideen kommen, würde ich sie
> selbstverständlich wissen wollen :)

Die Ideen sind schon alt, nur die Verpackung ist eine neue :-)
Multiplexen fehlt zwar, läßt sich aber im Timer1-OVF-Interrupt ergänzen.
http://www.mino-elektronik.de/fmeter/fm_software.htm
und für 4-stellige Anzeige völlig ausreichend:
http://www.mino-elektronik.de/7-Segment-Variationen/LCD.htm#lcd2

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.