Forum: Compiler & IDEs Probleme Tachosignal Auswertung


von Balou B. (baloubaer)


Lesenswert?

Hallo zusammen,

ich benötige Hilfe bei der Auswertung eines Tachosignals.

Das Tachosignal wird von einem Anemometer erzeugt, und soll ausgewertet 
werden.

Unten habe ich das Programm mal eingefügt. Ich verwende einen ATMega32, 
AVR Studio 4 und den GCC.

Ich möchte das ab einer Windgeschwindkeit von ca. 50km/h der Ausgang 
PORTD1 aus "high" gesetzt wird und unter 50km/h auf "Low" gesetzt wird.

Das Programm scheint auch zu Funktionieren, jedoch ist das Zählen der 
Tachoimpulse leider sehr ungenau. Ich vermute mal, dass der Kontakt in 
dem Anemometer sehr stark prellt. Nur so kann ich mir erklären warum bei 
nur 3 Umdrehungen des Anemomenters der PORTD1 auf "high" geht.

Ich habe zum testen das Tachosignal im Programm gleich der 
Windgeschwindigkeit gesetzt, da ich das Anemomenter selber drehe. So 
sollte doch erst ab 10 Drehungen der Ausgang auf "high" gehen.

Versuche ich den Kontakt durch Wartezeiten zu entprellen, habe ich das 
Problem, dass bei höheren Drehzahlen diese nicht mehr richtig erfasst 
werden.

Wie kann ich vorgehen? Was kann ich tun um dieses Problem in den Griff 
zubekommen.

Ist das Programm ggf. noch zu optimieren?

Ich bedanke mich für euere Hilfe und verbleibe

mit freundlichen Grüßen

Balou Baer


1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
4
#define F_CPU 16000000UL
5
#include <C:\AVR\warten.h>
6
//#include <avr/bit.h>  // Wird noch nicht benötigt.
7
#include <C:\AVR\lcd-pollin-jumper.h> // Am Pollin Board feur
8
// das LCD nur die Jumper DB4-DB7, R/W,E,RS stecken
9
10
volatile uint8_t a=0;
11
12
13
ISR(TIMER1_COMPA_vect)
14
{
15
  a=1;
16
}
17
18
int main (void)
19
20
{
21
  uint8_t b=15;
22
  uint16_t Tachosignal=0, Windgeschwindigkeit=0;
23
24
  DDRA=0b11110001;   //Datenleitung Display die ersten 4 (0b1111XXXX)
25
  DDRB=0b00000111;   //Steuerleitung Display die letzten 3 (0bXXXXX111)
26
  DDRC=0b00000000;  //Konfigurieren der Ein und Ausgänge an Pord C; 0=Eingang, 1=Ausgang
27
  DDRD=0b00001110;  //Konfiguration der Ein und Ausgänge an Pord D; 0=Eingang, 1=Ausgang
28
  
29
  TCCR1B=0b00001101;  //Timer1 Konfigurieren
30
  OCR1A=15624;    //Vergleichsregister setzten
31
  TIMSK=0b00010000;  //Timer1 CompA-Interrupt AKTIVIEREN
32
  sei();        //Alle freigegebenen Interrupts werden EINGESCHALTET!!!!!!!!!!
33
34
  while(1)
35
  {
36
    PORTA^=(1<<PA0);  //Kontrolle ob Schleife verlassen wird LED7 = toggeln (15 Sekunden Takt)
37
    b=15;        // Variable B wird nach Durchgang 15 gesetzt
38
    Tachosignal=0;    // Variable Tachosignal wird nach Durchgang wieder auf 0 gesetz
39
    
40
//Ermittlung der Windgeschwindigkeit:
41
42
    //Ermitlung der Tachosignal in einer Zeit von 15 Sekunden:
43
44
    while(b!=0)  
45
    {
46
      if (!((PIND &(1<<PD0)) == 0))  //Abfrage des Eingangs
47
      {
48
        warten10ms(); //Versuch Taster zu entprellen
49
        //warten10ms();
50
        //warten10ms();
51
        //warten10ms();
52
        Tachosignal=Tachosignal+1;
53
        PORTD=(1<<PD2); //Kontrolle ob Tachosignal erkannt LED2 = an
54
      };
55
      if (a==1) //Timer abgelaufen
56
      {
57
        PORTD^=(1<<PD3); //Kontrolle ob Timer läuft LED3 = toggeln
58
        a=0;
59
        b=b-1;
60
        PORTD&=~(1<<PD2); //Kontrolle Tachsosignal LED2 = aus
61
      };
62
    
63
    }
64
65
//Berechnung der Windgeschwindigkeit:
66
67
  //  Tachosignal=Tachosignal*4; //Tachosignale hochgerechnet pro Minute
68
  //  Tachosignal=Tachosignal*60; //Tachosignal hochgerechnet pro Stunde
69
    Windgeschwindigkeit=Tachosignal; //Tachosignale mit Umfang des Windmessers zu Windgeschwindigkeit
70
    
71
    if (Windgeschwindigkeit>=10) 
72
    {
73
      PORTD=(1<<PD1); //PORTD1 wird auf 1 gesetzt  LED1 = an      
74
    }
75
    else 
76
    {
77
      PORTD&=~(1<<PD1); //PORTD1 wird auf 0 gesetzt LED1 = aus
78
    }
79
    
80
  }
81
  return 0;
82
}

von Karl H. (kbuchegg)


Lesenswert?

Du musst da schon
1
      if (!((PIND &(1<<PD0)) == 0))  //Abfrage des Eingangs
2
      {
3
        warten10ms(); //Versuch Taster zu entprellen
4
        //warten10ms();
5
        //warten10ms();
6
        //warten10ms();
7
        Tachosignal=Tachosignal+1;
8
        PORTD=(1<<PD2); //Kontrolle ob Tachosignal erkannt LED2 = an
9
      };

grundsätzlich erst mal eine Flankenerkennung rein machen!
Dich interessiert nur, wie oft der Pin von 0 auf 1 geht und nicht wie 
oft ihn das Programm als auf 1 stehend vorgefunden hat. Denn der Pin 
kann auch eine halbe Stunde lang auf 1 stehen. Deswegen willst du diese 
Zeiten aber nicht zählen, was dein Programm aber munter tut.

Flanken, und damit Pulse erkennt man durch einen vorher-nachher 
Vergleich. Wenn du kurz hintereinander auf das zu überwachende Signal 
schaust und vorher war es auf 0 und jetzt ist es auf 1, dann ist da 
offenbar ein Puls reingekommen

1
uint8_t vorher;
2
uint8_t jetzt;
3
4
...
5
6
int main()
7
{
8
9
....
10
11
  vorher = ( PIND & (1<<PD0) );
12
13
  while( 1 ) {
14
15
    jetzt = ( PIND & (1<<PDO) );
16
17
    // Eine Flanke kann nur vorliegen, wenn sich 'jetzt' von 'vorher'
18
    // unterscheidet
19
    if( jetzt != vorher ) {
20
21
      // wir wollen aber nicht jede Flanke haben, sondern nur
22
      // die, an denen das Signal von 0 auf 1 wechselt
23
      // d.h. 'jetzt' darf nicht 0 sein, sondern muss ein 1 Bit
24
      // aufweisen.
25
26
      if( jetzt ) {
27
        zaehler++;
28
 
29
        ....
30
      }
31
    }
32
  }

und schreib nicht so viel Code auf einmal. DIe ganze Auswertung und 
Zeitsteuerung ist jetzt noch uninteressant. Lass von mir aus eine LED 
toggeln, wenn eine Flanke detektiert wird. Das ist einfach und schnell 
implementiert. Wenn du das Rad langsam mit der Hand drehst, muss die LED 
dann dauernd den Zustand wechseln. Dann sieht man schon, ob die Kontakte 
prellen oder nicht.

: Bearbeitet durch User
von Balou B. (baloubaer)


Lesenswert?

Danke dir!

Der Gedanke ist mir noch nicht gekommen mit den Flanken. Habe leider nur 
ein begrenztes Wissen von der C und uC Programmierung. Ich versuche mich 
da nun etwas rein zu arbeiten. Grucken ob es mir gelingt ;)

von Karl H. (kbuchegg)


Lesenswert?

Karl Heinz schrieb:

Fehler!

