Forum: Mikrocontroller und Digitale Elektronik ADC funktioniert nicht


von Ja (Gast)


Lesenswert?

Guten Tag,
Ich habe einen Code geschaffen, der bei Sekundenänderung das Ergebnis 
des ADC mit einem vorherigen ADC (Referenz) vergleicht und bei gleichem 
Ergebnis einen Zähler hochzählen soll, der dann bei erreichtem Wert 
einen Ausgang schalten soll.

Der Code an sich funktioniert, wenn es um den Zähler geht: nach 59 
Zählungen schlatet nämlich der Ausgang. Wenn ich aber die Spannung am 
ADC verändere, so passiert nichts, der Ausgang bleibt geschaltet, obwohl 
der Wert nicht mehr im Rahmen liegt.
Ja, ich habe alles mögliche am Code probiert, allerdings scheint es 
nicht zu funktionieren, deshalb frage ich nun hier:
1
#include <avr/io.h>
2
#define F_CPU 1000000UL // 1 MHZ
3
#include <util/delay.h>
4
#include <avr/interrupt.h>
5
6
volatile unsigned int millisekunden;
7
volatile unsigned int sekunde;
8
volatile unsigned int minute;
9
volatile unsigned int stunde;
10
volatile int analogResult = 0; 
11
volatile int analogReferenz = 512;
12
volatile int Zaehler = 0;
13
volatile int sekundeCOM = 0;
14
volatile int sekundeCOM2 = 0;
15
16
ISR(ADC_vect)
17
{
18
  analogResult = (ADCH<<8)|ADCL;
19
}
20
21
22
int main(void)
23
{
24
  DDRB &= ~(1<<DDB4);
25
  // Timer 0 konfigurieren
26
  TCCR0A = (1<<WGM01); // CTC Modus
27
  TCCR0B |= (1<<CS01); // Prescaler 8
28
  // ((1000000/8)/1000) = 125
29
  OCR0A = 125-1;
30
  
31
  // Compare Interrupt erlauben
32
  TIMSK |= (1<<OCIE0A);
33
  
34
  // Global Interrupts aktivieren
35
  sei();
36
  DDRB |= (1<<DDB0) | (1<<DDB1) | (1<<DDB2) | (1<<DDB3);
37
  ADMUX &= ~( (1<<REFS1)|(1<<REFS0)|(1<<ADLAR) );
38
  ADMUX |= (1<<MUX1);
39
  ADCSRB &=~( (1<<ADTS2)|(1<<ADTS1)|(1<<ADTS0) );
40
  DIDR0 |= (1<<ADC2D);
41
  ADCSRA |= (1<<ADEN)|(1<<ADIE);
42
  ADCSRA |= (1<<ADSC);
43
    while(1)
44
    {  
45
    
46
    
47
    PINB |= (1<<PINB3);
48
    sekundeCOM2 = sekunde;
49
    
50
      
51
    if ((sekundeCOM2 != sekundeCOM) & (analogResult < (analogReferenz +10)) & (analogResult > (analogReferenz -10)) )// & (analogResult > analogReferenz - 100) & (analogResult < analogReferenz + 100))
52
      { Zaehler++;
53
        sekundeCOM = sekundeCOM2;
54
      } 
55
      
56
      
57
      if ((analogResult > (analogReferenz + 10)) | (analogResult < (analogReferenz - 10)) ) //| (analogResult > (analogReferenz - 100)))
58
    { 
59
        Zaehler = 0;
60
        analogReferenz = analogResult;
61
        sekundeCOM = sekundeCOM2;
62
      }
63
      
64
        
65
          
66
          
67
    if ( Zaehler > 59)
68
    { PINB |= (1<<PINB0)|(1<<PINB1)|(1<<PINB2);
69
    } 
70
    
71
        
72
    
73
    
74
    }
75
}
76
77
ISR (TIMER0_COMPA_vect)
78
{
79
  millisekunden++;
80
  if(millisekunden == 1000)
81
  {
82
    sekunde++;
83
    millisekunden = 0;
84
    if(sekunde == 60)
85
    {
86
      minute++;
87
      sekunde = 0;
88
    }
89
    if(minute == 60)
90
    {
91
      stunde++;
92
      minute = 0;
93
    }
94
    if(stunde == 24)
95
    {
96
      stunde = 0;
97
    }
98
  }
99
}

Habt ihr Lösungsvorschläge?

von adenin (Gast)


Lesenswert?

Ja schrieb:
> Habt ihr Lösungsvorschläge?

C-Grundlagen büffeln, speziell Unterschied zwischen & und &&

von Karl H. (kbuchegg)


Lesenswert?

Ja schrieb:

Das hier
>     if ( Zaehler > 59)
>     { PINB |= (1<<PINB0)|(1<<PINB1)|(1<<PINB2);
>     }
ist wohl das einschalten.

Aber wo ist das Ausschalten?

So ein Ausgang schaltet sich ja nicht von selbst aus. Er behält immer 
den letzten Zustand bei. Wenn du ihn also abgeschaltet haben willst, 
dann musst du das auch programmieren.

Ganz angesehen davon, denke ich nicht, dass du hier das PIN Register 
benutzen willst. Je nach verwendetem Prozessor ist das maximal ein 
Toggeln des Ausgangs.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Ganz abgesehen davon.
Wo innerhalb der Schleife liest du den den ADC erneut aus?

von Karl H. (kbuchegg)


Lesenswert?

Karl Heinz schrieb:
> Ganz abgesehen davon.
> Wo innerhalb der Schleife liest du den den ADC erneut aus?

Habs gerade gesehen. Der ADC ist auf Interrupt geschaltet.

Noch was komplizierteres ist dir nicht eingefallen?
Oh Mann. Du hast schon eine gewisse Tendenz, dir selbst mit möglichst 
komplizierten Programmstrukturen selbst ein Bein zu stellen anstatt dir 
erst mal mit einem ganz simplen Programm eine funktionierende Basis zu 
schaffen.

: Bearbeitet durch User
von Bernhard S. (b_spitzer)


Lesenswert?

Wieso die unnötige Flankenerkennung und das Rumgeschubse mit der 
Sekunde??
1
  if(millisekunden == 1000)
2
  {
3
    sekunde++;
4
    millisekunden = 0;
5
    FLAG = 1;            // Signal an Hauptprogramm
6
    if(sekunde == 60)
7
    {
8
      minute++;
9
      sekunde = 0;
10
    }
und im Hauptprogramm
1
if (FLAG)
2
{
3
   FLAG = 0;        // Signal löschen
4
   TuWasBeiJederSekunde( );
5
}


Davon Abgesehen, die gegebenen Hinweise zu && und || beachten.

: Bearbeitet durch User
von Ralph S. (jjflash)


Lesenswert?

Bernhard Spitzer schrieb:
> Davon Abgesehen, die gegebenen Hinweise zu && und || beachten.

besonders hier:

Ja schrieb:
> if ((sekundeCOM2 != sekundeCOM) & (analogResult < (analogReferenz +10))
> & (analogResult > (analogReferenz -10)) )

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

Ralph S. schrieb:
> Bernhard Spitzer schrieb:
>> Davon Abgesehen, die gegebenen Hinweise zu && und || beachten.
>
> besonders hier:
>
> Ja schrieb:
>> if ((sekundeCOM2 != sekundeCOM) & (analogResult < (analogReferenz +10))
>> & (analogResult > (analogReferenz -10)) )

Wobei das an der Stelle vermutlich gar keinen Unterschied macht 
(korrigiert gehört es natürlich trotzdem)

von Ja (Gast)


Lesenswert?

Sieht das in euren geübten Augen besser aus?
MfG
1
#include <avr/io.h>
2
#define F_CPU 1000000UL // 1 MHZ
3
#include <util/delay.h>
4
#include <avr/interrupt.h>
5
6
volatile unsigned int millisekunden;
7
volatile unsigned int sekunde;
8
volatile unsigned int minute;
9
volatile unsigned int stunde;
10
volatile unsigned int FLAG;
11
volatile unsigned int analogresult;
12
volatile unsigned int analogreferenz = 128;
13
volatile unsigned int Zaehler = 0;
14
15
16
17
18
int main(void)
19
{
20
  DDRB &= ~(1<<DDB4);
21
  // Timer 0 konfigurieren
22
  TCCR0A = (1<<WGM01); // CTC Modus
23
  TCCR0B |= (1<<CS01); // Prescaler 8
24
  // ((1000000/8)/1000) = 125
25
  OCR0A = 125-1;
26
  
27
  // Compare Interrupt erlauben
28
  TIMSK |= (1<<OCIE0A);
29
  
30
  // Global Interrupts aktivieren
31
  sei();
32
  DDRB |= (1<<DDB0) | (1<<DDB1) | (1<<DDB2) | (1<<DDB3);
33
  DDRB &= ~(1<<DDB4);
34
   ADMUX =
35
   (1 << ADLAR) |     // left shift result
36
   (0 << REFS1) |     // Sets ref. voltage to VCC, bit 1
37
    (0 << REFS0) |     // Sets ref. voltage to VCC, bit 0
38
   (0 << MUX3)  |     // use ADC2 for input (PB4), MUX bit 3
39
    (0 << MUX2)  |     // use ADC2 for input (PB4), MUX bit 2
40
    (1 << MUX1)  |     // use ADC2 for input (PB4), MUX bit 1
41
    (0 << MUX0);       // use ADC2 for input (PB4), MUX bit 0
42
     ADCSRA =
43
     (1 << ADEN)  |     // Enable ADC
44
     (0 << ADPS2) |     // set prescaler to 8, bit 2
45
    (1 << ADPS1) |     // set prescaler to 8, bit 1
46
    (1 << ADPS0);      // set prescaler to 8, bit 0
47
48
  
49
  
50
    while(1)
51
  
52
    {
53
    
54
    if (FLAG == 1)
55
  {    ADCSRA |= (1 << ADSC);         // start ADC measurement
56
      while (ADCSRA & (1 << ADSC) ); // wait till conversion complete
57
      ADCH = analogresult;
58
      FLAG = 0;
59
    }
60
    
61
    if ( (analogresult < (analogreferenz + 20)) && (analogresult > (analogreferenz - 20)))
62
    {
63
      Zaehler++;
64
    }
65
    
66
    else {
67
      Zaehler = 0;
68
      analogreferenz = analogresult;
69
    }
70
      
71
      if ( Zaehler > 59 )
72
      {
73
        PORTB |= (1<<PB0)|(1<<PB1)|(1<<PB2)|(1<<PB3);
74
      }
75
      
76
    if ( Zaehler < 60 && (PORTB |= ((1<<PB0)|(1<<PB1)|(1<<PB2)|(PB3))))
77
    {
78
      PORTB &= ~((1<<PB0)|(1<<PB1)|(1<<PB2)|(1<<PB3));
79
    }
80
    
81
    
82
    
83
    
84
    
85
    }
86
}
87
88
ISR (TIMER0_COMPA_vect)
89
{
90
  millisekunden++;
91
  if(millisekunden == 1000)
92
  {
93
    sekunde++;
94
    FLAG = 1
95
    millisekunden = 0;
96
    if(sekunde == 60)
97
    {
98
      minute++;
99
      sekunde = 0;
100
    }
101
    if(minute == 60)
102
    {
103
      stunde++;
104
      minute = 0;
105
    }
106
    if(stunde == 24)
107
    {
108
      stunde = 0;
109
    }
110
  }
111
}

von Hubert G. (hubertg)


Lesenswert?

Ja schrieb:
> ADCH = analogresult;

Hm, ich schreib das immer andersrum.

von Ja (Gast)


Lesenswert?

Hubert G. schrieb:
> Hm, ich schreib das immer andersrum.

Hättest du für mich (den Dummen) eine Begründung dafür?

von Hubert G. (hubertg)


Lesenswert?

Weil Zuweisungen immer von rechts nach links gehen.
Du schreibst ja auch OCR0A = 125-1; weil du die Zahl ins Register 
schreiben willst und nicht umgekehrt.

von Ja (Gast)


Lesenswert?

Dankeschön!

von M. K. (sylaina)


Lesenswert?

Und warum eigentlich
1
analogResult = (ADCH<<8)|ADCL;

und nicht gleich
1
analogResult = ADC;

?

Und warum soll hier

Ja schrieb:
> ADCH = analogresult;

nur das HI-Byte ausgelesen werden, nicht aber nicht das LOW-Byte? 
(Abgesehen davon, dass es eigentlich anders herum stehen müsste)

: Bearbeitet durch User
von spess53 (Gast)


Lesenswert?

Hi

>Und warum eigentlich

>analogResult = (ADCH<<8)|ADCL;


>und nicht gleich

>analogResult = ADC;

Weil ADLAR gesetzt ist und damit das Ergebnis 8-Bittig in ADCH steht.

MfG Spess

von Ja (Gast)


Lesenswert?

spess53 schrieb:


> Weil ADLAR gesetzt ist und damit das Ergebnis 8-Bittig in ADCH steht.
 ich dachte schon, ich blicke nichts..

von Ja (Gast)


Lesenswert?

Bin ich ein dummer Mensch, oder warum funktioniert das nicht?
1
/*
2
 * Bewegungssensor.c
3
 *
4
 * Created: 04.03.2015 10:21:49
5
 *  Author: Daniel
6
 */ 
7
8
/*
9
 * LEDblink_attiny85.c
10
 *
11
 * Created: 28.02.2015 12:40:40
12
 *  Author: Daniel
13
 */ 
14
15
16
#include <avr/io.h>
17
#define F_CPU 1000000UL // 1 MHZ
18
#include <util/delay.h>
19
#include <avr/interrupt.h>
20
21
volatile unsigned int millisekunden;
22
volatile unsigned int sekunde;
23
volatile unsigned int minute;
24
volatile unsigned int stunde;
25
volatile unsigned int FLAG;
26
volatile unsigned int analogresult;
27
volatile unsigned int analogreferenz = 128;
28
volatile unsigned int Zaehler = 0;
29
30
31
32
33
int main(void)
34
{
35
  DDRB &= ~(1<<DDB4);
36
  // Timer 0 konfigurieren
37
  TCCR0A = (1<<WGM01); // CTC Modus
38
  TCCR0B |= (1<<CS01); // Prescaler 8
39
  // ((1000000/8)/1000) = 125
40
  OCR0A = 125-1;
41
  
42
  // Compare Interrupt erlauben
43
  TIMSK |= (1<<OCIE0A);
44
  
45
  // Global Interrupts aktivieren
46
  sei();
47
  DDRB |= (1<<DDB0) | (1<<DDB1) | (1<<DDB2) | (1<<DDB3);
48
  DDRB &= ~(1<<DDB4);
49
   ADMUX =
50
   (1 << ADLAR) |     // left shift result
51
   (1 << REFS1) |     // Sets ref. voltage to VCC, bit 1
52
   (1 << REFS0) |     // Sets ref. voltage to VCC, bit 0
53
   (0 << MUX3)  |     // use ADC2 for input (PB4), MUX bit 3
54
   (0 << MUX2)  |     // use ADC2 for input (PB4), MUX bit 2
55
   (1 << MUX1)  |     // use ADC2 for input (PB4), MUX bit 1
56
   (0 << MUX0);       // use ADC2 for input (PB4), MUX bit 0
57
    ADCSRA =
58
    (1 << ADEN)  |     // Enable ADC
59
    (0 << ADPS2) |     // set prescaler to 8, bit 2
60
    (1 << ADPS1) |     // set prescaler to 8, bit 1
61
    (1 << ADPS0);      // set prescaler to 8, bit 0
62
63
  
64
  
65
 while(1)
66
  
67
    {
68
    
69
    if (FLAG == 1)
70
        {    ADCSRA |= (1 << ADSC);         // start ADC measurement
71
          while (ADCSRA & (1 << ADSC) ); // wait till conversion complete
72
          analogresult = ADCH;
73
          
74
        }
75
        
76
        if ( (FLAG == 1) && (analogresult < (analogreferenz + 20)) && (analogresult > (analogreferenz - 20)))
77
        {
78
          Zaehler++;
79
          FLAG = 0;
80
        }
81
        
82
        else {
83
          Zaehler = 0;
84
          analogreferenz = analogresult;
85
          FLAG = 0;
86
        }
87
          
88
          if ( Zaehler > 9 )
89
          {
90
            PORTB |= (1<<PB0)|(1<<PB1)|(1<<PB2)|(1<<PB3);
91
          }
92
          
93
        else //if ( Zaehler < 10 && (PORTB |= ((1<<PB0)|(1<<PB1)|(1<<PB2)|(PB3))))
94
        {
95
          PORTB &= ~((1<<PB0)|(1<<PB1)|(1<<PB2)|(1<<PB3));
96
        }
97
98
ISR (TIMER0_COMPA_vect)
99
{
100
  millisekunden++;
101
  if(millisekunden == 1000)
102
  {
103
    sekunde++;
104
    FLAG = 1;
105
    millisekunden = 0;
106
    if(sekunde == 60)
107
    {
108
      minute++;
109
      sekunde = 0;
110
    }
111
    if(minute == 60)
112
    {
113
      stunde++;
114
      minute = 0;
115
    }
116
    if(stunde == 24)
117
    {
118
      stunde = 0;
119
    }
120
  }
121
}

von Karl H. (kbuchegg)


Lesenswert?

1
        if ( (FLAG == 1) && (analogresult < (analogreferenz + 20)) && (analogresult > (analogreferenz - 20)))
2
        {
3
          Zaehler++;
4
          FLAG = 0;
5
        }
6
        
7
        else {
8
          Zaehler = 0;

ich glaube kaum, dass du den Zähler auch dann immer wieder zurück setzen 
willst, wenn die 1 Sekunde noch nicht um ist.

Tip: Trenn doch erst mal die Dinge.
Das eine ist, dass du jede 1 Sekunde eine Messung machen und auswerten 
willst.
Und das andere ist die Auswertung der Messung. Gerade am Anfang ist es 
des öfteren keine so gute Idee, wenn man das alles in ein einziges if 
quetschen will. Man verheddert sich dann leicht in den Bedingungen.

(Und es ist auch eine gute Idee, sich für Variablen einen vernünftigen 
Namen einfallen zu lassen, der ihrer Funktion entspricht. FLAG ist ein 
recht nichtssagender Name. 'MessungMachen' oder 'SekundeVorbei' wären 
jetzt mal angesehen vom Läge der Namen, wesentlich besser. Denn genau 
das wird damit angezeigt: 1 Sekunde ist vorbei. Es soll eine neue 
Messung gemacht werden.
1
....
2
3
  while( 1 ) {
4
5
    if( FLAG == 1 ) {      // 1 Sekunde ist vorbei. Messung machen und auswerten
6
7
       FLAG = 0;
8
9
       ADC abfragen
10
11
       if( abs( analogresult - analogreferenz ) < 20 ) {
12
         if( zaehler < 250 )   // Überlauf verhindern!
13
           zaehler++;
14
       }
15
       else {
16
         zaehler = 0;
17
         analogreferenz = analogresult;
18
       }
19
20
       if( zaehler > 59 )
21
         PORTB |= (1<<PB0)|(1<<PB1)|(1<<PB2)|(1<<PB3);
22
       else
23
         PORTB &= ~((1<<PB0)|(1<<PB1)|(1<<PB2)|(1<<PB3));
24
    }
25
  }
26
}
27
28
...

: Bearbeitet durch User
von Eric B. (beric)


Lesenswert?

Ungetestet! Aber such schon mal die Unterschiede ;-)
1
#include <avr/io.h>
2
#define F_CPU 1000000UL // 1 MHZ
3
#include <util/delay.h>
4
#include <avr/interrupt.h>
5
6
volatile unsigned int miliSeconds;
7
const    unsigned int adcReference      = 128u;
8
const    unsigned int adcRefMargin      =  20u;
9
const    unsigned int inRangeCountLimit =   9u;
10
11
#define PB0_TO_3 ((1<<PB0)|(1<<PB1)|(1<<PB2)|(1<<PB3)) 
12
      
13
static void initialize(void)
14
{
15
   DDRB &= ~(1<<DDB4);
16
   
17
   //Configure Timer 0
18
   TCCR0A = (1<<WGM01); // CTC Mode
19
   TCCR0B |= (1<<CS01); // Prescaler 8
20
   OCR0A = 125-1;       // ((F_CPU/8)/1000) = 125
21
22
   // Enable Compare Interrupt
23
   TIMSK |= (1<<OCIE0A);
24
  
25
   DDRB |= (1<<DDB0) | (1<<DDB1) | (1<<DDB2) | (1<<DDB3);
26
   DDRB &= ~(1<<DDB4);
27
   ADMUX =
28
      (1 << ADLAR) |     // left shift result
29
      (1 << REFS1) |     // Sets ref. voltage to VCC, bit 1
30
      (1 << REFS0) |     // Sets ref. voltage to VCC, bit 0
31
      (0 << MUX3)  |     // use ADC2 for input (PB4), MUX bit 3
32
      (0 << MUX2)  |     // use ADC2 for input (PB4), MUX bit 2
33
      (1 << MUX1)  |     // use ADC2 for input (PB4), MUX bit 1
34
      (0 << MUX0);       // use ADC2 for input (PB4), MUX bit 0
35
   ADCSRA =
36
      (1 << ADEN)  |     // Enable ADC
37
      (0 << ADPS2) |     // set prescaler to 8, bit 2
38
      (1 << ADPS1) |     // set prescaler to 8, bit 1
39
      (1 << ADPS0);      // set prescaler to 8, bit 0
40
    
41
   sei();
42
}
43
44
int main(void)
45
{
46
   unsigned int adcResult;
47
   unsigned int inRangeCounter;
48
   
49
   initialize();
50
   for(;;)
51
   {
52
      if (doMeasurement)
53
      {
54
         // One second has passed - do the next AD conversion
55
         
56
         ADCSRA |= (1 << ADSC);
57
         while (ADCSRA & (1 << ADSC))
58
         {
59
            //do nothing - just wait till conversion complete
60
         }
61
         adcResult = ADCH;
62
         
63
         // Check result range
64
         if (((adcRefererence - adcRefMargin) <= adcResult)
65
            && (adcResult < (adcRefererence + adcRefMargin)))
66
         {
67
            // Show that count limit is reached
68
            if(inRangeCounter > inRangeCountLimit)
69
            {
70
               PORTB |= PB0_TO_3;
71
            }
72
            else
73
            {
74
               inRangeCounter ++;
75
            }
76
         }
77
         else
78
         {
79
            // Reset everything
80
            inRangeCounter = 0;
81
            analogreferenz = adcResult;
82
            PORTB &= ~PB0_TO_3
83
         }
84
         
85
         // Ready for next measurement
86
         doMeasurement = 0;
87
      }
88
   }
89
   return 0;
90
}
91
92
ISR(TIMER0_COMPA_vect)
93
{
94
   milliSecs ++;
95
   if(milliSecs >= 1000)
96
   {
97
      milliSecs = 0;
98
      doMeasurement = 1;
99
   }
100
}

: Bearbeitet durch User
von Bernhard S. (b_spitzer)


Lesenswert?

[Erbsenzähler]
> Ungetestet!
Stimmt, doMeasurement  ist nicht deklariert :-)
Außerdem: gibt es auf dem AVR wirklich keine Möglichkeit, einzelne Bits 
als Flags zu nutzen?
Und ich finde
1
for(;;)
irgendwie verwirrender als
1
while(1)
auch wenn das 1 Tastendruck mehr ist. Dafür sind
1
return 0;
nach einer ENDLOSSCHLEIFE 9 unnötige Tastendrücke.
[/Erbsenzähler]

;-)

von Ja (Gast)


Lesenswert?

Ich würde mal schätzen, das bei jedem doMeasurement == 1 (entspricht 
Sekundenzyklus, die if-Zeile ausgeführt wird (in ihr steckt das Messen)

von M. K. (sylaina)


Lesenswert?

spess53 schrieb:
> Hi
>
>>Und warum eigentlich
>
>>analogResult = (ADCH<<8)|ADCL;
>
>
>>und nicht gleich
>
>>analogResult = ADC;
>
> Weil ADLAR gesetzt ist und damit das Ergebnis 8-Bittig in ADCH steht.
>
> MfG Spess

Ja und? ADC ist (ADCH << 8) | ADCL, dabei ist es völlig egal ob ADLAR 
gesetzt ist oder nicht. Hätte da jetzt nur analogResult = ADCH gestanden 
wäre es noch logisch gewesen aber so? Das macht hier keinen Sinn ADC 
nicht zu benutzen.

von Eric B. (beric)


Lesenswert?

Bernhard Spitzer schrieb:

>> Ungetestet!
> Stimmt, doMeasurement  ist nicht deklariert :-)

Mea culpa, mea maxima culpa!
1
unsigned int doMeasurement;
Bitte schön!

> Außerdem: gibt es auf dem AVR wirklich keine Möglichkeit, einzelne Bits
> als Flags zu nutzen?

Ich glaub nicht. Wenigstens nicht in dem Sinne wie bei den ARM Cortex 
Bitfields...

> Und ich finde
>
1
for(;;)
> irgendwie verwirrender als
>
1
while(1)
> auch wenn das 1 Tastendruck mehr ist.

Geschmackssache.

> Dafür sind
>
1
return 0;
> nach einer ENDLOSSCHLEIFE 9 unnötige Tastendrücke.

Nicht für den OT. Der muss das ganze nur Copy/Pasten auf gut Neu-deutsch 
;-)