>
1
>   while( 1 ) {
2
> 
3
>     jetzt = ( PIND & (1<<PDO) );
4
> 
5
>     // Eine Flanke kann nur vorliegen, wenn sich 'jetzt' von 'vorher'
6
>     // unterscheidet
7
>     if( jetzt != vorher ) {
8
>

da muss natürlich noch ein
1
        vorher = jetzt;
rein

: Bearbeitet durch User
von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Sind das „harte“ Flanken (von Elektronik erzeugt) oder „klapprige“
(von einem mechanischen Kontakt)?  Für den erstgenannten Fall würde
sich wohl auf jeden Fall eine Zeitmessung per input capture direkt
durch die Timer-Hardware eignen.  Im zweiten Fall kann man das auch
damit machen, muss aber zum Entprellen noch etwas mehr Aufwand
treiben.

von Balou B. (baloubaer)


Lesenswert?

jetzt bin ich komplett verwirrt ??????????

Sorry, aber ich weiß nicht wie das Anemometer das Signal erzeugt, ob es 
mit einem Reedkontakt oder mit einem mechanischen Schalter versehen 
wurde.

Aus Mechanischersicht, um so wenig Reibung wie möglich zu verursachen, 
würde ich einen Reedkontakt nehmen. Das Anemometer ist leider nicht 
aufschraubbar, daher keine Aussage möglich.

Vielleicht noch ein Wort zur Beschaltung.

VCC = 5V

Eingang Anemometer an +5V (VCC)
Ausgang Anemometer an PinD0 und über Pulldown Widerstand (15kOhm) an 
Ground). Bei jeder Drehung kommt an PIND0 +5V kurz an.

Messgerät:
Würth Basic 71553400

Wäre es unverschämt, wenn ich fragen würde ob ihr mir für die Codezeilen 
etwas stärker unter die Arme greifen könntet. Ich bin, sagen wir es mal 
so, blutiger Anfänger in C und weiß nicht weiter.

Ich bedanke mich für euere Mühen und verbleibe

mit freundlichen Grüßen
Balou Baer


Der Code ist jetzt noch nicht auf Funktion getestet:
1
  while(b!=0)  
2
    {
3
      VorherWind=(!(PIND &(1<<PD0)));
4
5
      if (((!(PIND &(1<<PD0))) != VorherWind) && (VorherWind = (!(PIND &(1<<PD0)))))
6
      {
7
        if (!((PIND &(1<<PD0)) == 0))  //Abfrage des Eingangs
8
        {
9
        
10
      
11
          Tachosignal=Tachosignal+1;
12
          PORTD=(1<<PD2); //Kontrolle ob Tachosignal erkannt LED2 = an
13
        };
14
        if (a==1) //Timer abgelaufen
15
        {
16
          PORTD^=(1<<PD3); //Kontrolle ob Timer läuft LED3 = toggeln
17
          a=0;
18
          b=b-1;
19
          PORTD&=~(1<<PD2); //Kontrolle Tachsosignal LED2 = aus
20
        };
21
      }
22
    }

von npn (Gast)


Lesenswert?

Balou Baer schrieb:
> Das Anemometer ist leider nicht
> aufschraubbar, daher keine Aussage möglich.

Aber das Signal kann man sich mal mit einem Oszi anschauen, ob es sauber 
ist oder Prellungen macht. Wenn du keinen hast, kannst du vielleicht mal 
jemanden fragen, der einen besitzt.
Abgesehen davon ist es trotzdem richtig, mit Flankenerkennung und 
Software-Entprellung zu arbeiten. Nur um die Sache grob einschätzen zu 
können, wäre es schon vorteilhaft, wenn man das Aussehen des 
Eingangssignals kennt.

von Karl H. (kbuchegg)


Lesenswert?

Balou Baer schrieb:

> Wäre es unverschämt, wenn ich fragen würde ob ihr mir für die Codezeilen
> etwas stärker unter die Arme greifen könntet.

und ich schreib ihm auch noch die wesentlichen Codezeilen hin (auch wenn 
ich erst mal einen Fehler gemacht hab)

>  Ich bin, sagen wir es mal so, blutiger Anfänger in C und weiß nicht weiter.

Lass ich nicht gelten.
Das man nicht von alleine auf die Idee kommt, kann ich akzeptieren. Aber 
das man 5 Zeilen Code studiert und sich überlegt, wie und warum die 
arbeiten, das bleibt dir nicht erspart. Da müssen alle durch. Musste ich 
auch mal.
Programmieren ist nun mal ca 15% Beherrschung der Programmiersprache und 
85% Algorithmen (sprich: Verfahren).

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

wenn du sowas
1
      if (((!(PIND &(1<<PD0))) != VorherWind) && (VorherWind = (!(PIND &(1<<PD0)))))

nicht mehr durchblickst (um ehrlich zu sein bräuchte ich da auch ein 
wenig Zeit, bis ich die exakte Logik dahinter raus hab), dann mach dir 
halt Hilfsvariablen. So wie ich das gezeigt habe. 2 Variablen, eine 
'vorher', eine 'jetzt' und verfeinfache den Code durch die 
Hilfsvariablen. Da ist doch nichts dabei und von Atmel kriegst du kein 
Geld zurück, wenn du das SRAM nicht 'abnutzt'.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Und ich sag auch noch: fang erst mal einfacher an
Und mach auch noch einen Vorschlag dazu.
Ich mach den Vorschlag doch nicht aus Lust und Laune.

Was ist so schwer an.
1
#include <avr/io.h>
2
3
uint8_t vorher;
4
uitn8_t jetzt;
5
6
int main()
7
{
8
  DDRD |= ( 1<<PD3);
9
10
  // PORTD |= ( 1<<PD0 );  // ev. Pullup einschalten?
11
12
  vorher = ( PIND & (1<<PD0) );
13
14
  while( 1 ) {
15
16
    jetzt = ( PIND & (1<<PD0) );
17
18
    if( jetzt != vorher ) {
19
      vorher = jetzt;
20
21
      if( jetzt ) {
22
        PORTD ^= (1<<PD3);
23
      }
24
    }
25
  }
26
}

und dann drehst du mal am Anemometer und siehst nach, ob und wann die 
LED umschaltet und ob es da zu Fehlern kommt (wegen Prellen) und ob du 
am Eingang einen Pullup Widerstand brauchst oder nicht.

Wer gleich zu Anfang zu viel will, der fällt auf die Nase. Das Um und 
Auf bei der SW Entwicklung ist es, in Schritten vorzugehen. Erst mal mit 
einer einfach Variante anfangen! Komplizierter wird es dann ohnehin von 
alleine. Aber erst mal will man so schnell wie möglich was einfaches 
haben, das man testen kann und das als Ausgangspunkt für den nächsten 
Schritt dient.

: Bearbeitet durch User
von Balou Baer (Gast)


Lesenswert?

Karl Heinz,

Ich wollte doch nicht kränken oder beleidigen. Ich bin nur nicht schlau 
geworden aus deinen Teilen und habe mich dann, mit Hilfe der 
Suchfunktion durchs Forum gerobbt. Suchbegriff Flankenerkennung.

Ich verstehe bei deiner ersten Antwort nicht, warum die Variable 
“vorher“ vor der Schleife steht, damit ist die Variable fest und ändert 
sich für meine Begriffe nicht mehr. Daher war für mich die Aussage 
jetzt!= vorher immer wahr, wenn ich in der Schleife mehrmals den Pin 
Abfrage. Egal ob er zwischendurch gesetzt oder nicht gesetzt war.


@npn:

Sorry besitze ich nicht und kenne leider auch keinen der eins haben 
könnte. Das Anemometer ist von der Funkwetterstation PCE-FWS 20.

Gruß Balou Baer

von Karl H. (kbuchegg)


Lesenswert?

Balou Baer schrieb:

> Ich verstehe bei deiner ersten Antwort nicht, warum die Variable
> “vorher“ vor der Schleife steht,

damit beim ersten Vergleich ein gültiger Zustand vorliegt. Sonst kann es 
dir passieren, dass du vorher mit 'Pin liegt auf 0' annimmst und bei der 
ersten Abfrage sofort auf einen Puls schliesst, weil der Pin in 
Wirklichkeit die ganze Zeit auf 1 liegt.


> damit ist die Variable fest und ändert
> sich für meine Begriffe nicht mehr.

Ich hab eine Ergänzung angebracht! Nicht gesehen?

Natürlich ändert sich 'vorher'. WEnn ein UNterschied festgestellt wird, 
dann wird vorher auf den neuen WErt gesetzt. Man könnte das auch am ENde 
der Schleife einfach immer machen, aber im Grunde ist es nur notwendig, 
wenn man das erste mal einen Unterschied feststellt.

> Daher war für mich die Aussage
> jetzt!= vorher immer wahr, wenn ich in der Schleife mehrmals den Pin
> Abfrage.

Nein, denn durch
1
       if( jetzt != vorher ) {
2
         vorher = jetzt;
3
4
         ....

wird ja der neue Zustand gemerkt, damit im nächsten Schleifendurchlauf 
dann festgestellt wird, das der Pin zb immer noch auf 1 liegt und daher 
keine Flanke vorliegen kann. Erst wenn der Pin wieder auf 0 zurück geht, 
gibt es wieder einen Unterschied. Den merkt man sich und im 
darauffolgenden Durchlauf sind dann beide wieder gleich, nämlich 0, 
woran man erkennen kann das jetzt wieder keine Flanke da gewesen sein 
kann.
Und so geht das immer dahin.

von Balou B. (baloubaer)


Lesenswert?

Hallo Karl Heinz,

jetzt habe ich das Verstanden mit dem "Vorher" vor der Whileschleife.

Habe gerade den Code getestet und toggeln tut er die LED ohne Probleme.
Jetzt habe ich noch einen Zähler ergänzt, diesen in eine IF Abfrage 
gepackt und!!!!!! WAS!!!!! SOLL!!!!! ICH!!!!!! SAGEN!!!!

;)  ;D HURA!!! egal ab ich schnell dreh oder langsam drehe!!!!!!!!! Er 
zählt genau!!!!

Wenn das jetzt noch in meinem Programm so läuft!

KLASSE!!!!! und DANNNNKKKEEEEEEEE!!!!!!

Balou Baer

von Balou B. (baloubaer)


Lesenswert?

Hallo zu sammen, ich hoffe ich darf noch einmal stören.

anbei nun mein fertiger Code. Er funktioniert und die Kontroll LEDs sind 
auch raus. Also so zu sagen von unnötigem Ballast befreit. Aber 
vielleicht habt ihr ja noch eine Idee der Optimierung für diesen Code um 
ihn kompakter oder evtl. "schneller" zumachen.
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
4
#define F_CPU 16000000UL
5
6
volatile uint8_t a=0;
7
uint8_t vorherwind,jetztwind,b=15;
8
double Windgeschwindigkeit=0, Tachosignal=0;
9
10
11
ISR(TIMER1_COMPA_vect)
12
{
13
  a=1;  //Wenn Timer abgelaufen wird a=1 gesetzt.
14
}
15
16
int main (void)
17
18
{
19
  DDRD=0b00000010;  //Konfiguration der Ein und Ausgänge an Pord D; 0=Eingang, 1=Ausgang
20
  
21
  TCCR1B=0b00001101;  //Timer1 Konfigurieren
22
  OCR1A=15624;    //Vergleichsregister setzten
23
  TIMSK=0b00010000;  //Timer1 CompA-Interrupt AKTIVIEREN
24
  sei();        //Alle freigegebenen Interrupts werden EINGESCHALTET!!!!!!!!!!
25
26
  while(1)
27
  {
28
    b=15;        // Variable B wird wieder auf 15 gesetzt.
29
    Tachosignal=0;    // Variable Tachosignal wird wieder auf 0 gesetz
30
    
31
//Ermittlung der Windgeschwindigkeit:
32
    
33
    //Ermitlung der Tachosignal in einer Zeit von 15 Sekunden:
34
35
    while(b!=0)
36
    {
37
        jetztwind=(PIND &(1<<PD0));  //Abfrage ob Taster gedrückt und Abfrage speichern
38
39
         if(jetztwind!=vorherwind)   //Steigende Flanke des Eingangssignal an ProtD0 erkennen
40
      {
41
            vorherwind=jetztwind;
42
43
        if(jetztwind) 
44
          {
45
              Tachosignal++;  //Variable wird um 1 erhöht, wenn steigende Flanke am Eingang PD0 erkannt
46
            }
47
      };
48
        
49
        if (a==1)   //Wenn ISR Merker 1 ist (Timer abgelaufen)
50
      {
51
        a=0;  //Setzt ISR Merker zurück  
52
        b--;  //Anzahl der durchzulaufenden Druchgänge wird im 1 verringert
53
      };
54
    }
55
56
//Berechnung der Windgeschwindigkeit:
57
58
    Tachosignal=Tachosignal*4; //Tachosignale hochgerechnet pro Minute
59
    Tachosignal=Tachosignal*60; //Tachosignal hochgerechnet pro Stunde
60
    Windgeschwindigkeit=Tachosignal*0.00094; //Tachosignale mit Umfang des Windmessers zu Windgeschwindigkeit
61
    
62
    if (Windgeschwindigkeit>=50)  //Vergleichszahl ist Windgeschwindigkeit in km/h 
63
    {
64
      PORTD=(1<<PD1); //PORTD1 wird auf 1 gesetzt  LED1 = an      
65
    }
66
    else 
67
    {
68
      PORTD&=~(1<<PD1); //PORTD1 wird auf 0 gesetzt LED1 = aus
69
    }
70
    
71
  }
72
  return 0;
73
}

von Karl H. (kbuchegg)


Lesenswert?

Balou Baer schrieb:

> vielleicht habt ihr ja noch eine Idee der Optimierung für diesen Code um
> ihn kompakter oder evtl. "schneller" zumachen.


Da deine Windgeschwindigkeit nur dazu benutzt wird, um eine LED zu 
schalten und der Zahlenwert an sich ja nirgends auf einem Display 
auftaucht, verschaffst du hier
1
    Tachosignal=Tachosignal*4; //Tachosignale hochgerechnet pro Minute
2
    Tachosignal=Tachosignal*60; //Tachosignal hochgerechnet pro Stunde
3
    Windgeschwindigkeit=Tachosignal*0.00094; //Tachosignale mit Umfang des Windmessers zu Windgeschwindigkeit
4
    
5
    if (Windgeschwindigkeit>=50)  //Vergleichszahl ist Windgeschwindigkeit in km/h

deinem µC eine schöne Fleissaufgabe.
Denn anstelle den µC jedesmal die Anzahl der Pulse in eine 
Geschwindigkeit umrechnen zu lassen, könntest du dir auch einmalig mit 
dem Taschenrechner ausrechnen, wieviele Pulse in deiner Messzeit bei 
50km/h erwartet werden. Wenn du das ein wenig geschickt machst, dann 
kann das sogar der Compiler für dich ausrechnen.

Nebeneffekt: die ganze Rechnerei und damit vor allem die ganze Floating 
Point Rechnerei fällt für den µC zur Laufzeit weg.

Aber im Grunde wird das ziemlich egal sein. Denn dein µC hat sowieso 
massig Zeit.

Anderes Thema. Anstelle der b-Schleife in der Hauptschleife, könntest du 
auch a einfach weiter zählen lassen. als nur bis 1.

So ungefähr
1
#define LIMIT_SPEED 50  // km/h
2
#define PULSE_LIMIT (LIMIT_SPEED * 1063 ) / 60 / 4
3
4
uint16_t pulse;
5
volatile uint8_t time;
6
7
ISR( ... )
8
{
9
  if( time > 0 )
10
    time--;
11
}
12
13
14
int main()
15
{
16
   ....
17
18
  time = 15;
19
  vorher = ....
20
21
  while( 1 ) {
22
23
    jetzt = ....
24
    if( jetzt != vorher ) {
25
      vorher = jetzt;
26
      if( jetzt )
27
        pulse++;
28
    }
29
30
    if( time == 0 ) {
31
      if( pulse > PULSE_LIMIT )
32
        Led ein
33
      else
34
        Led aus
35
36
      pulse = 0;
37
      time = 15;
38
    }
39
  }
40
}

Man darf in einer ISR durchaus ein klein wenig mehr machen, als nur eine 
Variable auf 1 setzen. Das 'In einer ISR nichts machen' sollst du nicht 
als Dogma sehen. Gemeint ist damit 'nichts was extrem lange dauert, wie 
zb Ausgaben auf ein LCD oder eine UART'. Ein bischen rechnen ist schon 
ok und im Endeffekt vereinfacht sich dadurch dann an anderen Stellen im 
Programm die Logik oft enorm.

PS: Benenn deine Variablen vernünftig. Was an 'Windgeschwindigkeit' 
etwas zu lang ist, das ist an 'a' bzw. 'b' zu kurz. Derartige 
ein-buchstaben Variablen solltest du dir für Hilfsvariablen aufheben, 
wie sie zb in for-Schleifen oft vorkommen. Aber sobald etwas eine vitale 
Information enthält, hat es sich auch einen ordentlichen Namen verdient.

hier zb
1
        b--;  //Anzahl der durchzulaufenden Druchgänge wird im 1 verringert
der Kommentar ist schön und gut. Aber: das hier etwas um 1 verringert 
wird, das sehe ich auch im Code. Nur steht im Code 'b' und der Kommentar 
klärt mich darüber auf, dass das die Durchgänge sind. Dann nenn die 
Variable doch gleich Durchgaenge, dann steht da
1
        Durchgaenge--;
und der Kommentar wird komplett überflüssig, weil alles was mir der 
Kommentar erzählt gleich direkt im Code steht.
Der Kommentar hier
1
    b=15;        // Variable B wird wieder auf 15 gesetzt.
der ist sowieso komplett überflüssig. Wen njemand nicht sieht, dass hier 
die Variable b auf 15 gesetzt wird, dann soll er sich eine Brille 
kaufen. Erzähl mir nicht im Kommentar, wie etwas gemacht wird. Das seh 
ich im Code. Erzähl mir WARUM etwas gemacht wird. Zb würde ich gerne 
wissen, warum ausgerechnet 15. Warum nicht 14 oder 16 oder 87? Aber 
darüber schweigt der Kommentar. Dafür erzählt er mir etwas, was ich 
sowieso schon weiß, wenn ich nur den Code lese.
Das zb
1
    Tachosignal=Tachosignal*4; //Tachosignale hochgerechnet pro Minute
2
    Tachosignal=Tachosignal*60; //Tachosignal hochgerechnet pro Stunde
ist ein guter Kommentar. Der erzählt mir, WARUM da mit 4 multipliziert 
wird. Wenn du dann noch die C typischen Zusammenfassungen benutzt, dann 
kann auch ein Blinder greifen, dass es einzig und alleine nur um das 
Tachosignal geht, das braucht dann auch im Kommentar nicht mehr erwähnt 
werden.
1
    Tachosignal *= 4;      // -> Minute
2
    Tachosignal *= 60;     // -> Stunde
(man will sich schliesslich ja auch nicht zu Tode tippen und das was du 
dir an Tipparbeit ersparst kannst du ruhig in ein paar Leerzeichen 
investieren. Die machen das Lesen des Codes leichter. Schliesslich ist 
dein Gehirn seit Kindheit darauf trainiert, dass zwischen Wörtern ein 
Leerraum ist. Das solltest du ausnutzen und nicht alles in einer 
Buchstabenwurscht dahinschreiben.)

Also: Gestalte deinen Code so, dass der Code sein eigener Kommentar 
wird. Ordentliche und gut gewählte Variablennamen sind da der erste 
Schritt. Was dann noch an Kommentierung übrig bleibt, soll sich um das 
WARUM drehen und nicht um das WIE.

: Bearbeitet durch User
von Balou Baer (Gast)


Lesenswert?

Hallo Karl Heinz,

Ich werde mir deine Ratschläge zu Herzen nehmen. Was du sagst mit den 
Variablen und Kommentaren macht Sinn.
Auch dein Ansatz mit dem Tachosignal und der ISR Routine. Nur wenn ich 
die Zeit (1 Sekunde) noch für andere Sachen benötigen würde..... wobei, 
dann setzt man halt eine weiter Variable in die ISR. Unser Lehrer, bei 
dem wir uC programmieren lernen, sagt “ISRs immer!!! so kurz wie möglich 
halten.“, den wenn der uC (hier jetzt gerade nicht) Zeitkitische Sachen 
macht, kann das böse Folgen für eine Sache haben. Darum sollen wir uns 
angewöhnen, immer nur einen Merker zu setzen, wenn die ISR nicht 
kritisch ist, und diese später auswerten.


Ob ich noch Zeitkitische Sachen für die geplante Steuerung programmieren 
muß, weiß ich nicht, da noch einige  Sensoren und Aktoren, sowie der IIC 
Bus fehlen/noch nicht programmiert sind. Auch der Ausgang von der 
Geschwindigkeitsmessung wird nicht nur eine LED ein und aus schalten 
;-). Ich programmiere den uC auf einem Experimentierboard und nutze die 
LEDs nur zur optischen Darstellung bis die eigentliche Platine fertig 
erstellt ist ;-).