Und ich mag den Sonderfall für main() nicht. main() ist als int 
definiert und soll deswegen irgendeine integer zurückliefern, auch wenn 
er davor ein endlos schleift.

Edit: mich würde aber interessieren ob meine Version für den OT 
funktioniert. ich habe kein AVR hier zum ausprobieren.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Eric B. schrieb:

>
1
> unsigned int doMeasurement;
2
>
> Bitte schön!
>

Du bist auf ARM zu Hause?
Dann sei dir verziehen.

Auf einem AVR nimmst du nicht ungestraft und gedankenlos einfach einen 
int her. Ein int ist 16 Bit gross! Damit halst du dem armen 8-Bitter 
unnötige Mehrarbeit auf.

ein
1
uint8_t doMeasurement;
tuts völlig.

>> Außerdem: gibt es auf dem AVR wirklich keine Möglichkeit, einzelne Bits
>> als Flags zu nutzen?
>
> Ich glaub nicht. Wenigstens nicht in dem Sinne wie bei den ARM Cortex
> Bitfields...

Bitfields gibt es als C Ding selbstverständlich.

von Ja (Gast)


Lesenswert?

Eric B. schrieb:
> mich würde aber interessieren ob meine Version für den OT
> funktioniert. ich habe kein AVR hier zum ausprobieren.

Hallo, vielen Dank für deine Mühe. Leider funktioniert dein Code nicht.
Ich habe den ADC sogar an Masse gelegt, um Fehlerquellen auszuschließen, 
leider wird PB0_TO_3 anscheinend nie aktiviert...

von Karl H. (kbuchegg)


Lesenswert?

1
volatile uint8_t doMeasurement;
2
//******

von Ja (Gast)


Lesenswert?

Ich habe kleinere Fehler im Code ausgemerzt (millisecs und miliseconds), 
habe aber keine Ahnung, warum das so nicht funktioniert..

von Ja (Gast)


Lesenswert?

Karl Heinz schrieb:
> volatile uint8_t doMeasurement;

Ja und ADCReference darf auch keine constante sein oder?

von Karl H. (kbuchegg)


Lesenswert?

Ja schrieb:
> Karl Heinz schrieb:
>> volatile uint8_t doMeasurement;
>
> Ja und ADCReference darf auch keine constante sein oder?

Moment. Muss den Code noch genauer studieren.

von Karl H. (kbuchegg)


Lesenswert?

Ja schrieb:
> Karl Heinz schrieb:
>> volatile uint8_t doMeasurement;
>
> Ja und ADCReference darf auch keine constante sein oder?

So wie das vom TO angedacht war. Korrekt: darf keine Konstante sein.

Da ist auch noch ein Mischmasch
1
         if (((adcRefererence - adcRefMargin) <= adcResult)
2
//             **************
3
            && (adcResult < (adcRefererence + adcRefMargin)))
4
//                           **************
5
         {
6
....
7
         }
8
         else
9
         {
10
....
11
            analogreferenz = adcResult;
12
//          **************
13
...

die Idee ist es ja, dass sich die Referenz dauernd ständig anpasst. Das 
muss also dieselbe Variable sein.

Das bringt uns zur Frage:
> Leider funktioniert dein Code nicht.
was hast du noch verändert? So wie gepostet compiliert der Code ganz 
sicher nicht. Du musst ihn also verändert haben.

: Bearbeitet durch User
von Ja (Gast)


Lesenswert?

1
#include <avr/io.h>
2
#define F_CPU 1000000UL // 1 MHZ
3
#include <util/delay.h>
4
#include <avr/interrupt.h>
5
6
volatile unsigned int milliSeconds;
7
volatile unsigned int adcReference      = 128u;
8
const    unsigned int adcRefMargin      =  20u;
9
const    unsigned int inRangeCountLimit =   9u;
10
volatile uint8_t doMeasurement;
11
12
//#define PB0_TO_3 ((1<<PB0)|(1<<PB1)|(1<<PB2));//|(1<<PB3))
13
14
static void initialize(void)
15
{
16
  DDRB &= ~(1<<DDB4);
17
  
18
  //Configure Timer 0
19
  TCCR0A = (1<<WGM01); // CTC Mode
20
  TCCR0B |= (1<<CS01); // Prescaler 8
21
  OCR0A = 125-1;       // ((F_CPU/8)/1000) = 125
22
23
  // Enable Compare Interrupt
24
  TIMSK |= (1<<OCIE0A);
25
  
26
  DDRB |= (1<<DDB0) | (1<<DDB1) | (1<<DDB2) | (1<<DDB3);
27
  DDRB &= ~(1<<DDB4);
28
  ADMUX =
29
  (1 << ADLAR) |     // left shift result
30
  (1 << REFS1) |     // Sets ref. voltage to VCC, bit 1
31
  (1 << REFS0) |     // Sets ref. voltage to VCC, bit 0
32
  (0 << MUX3)  |     // use ADC2 for input (PB4), MUX bit 3
33
  (0 << MUX2)  |     // use ADC2 for input (PB4), MUX bit 2
34
  (1 << MUX1)  |     // use ADC2 for input (PB4), MUX bit 1
35
  (0 << MUX0);       // use ADC2 for input (PB4), MUX bit 0
36
  ADCSRA =
37
  (1 << ADEN)  |     // Enable ADC
38
  (0 << ADPS2) |     // set prescaler to 8, bit 2
39
  (1 << ADPS1) |     // set prescaler to 8, bit 1
40
  (1 << ADPS0);      // set prescaler to 8, bit 0
41
  
42
  sei();
43
}
44
45
int main(void)
46
{
47
  
48
  unsigned int adcResult;
49
  unsigned int inRangeCounter;
50
  
51
  initialize();
52
  for(;;)
53
  {
54
    //if (milliSeconds > 100)
55
    //{
56
    //  PORTB |= (1<<PB3);
57
    //}
58
    //else {
59
    //  PORTB &= ~(1<<PB3);
60
    //}
61
    if (doMeasurement == 1)
62
    {
63
      // One second has passed - do the next AD conversion
64
      
65
      ADCSRA |= (1 << ADSC);
66
      while (ADCSRA & (1 << ADSC))
67
      {
68
        //do nothing - just wait till conversion complete
69
      }
70
      adcResult = ADCH;
71
      
72
      // Check result range
73
      if (((adcReference - adcRefMargin) <= adcResult)
74
      && (adcResult < (adcReference + adcRefMargin)))
75
      {
76
        // Show that count limit is reached
77
        if(inRangeCounter > inRangeCountLimit)
78
        {
79
          PORTB |= (1<<PB3);
80
        }
81
        else
82
        {
83
          inRangeCounter ++;
84
        }
85
      }
86
      else
87
      {
88
        // Reset everything
89
        inRangeCounter = 0;
90
        adcReference = adcResult;
91
        PORTB &= ~(1<<PB3);
92
      }
93
      
94
      // Ready for next measurement
95
      doMeasurement = 0;
96
    }
97
  }
98
  return 0;
99
}
100
101
ISR(TIMER0_COMPA_vect)
102
{
103
  milliSeconds ++;
104
  if(milliSeconds >= 1000)
105
  {
106
    milliSeconds = 0;
107
    doMeasurement = 1;
108
  }
109
}

einige Variablen waren Quark, so compililiert er mir das. Allerdings 
schaltet der Ausgang niemals. Selbst dann nicht, wenn ADC an Masse liegt 
(Veränderung = 0).

von Ja (Gast)


Lesenswert?

Ich habe so das Gefühl, dass das an der definierten ADCReference = 128u; 
liegt, vielleicht ändert der die mir dann nicht mehr.

von Ja (Gast)


Lesenswert?

initialize();
for(;;) und return 0; können doch auch einfach mit while (1) dargestellt 
werden oder?

von Karl H. (kbuchegg)


Lesenswert?

Nur zur Sicherheit:
Deine Eingangsspannung liegt aber schon am ADC2 Pin an?


(Persönlich hasse ich den Sch..., die ADC Routinen jedesmal neu zu 
schreiben. Im Tut gibt es so schöne Routinen dafür)

von Karl H. (kbuchegg)


Lesenswert?

Ja schrieb:
> initialize();
> for(;;) und return 0; können doch auch einfach mit while (1) dargestellt
> werden oder?


Ja, aber das ist alles nicht das Problem.

von Ja (Gast)


Lesenswert?

Karl Heinz schrieb:
> Eingangsspannung

Die auszuwertende, analoge Spannung liegt an ADC2 an, ja.
Ist ein Attiny85...

Karl Heinz schrieb:
> Ja, aber das ist alles nicht das Problem.

okay...

von Karl H. (kbuchegg)


Lesenswert?

Da sich das jetzt schon in den 5. Tag reinzieht:
Können wir das ganze systematisch von mehr oder weniger bei 0 beginnend, 
in Schritten noch mal neu aufsetzen?

(Problem bei mir ist nämlich, dass ich auch zur Zeit auf keinen AVR 
zugreifen kann.)

von Karl H. (kbuchegg)


Lesenswert?

Ja schrieb:

> Die auszuwertende, analoge Spannung liegt an ADC2 an, ja.
> Ist ein Attiny85...

Das ist schon mal gut zu wissen.

Denn der hat 3 Bits für die Referenzspannung.

Und die Kombination
1
  REFS2   REFS1   REFS0
2
    0       1       1

ist ungültig.

Womit hier
1
  ADMUX =
2
  (1 << ADLAR) |     // left shift result
3
  (1 << REFS1) |     // Sets ref. voltage to VCC, bit 1
4
  (1 << REFS0) |     // Sets ref. voltage to VCC, bit 0
5
  (0 << MUX3)  |     // use ADC2 for input (PB4), MUX bit 3
6
  (0 << MUX2)  |     // use ADC2 for input (PB4), MUX bit 2
7
  (1 << MUX1)  |     // use ADC2 for input (PB4), MUX bit 1
8
  (0 << MUX0);       // use ADC2 for input (PB4), MUX bit 0
schon mal der erste Fehler wäre.

von Ja (Gast)


Lesenswert?

Karl Heinz schrieb:
> Da sich das jetzt schon in den 5. Tag reinzieht:
> Können wir das ganze systematisch von mehr oder weniger bei 0 beginnend,
> in Schritten noch mal neu aufsetzen

Klar, also:
Meine Intention:
Ich habe einen Attiny85 und eine analoge Spannung, die ausgewertet 
werden soll.
Auswertung: es soll ausgewertet werden, ob sich die Spannung verändert 
(in Betrachtung der Toleranz). Am besten soll dieser Check sekündlich 
stattfinden.
Wenn sich die Spannung nämlich nicht sonderlich verändert hat über eine 
Minute (nicht sonderlich = incl. Toleranz), dann soll/en Port/s als 
Ausgang geschaltet werden.
Das ist so die prinzipielle Idee.

von Karl H. (kbuchegg)


Lesenswert?

Ja schrieb:
> Karl Heinz schrieb:
>> Da sich das jetzt schon in den 5. Tag reinzieht:
>> Können wir das ganze systematisch von mehr oder weniger bei 0 beginnend,
>> in Schritten noch mal neu aufsetzen
>
> Klar, also:
> Meine Intention:
> Ich habe einen Attiny85 und eine analoge Spannung, die ausgewertet
> werden soll.
> Auswertung: es soll ausgewertet werden, ob sich die Spannung verändert
> (in Betrachtung der Toleranz). Am besten soll dieser Check sekündlich
> stattfinden.
> Wenn sich die Spannung nämlich nicht sonderlich verändert hat über eine
> Minute (nicht sonderlich = incl. Toleranz), dann soll/en Port/s als
> Ausgang geschaltet werden.
> Das ist so die prinzipielle Idee.

Alles schön und gut.
Nur fangen wir viel simpler an.

Du hast 4 LED und an diese 4 LED wird der Messwert ausgegeben (geeignet 
runtergeteilt). Ein Poti hast du am ADC EIngang oder sonst irgendeine 
Möglichkeit, dort eine veränderbare Spannung anzulegen?
Wie gross ist die zu messende Spannung maximal und was wollen wir als 
Referenzspannung nehmen.

Ehe das nicht klappt, dass du beim Drehen am Poti die LED sich nicht 
verändern siehst, brauchen wir mit komplexeren Dingen gar nicht erst 
anfangen. erst mal muss das einfache funktionieren.

von Ja (Gast)


Angehängte Dateien:

Lesenswert?

Ergibt durchaus Sinn; ja, Potis hab ich hier ein paar liegen.
Ich leg Vcc einfach an ADC an mit zwischengeschaltetem Poti, oder?

Zum Bild: was bedeutet das "X"?

von Karl H. (kbuchegg)


Lesenswert?

Ja schrieb:
> Ergibt durchaus Sinn; ja, Potis hab ich hier ein paar liegen.
> Ich leg Vcc einfach an ADC an mit zwischengeschaltetem Poti, oder?

Jep.
1
        + Vcc
2
        |
3
       +-+
4
       | |
5
     <--------------- ADC2
6
       | |
7
       +-+
8
        |
9
     ---+--------- GND

Potiwert: 10k wären super.

Dann nehmen wir Vcc als Referenzspannung.


> Zum Bild: was bedeutet das "X"?

X ... don't care (also: spielt keine Rolle)

von Karl H. (kbuchegg)


Lesenswert?

Dann ist das hier
1
#define F_CPU 1000000UL // 1 MHZ
2
3
#include <avr/io.h>
4
#include <util/delay.h>
5
6
/* ADC initialisieren */
7
void ADC_Init(void)
8
{
9
  // die Versorgungsspannung AVcc als Refernz wählen:
10
  ADMUX = 0;
11
12
  ADCSRA = (1<<ADPS1) | (1<<ADPS0);     // Frequenzvorteiler
13
  ADCSRA |= (1<<ADEN);                  // ADC aktivieren
14
15
  ADCSRA |= (1<<ADSC);                  // eine ADC-Wandlung
16
  while (ADCSRA & (1<<ADSC) ) {         // auf Abschluss der Konvertierung warten
17
  }
18
  (void) ADCW;
19
}
20
21
uint16_t ADC_Read( uint8_t channel )
22
{
23
  // Kanal waehlen, ohne andere Bits zu beeinflußen
24
  ADMUX = (ADMUX & ~(0x0F)) | (channel & 0x0F);
25
26
  ADCSRA |= (1<<ADSC);            // eine Wandlung "single conversion"
27
  while (ADCSRA & (1<<ADSC) ) {   // auf Abschluss der Konvertierung warten
28
  }
29
  return ADCW;                    // ADC auslesen und zurückgeben
30
}
31
32
int main(void)
33
{
34
  uint16_t adcValue;
35
36
  DDRB |= (1<<DDB0) | (1<<DDB1) | (1<<DDB2) | (1<<DDB3);
37
38
  ADC_Init();
39
40
  while(1)
41
  {
42
    adcValue = ADC_Read( 2  );
43
    PORTB = adcValue / 64;
44
  }
45
}
das erste Testprogramm.

Bei Drehen am Poti müss sich an den LED was tun.

by the Way: wie rum sind deine LED angeschlossen? Leuchten die bei einer 
0 am Portpin oder bei einer 1?

Wenn: bei einer 0, dann eben
1
    PORTB = ~( adcValue / 64 );

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Gibts ein Problem?

von Karl H. (kbuchegg)


Lesenswert?

Klappts nicht?

von Ja (Gast)


Lesenswert?

Karl Heinz schrieb:
> Klappts nicht?

Vielen vielen Dank erstmal: also, da scheint sich nichts zu tun...
Ich habe es mit Masse, Widerstand und VCC probiert, da leuchtet nichts. 
Kathode der LED liegt an Masse an, also passt das theoretisch. Ich weiß 
echt nicht, woran das liegen könnte...

von Ja (Gast)


Lesenswert?

Okay sorry, ich scheine gerade einen heftigen geistigen Aussetzer gehabt 
zu haben: ES FUNKTIONIERT.

von Karl H. (kbuchegg)


Lesenswert?

Hmmmmm.
Sollte aber eigentlich. Ausser der Einstellung für die Referenzspannung 
mit allen MUX Bits auf 0, ist mir nichts grossartiges mehr im Datenblatt 
aufgefallen.

OK, den Digitalportpin sollte man noch abschalten und da bin ich 
überfragt, ob das für den ADC ein Problem darstellt, wenn man es nicht 
macht. Sicherheitshalber
1
int main(void)
2
{
3
  uint16_t adcValue;
4
5
  DDRB |= (1<<DDB0) | (1<<DDB1) | (1<<DDB2) | (1<<DDB3);
6
  DIDR0 |= (1<<ADC2D);
7
8
  ADC_Init();
9
10
  while .....
11
....

Deine LED funktionieren aber schon?
Wieder: sicherheitshalber
1
int main(void)
2
{
3
  uint16_t adcValue;
4
5
  DDRB |= (1<<DDB0) | (1<<DDB1) | (1<<DDB2) | (1<<DDB3);
6
  DIDR0 |= (1<<ADC2D);
7
8
  ADC_Init();
9
10
  PORTB = 0x0F;
11
  _delay_ms( 200 );
12
  PORTB = 0x00;
13
  _delay_ms( 200 );
14
  PORTB = 0x0F;
15
  _delay_ms( 200 );
16
17
  while .....
18
....

Das gibt mal Lichtspiele, wenn das Programm startet. Kann nie schaden, 
wenn man da eine Bestätigung hat.

von Ja (Gast)


Lesenswert?

Karl Heinz schrieb:
> Das gibt mal Lichtspiele, wenn das Programm startet. Kann nie schaden,
> wenn man da eine Bestätigung hat.

Der erste Code funktionert, also der mit dem ADC-Code. Also der ADC 
funktioniert!

von Karl H. (kbuchegg)


Lesenswert?

Ja schrieb:
> Karl Heinz schrieb:
>> Das gibt mal Lichtspiele, wenn das Programm startet. Kann nie schaden,
>> wenn man da eine Bestätigung hat.
>
> Der erste Code funktionert, also der mit dem ADC-Code. Also der ADC
> funktioniert!

Sag das doch gleich.

Gut. Dann gehts weiter.

von Karl H. (kbuchegg)


Lesenswert?

Wir bringen jetzt mal die 1 Sekunde ins Spiel.
Das mach ich erst mal pragmatisch, indem ich mit einem _delay_ms() die 
Durchlaufzeit durch die Schleife auf ca 1 Sekunde anhebe. Um den delay 
kümmern wir uns später und ersetzen den durch einen Timer. Das ist im 
Moment nicht so wichtig.

Weiters bauen wir den Referenzwert ein, wobei uns diese zeitliche 
Mehfachreferenz noch wurscht ist. Wir gewinnen einfach jede 1 Sekunde 
einen neuen Wert und vergleichen ihn mit dem 'Referenzwert' aus der 
vorhergehenden Runde. Sind die beide (mit einer erlaubten Abweichung) 
gleich, dann: LED an (oder aus), wenn nicht dann eben LED aus (oder an).

Da die Differenz der beiden Werte negativ sein kann, hab ich den 
Datentyp für die Werte von einem uint16_t auf einen int16_t verändert. 
Dann gibt es bei der Subtraktion kein Problem mit negativen Werten.
1
int main(void)
2
{
3
  int16_t adcReference;
4
  int16_t adcValue;
5
6
  DDRB |= (1<<DDB0) | (1<<DDB1) | (1<<DDB2) | (1<<DDB3);
7
8
  ADC_Init();
9
10
  adcReference = ADC_Read( 2  );
11
12
  while(1)
13
  {
14
    adcValue = ADC_Read( 2  );
15
16
    if( abs( adcReference - adcValue ) < 20 )
17
      PORTB |= 0xF0;
18
    else
19
      PORTB &= ~0xF0;
20
21
    adcReference = adcValue;
22
    _delay_ms( 1000 );
23
  }
24
}

Erwartetes Verhalten:
nach einer Änderung an der Eingangsspannung (am Poti drehen), sollten 
die LED erst mal abschalten um dann nach einer Zeit nicht länger als 1 
Sekunde wieder den ursprünglichen Zustand einzunehmen.

(Ich weiss nicht, ob Pin auf 1 bei dir LED leuchtet bedeutet oder nicht. 
Deine Aussage diesbezüglich war für mich nicht klar)

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Karl Heinz schrieb:

> die LED erst mal abschalten um dann nach einer Zeit nicht länger als 1
> Sekunde wieder den ursprünglichen Zustand einzunehmen.

die Zeit kann ich leider nicht genauer festmachen, weil wir ja nicht 
wissen, wann genau du am Poti drehst, während der µC im delay hängt. Das 
kann sein, dass der gerade erst seine 1 Sekunde Wartezeit begonnen hat, 
es kann aber auch sein, dass sich die Wartezeit schon dem Ende zuneigt. 
Sehen sollte man allerdings was.

von Karl H. (kbuchegg)


Lesenswert?

Autsch.,

Wie ist das denn passiert?
Tippfehler
1
    if( abs( adcReference - adcValue ) < 20 )
2
      PORTB |= 0xF0;

das muss natürlich
1
      PORTB |= 0x0F;

lauten!

(Was lernen wir daraus: wenn ich nicht zu faul gewesen wäre und mir 
statt dessen Makros für die LED definiert hätte, dann wäre der Fehler 
nicht passiert)

: Bearbeitet durch User
von Ja (Gast)


Lesenswert?

Karl Heinz schrieb:
> das muss natürlich      PORTB |= 0x0F;

Nachdem ich das geändert habe, funktioniert es jetzt!

von Karl H. (kbuchegg)


Lesenswert?

Ja schrieb:
> Karl Heinz schrieb:
>> das muss natürlich      PORTB |= 0x0F;
>
> Nachdem ich das geändert habe, funktioniert es jetzt!


Gut.

Dann kommt als nächstes ein Zähler dazu, der mitzählt, wie oft auf 
'gleich' detektiert wurde.

Meld mich gleich wieder.

von Ja (Gast)


Lesenswert?

1
int main(void)
2
{
3
  int16_t adcReference;
4
  int16_t adcValue;
5
  uint16_t Zaehler;
6
7
  DDRB |= (1<<DDB0) | (1<<DDB1) | (1<<DDB2) | (1<<DDB3);
8
9
  ADC_Init();
10
11
  adcReference = ADC_Read( 2  );
12
13
  while(1)
14
  {
15
    adcValue = ADC_Read( 2  );
16
17
    if( abs( adcReference - adcValue ) < 20 )
18
    Zaehler++;
19
    //PORTB |= 0x0F;
20
    else
21
    //PORTB &= ~0x0F;
22
    Zaehler = 0;
23
24
    adcReference = adcValue;
25
    _delay_ms( 1000 );
26
    if (Zaehler > 9)
27
    {
28
      PORTB |= 0x0F;
29
    }
30
    
31
    else {
32
      PORTB &= ~0x0F;
33
    }
34
  }
35
}
Funktioniert ;)

von Karl H. (kbuchegg)


Lesenswert?

OK. Nächste Version

Dazugekommen ist ein Zähler, der bei Übereinstimmung um 1 hochgezählt 
wird. Sind die Werte zu verschieden, dann wird der Zähler auf 0 gesetzt 
und die dann anliegende SPannung als neue Referenz genommen.

Ich hab die Anzahl erforderlicher Übereinstimmungen mal auf 5 gesetzt.

Auch hab ich eine kleine Modifikation gemacht. Solange die erforderliche 
Anzahl nicht beisammen ist, schalte ich die LED nicht einfach ab, 
sondern lass mir dort diesen Zähler ausgeben. D.h. an den LED müsste man 
sehen können, dass der Zähler sukzessive hochzählt.
Erst wenn die erforderliche Anzahl beisammen ist, dann gibt es an den 4 
LED 'die volle Dröhnung'
(OK, ich geb zu, dass ist auch eine kleine Spielerei. Aber ich seh sonst 
keine Möglichkeit festzustellen, ob der Zähler tatsächlich hochgezählt 
wird, oder ob es da noch einen nicht bedachten Effekt gibt)

1
#define F_CPU 1000000UL // 1 MHZ
2
3
#include <avr/io.h>
4
#include <util/delay.h>
5
6
/* ADC initialisieren */
7
void ADC_Init(void)
8
{
9
  // die Versorgungsspannung AVcc als Refernz wählen:
10
  ADMUX = 0;
11
12
  ADCSRA = (1<<ADPS1) | (1<<ADPS0);     // Frequenzvorteiler
13
  ADCSRA |= (1<<ADEN);                  // ADC aktivieren
14
15
  ADCSRA |= (1<<ADSC);                  // eine ADC-Wandlung
16
  while (ADCSRA & (1<<ADSC) ) {         // auf Abschluss der Konvertierung warten
17
  }
18
  (void) ADCW;
19
}
20
21
uint16_t ADC_Read( uint8_t channel )
22
{
23
  // Kanal waehlen, ohne andere Bits zu beeinflußen
24
  ADMUX = (ADMUX & ~(0x0F)) | (channel & 0x0F);
25
26
  ADCSRA |= (1<<ADSC);            // eine Wandlung "single conversion"
27
  while (ADCSRA & (1<<ADSC) ) {   // auf Abschluss der Konvertierung warten
28
  }
29
  return ADCW;                    // ADC auslesen und zurückgeben
30
}
31
32
#define REQUIRED  5
33
34
int main(void)
35
{
36
  int16_t adcReference;
37
  int16_t adcValue;
38
  uint8_t countEqual;
39
40
  DDRB |= (1<<DDB0) | (1<<DDB1) | (1<<DDB2) | (1<<DDB3);
41
42
  ADC_Init();
43
44
  adcReference = ADC_Read( 2  );
45
  countEqual = 0;
46
47
  while(1)
48
  {
49
    adcValue = ADC_Read( 2  );
50
51
    if( abs( adcReference - adcValue ) < 20 )
52
    {
53
      if( count < REQUIRED )
54
        countEqual++;
55
    }
56
    else {
57
      countEqual = 0;
58
      adcReference = adcValue;
59
    }
60
61
    if( count == REQUIRED )
62
      PORTB |= 0x0F;
63
    else
64
    {
65
//      PORTB &= ~0xF0;               // so wirds dann im Endeffekt werden, ....
66
      PORTB = ( count & 0x0F );     // aber z uDebugzwecken lassen wir uns den count ausgeben.
67
    }
68
69
    _delay_ms( 1000 );
70
  }
71
}

von Ja (Gast)


Lesenswert?

Karl Heinz schrieb:
> count

ist nicht definiert oder?

von Karl H. (kbuchegg)


Lesenswert?

Karl Heinz schrieb:
> OK. Nächste Version

Ach ja.
Was ist das erwartete Verhalten?

Wird am Poti gedreht, dann sollten alle LED erst mal ausgehen Nach 
Beendigung des Drehens sollten die LED sukzessive binär von 0 bis 4 
hochzählen
1
        .     .     .     .
2
        .     .     .     X
3
        .     .     X     .
4
        .     .     X     X
5
        .     X     .     .
und beim 5.ten 'Takt', dann das Feuerwerk einschalten
1
        X     X     X     X


Soweit zur Erwartungshaltung, was passieren sollte :-)
Mal sehen, was wirklich passiert.

von Karl H. (kbuchegg)


Lesenswert?

Ja schrieb:
> Karl Heinz schrieb:
>> count
>
> ist nicht definiert oder?

Danke. Muss natürlich countEqual heissen.

Ich hätte noch mal drüber compilieren sollen. Ich änder das in meinem 
Studio gleich

von Ja (Gast)


Lesenswert?

Super, funktioniert!
Der nächste Schritt wäre vermutlich der Timer oder?

von Karl H. (kbuchegg)


Lesenswert?

Ja schrieb:

> Funktioniert ;)