Ich befürchte nur, das ich euch noch öfters für mein kleines privates 
Übungsprojekt um Hilfe bitten muss.

Von daher bis später :-P.

Mit freundlichen Grüßen

Balou Baer

von Balou B. (baloubaer)


Lesenswert?

Hallo Karl Heinz,

ich würde deine Hilfe jetzt noch einmal benötigen, sorry.

Ich versuche gerade mit Hilfe eines Interrups (Int0) eine Regenmenge zu 
erfassen. Dieses Signal wird ebenfalls über ein Tachosignal mittelt. Der 
Interrups wird erkannt, aber die Ausgänge werden nicht geschaltet, woran 
kann das liegen. Falsche Logik von mir?

Den Interrupt INT0 habe ich so konfiguriert, dass er bei einer fallenden 
Ansprechen soll, das Funktioniert auch soweit ganz gut und die Kontroll 
LED in der ISR Toggelt auch.
Nur die eigentlichen Abfragen scheinen nicht zu funktionieren. Daher nun 
der Code aufgesplittet mit Komentaren meiner "Logik".


Hier stelle ich mir die Variablen ein und definiere mir die Interrups 
usw.:
1
#define PULSEREGEN_LIMIT 2  // <- Hier die gewünschte Regenmenge eingeben
2
3
#define TIMEREGEN 10  // <- Hier Zeit zur Messung der Regenmenge eingeben.
4
5
volatile uint8_t timewind = 0, timeregen = 0, regen=0;
6
7
uint8_t vorherwind, jetztwind, vorherregen, jetztregen, pulseregen;
8
9
uint16_t pulsewind;
10
11
12
ISR(TIMER1_COMPA_vect)
13
{
14
  if ( timewind > 0 )
15
  {
16
    timewind--;
17
  }
18
  if ( timeregen > 0 )
19
  {
20
    timeregen--;
21
  }
22
}
23
24
ISR(INT0_vect)
25
{
26
        pulseregen++;
27
  PORTB  ^=  ( 1 << PB6);  //Test LED
28
}
29
30
int main (void)
31
{
32
  DDRB = 0b11110011;  //Bit 4-7 TestLED´s
33
  DDRD = 0b00010010;
34

35
36
//Interrupts einstellen:
37
  
38
  MCUCR = 0b00000010;    //Externe Interrupts konfigurieren
39
  GICR = 0b11000000;    //Freigeben der Externen Interrups
40
  TCCR1B …….
41
42
  sei();
43
44
//Zeit für Regen:
45
timeregen = TIMEREGEN;
46
47
..
48
49
While(1) 
50
{
51
//Ermittlung Windgeschwindigkeit 
52
……

Ab hier steht der Code in einem durch siehe ganz Unten.

Hier habe ich mir gedacht:

Wenn es geregnet hat (pulseregen = X), frage ich den Timer ab und die 
Anzahl der Pulse. Ist die Zeit abgelaufen UND die Anzahl der Pulse 
GRÖßER oder GLEICH dem Pulse_Limit, sollen halt die Ausgänge geschaltet 
werden.
1
          if ( ( timeregen ==0 ) & ( pulseregen >= PULSEREGEN_LIMIT )  )  //Abfrage der Pulse
2
      {
3
        //Einschalten der Ausgänge
4
5
        PORTD = ( 1 << PD4 );  //Ausgang zum Haupt uC an PIND3 INT1
6
        PORTB &= ^( 1 << PB1 );  //Anzeige LED
7
        
8
        pulseregen = 0;  //Zurücksetzen der Werte
9
            timeregen = TIMEREGEN;  //Zurücksetzen der Werte
10
      }

Ist das nicht der Fall, die Zeit ist abgelaufen UND die Anzahl der Pulse 
ist kleiner als das Limit, sollen die Ausgänge wieder ausgeschaltet 
werden.
1
      if ( ( timeregen == 0 ) & ( pulseregen << PULSEREGEN_LIMIT ) )
2
      {
3
        //Ausschalten der Ausgänge
4
5
        PORTD &= ~( 1 << PD4 );  //Ausgang zum Haupt uC an PIND3 INT1
6
        PORTB = ( 1 << PB1 );  //Anzeige LED
7
      
8
        pulseregen = 0;  //Zurücksetzen der Werte
9
            timeregen = TIMEREGEN;  //Zurücksetzen der Werte
10
      }
11
              
12
          
13
    }

Macht aber der uC leider nicht so, wie ich mir das gedacht habe. Es wird 
kein Ausgang gesetzt. Habe ich einen Denkfehler? Oder geht das so nicht?

Ich bedanke mich für die Hilfe und verbleibe

Mit freundlichen Grüßen

Balou Baer
1
// Ermittlung der Regenmenge 
2
3
if ( ( timeregen ==0 ) & ( pulseregen >= PULSEREGEN_LIMIT )  )  //Abfrage der Pulse
4
    {
5
      //Einschalten der Ausgänge
6
7
      PORTD = ( 1 << PD4 );  //Ausgang zum Haupt uC an PIND3 INT1
8
      PORTB &= ~( 1 << PB1 );  //Anzeige LED
9
        
10
      pulseregen = 0;  //Zurücksetzen der Werte
11
      timeregen = TIMEREGEN;  //Zurücksetzen der Werte
12
    }
13
14
    if ( ( timeregen == 0 ) & ( pulseregen << PULSEREGEN_LIMIT ) )
15
    {
16
      //Ausschalten der Ausgänge
17
18
      PORTD &= ~( 1 << PD4 );  //Ausgang zum Haupt uC an PIND3 INT1
19
      PORTB = ( 1 << PB1 );  //Anzeige LED
20
      
21
      pulseregen = 0;  //Zurücksetzen der Werte
22
          timeregen = TIMEREGEN;  //Zurücksetzen der Werte
23
    }

von Stefan E. (sternst)


Lesenswert?

Balou Baer schrieb:
> Macht aber der uC leider nicht so, wie ich mir das gedacht habe. Es wird
> kein Ausgang gesetzt. Habe ich einen Denkfehler?

1)
volatile

2)
1
    if ( ( timeregen == 0 ) & ( pulseregen << PULSEREGEN_LIMIT ) )
Was denkst du, was das '<<' hier bedeutet? "viel kleiner"?
(das '&' ist hier eigentlich auch falsch, funktioniert aber)

von Balou B. (baloubaer)


Lesenswert?

@ Stefan,

ich danke dir für die Info, dennoch läuft es nicht.


Was mir nach einigen weiteren Versuchen jetzt aufgefallen ist. Das Der 
Timer nicht gestartet wird. Nehme ich die Zeile
1
MCUCR = 0b00000011;
2
GICR = 0b11000000;

raus. Läuft zwar der Timer wieder, aber der Interrupt an PIND3 wird 
nicht mehr erkannt, weil der uC ja nicht mehr weiß, dass dieser PIN als 
ein Interrupt verwendet werden soll.

Was mache ich falsch wenn ich das hier schreibe:
1
MCUCR = 0b00000011;
2
GICR = 0b11000000;
3
4
TCCR1B=0b00001101;  //Timer1 Konfigurieren
5
OCR1A=15624;    //Vergleichsregister setzten
6
TIMSK=0b00010000;  //Timer1 CompA-Interrupt AKTIVIEREN
7
sei();        //Alle freigegebenen Interrupts werden EINGESCHALTET!!!!!!!!!!