zu spät gesehen.
Gratulation.

Aber noch nicht ganz.
WEnn die Schaltung etwas über 18 Stunden steht, ohne dass sich die 
Spannung am Eingang ändert, dann läuft dir dein Zaehler über. Aus 65535 
wird 0 und dann ... meldet deine Auswertung das falsche. D.h du musst 
diesen Überlauf verhindern.

Du kannst also hier
1
    if( abs( adcReference - adcValue ) < 20 )
2
    Zaehler++;

nicht einfach nur den Zaehler in jedem Fall erhöhen.

Auf der sicheren Seite bist du, wenn du den Zaehler nur dann erhöhst, 
wenn er kleiner als die von dir geforderte Anzahl bist. Da kann nichts 
passieren. Sieh dir an, wie ich das gemacht habe. Und sieh dir auch an, 
wie ich es gemacht habe, dass die beiden Zahlenwerte bei dieser 
Sicherung und bei der Auswertung auch definitiv immer übereinstimmen.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Ja schrieb:
> Super, funktioniert!
> Der nächste Schritt wäre vermutlich der Timer oder?

Jep.
D.h. falls du den überhaupt noch brauchst.

Wie wichtig ist die genaue Zeit? Gibt es noch etwas, was der µC tun 
müsste?

Wenn du sagst "Soooo wichtig ist die Zeit nicht, denn ich hab ja sowieso 
eine Granulierung in der ersten Sekunde nachdem sich die Spannung 
stabilisiert hat, also kommt es auf ein paar Zehntelmillisekunden pro 
Schleifendurchlauf auch nicht an"