von Karl H. (kbuchegg)


Lesenswert?

Balou Baer schrieb:

> dann setzt man halt eine weiter Variable in die ISR. Unser Lehrer, bei
> dem wir uC programmieren lernen, sagt “ISRs immer!!! so kurz wie möglich
> halten.“,

Das ist aber auch kein Dogma.
Ein paar Variablen addieren, vergleichen oder dergleichen, das dauert 
alles nur wenige Taktzyklen. Eine Uhr würde man so machen
1
ISR( .... )
2
{
3
  milliSekunden++;
4
  if( milliSekunden == 1000 ) {
5
    Sekunden++;
6
    if( Sekunden == 60 ) {
7
      Sekunden = 0;
8
      Minuten++;
9
      if( Minuten == 60 ) {
10
        Stunden++;
11
        if( Stunden == 24 )
12
          Stunden = 0;
13
      }
14
    }
15
  }
16
}
d.h. da kommmt die komplette Uhren-Logik in die ISR hinein. Auch wenn 
das im C Code recht lang aussieht, in Assembler sind das nicht viele 
Taktzyklen. Und letzten Endes ist es wichtig, dass die Uhr dann auch 
wirklich jeden ISR Aufruf korrekt zählt und die Uhrzeit konsistent 
hochgezählt wird. Die Alternative
1
ISR( ... )
2
{
3
  countClock = 1;
4
}
5
6
int main()
7
{
8
  ...
9
10
  while( 1 ) {
11
12
     Code A
13
14
     if( countClock ) {
15
       countClock = 0;
16
17
       milliSekunden++;
18
       if( milliSekunden == 1000 ) {
19
         Sekunden++;
20
         ....
21
22
     }
23
24
     Code B
25
   }
26
}

krankt dran, dass es dir passieren kann, dass je nachdem, was alles an 
den Stellen 'Code A' bzw. 'Code B' passiert, es dir passieren kann, dass 
ein paar milliSekunden 'übersehen' werden, weil die Codestellen A oder B 
zu lange dauern. Für eine Uhr ist das unbrauchbar.
Solange die INterrupts eingeschaltet sind und der Timer läuft, wird die 
ISR aufgerufen. Und zwar zuverläsig und als Folge davon wird auch die 
Zeit zuverlässig gezählt - da gibt es keine sich summierenden vergessene 
Millisekunden.

D.h. ISR kurz halten ist ok. Aber auch nicht zu kurz. Generell in der 
Informatik: sobald du anfängst Dinge dogmatisch zu sehen, gehst du den 
falschen Weg. Man muss die Dinge verstehen und nicht einfach nur 
auswendig gelerntes von sich geben. Du tust in einer ISR das was es zu 
tun gibt, wobei du 'lang dauernde Dinge' rausnimmst. Was im konkreten 
'lang dauernd' bedeutet, hängt von der Aufgabenstellung ab. Aber wenn 
dir 10 oder 15 Taktzyklen in einer ISR zu viel sind, dann sollte man mal 
in die Richtung überlegen, ob man nicht generell mit dem falschen µC 
unterwegs ist. Bei 16Mhz sind 10 Taktzyklen weniger als 1µs (millionstel 
Sekunde)! Es gibt die Anwendungen, bei denen es wirklich auf jede µs 
ankommt. Zb bei der Generierung von Videosignalen. Aber ob ein Relais 1 
oder 2µs früher oder später schaltet, spielt überhaupt keine Rolle. Da 
ist die zeitliche Unsicherheit durch die Mechanik schon größer.

Also: alles mit Mass und Ziel. Letzten Endes ist durch die Modifikation 
ja auch deine Hauptschleife einfacher geworden, so dass ich eher im 
Gegenteil sogar sagen würde, dass in Summe gesehen das Programm ein 
besseres Zeitverhalten hat.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Balou Baer schrieb:

> TCCR1B=0b00001101;  //Timer1 Konfigurieren
> OCR1A=15624;    //Vergleichsregister setzten
> TIMSK=0b00010000;  //Timer1 CompA-Interrupt AKTIVIEREN

solage du das so schreibst (also mit Binärzahlen anstelle ordentlicher 
Schreibweise mit benannten Bits), weigere ich mich, den Code weiter zu 
studieren.
Jemandem helfen ist ok. Aber wenn er mir dann auch noch Prügel zwischen 
die Beine wirft, lass ich es bleiben.

: Bearbeitet durch User
von Ernst O. (ernstj)


Lesenswert?

Balou Baer schrieb:
> Tachosignal=Tachosignal*4; //Tachosignale hochgerechnet pro Minute
>     Tachosignal=Tachosignal*60; //Tachosignal hochgerechnet pro Stunde

Wie soll man denn das verstehen? Tachosignal wird für die Dauer von ein 
paar Maschinenzyklen auf Tachosignal * 4 gesetzt und dann gleich mit 60 
multipliziert? warum? Wird die Anzahl der Pulse pro Minute noch irgendwo 
gebraucht? Wenn ja, sollte sie einer eigenen Variablen zugewiesen 
werden.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Balou Baer schrieb:
>         PORTD = ( 1 << PD4 );  //Ausgang zum Haupt uC an PIND3 INT1

Richtig wäre:

          PORTD |= ( 1 << PD4 );  //Ausgang zum Haupt uC an PIND3 INT1

Sonst schaltest Du alle anderen Pins an dem Port versehentlich auf 0.

von Karl H. (kbuchegg)


Lesenswert?

ernst oellers schrieb:
> Balou Baer schrieb:
>> Tachosignal=Tachosignal*4; //Tachosignale hochgerechnet pro Minute
>>     Tachosignal=Tachosignal*60; //Tachosignal hochgerechnet pro Stunde
>
> Wie soll man denn das verstehen? Tachosignal wird für die Dauer von ein
> paar Maschinenzyklen auf Tachosignal * 4 gesetzt und dann gleich mit 60
> multipliziert? warum?

Find ich nicht so schlimm.
Da die Variable nicht volatile ist, wird der Compiler den 
Zwischenschritt rausoptimieren und wenn nicht, ist es auch nicht so 
wild.

Man hätte natürlich genausogut auch
1
  Tachosignal *= 4 * 60;   // von 1/4 Minute (15 Sekunden) auf Stunden
schreiben können. Noch besser wäre eine Präprozessor Lösung, die die 
Multiplikation mit 4 mit den 15 Durchgängen in Beziehung setzt, von 
denen jeder 1 Sekunde dauert.

von Balou B. (baloubaer)


Lesenswert?

@ Frank M.

Danke dir, für die Info, hatte mich schon immer gefragt warum da dieses 
| bei einigen anderen vor dem = steht.

@ Karl Hein

Ich verwende die Binärcodierung nicht um dich oder anderen zu ärgern, 
sondern ich kann dann sehr leicht die gesetzten Bits erkennen. Was aus 
einer HEX Zahl nicht immer sofort zu sehen ist, oder was meinst du 
genau?

oder Soll ich jetzt MCUCR |= (1<<ISC01);
                    MCUCR &=~(1<<ISC00);usw. schreiben?

Wenn ich 0bXXXXXXXX Schreibe z.B. MCUCR Register kann ich ganz einfach 
aus den Datenblatt sehen, das

0 b    X    X    X    X    X     X     X     X
Bit    7    6    5    4    3     2     1     0
      SE   SM2  SM1  SM0 ISC11 ISC10 ISC01 ISC00  Bezeichung

bit 3 und 2 für das Ansprechverhalten von INT1 zuständig sind und Bit 1 
und 0 für INT0.  Hierbei habe ich setehen 0b00000010, Soll heißen löse 
aus, wenn eine fallende Flanke erkannt wird.

Bit 7,6 und 5 sind für den Sleep Modus und wie ich den uC wieder 
aufwecken kann. SleepMode braucht dieser uC nicht.

bei dem GICR Register für die Externen Interrupts das selber.

Bit   7    6    5   4 3 2   1    0
     INT1 INT0 INT2 – – – IVSEL IVCE

Bit 7 oder 6 oder 5 sind für das Freischalten der Inerrupts zuständig. 
und mit sei() werden dann quasi alle gesetzten Interrupts aktiviert.

Also ich kann dann die Bits über das Datenblatt halten und kann es 
einfacher vergleichen.

Nur ich verstehe halt nicht, warum entweder nur die Timer laufen oder 
nur die normalen Externen Interrupts. Kann ich diese nicht beide Laufen 
lassen?

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Balou Baer schrieb:
> oder Soll ich jetzt MCUCR |= (1<<ISC01);
>                     MCUCR &=~(1<<ISC00);usw. schreiben?

Nicht "|=" und "&=" sind entscheidend, aber die Benutzung der
Bitnamen schon.

Wenn dich die Schiebeoperatoren stören, kannst du den Macro _BV()
benutzen, den die avr-libc seit mehr als 10 Jahren mitbringt:
1
MCUCR = _BV(ISC01);

Der Vorteil der Bitnamen ist, dass man, wenn man diese einigermaßen
„intus“ hat, eben nicht erst noch im Datenblatt gegenlesen muss, ganz
im Gegensatz zu deinen Binärzahlen; die muss man auch, wenn man 10
Jahre lang schon mit AVRs arbeitet, immer wieder erst nachlesen gehen.

Wenn du unbedingt auch die nicht gesetzten Bits noch dokumentieren
willst, kannst du dir natürlich auch Makros „I“ (für „eins“) und
„O“ (für „null“) erfinden:
1
#define I(bit) (1 << (bit))
2
#define O(bit) 0
3
4
...
5
   MCUCR = O(SE) | O(SM2) | O(SM1) | O(SM0) | O(ISC11) | O(ISC10) | I(ISC01) | O(ISC00);

: Bearbeitet durch Moderator
von Balou B. (baloubaer)


Lesenswert?

Hallo Jörg,

danke dir, jedoch verstehe ich als Anfänger jetzt nur Bahnhof.

Ich kann leider als Anfänger nur Werkzeuge einsetzten die ich kenne, 
bzw. die ich bis jetzt erst gelernt habe. Leider sind die Aufgaben bzw. 
der Unterricht nicht immer Realitätsnah, wenn ich mir hier die Tips und 
Ratschläge, sowie die Kritiken durchlesen.

Ich stelle mir nun folgende Frage:

Also wenn ich jetzt
1
MCUCR = _BV(ISC01)
 schreibe, woher will der Compiler dann wissen, welche Bits er setzten 
soll, es gibt jetzt 4 Varianten: 0 0 Low Signal löst aus ; 0 1 jede 
Eingangsänderung löst aus; 1 0 fallende Flanke löst aus; 1 1 steigende 
Flanke löst aus.

Auch die Idee mit den Makros finde ich viel aufwendiger und 
unübersichtlicher als meine binäre Sache, wenn ich den Schreibaufwand 
sehe.

Jetzt eine kleine Kritik von mir und diese bitte nicht falsch verstehen, 
denn ich bin wirklich, wirklich dankbar für die Hilfe von euch und auch 
von Karl Heinz, denn auf die Flankenerkennung wäre ich nie gekommen. 
Erstrecht nicht auf den Programmcode.
Jedoch ich bin Anfänger und hatte jetzt ca. ein halbes Jahr in 
Abendschule (1 mal die Woche für 2 Stunden das Unterrichtsfach uC) in 
dem wir mit GCC unser Experimentierboard mit einem ATMega32 
programmieren lernen.

Jetzt wird hier ein Anfänger, sagen wir mal, zurecht gewiesen, warum und 
weshalb man es so macht, wie man es macht. Vielleicht, weil man es nicht 
anders gelernt hat und es nicht besser weiß.

Ich weiß das ich nicht optimal programmiere, weil ich es noch erlernen 
muss. Jedoch scheinen hier Leute zu sein, die sich durch Nullen und 
Einsen persöhnlich angegriffen fühlen. Klar, ich kann niemanden zur 
Hilfe zwingen.

Ich versuche Ratschläge umzusetzten, jedoch kann ich nur Sachen 
umsetzten, die ich auch verstehe. Und wenn ich mir keinen Rat mehr weiß, 
möchte ich gern meine Frage hier stellen dürfen, ohne das ich für meine 
nicht perfekt vorhandenen C und uC Kenntnisse in den Arsch getreten zu 
werden.

Ich habe nichts dagen, wenn man mir sagt, warum machst du das mit 
0bXXXXXXXXX und nicht mit XY Ungelöst und erklärt mir das dann so, das 
es ein Anfänger auch verstehen könnte. Also warum man das so macht und 
welche Vorteile diese Sache hat.

Und jetzt hängen wir uns hier an Binärer Schreibweise auf, um ein 
Register zusetzten. Vielleicht wäre es ja einfach einfacher mir eine 
Erklärung zu geben, warum ich nicht das Timer1 Register als Interrupt 
und gleichzeitig den Externen Interrupt (Int0) mit dem MCUCR Register 
nutzen kann.

Denn wenn mein Programm so aussieht, läuft der Timer nicht.
1
MCUCR = 0b00000010; //Externe Interrupts konfigurieren
2
GICR = 0b11000000; //Externe Interrups aktivieren
3
4
TCCR1B=0b00001101;  //Timer1 Konfigurieren
5
OCR1A=15624;    //Vergleichsregister setzten
6
TIMSK=0b00010000;  //Timer1 CompA-Interrupt AKTIVIEREN
7
sei();

Wenn ich die externen Interrups wieder rauskommentiere, läuft der Timer 
wieder.

WARUM????????   Das Kann doch jetzt nicht nur an Nullen und Einsen 
liegen, die das gleiche bewirken wie
1
MCUCR = _BV(ISC01);
 bei dem ICH noch nicht einmal sehe, welche Bits gesetzt oder nicht 
gesetzt werden, also durch welche Bedingung der Interrupt ausgelöst 
wird.

Bitte entschuldigt meine Worte und den vielen Text. Wenn jetzt noch die 
Lösung des Problems heißt: "Es geht grundsätzlich so nicht, weil die 
internen Interrupts nicht gleichzeitig mit externen Interrups laufen." 
Dann verstehe ich erst recht die Welt nicht mehr!

Mit freundlichen Grüßen

Balou Baer

: Bearbeitet durch User
von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Balou Baer schrieb:
> Also wenn ich jetzt
>
1
MCUCR = _BV(ISC01)
> schreibe, woher will der Compiler dann wissen,
> welche Bits er setzten soll, [...]

Es gibt eine prozessorspezifische Include-Datei, welche vom Compiler 
herangezogen wird, wenn Du

#include <avr/io.h>

schreibst. Dort steht das #define für ISC01 drin.

> Auch die Idee mit den Makros finde ich viel aufwendiger und
> unübersichtlicher als meine binäre Sache, wenn ich den Schreibaufwand
> sehe.

Es ist viel lesbarer. Wenn Du Deinen Code in ein paar Monaten liest, 
wirst Du denken: "ISC01, ahja, ich weiß, was das ist". Dann wirst Du 
dafür dankbar sein. Wenn Du aber 0b000010 liest, wirst Du erneut im 
Datenblatt nachschauen: "Was war das denn nochmal?".

Ausserdem ist die Schreibweise mit Preprocessor-Konstanten weitaus 
portabler. Wenn Du später mal ein Programm für einen anderen AVR 
schreibst, kannst Du Deinen Code wiederverwenden. Denn auch wenn das Bit 
ISC01 dann an einer anderen Stelle im Register sitzt, wird das korrekte 
Bit gesetzt. Ja, das kommt vor!

Wenn Du jedoch 0b00000010 schreibst, dann ist der Code 
höchstwahrscheinlich nicht portabel. Du müsstest dann bei der Übernahme 
des Codes für einen anderen AVR-µC alle binären Konstanten im neuen 
Datenblatt überprüfen. Bei Verwendung von ISC01 kannst Du jedoch den 
Code quasi "blind" übernehmen.

Gruß,

Frank

: Bearbeitet durch Moderator
von Balou B. (baloubaer)


Lesenswert?

Ok,

danke für die hinweise, also wie kann ich mit mcucr = _BV ( ISC01 ); im 
Programm schreibe, wie ist denn dann das Bit vom ISC01 gesetzt? 0 oder 
1??

Oder anders gefragt wie setzte ich mit der Schreibweise Bit auf 1 oder 
0, das verstehe ich immer noch nicht so richtig.

Oder wie geht das hier:

"Wenn du unbedingt auch die nicht gesetzten Bits noch dokumentieren
willst, kannst du dir natürlich auch Makros „I“ (für „eins“) und
„O“ (für „null“) erfinden:

#define I(bit) (1 << (bit))
#define O(bit) 0

...
   MCUCR = O(SE) | O(SM2) | O(SM1) | O(SM0) | O(ISC11) | O(ISC10) | 
I(ISC01) | O(ISC00);"

????

( SE ) ( SM2 ) ( SM1 ) usw. versteht der Compiler? oder muss ich da noch 
etwas einbinden? Muss ich bei dem define 0 ( bit ) muss ich da nicht 
auch ( 0 << ( bit ) ) schreiben?

Ich bedankem ich für deine Mühen und verbleibe

mit freundlichen Grüßen

Balou Baer

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Balou Baer schrieb:

> danke für die hinweise, also wie kann ich mit mcucr = _BV ( ISC01 ); im
> Programm schreibe, wie ist denn dann das Bit vom ISC01 gesetzt? 0 oder
> 1??

Es ist gesetzt, also 1.

Was 0 ist, bedarf keiner weiteren Erwähnung.

> Oder anders gefragt wie setzte ich mit der Schreibweise Bit auf 1 oder
> 0, das verstehe ich immer noch nicht so richtig.

Setzen kannst du immer nur auf 1.  Was nicht gesetzt ist, ist 0.

> Oder wie geht das hier:

Lös' es dir einfach gedanklich auf und überführ es in die
Binärschreibweise.  Was anderes macht der Compiler auch nicht
draus.

Ich wollte dir das auch nicht unbedingt so empfehlen; ich wollte
damit nur eine Methode aufzeigen, wie man auch die 0-Bits (die
man genauso gut ganz weglassen könnte) halt noch dokumentieren
kann.

Zugegebenermaßen etwas subtil ist dabei natürlich die Verwendung
eines Makros namens I für ein gesetztes Bit und O für ein nicht
gesetztes; das O kann man in vielen Fonts kaum von der 0
unterscheiden.  Eine 0 kann aber natürlich nicht als Makro-Name nach
dem #define auftauchen.

> ( SE ) ( SM2 ) ( SM1 ) usw. versteht der Compiler?

Ja.

> oder muss ich da noch
> etwas einbinden?

Ja, <avr/io.h>, aber die wirst du wohl sowieso immer mit dabei
haben.

> Muss ich bei dem define 0 ( bit ) muss ich da nicht

Nein, nicht "define 0", sondern "define O".  Falls du da keinen
Unterschied siehst, musst du deinen Browser einen anderen Font
benutzen lassen.

> auch ( 0 << ( bit ) ) schreiben?

Eine 0 kannst du so oft nach links schieben, wie du willst: es
bleibt einfach eine 0.

: Bearbeitet durch Moderator
von Balou B. (baloubaer)


Lesenswert?

Hallo Jörg,

ich habe es jetzt mal so geschrieben:
1
TCCR1B = _BV ( CS10 ) | _BV ( CS12 ) | _BV ( WGM13 );   //0b00001101;  //Timer1 konfigurieren
2
OCR1A = 15624;    //Vergleichsregister setzten
3
TIMSK = _BV ( OCIE1A );  //0b00010000;  //Timer1 CompA-Interrupt AKTIVIEREN
4
5
MCUCR = _BV ( ISC01 );  // 0b00000010;    //Externe Interrupts konfigurieren
6
GICR = _BV ( INT0 );  // 0b01000000;    //Freigeben der Externen Interrups
7
8
sei();

warum funktioniert das so nicht?

Oder muss ich jetzt jetes einzelne TCCR1B schreiben?

P.S.: Der Timer löuft so nicht mehr, wenn ich das TCCR1B so schreibe, 
alles andere Funktioniert.

P.S.: Wegen den Vorherigen Fragen, warum der Timer nicht läuft wenn ich 
das MCUCR drin habe: INT 1 war nicht beschaltet! Daher hat Int1 ständig 
ausgelöst und hat alles andere blockiert

: Bearbeitet durch User
von Balou B. (baloubaer)


Lesenswert?

Hallo Jörg,

Timer Routine läuft nun auch, falsches Bit gesetzt ;). WGM13 statt 
WGM12.