bzw. wenn du sagst "Nö, das ist die einzige Aufgabe, die der µC hat"

dann brauchen wir nicht päpstlicher als der Papst sein und können das so 
lassen.

Wenn du aber sagst, nope geht so nicht, dann bauen wir den Timer rein.

von Ja (Gast)


Lesenswert?

Karl Heinz schrieb:
> Auf der sicheren Seite bist du, wenn du den Zaehler nur dann erhöhst,
> wenn er kleiner als die von dir geforderte Anzahl bist. Da kann nichts
> passieren. Sieh dir an, wie ich das gemacht habe. Und sieh dir auch an,
> wie ich es gemacht habe, dass die beiden Zahlenwerte bei dieser
> Sicherung und bei der Auswertung auch definitiv immer übereinstimmen

Danke für den Tipp!
Ergibt durchaus Sinn.

von Karl H. (kbuchegg)


Lesenswert?

Das hier
1
  while(1)
2
  {
3
    adcValue = ADC_Read( 2  );
4
5
    if( abs( adcReference - adcValue ) < 20 )
6
    Zaehler++;
7
    //PORTB |= 0x0F;
8
    else
9
    //PORTB &= ~0x0F;
10
    Zaehler = 0;
11
12
    adcReference = adcValue;
13
    _delay_ms( 1000 );

stimmt in deinem Programm auch noch nicht. Du willst nicht auf jeden 
Fall immer den aktuellen Spannungswert als neue Referenz nehmen. Denn 
wenn du nur langsam genug am Poti drehst, dann ändert sich der auch pro 
Durchlauf nur wenig. D.h. Obwohl sich deine Spannung laufend verändert, 
ist von einem Durchgang zum nächsten die Differenz immer kleiner als die 
vorgegebenen 20 und du zählst munter den Zaehler hoch, obwohl du bei 
entsprechend langsamen Drehen das Poti von einem Ende zum anderen 
durchdrehen kannst!


Und rück deinen Code ein!

von Ja (Gast)


Lesenswert?

Karl Heinz schrieb:
> Wenn du aber sagst, nope geht so nicht, dann bauen wir den Timer rein

Timer wäre schon nötig, da das ganze auch erst nach einer bestimmten 
Zeit losgehen soll..

OK.

Das ist jetzt leicht, denn der Timer hat ja (denk ich) von Anfang an 
funktioniert.
Den Teil übernehmen wir einfach mal im wesentlichen.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

OK. Hier mein Vorschlag.

Die Sache mit den Millisekunden ist gleich geblieben.
Die Auswertung (Messung plus vergleich) findet als EInheit nur dann 
statt,
wenn der Timer sein 'GO' gegeben hat.
Die Technik, wie man sowas macht, kennst du ja schon: eine (volatile) 
uint8_t Variable, die von der Timer-ISR auf 1 gesetzt wird und in der 
Hauptschleife wird geprüft, ob sie 1 ist und wenn ja, wird der 
Mechanismus angeworfen.
1
#define F_CPU 1000000UL // 1 MHZ
2
3
#include <avr/io.h>
4
#include <util/delay.h>
5
#include <avr/interrupt.h>
6
7
#define REQUIRED  9                     // wieviele 'gleiche' Messwerte sind erforderlich?
8
#define EPSILON   20                    // in welchem +-Bereich sind die Messwerte noch als gleich anzusehen (ADC-EInheiten) 
9
10
volatile uint16_t milliSekunden;
11
volatile uint8_t  doMeasure;
12
13
/* ADC initialisieren */
14
void ADC_Init(void)
15
{
16
  // die Versorgungsspannung AVcc als Refernz wählen:
17
  ADMUX = 0;
18
19
  ADCSRA = (1<<ADPS1) | (1<<ADPS0);     // Frequenzvorteiler
20
  ADCSRA |= (1<<ADEN);                  // ADC aktivieren
21
22
  ADCSRA |= (1<<ADSC);                  // eine ADC-Wandlung
23
  while (ADCSRA & (1<<ADSC) ) {         // auf Abschluss der Konvertierung warten
24
  }
25
  (void) ADCW;
26
}
27
28
uint16_t ADC_Read( uint8_t channel )
29
{
30
  // Kanal waehlen, ohne andere Bits zu beeinflußen
31
  ADMUX = (ADMUX & ~(0x0F)) | (channel & 0x0F);
32
33
  ADCSRA |= (1<<ADSC);            // eine Wandlung "single conversion"
34
  while (ADCSRA & (1<<ADSC) ) {   // auf Abschluss der Konvertierung warten
35
  }
36
  return ADCW;                    // ADC auslesen und zurückgeben
37
}
38
39
int main(void)
40
{
41
  int16_t adcReference;
42
  int16_t adcValue;
43
  uint8_t countEqual;
44
45
  DDRB |= (1<<DDB0) | (1<<DDB1) | (1<<DDB2) | (1<<DDB3);
46
47
  TCCR0A = (1<<WGM01);  // CTC Modus
48
  TCCR0B |= (1<<CS01);  // Prescaler 8
49
  OCR0A = ( (F_CPU / 8 ) / 1000 ) - 1;    // auf 1 Millisekunde einstellen
50
  
51
  // Compare Interrupt erlauben
52
  TIMSK |= (1<<OCIE0A);
53
  ADC_Init();
54
55
  adcReference = ADC_Read( 2  );
56
  countEqual = 0;
57
58
  sei();      // alles fertig eingestellt: und los gehts!
59
60
  while(1)
61
  {
62
    if( doMeasure )
63
    {
64
      doMeasure = 0;
65
66
      adcValue = ADC_Read( 2  );
67
68
      if( abs( adcReference - adcValue ) < EPSILON )
69
      {
70
        if( countEqual < REQUIRED )
71
          countEqual++;
72
      }
73
      else {
74
        countEqual = 0;
75
        adcReference = adcValue;
76
      }
77
78
      if( countEqual == REQUIRED )
79
        PORTB |= 0x0F;
80
      else
81
        PORTB &= ~0xF0;
82
    }
83
  }
84
}
85
86
ISR (TIMER0_COMPA_vect)   // wird im Millisekundentakt aufgerufen
87
{
88
  milliSekunden++;
89
  if( milliSekunden == 1000 )
90
  {
91
    milliSekunden = 0;
92
93
    doMeasure = 1;
94
  }
95
}

von Karl H. (kbuchegg)


Lesenswert?

Ein bischen übersichtlicher wird das Ganze, wenn wir den ganzen Teil mit 
der Messung und Auswertung aus main() rausnehmen und in eine eigene 
Funktion stecken.
1
#define F_CPU 1000000UL // 1 MHZ
2
3
#include <avr/io.h>
4
#include <util/delay.h>
5
#include <avr/interrupt.h>
6
7
#define REQUIRED  9                     // wieviele 'gleiche' Messwerte sind erforderlich?
8
#define EPSILON   20                    // in welchem +-Bereich sind die Messwerte noch als gleich anzusehen (ADC-EInheiten) 
9
10
volatile uint16_t milliSekunden;
11
volatile uint8_t  doMeasure;
12
13
/* ADC initialisieren */
14
void ADC_Init(void)
15
{
16
  // die Versorgungsspannung AVcc als Refernz wählen:
17
  ADMUX = 0;
18
19
  ADCSRA = (1<<ADPS1) | (1<<ADPS0);     // Frequenzvorteiler
20
  ADCSRA |= (1<<ADEN);                  // ADC aktivieren
21
22
  ADCSRA |= (1<<ADSC);                  // eine ADC-Wandlung
23
  while (ADCSRA & (1<<ADSC) ) {         // auf Abschluss der Konvertierung warten
24
  }
25
  (void) ADCW;
26
}
27
28
uint16_t ADC_Read( uint8_t channel )
29
{
30
  // Kanal waehlen, ohne andere Bits zu beeinflußen
31
  ADMUX = (ADMUX & ~(0x0F)) | (channel & 0x0F);
32
33
  ADCSRA |= (1<<ADSC);            // eine Wandlung "single conversion"
34
  while (ADCSRA & (1<<ADSC) ) {   // auf Abschluss der Konvertierung warten
35
  }
36
  return ADCW;                    // ADC auslesen und zurückgeben
37
}
38
39
int16_t adcReference;
40
int16_t adcValue;
41
uint8_t countEqual;
42
43
void performCheck()
44
{
45
   adcValue = ADC_Read( 2  );
46
47
  if( abs( adcReference - adcValue ) < EPSILON )
48
  {
49
    if( countEqual < REQUIRED )
50
      countEqual++;
51
  }
52
  else {
53
    countEqual = 0;
54
    adcReference = adcValue;
55
  }
56
57
  if( countEqual == REQUIRED )
58
    PORTB |= 0x0F;
59
  else
60
    PORTB &= ~0xF0;
61
}
62
63
int main(void)
64
{
65
  DDRB |= (1<<DDB0) | (1<<DDB1) | (1<<DDB2) | (1<<DDB3);
66
67
  TCCR0A = (1<<WGM01);  // CTC Modus
68
  TCCR0B |= (1<<CS01);  // Prescaler 8
69
  OCR0A = ( (F_CPU / 8 ) / 1000 ) - 1;    // auf 1 Millisekunde einstellen
70
  
71
  // Compare Interrupt erlauben
72
  TIMSK |= (1<<OCIE0A);
73
  ADC_Init();
74
75
  adcReference = ADC_Read( 2  );
76
  countEqual = 0;
77
78
  sei();      // alles fertig eingestellt: und los gehts!
79
80
  while(1)
81
  {
82
    if( doMeasure )
83
    {
84
      doMeasure = 0;
85
      performCheck();
86
    }
87
  }
88
}
89
90
ISR (TIMER0_COMPA_vect)   // wird im Millisekundentakt aufgerufen
91
{
92
  milliSekunden++;
93
  if( milliSekunden == 1000 )
94
  {
95
    milliSekunden = 0;
96
97
    doMeasure = 1;
98
  }
99
}

Dann ist der ganze Mess-Auswertevorgang eine abgeschlossene Einheit und 
hier in der Hauptschleife
1
...
2
  while(1)
3
  {
4
    if( doMeasure )
5
    {
6
      doMeasure = 0;
7
      performCheck();
8
    }
9
  }

ist es auch sehr leicht zu verstehen, wie die Logik läuft. Wenn der 
Timer sagt, dass der ADC zu überprüfen ist, dann macht das Programm 
genau das: den ADC überprüfen.

von Ja (Gast)


Lesenswert?

Okay, es funktioniert perfekt, viel viel viel Dank, echt, ich habe durch 
diesen Prozess hier gerade sehr viel gelernt, und vermutlich auch viele 
andere, die das hier noch lesen werden!
Eine Frage hätte ich noch:
Ich habe stunden, minuten, sekunden auch noch in den ISR 
(TIMER0_COMPA_vect) hinzugefügt nach bekanntem Muster und auch als 
variable deklariert, meine Frage:
wie ist es zu lösen, dass die ganze Abfragerei erst nach minute > 0 
beginnt?
1
while (minute > 0 )
 funktioniert nicht [es passiert nichts], ebenso wie
1
 if( countEqual == REQUIRED && (minute > 0 )
2
    PORTB |= 0x0F;
 [hier leuchten von Anfang an 2 LEDS, nach 1 minute leuchten dann die 
anderen]?

von Karl H. (kbuchegg)


Lesenswert?

Ja schrieb:
> Okay, es funktioniert perfekt, viel viel viel Dank, echt, ich habe durch
> diesen Prozess hier gerade sehr viel gelernt,


genau das war der Sinn der ganzen Sache.

> Eine Frage hätte ich noch:
> Ich habe stunden, minuten, sekunden auch noch in den ISR
> (TIMER0_COMPA_vect) hinzugefügt nach bekanntem Muster und auch als
> variable deklariert, meine Frage:
> wie ist es zu lösen, dass die ganze Abfragerei erst nach minute > 0
> beginnt?

Da gibts mehere möglichkeiten.
Eine wäre zb, dass in der ISR die Variable doMeasure erst dann auf 1 
gesetzt wird, wenn die Stunden bzw. Minuten richtig stehen :-)

>
>
1
> while (minute > 0 )
2
>
> funktioniert nicht [es passiert nichts],

Du musst weg von deinem Schleifendenken!

Du musst hin zu einem denken von:

Ich wach jetzt gerade auf. Ich seh mich um, wie ist meine Situation, was 
spielt sich in meiner Umgebung ab? Muss ich auf ein Ereignis reagieren? 
WEnn ja, dann mach ich das und dann leg ich mich wieder schlafen.

Auf etwas warten bedeutet nicht notwendigerweise, dass man aktiv daneben 
stehen bleibt und zusieht. Auf etwas warten kann auch bedeuten, dass man 
sich sozusagen 'den Weckere stellt' und wenn der klingelt man sich 
umsieht, ob es schon etwas zu tun gibt. Wenn ja, dann macht man das. 
Wenn nein, dann stellt man sich den Wecker für die nächste Periode und 
macht was anderes oder schläft.

Genau das ist die Art und Weise, wie man µC sinnvoll programmiert. Nicht 
in Warteschleifen denken, sondern in FOrm von Ereignissen die eintreten.
Bei dir ist das leicht, denn das einzige Ereignis, das 'eintritt', das 
ist, dass wieder eine Millisekunde um ist.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Ja schrieb:


> ebenso wie
1
 if( countEqual == REQUIRED && (minute > 0 )
2
>     PORTB |= 0x0F;
 [hier leuchten von Anfang an 2 LEDS, nach 1
> minute leuchten dann die anderen]?

Du musst hier ein bisschen vorsichtig sein!
Denn dieses if hat ein else, das immer dann zum Zug kommt, wenn die 
Bedingung IN IHRERE GESAMTHEIT nicht zutrifft. IN diesem else passieren 
aber Dinge. Und die passieren dann auch dann, wenn die minute eben noch 
nicht größer als 0 ist.

Wenn du also zu einer Bedingung weitere Ausdrücke hinzufügst (die in 
deinem Beispiel mit einem && verknüpft werden), dann musst du dich auch 
immer fragen: was bedeutet das jetzt wenn genau dieser Teil der Bedingun 
eben NICHT zutrifft. Was geht dann in meinem else ab?
1
   if( minuten > 0 )
2
   {
3
     if( countEqual == REQUIRED )
4
       ....  A
5
     else
6
       ....  B
7
   }
ist eben nicht dasselbe wie
1
   if( countEqual == REQUIRED && minuten > 0 )
2
     .... A
3
   else
4
     .... B

Für den Codeteil A ändert sich nichts. Der wird auch dann unter den 
gleichen Bedingungen ausgeführt. Aber für den Codeteil B ist alles ganz 
anders!

: Bearbeitet durch User
von Ja (Gast)


Lesenswert?

Karl Heinz schrieb:
> ine wäre zb, dass in der ISR die Variable doMeasure erst dann auf 1
> gesetzt wird, wenn die Stunden bzw. Minuten richtig stehen

Aber damit würde die Messung ja nur noch minütlich stattfinden, oder 
nicht?

von Themaverwirrter (Gast)


Lesenswert?

>> ADC funktioniert nicht

So wie es der Thread-Titel ausdrückt wäre der ausgerufene Sachverhalt
ja gleichzusetzen einem Defekt im/am ADC oder ein grösserer Defekt des
gesamten Prozessors.

.... also ein bischen voreilig ..... bzw Themaverfehlung ....

Oder frage ich mal in die Runde:

Ist es einfach "wurscht" was man im Titel schreibt?

von Karl H. (kbuchegg)


Lesenswert?

Ja schrieb:
> Karl Heinz schrieb:
>> ine wäre zb, dass in der ISR die Variable doMeasure erst dann auf 1
>> gesetzt wird, wenn die Stunden bzw. Minuten richtig stehen
>
> Aber damit würde die Messung ja nur noch minütlich stattfinden, oder
> nicht?


Wieso?
1
ISR (TIMER0_COMPA_vect)   // wird im Millisekundentakt aufgerufen
2
{
3
  milliSekunden++;
4
  if( milliSekunden == 1000 )
5
  {
6
    milliSekunden = 0;
7
    Sekunden++;
8
9
    if( Sekunden == 60 )
10
    {
11
      Sekunden = 0;
12
      Minuten++;
13
    }
14
15
    // wir sind auf jeden Fall schon mal auf einer ganzen Sekunde
16
    // sind seit dem Start mehr als 5 Minuten vergangen, dann darf
17
    // daher auch der Auftrag zum Messen rausgegeben werden
18
    if( Minuten > 5 )
19
      doMeasure = 1;
20
  }
21
}

von Karl H. (kbuchegg)


Lesenswert?

Und wegen der Stunden.

Es ist einfacher, wenn du die Zeit komplett in Form von Minuten 
ausdrückst. 2 Stunden und 23 Minuten sind 143 Minuten.

Wenn du da jetzt auch noch Stunden als eigene EInheit mit ins Boot 
holst, dann kann man das natürlich machen. Aber der Aufwand!
1
ISR (TIMER0_COMPA_vect)   // wird im Millisekundentakt aufgerufen
2
{
3
  milliSekunden++;
4
  if( milliSekunden == 1000 )
5
  {
6
    milliSekunden = 0;
7
    Sekunden++;
8
9
    if( Sekunden == 60 )
10
    {
11
      Sekunden = 0;
12
      Minuten++;
13
14
      if( Minuten == 60 )
15
      {
16
        Minuten = 0;
17
        Stunden++;
18
      }
19
    }
20
21
    // wir sind auf jeden Fall schon mal auf einer ganzen Sekunde
22
    // sind seit dem Start mehr als 2 Stunden und 23 Minuten vergangen,
23
    // dann darf
24
    // daher auch der Auftrag zum Messen rausgegeben werden
25
    if( ( Stunden > 2 ) ||
26
        ( Stunden == 2 && Minuten > 5 ) )
27
      doMeasure = 1;
28
  }
29
}

und seien wir uns mal ehrlich. Mit einem uint16_t für die Minuten kann 
man bis zu 65535 abzählen. Das sind 1092 Stunden. Oder 45.5 Tage.
Das sollte wohl reichen. :-)

: Bearbeitet durch User
von Ja (Gast)


Lesenswert?

Alles klar, ich bedanke mich noch mal recht herzlich, du könntest dich 
auf jedem Fall dem Lehren widmen, so gut wie du das machst!
Dankeschön.

von Ja (Gast)


Lesenswert?

Es tut mir fast ein wenig Leid, euch noch einmal belästigen zu müssen 
aber:
ich habe versucht, 2 Modi einzufügen.
Der erste soll aktiviert werden, wenn ein Schalter auf Ground geht.
Der zweite, wenn der Schalter gegen nichts geht und der interne Pull-up 
resistor greift und so an VCC geht.
der erste Modus: ganz normale Funktion, wie oben auch beschrieben
der zweite Modus: nach Wiederholungen des Watchdogs soll der gleiche 
Code wie bei 1 ausgeführt werden; Watchdog wegen des Stromsparens.
Problem:
Bei dem ersten Modus schalten sich die Pins 10 Sekunden nach Aktivierung 
wieder ab.
Der zweite scheint gar nicht erst zu funktionieren.
Könnte einer mal den Code überfliegen und Tipps geben?
Danke.
1
#define F_CPU 1000000UL // 1 MHZ
2
3
#include <avr/io.h>
4
#include <util/delay.h>
5
#include <avr/interrupt.h>
6
#include <avr/wdt.h>        // Supplied Watch Dog Timer Macros
7
#include <avr/sleep.h>      // Supplied AVR Sleep Macros
8
9
#define REQUIRED  5                     // wieviele 'gleiche' Messwerte sind erforderlich?
10
#define EPSILON   20                    // in welchem +-Bereich sind die Messwerte noch als gleich anzusehen (ADC-EInheiten)
11
12
volatile uint16_t milliSekunden;
13
volatile uint16_t watchdogcounter;
14
volatile uint8_t  doMeasure;
15
volatile unsigned int sekunde;
16
volatile unsigned int minute;
17
volatile unsigned int stunde;
18
19
/* ADC initialisieren */
20
void ADC_Init(void)
21
{
22
  // die Versorgungsspannung AVcc als Refernz wählen:
23
  ADMUX = 0;
24
25
  ADCSRA = (1<<ADPS1) | (1<<ADPS0);     // Frequenzvorteiler
26
  ADCSRA |= (1<<ADEN);                  // ADC aktivieren
27
28
  ADCSRA |= (1<<ADSC);                  // eine ADC-Wandlung
29
  while (ADCSRA & (1<<ADSC) ) {         // auf Abschluss der Konvertierung warten
30
}
31
(void) ADCW;
32
}
33
34
uint16_t ADC_Read( uint8_t channel )
35
{
36
  // Kanal waehlen, ohne andere Bits zu beeinflußen
37
  ADMUX = (ADMUX & ~(0x0F)) | (channel & 0x0F);
38
39
  ADCSRA |= (1<<ADSC);            // eine Wandlung "single conversion"
40
  while (ADCSRA & (1<<ADSC) ) {   // auf Abschluss der Konvertierung warten
41
  }
42
  return ADCW;                    // ADC auslesen und zurückgeben
43
}
44
45
int16_t adcReference;
46
int16_t adcValue;
47
uint8_t countEqual;
48
49
void performCheck()// modus: 1
50
{
51
  //PORTB |= (1<<PB3);// sensor: an
52
  //ADCSRA |= (1<<ADEN);                  // ADC aktivieren
53
  if (doMeasure == 1)
54
  { doMeasure = 0;
55
  
56
   adcValue = ADC_Read( 2  );
57
58
  if( abs( adcReference - adcValue ) < EPSILON )
59
  {
60
    if( countEqual < REQUIRED )
61
      countEqual++;
62
  }
63
  else {
64
    countEqual = 0;
65
    adcReference = adcValue;
66
  }
67
if (minute >= 0)
68
{
69
70
  if( countEqual == REQUIRED )
71
    PORTB |= (1<<PB1) | (1<<PB2);//0x0F;
72
  else
73
    PORTB &= ~((1<<PB1) | (1<<PB2));//( countEqual & 0x0F );//&= ~0x0F; //( countEqual & 0x0F )=binäre anzeige des zählers;
74
}  
75
}
76
}
77
ISR(WDT_vect) {
78
  watchdogcounter++;
79
}
80
81
void performCheck2()
82
{
83
  if (watchdogcounter < 2) //2250 = 24*60*60/8 (5h)
84
  {
85
    //sleep_enable();//8 sec schlaf
86
    sleep_mode();
87
  }
88
  else{
89
    //PORTB |= (1<<PB3);
90
    if (doMeasure == 1)
91
    {
92
    doMeasure = 0;
93
  adcValue = ADC_Read( 2  );
94
95
  if( abs( adcReference - adcValue ) < EPSILON )
96
  {
97
    if( countEqual < REQUIRED )
98
    countEqual++;
99
  }
100
  else {
101
    countEqual = 0;
102
    adcReference = adcValue;
103
  }
104
  if (minute >= 0)
105
  {
106
107
    if( countEqual == REQUIRED )
108
    PORTB |= (1<<PB1) | (1<<PB2); //0x0F;
109
    else
110
    PORTB &= ~((1<<PB1) | (1<<PB2));//( countEqual & 0x0F );//&= ~0x0F; //( countEqual & 0x0F )=binäre anzeige des zählers;
111
  }
112
  }  
113
    }  
114
}
115
116
int main(void)
117
{
118
  DDRB |= (0<<DDB0) | (1<<DDB1) | (1<<DDB2) | (1<<DDB3);// | (0<<DDB4); //DDB0 = input
119
  PORTB |= (1<<PB0); // pull up resistor
120
  PORTB |= (1<<PB3); // sensor: vcc=1
121
122
  TCCR0A = (1<<WGM01);  // CTC Modus
123
  TCCR0B |= (1<<CS01);  // Prescaler 8
124
  OCR0A = ( (F_CPU / 8 ) / 1000 ) - 1;    // auf 1 Millisekunde einstellen
125
  
126
  // Compare Interrupt erlauben
127
  TIMSK |= (1<<OCIE0A);
128
  ADC_Init();
129
130
  adcReference = ADC_Read( 2  );
131
  countEqual = 0;
132
133
  sei();      // alles fertig eingestellt: und los gehts!
134
  WDTCR |= (1<<WDP3) | (1<<WDP0) | (1<<WDE) | (1<<WDIE); //watchdog interrupt + 8sec
135
136
  while(1)
137
  {
138
    if(  (PINB & (1<<PINB0)) == 0 )//pressed (connection to ground)//doMeasure )
139
    {
140
      //doMeasure = 0;
141
      performCheck();//1
142
    }
143
   if(  (PINB & (1<<PINB0)) == 1 )//not pressed (connection to vcc) //doMeasure )
144
   {
145
     //doMeasure = 0;
146
     performCheck2();
147
   }
148
  
149
  }
150
}
151
152
ISR (TIMER0_COMPA_vect)   // wird im Millisekundentakt aufgerufen
153
{
154
  milliSekunden++;
155
  if( milliSekunden == 1000 )
156
  {  sekunde++;
157
    milliSekunden = 0;
158
159
    doMeasure = 1;
160
  }
161
   if(sekunde == 60)
162
   {
163
     minute++;
164
     sekunde = 0;
165
   }
166
   if(minute == 60)
167
   {
168
     stunde++;
169
     minute = 0;
170
   }
171
   if(stunde == 24)
172
   {
173
     stunde = 0;
174
   }
175
}

von Ja (Gast)


Lesenswert?

Ich habe die while-Schleife grundlegend verändert, den Watchdog und 
seinen Interrupt definiert und einen neuen Modus hinzugefügt. In meinen 
Augen ist da alles korrekt. Compilen geht auch. Leider will der 
übermenschliche Code-Gott nicht so, wie ich das will.

von Ja (Gast)


Lesenswert?

okay habs geschafft.

von Paul B. (paul_baumann)


Lesenswert?

Ja schrieb:
> okay habs geschafft.

Glückwunsch dazu.

Schick doch den nun funktionierenden Quelltext hinterher, damit der 
Nächste, dem das Gleiche passiert, nicht in der Sackgasse endet.

MfG Paul

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.