Aber was anderes, wäre die Schreibweise so ok???
1
//Interrupts einstellen:
2
    
3
  TCCR1B = _BV ( WGM12 ) | _BV ( CS12 ) | _BV ( CS10 );   //0b00001101;  //Timer1 konfigurieren
4
  OCR1A = 15624;    //Vergleichsregister setzten
5
  TIMSK = _BV ( OCIE1A );  //0b00010000;  //Timer1 CompA-Interrupt AKTIVIEREN
6
7
  MCUCR = _BV ( ISC11 ) | _BV ( ISC01 );  // 0b00001010;    //Externe Interrupts konfigurieren (Fallende Flanke)
8
  GICR = _BV ( INT1 ) | _BV ( INT0 );  // 0b11000000;    //Freigeben der Externen Interrups
9
10
  sei();        //Alle freigegebenen Interrupts werden EINGESCHALTET!!!!!!!!!!
11
12
13
///*
14
//ADC Wandler einstellen:
15
16
  ADCSRA =  _BV ( ADEN ) | _BV ( ADIE ) | _BV ( ADPS0 ) | _BV ( ADPS1 ) | _BV ( ADPS2 );  //0b10000111;  //Aktivieren und einstellen des ADC Wandlers hier 128  
17
  ADMUX = _BV ( REFS0 ) ; //Aktivieren der externen Referencspannung
18
//*/

Dann habe ich noch eine Frage mit dem setzten der Bits im Register:

Wenn ich jetzt z.B. den AD - Wandler starten möchte, muss ich doch das 
BIT ADSC im ADCSRA-Register setzen.
Jetzt möchte ich aber erst später im Programm dieses BIT setzen und 
schreibe an die entsprechende stelle im Programm;
1
 
2
ADCSRA = _BV ( ADSC );  //Starte AD - Messung

Wie verhalten sich die anderen BITS in dem Register, die ich ja schon 
vorher bei der Initialisierung gesetzt habe? Gehen die wieder auf 0?

Ich bedanke mich für deine Hilfe und verbleibe

mit freundlichen Grüßen

Balou Baer

von Karl H. (kbuchegg)


Lesenswert?

Balou Baer schrieb:

> Aber was anderes, wäre die Schreibweise so ok???

Grundsätzlich: ja.


> Jetzt möchte ich aber erst später im Programm dieses BIT setzen und
> schreibe an die entsprechende stelle im Programm;
>
>
1
> ADCSRA = _BV ( ADSC );  //Starte AD - Messung
2
>

Damit löscht du aber die Anderen Bits wieder.

Um 1 Bit zu setzen, und NUR DIESES eine Bit zu setzen und alle anderen 
unverändert zu lassen, veroderst du das Bit
1
  ADCSRA |= _BV( ADSC );

WEnn du 1 Bit gezielt auf 0 zwingen willst, dann wird es als 'Kehrwert' 
verundet
1
  register &= ~ _BV( bitname );

lerne diese Dinge. Sie sind das um und auf in der µC-Programmierung. 
Dinge wie Einzelbits setzen bzw. löschen musst du im SChlaf beherrschen. 
Dann spielt es auch keine ROlle mehr, ob du
1
  ADCSRA |= ( 1 << ADSC );
oder
1
  ADCSRA |= _BV( ADSC );
schreibst. Ist sowieso dassselbe, weil sich hinter dem Makro _BV nichts 
anderes als eine Umsetzung in die << Schreibweise verbirgt.


Bitmanipulation


Um die Frage noch zu beantworten:

> Wie verhalten sich die anderen BITS in dem Register, die ich ja schon
> vorher bei der Initialisierung gesetzt habe? Gehen die wieder auf 0?

Was erwartest du, was passiert, wenn du
1
  uint8_t i = 5;
2
3
  i = 8;
schreibst? Nach der Zuweisung von 8 steht auch 8 im Register. Von der 5 
bleibt kein Bit übrig.
Nicht anders ist es hier: Zuweisung ist Zuweisung. Dem Register wird ein 
neuer WErt zugewisen. Und zwar dem kompletten Register. Nach
1
  ADCSRA = ( 1 << ADSC );
hat das Register ADCSRA einen neuen Wert bekommen. Und zwar alle 8 Bit. 
Und zwar den Wert, der sich aus der Auswertung des Ausdrucks 1<<ADSC 
ergibt.

Da ist nichts magisch drann. Das ist einfach nur eine Zuweisung und ein 
Verschiebeoperator, der das 1-Bit der dezimalen 1 soooft nach links 
schiebt, bis es an der Position ADSC (welche Bitnummer das auch immer 
ist) zu stehen kommt. Dieser Wert wird an ADCSRA zugeiwesen und damit 
krigt ADCSRA als ganzes einen neuen Wert.

Und genau darin besteht ja der Trick bei
1
  ADCSRA |= ( 1 << ADSC );

hier wird nicht einfach nur zugewisen. Sonder zuerst (wegen der |= 
Operation) wird diese zurechtgeschobene 1 mit dem alten INhalt von 
ADCSRA verodert. Beim Verodern kann aber ein Bit maximal von 0 auf 1 
wechseln. Ein Bit welches schon 1 war, kann mittels einer Oder Operation 
nicht auf 0 gebracht werden. Im Endeffekt bleiben daher bei dieser 
Operation alle 1 Bits erhalten und es kommt noch ein 1 Bit an der 
Position ADSC dazu.

von Balou B. (baloubaer)


Lesenswert?

Danke dir.

Das mit der  uint8_t b = 7 und später b = 15 ist bekannt.

nur wie sich die Bits im Register verhalten wenn man die _BV Anweisung 
nimmt war mir nicht klar.

Ich hoffe, dass ich das jetzt soweit verstanden haben.

Ich bedanke mich für euere Hilfe und verbleibe

mit freundlichen Grüßen

Balou Baer

von Balou B. (baloubaer)


Lesenswert?

Ich muss euch noch einmal stören.

Karl Heiz hatte mir damals beim entprellen eines Tasters geholfen s.o.
1
jetztzaehltaster_Y = ( PIND & ( 1 << PD7 ) //Aktueller zustand an PIN D 7 wird abgerfagt
2
   if ( jetztzaehltaster_Y != vorherzaehltaster_Y )  //Ist der aktuelle Wert von PIN D7 nicht gleich dem vorherigen Wert, dann
3
   {
4
  vorherzaehltaster_Y = jetztzaehltaster_Y   //wird der vorherige Wert mit dem aktuellen Wert überschrieben und
5
  if ( jetztzaehltaster_Y) //wenn jetztendschalter_Y, dann
6
  { 
7
        zaehler_y_achse_runter -- ;
8
  }
9
   }

das war für eine positive Flanke. Jetzt benötige ich eine negative 
Flanken erkennung, wenn ein gedrückter Taster losgelassen wird.

Wie würde das aussehen. so?
1
jetztzaehltaster_Y = ( PIND & ( 1 << PD7 ) //Aktueller zustand an PIN D 7 wird abgerfagt
2
   if ( jetztzaehltaster_Y != vorherzaehltaster_Y )  //Ist der aktuelle Wert von PIN D7 nicht gleich dem vorherigen Wert, dann
3
   {
4
  vorherzaehltaster_Y = jetztzaehltaster_Y   //wird der vorherige Wert mit dem aktuellen Wert überschrieben und
5
  if ( !jetztzaehltaster_Y) //wenn nicht jetztendschalter_Y, dann   ???? So richtig????
6
  { 
7
        zaehler_y_achse_runter -- ;
8
  }
9
   }
[/c]
Ich bedanke mich für euere Mühen und verbleibe

mit freunldichen Grüßen

Balou Baer

von Karl H. (kbuchegg)


Lesenswert?

Balou Baer schrieb:

> Wie würde das aussehen. so?

korrekt.

von Uwe S. (de0508)


Lesenswert?

Guten Morgen,

so etwas kann man nicht zu Code übersetzen - syntax error:
1
jetztzaehltaster_Y = ( PIND & ( 1 << PD7 ) //Aktueller zustand an PIN D 7 wird abgerfagt

von (prx) A. K. (prx)


Lesenswert?

Zähl mal die Klammern. Und such das Ende vom Statement.

von Karl H. (kbuchegg)


Lesenswert?

Uwe S. schrieb:
> Guten Morgen,
>
> so etwas kann man nicht zu Code übersetzen - syntax error:

Ihm gings um das ! hier
1
  if ( !jetztzaehltaster_Y) //wenn nicht jetztendschalter_Y, dann   ???? So richtig????

dass er seine Klammern selbst richtig stellen kann, davon gehe ich aus.

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.