Forum: Mikrocontroller und Digitale Elektronik Bitte um Hilfe bei Timerprogrammierung


von Nicola R. (pastulla)


Lesenswert?

Hallo zusammen,

vor kurzem habe ich begonnen mich mit Mikrocontrollern zu beschäftigen. 
Im Moment bin ich noch gaaanz am Anfang der Lernphase und würde mich 
freuen, wenn wir jemand bei folgendem Problem helfen könnte.

Zuerst ein paar Randbedingungen:
- ATmega8A
- AtmelStudio 7
- STK500

Ich möchte ein Programm ertellen, dass eine LED mit der doppelten 
Frequenz eines externen Rechtecksignals (etwa 1-5 Hz, Tastgrad variabel 
zwischen 10-90%) blinken lässt.
Einen tieferen Sinn hat das Ganze zunächst nicht, es ist mehr eine 
Verständnisaufgabe für mich.

So weit bin ich bisher gekommen:
Man kann die input capture Funktion des 16bit-Timers nutzen um die 
positiven Flanken zu erkennen. Damit kann man die Periode des 
Eingangssignals bestimmen.
Aufgrund der niedrigen Frequenz benutze ich einen Teiler von 256.

Nachdem die Periode bestimmt ist soll Timer0 starten. Sobald dieser den 
halben Periodenwert erreicht, soll an Port D2 eine LED für 20ms 
angeschaltet werden und der Timer0 zurückgesetzt werden.
Dann sollte der Timer wieder neu starten usw.

Leider klappt es irgendwie noch nicht, es scheint der Fall nicht 
einzutreten, dass der Wert von Timer0 dem Periodenwert entspricht.

Es würde mich sehr freuen, wenn mir jemand einen Tip geben könnte was 
ich falsch gemacht habe. Falls es einen einfacheren Weg gibt bin ich 
ebenfalls für Hinweise dankbar. Danke im voraus!

Basis des Programms ist übrigens der Code hier, danke dafür an den 
Autor:
https://www.mikrocontroller.net/attachment/highlight/77856

Hier ist der Code:
1
#ifndef F_CPU
2
#define F_CPU 1000000
3
#endif
4
5
6
#include <avr/io.h>
7
#include <avr/interrupt.h>
8
#include <util/delay.h>
9
#include <stdlib.h>
10
11
#ifndef TRUE
12
#define TRUE 1
13
#define FALSE 0
14
#endif
15
16
volatile unsigned char NrOverflows = 0; //Durchlaufe Timer1 Flankenerkennung
17
volatile unsigned char NrOverflows2 = 0; //Durchlaufe Timer0
18
volatile unsigned int  StartTime = 0; //Zeitstempel erste Flanke
19
volatile unsigned int  counter2 = 0;  //Gesamtwert Timer0
20
volatile unsigned int  EndTime = 0; //Zeitstempel zweite Flanke
21
volatile unsigned char Messungfertig = FALSE;  
22
volatile uint32_t periode = 0; //Periode für Timer0 = 1/2 Periode Eingangssignal
23
static unsigned char ErsteFlanke = TRUE;
24
25
ISR( TIMER1_CAPT_vect )
26
{
27
  if (Messungfertig == FALSE)
28
  {
29
    if( ErsteFlanke ) // Wenn noch keine Flanke vorher erkannt
30
    {
31
      StartTime = ICR1; // Timerwert in StartTime
32
      NrOverflows = 0; // Durchlaufe Timer 0 zurücksetzen
33
      ErsteFlanke = FALSE; // Dieser Zweig wird nicht mehr durchlaufen
34
    }
35
    else // Wenn Flanke vorher erkannt
36
    {
37
      EndTime = ICR1; // Timerwert in EndTime
38
      Messungfertig = TRUE; // Dieser Zweig wird nicht mehr durchlaufen
39
      TCNT0=0; //Timer0 wird zurückgesetzt
40
      NrOverflows2 =0; //Durchläufe Timer0 werden zurückgesetzt
41
    }
42
  }
43
}
44
45
46
ISR( TIMER1_OVF_vect ) //Nicht benoetigt, da Teiler 256 und 16bit
47
{
48
  //NrOverflows++;
49
}
50
51
ISR (TIMER0_OVF_vect)
52
{
53
  NrOverflows2++;
54
}
55
int main()
56
{
57
58
  TCCR1B = (1<<ICES1) | (1<<CS12); // Timer1: Input Capture Edge, PreScale 256
59
  TIMSK = (1<<TICIE1) | (1<<TOIE1); // Interrupts akivieren, Capture + Overflow
60
  TCCR0 = (1<<CS02); // Timer 0: PreScale 256 
61
  DDRD = 0xff; // Ports D als Ausgang
62
  PORTD &= ~_BV(PD2); //LED an Port D2 aus
63
  
64
  sei();
65
  
66
  while(1)
67
  {  
68
    if( Messungfertig ) // Beide Flanken wurden erkannt
69
    { 
70
      periode =(EndTime - StartTime)/2; //Berechnung Periode fuer Timer0 = halbe Periode Eingangssignal
71
      counter2 = (NrOverflows2*256) + TCNT0; //Wert Timer1 bestimmen: Anzahl Durchläufe*256 + aktueller Wert
72
      
73
      if (counter2==periode) //Wenn Wert Timer0 = neue Periode dann Timer0 zurücksetzen und LED Port D2 an
74
      { 
75
        NrOverflows2 =0;
76
        TCNT0=0;
77
        PORTD |= _BV(PD2);
78
        _delay_ms(20);
79
      }
80
      
81
    }
82
  }
83
}

von Theor (Gast)


Lesenswert?

Bitte beschreibe die Fehlersymptome. Woran erkennst Du, daß:

> Leider klappt es irgendwie noch nicht, es scheint der Fall nicht ...


Auffallig ist bei oberflächlicher Betrachtung der Code und der 
Kommentar:
1
      Messungfertig = TRUE; // Dieser Zweig wird nicht mehr durchlaufen

in Verbindung damit, dass er von der folgenden Bedingung abhängig ist:
1
      if (Messungfertig == FALSE)

Das heisst, "Messungfertig = TRUE;" verhindert nicht nur, dass der 
fragliche Zweig durchlaufen wird, sondern, dass überhaupt in der ISR 
noch  irgend etwas geschieht.


Ich empfehle Dir, für eine Analyse, eine Zeitlinie hinzumalen und 
einzutragen, in welcher Reihenfolge, welche Aktion ausgeführt wird, 
resp. welche Entscheidung wie ausfällt.

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Hier noch ein ganz anderer Lösungsweg:
External Interrupt auf jede Flanke des Eingangssignals konfigurieren und 
damit die 20ms Blinke der LED starten. 100ms sieht man aber besser.

von Nicola R. (pastulla)


Lesenswert?

Hallo Theor,

danke für die schnelle Antwort.

Theor schrieb:
> Das heisst, "Messungfertig = TRUE;" verhindert nicht nur, dass der
> fragliche Zweig durchlaufen wird, sondern, dass überhaupt in der ISR
> noch  irgend etwas geschieht.

Ja, das stimmt. Die Periode des Eingangssignals ist dann ja schon 
bekannt, deshalb dachte ich mir ich könnte diesen Zweig komplett 
überspringen, damit der Wert nicht wieder überschrieben wird. Natürlich 
gibt es dann ein Problem wenn sich die Frequenz des Signals ändert.

Was mir gerade auffällt: Ich könnte später auch wieder Timer1 statt 
Timer0 nutzen, den brauche ich dann ja nicht mehr..

Theor schrieb:
> Bitte beschreibe die Fehlersymptome.

Wenn ich das Programm lade passiert nichts an Port D2.
Wenn ich die Bedingung in if (counter2 < periode)ändere landet er in dem 
Zweig und die LED geht wie geplant an. Also zählt Timer0 nicht hoch bzw. 
erreicht periode nicht.

Die Flankenerkennung habe ich auch getestet, indem ich die zwei 
LED-Zeilen (PORTD |= _BV(PD2);_delay_ms(20);) in die entsprechenden 
Zweige kopiert habe. Läuft.

Starte ich den Timer0 vielleicht falsch?


Danke und Grüße

Nicola

von Nicola R. (pastulla)


Angehängte Dateien:

Lesenswert?

Hallo Matthias,

danke für die Antwort.
Wenn ich zwei verschiedene Ausgänge in den beiden Zweigen des Interrupts 
aktiv schalte bekomme ich dies:

Sind jetzt keine LEDs mehr, aber so sieht man es besser..
Die Flankenerkennung sollte also funktionieren.

Meintest Du das?

Grüße

Nicola

von Nicola R. (pastulla)


Lesenswert?

Ach jetzt habe ich es verstanden.. Ja, wenn ich immer 50% Tastgrad hätte 
könnte ich auf steigende und fallende Flanke triggern, dann wäre ich bei 
der doppelten Frequenz.

von Nicola R. (pastulla)


Lesenswert?

So auf ganz lange Sicht möchte ich die Mikrocontroller gerne dazu nutzen 
Steuersignale von modularen Synthesizern zu verändern.

Da können dann auch Eingangssignale mit unterschiedlichem Tastgrad dabei 
sein.

Im Moment möchte ich erst einnmal lernen, wie ich mit dem Controller und 
den Timern überhaupt umgehe, damit ich später mit verschiedenen 
Eingangssignalen arbeiten kann. Deshalb diese Übungsaufgabe.

von Nicola R. (pastulla)


Lesenswert?

So, ich habe das Problem etwas eingegrenzt:
Offenbar wird der Durchlauf von timer0 nicht registriert, d.h. 
NrOverflows2 bleibt 0 und der Wert counter2 = (NrOverflows2 * 256) + 
TCNT0 bleibt damit bei maximal 255 stehen. Damit bleibt er immer 
unterhalb des Periodenwertes.

Mir ist schon aufgefallen, dass die bits für die Interrupts zum Zählen 
des Timerüberlaufes bei beiden Timern gleich sind:

TIMSK = (1<<TICIE1) | (1<<TOIE1); // Interrupts akivieren, Capture + 
Overflow

Hier
https://www.mikrocontroller.net/articles/AVR-GCC-Tutorial/Die_Timer_und_Z%C3%A4hler_des_AVR

steht ganz unten etwas von einer Global Enable Interrupt Flag. hat es 
etwas damit zu tun?

von S. Landolt (Gast)


Lesenswert?

Soll das heißen, dass das eingangs gezeigte Programm noch immer aktuell 
ist?

> Global Enable Interrupt Flag

Wird dort doch gesetzt per /sei()/.

von Nicola R. (pastulla)


Lesenswert?

Hallo,
ja, das Programm ist noch aktuell.

Ich dachte Global Enable Interrupt Flag wäre vielleicht etwas anderes 
als sei().

von Matthias L. (Gast)


Lesenswert?

Ich würde Dir folgende Tipps geben:

1)
Nutze Digitale Ausgänge als Diagnose. Das main toggelt einen Ausgang. 
Damit siehst Du (am Oszi) das das Prg läuft.
1
int main()
2
{
3
  .. initalisation
4
  while(1)
5
  {  
6
    PORTA ^= _BV(PD1);
7
    .. cyclic work
8
  }
9
}

Die ISR zeigt Dir so an, das sie regelmässig aufgerufen wird:
1
ISR (TIMER0_OVF_vect)
2
{
3
  PORTA |= _BV(PD2);
4
  .. do_something
5
  PORTA &= ~_BV(PD2);
6
}


2)
>etwa 1-5 Hz, Tastgrad variabel zwischen 10-90%) blinken lässt.
[10..90]% bei 5Hz sind [20..180]ms. Das sind also die kurzesten Zeiten, 
die Du detektieren möchtest. Das ist für einen µC langsam.

Lasse einen Timer laufen. Evtl. den TIM0_OVR_vect. Diesen stellst Du so 
ein, das er alle 1ms anspricht, also überläuft. In diesem prüfst DU 
jetzt einfach, wie es um das abzutastende Signal gerade und letztens 
aussah. Dann kannst Du die Messungen durchführen. Also etwa so:
1
// globale Variablen
2
  uint32_t MillisecondTick;
3
  uint32_t LeadingEdge;
4
  uint32_t Period;
5
  uint32_t Signal;
6
  uint8_t  PinValueLast;
7
8
ISR (TIMER0_OVF_vect)
9
{
10
  //-- Diagnose -------------------------------------
11
  PORTA |= _BV(PD2);
12
  //-- Wert abholen ---------------------------------
13
  MillisecondTick++;      // globale Variable
14
  uint8_t  PinValue = PINA & _BV(PA2);  // oder welcher auch immer
15
  //-- positive Flanke ------------------------------
16
  if (    (PinValue     != 0x00)
17
       && (PinValueLast == 0x00)  )
18
  {
19
    // FrequenZ/Periodendauer errechnen -
20
    Period = (MillisecondTick - LeadingEdge);
21
    Signal = Period / 2;
22
    //-- neue Werte speichern -----------
23
    LeadingEdge  = MillisecondTick;
24
  }
25
  PinValueLast = PinValue;
26
  //-- Signal generieren ----------------------------
27
  if      (Signal  == 0) PORTA &= ~_BV(PD7);
28
  else if (Signal-- > 0) PORTA |=  _BV(PD7);
29
  else                   PORTA &= ~_BV(PD7);
30
  //-- Diagnose -------------------------------------
31
  PORTA &= ~_BV(PD2);
32
}


PS: nicht getestet.

von S. Landolt (Gast)


Lesenswert?

an Nicola R.:

Fehlt da nicht die Freigabe der Timer0-ISR, wie wäre es mit
1
TIMSK = (1<<TICIE1) | (1<<TOIE1) | (1<<TOIE0);

von S. Landolt (Gast)


Lesenswert?

PS:
Und irgendwie muss innerhalb der while-Schleife die LED ja auch wieder 
ausgeschaltet werden.

von Nicola R. (pastulla)


Lesenswert?

Hallo S. Landolt,

ja, das war es tatsächlich! Danke für den Hinweis! Jetzt läuft es alles.

Ich werde aber trotzdem mal versuchen den Vorschlag von Matthias 
umzusetzen, das sieht irgendwie sinnvoller aus als mein Ansatz. Für die 
Weiterverarbeitung ist ein tick von 1 ms sicher handlicher.

Ich mache hier noch nicht zu, sicher habe ich später noch Fragen zu dem 
Vorschlag.

von Nicola R. (pastulla)


Lesenswert?

Das mit der LED hatte ich auch gerade gemerkt.. danke!

von Theor (Gast)


Lesenswert?

Nicola R. schrieb:
> Hallo Theor,
>
> danke für die schnelle Antwort.
>
> Theor schrieb:
>> Das heisst, "Messungfertig = TRUE;" verhindert nicht nur, dass der
>> fragliche Zweig durchlaufen wird, sondern, dass überhaupt in der ISR
>> noch  irgend etwas geschieht.
>
> Ja, das stimmt. Die Periode des Eingangssignals ist dann ja schon
> bekannt, deshalb dachte ich mir ich könnte diesen Zweig komplett
> überspringen, damit der Wert nicht wieder überschrieben wird. Natürlich
> gibt es dann ein Problem wenn sich die Frequenz des Signals ändert.

Richtig. Ich bin auch davon ausgegangen, dass sich die Periode ändert. 
Dumm von mir. Aber ich gehe schon immer allen auf die Nerven wenn ich 
sowas nachfrage. :-)

> Was mir gerade auffällt: Ich könnte später auch wieder Timer1 statt
> Timer0 nutzen, den brauche ich dann ja nicht mehr..
Richtig. Falls sich die Periode nicht ändert. Aber ich würde das erstmal 
weiter so verfolgen, weil ich den Lerneffekt so für größer halte.

> Theor schrieb:
>> Bitte beschreibe die Fehlersymptome.
>
> Wenn ich das Programm lade passiert nichts an Port D2.
> Wenn ich die Bedingung in if (counter2 < periode)ändere landet er in dem
> Zweig und die LED geht wie geplant an. Also zählt Timer0 nicht hoch bzw.
> erreicht periode nicht.

Der Schluss ist voreilig. Du kannst eigentlich nur sagen, dass. falls 
die LED nie ein Signal bekommte, immer zu den Zeitpunkten zu denen der 
Vergleich erfolgt, counter2 != periode gilt.
Das sollte Dir zu denken geben. Wann genau erfolgt dieser Vergleich? 
Steht dieser Zeitpunkt in einem passenden Verhältnis zu der Zykluszeit 
des Timers? Gibt es nicht einem Möglichkeit, den Vergleich unabhängig 
von der Befehlsauführung zu machen.
Auch solltest Du nochmal überdenken, warum Du NrOverflows2 mit 256 
multiplizierst und welche Folgen das für die Bitbreite des Ergebnisses 
hat.

> [...]

Ich rate Dir, wie oben schon einmal, Dir zunächst mal im Überblick zu 
überlegen, was wann und aufgrund welcher Bedingungen geschehen soll und 
erst dann eine Implementierung zu versuchen.
Zusätzlich rate ich Dir, die Kapitel über die Timer und Interrupts 
nochmal gründlichst zu studieren.
Und modifiziere am besten erstmal nicht vorhandenen Code sondern 
schreibe eigenen. Falls Du aber darauf bestehst, dann mache diese 
Überlegungen zunächst mal in Bezug auf den unmodifizierten Code.

von Theor (Gast)


Lesenswert?

Nicola R. schrieb:
> Hallo S. Landolt,
>
> ja, das war es tatsächlich! Danke für den Hinweis! Jetzt läuft es alles.

Tatsächlich? Meine Güte. Na gut.

> [...]

von S. Landolt (Gast)


Lesenswert?

> Tatsächlich?

Ja, ich hatte auch zuerst gestutzt und dann gestaunt, besonders wegen 
dieser '=='-Abfrage; aber da beide Timer mit Vorteiler 256 laufen und 
sonst nichts passiert in der Hauptschleife, klappt das tatsächlich.

von Theor (Gast)


Lesenswert?

Ja. Hast wohl recht.

Der Timer bleibt 256ms auf einem Wert stehen. Inzwischen holt die CPU 
noch ca. 10 Mal Kaffee und schwatzt mit der Sekreärin.

von Nicola R. (pastulla)


Angehängte Dateien:

Lesenswert?

Hallo,

das wäre der Versuch mit dem ms-Tick.
Leider klappt es noch nicht so richtig, das neue Signal entspricht dem 
alten Signal, siehe Bild 1.
Wenn ich die alte Periode durch vier teile stimmt die neue Periode, aber 
es wird nur jedes zweite Rechteck ausgegeben (Bild 2).

Das Eingangssignal ist rot, das Ausgangssignal braun.

Ich vermute mal das Problem liegt vielleicht im Bereich if (Signal  == 
0).
Jetzt essen wir erst mal, vielleicht komme ich ja noch dahinter.
Aber im Prinzip finde ich den Weg über den ms-Ticker gut.
1
/*
2
 * millisekundentick.c
3
 *
4
 * Created: 21.11.2019 18:31:46
5
 * Author : fdnr
6
 */ 
7
8
#ifndef F_CPU
9
#define F_CPU 1000000
10
#endif
11
12
#include <avr/io.h>
13
#include <avr/interrupt.h>
14
#include <util/delay.h>
15
#include <stdlib.h>
16
17
uint32_t mstick;
18
uint32_t flanke;
19
uint32_t periode;
20
uint32_t Signal;
21
uint8_t  aktueller_Wert =0;
22
uint8_t  letzter_Wert =0;
23
24
ISR( TIMER2_COMP_vect ) 
25
{
26
  PORTD |=_BV(PD0); //Diagnose-pin an (orange)
27
  mstick++; //counter hochzählen  
28
  aktueller_Wert = PINB & _BV(PB0); //Aktuellen Wert Rechteck übernehmen (weiss)
29
  
30
    if ((aktueller_Wert != 0x00) && (letzter_Wert == 0x00)) // Wenn neuer Wert true und alter Wert false -> Flanke
31
    {
32
      periode = (mstick-flanke); //Periodendauer = aktueller counterwert [ms] - counterwert von vorheriger Flanke
33
      Signal= periode/2; //Neue Periodendauer = 1/2 alte Periode
34
      flanke=mstick; // Wert für Zeitpunkt Flanke neu belegen
35
    }
36
    letzter_Wert = aktueller_Wert; // Wert für Zeitpunkt neu belegen
37
      if      (Signal  == 0) PORTD &= ~_BV(PD1); //Ausgabe neues Signal (braun): aus wenn mstick = flanke? Das passiert doch nie?
38
      else if (Signal-- > 0) PORTD |=  _BV(PD1); //An wenn Signal-1 größer null: Flanke wurde im vorherigen Durchgang erkannt
39
      else             PORTD &= ~_BV(PD1); //Aus in allen anderen Fällen
40
  
41
    
42
  PORTD &= ~_BV(PD0); //Diagnose-pin aus
43
}
44
45
int main(void)
46
{
47
    DDRD = 0xff; // Ports D als Ausgang
48
    DDRB = 0x00; // Ports B als Eingang
49
    //PORTD &= ~_BV(PD2); //LED an Port D2 aus
50
  
51
    TCCR2 = (1<<CS21) | (1<<WGM21); // Timer 2: PreScale 8, CTC ein 
52
  TIMSK = (1<<OCIE2); //interrupts ein
53
  OCR2 = 125; //f_cpu=1MHz, Teiler 8, counter bis 125 ergibt 1ms
54
  
55
  sei();
56
  
57
    while (1) 
58
    {
59
  
60
    }
61
}

: Bearbeitet durch User
von Matthias L. (Gast)


Lesenswert?

>Ich vermute mal das Problem liegt vielleicht im Bereich
Nein.

>aber es wird nur jedes zweite Rechteck ausgegeben
Nein. Es wird mit jeder positiven Flanke ein eigener Impuls mit 
definierter Länge kreiert. Das ist, wie ich später erkannt habe, nicht 
ganz das was Du möchtest. Aber mein Beispiel zsollte nur aufzeigen, wie 
ich daran gehen würde.


Das was Du ändern musst, ist das if-else if-else Konstrukt. Dort wird 
das Signal generiert. Direkt darüber wird ja nur die Periodendauer 
gemessen. Das wird ja benötigt.


EDIT:
>//Ausgabe neues Signal (braun): aus wenn mstick = flanke? Das passiert doch >nie?
Dies dient dazu zu verhindern, das bei Signal=0 weiter decrementiert 
wird.

>Flanke wurde im vorherigen Durchgang erkannt
Nein. In allen solange noch nicht auf Null runtergezählt wurde

von Nicola R. (pastulla)


Lesenswert?

Hallo Matthias,

vielen Dank für die Antwort und für die Erklärungen. Ich werde mir die 
Sache morgen noch einmal anschauen.


Hallo Theor,

Dir auch vielen Dank für Deine Anmerkungen.
Du hast recht, die Herangehensweise mit den beiden Timern ist 
wahrscheinlich nicht optimal und durch die Brust ins Auge. Für mich ist 
das eine gute Übung, ich lerne so etwas am besten, indem ich mich 
einfach dransetze und loslege.
Natürlich lese ich auch viel in den Datenblättern, Artikeln und 
Tutorials in diesem Forum. Ziemlich oft weiß ich aber noch nicht mal 
welche Information ich gerade brauche, da hilft mir vorhandener Code 
schon weiter.

Wenn ich dann ein größeres Projekt angehe werde ich tatsächlich einen 
Ablaufplan skizzieren, im Moment hilft mir trial, error, gezielt 
Nachlesen und Nachfragen gut weiter.
Und es ist ja auch nicht verkehrt, wenn dann Code dabei herauskommt, der
die CPU zum Kaffeeholen veranlasst. Das muss man ja auch erst einmal 
hinbekommen!

Grüße und bis demnächst mal

Nicola

von Theor (Gast)


Lesenswert?

Nicola R. schrieb:
> Hallo Matthias,
>
> vielen Dank für die Antwort und für die Erklärungen. Ich werde mir die
> Sache morgen noch einmal anschauen.
>
>
> Hallo Theor,
>
> Dir auch vielen Dank für Deine Anmerkungen.
> Du hast recht, die Herangehensweise mit den beiden Timern ist
> wahrscheinlich nicht optimal und durch die Brust ins Auge.
Das habe ich garnicht gesagt. 
Beitrag "Re: Bitte um Hilfe bei Timerprogrammierung" 
Ich habe nur zugestimmt, dass Du den selben Timer für die Messung und 
anschliessend für die LED-Ausgabe verwenden kannst, falls sich die 
Periodendauer nicht ändert. Das ist etwas ganz Anderes. Im Gegenteil 
habe ich Dir geraten bei der 2-Timer-Lösung wegen des Lerneffekts zu 
bleiben.
Es gibt dabei nämlich sehr viel Potential zur Vereinfachung, wenn man 
die Möglichkeiten der Timer kennt.
(Ich habe da eine Lösung im Kopf, bei der ich keine einzige if-Anweisung 
benötigen würde und lediglich eine statische und zwei Stack-Variablen im 
Interrupt. Stichwort wäre PWM.).

> Für mich ist
> das eine gute Übung, ich lerne so etwas am besten, indem ich mich
> einfach dransetze und loslege.

Meiner Erfahrung nach, kommt dabei im allgemeinen nicht viel rum. Aber 
vielleicht bist Du ja die Ausnahme, schreibst Dir das auf, machst Dir 
Gedanken, vergleichst mit dem Datenblatt und baust so ein gedankliches 
Modell der jeweiligen Algorithmus, des Prozessors auf. Erlebt habe ich 
das noch nie hier, bei Leuten, die meinen sie lernen von 
Trial-And-Error.

> Natürlich lese ich auch viel in den Datenblättern, Artikeln und
> Tutorials in diesem Forum.
Es geht mir nicht darum, dass Du viel liest, wo auch immer. Es geht 
nicht um die Menge. Sondern darum, dass Du die genannten Abschnitte über 
Timer und Interrupts liest.

> Ziemlich oft weiß ich aber noch nicht mal
> welche Information ich gerade brauche,
Das ist ein Kreislauf. Durch lesen lernst Du die Struktur und das 
Verhalten kennen. Danach ergibt sich aus der Aufgabenstellung eine 
bestimmte nötige Struktur und Verhalten. Danach, falls Du das nicht aus 
dem Gedächtnis in Übereinstimmung bringen kannst, suchst Du dann gezielt 
und lernst Neues. Bei der nächsten Aufgabenstellung musst Du wieder 
weniger lesen.

> da hilft mir vorhandener Code
> schon weiter.

Das halte ich für zweifelhaft, auch wenn ich annehme, dass das Deine 
aufrichtig Ansicht ist. Um den Code zu verstehen, musst Du nämlich 
gerade das im Datenblatt beschrieben Verhalten und die ebenso dort 
beschriebene Struktur kennen. Ein anderer Fall ist rein Daten 
manipulierender Code(, der keine Peripherie benötigt).

> Wenn ich dann ein größeres Projekt angehe werde ich tatsächlich einen
> Ablaufplan skizzieren, im Moment hilft mir trial, error, gezielt
> Nachlesen und Nachfragen gut weiter.

Hm. Ich meinte keinen Ablaufplan, sonst hätte ich "Ablaufplan" 
geschrieben, sondern, wie ich auch schrieb ein Gegenüberstellung der 
äusseren Ereignisse und des inneren Verhaltens, - also durchaus auch des 
Codes - aber nicht ausschliesslich.
Zitat: "... eine Analyse, eine Zeitlinie hinzumalen und
einzutragen, in welcher Reihenfolge, welche Aktion ausgeführt wird,
resp. welche Entscheidung wie ausfällt."
Zitat: "... was wann und aufgrund welcher Bedingungen geschehen soll 
..."
Ich will zugegeben, dass ich das Verhalten der Peripherie dabei nicht 
erwähnt habe, und die Annahme, ich rede von einem Programmablaufplan, 
nicht völlig unplausibel ist. Aber das ist ein Mißverständnis.

> Und es ist ja auch nicht verkehrt, wenn dann Code dabei herauskommt, der
> die CPU zum Kaffeeholen veranlasst. Das muss man ja auch erst einmal
> hinbekommen!
Nicht zu vergessen, der Plausch mit der Sekretärin. :-)

> [...]

von Peter D. (peda)


Lesenswert?

Nicola R. schrieb:
> Ich möchte ein Programm ertellen, dass eine LED mit der doppelten
> Frequenz eines externen Rechtecksignals (etwa 1-5 Hz, Tastgrad variabel
> zwischen 10-90%) blinken lässt.

Welchen Tastgrad soll dabei die LED haben?

von Nicola R. (pastulla)


Lesenswert?

Guten Morgen zusammen,

jetzt habe ich es hinbekommen und auch etwas besser verstanden. Ich 
hatte das -- falsch interpretiert, da hat meine c-Schwäche 
zugeschlagen..

Danke für die Hilfe und ein schönes Wochenende

Nicola
1
/*
2
 * millisekundentick.c
3
 *
4
 * Created: 21.11.2019 18:31:46
5
 * Author : fdnr
6
 */ 
7
8
#ifndef F_CPU
9
#define F_CPU 1000000
10
#endif
11
12
#include <avr/io.h>
13
#include <avr/interrupt.h>
14
#include <util/delay.h>
15
#include <stdlib.h>
16
17
uint32_t mstick =0;
18
uint32_t flanke;
19
uint32_t periode;
20
uint32_t Signal;
21
uint8_t  aktueller_Wert =0;
22
uint8_t  letzter_Wert =0;
23
24
ISR( TIMER2_COMP_vect ) 
25
{
26
  PORTD |=_BV(PD0); //Diagnose-pin an (orange)
27
  mstick++; //counter hochzählen  
28
  aktueller_Wert = PINB & _BV(PB0); //Aktuellen Wert Rechteck übernehmen (weiss)
29
  
30
    if ((aktueller_Wert != 0x00) && (letzter_Wert == 0x00)) // Wenn neuer Wert true und alter Wert false -> Flanke
31
    {
32
      periode = (mstick-flanke);
33
      Signal= periode;
34
      flanke = mstick;
35
    }
36
    letzter_Wert = aktueller_Wert;
37
    periode --;
38
      if    (periode >=  Signal*0.75)               PORTD |=  _BV(PD1);
39
      else if ((periode <= Signal*0.75) && (periode > Signal/2)) PORTD &= ~_BV(PD1);
40
    else if ((periode  <=  Signal/2) && (periode > Signal/4))  PORTD |=  _BV(PD1);
41
    else if (periode   <=  Signal/4)               PORTD &= ~_BV(PD1);
42
    else
43
   
44
  PORTD &= ~_BV(PD0); //Diagnose-pin aus
45
}
46
47
int main(void)
48
{
49
    DDRD = 0xff; // Ports D als Ausgang
50
    DDRB = 0x00; // Ports B als Eingang
51
    //PORTD &= ~_BV(PD2); //LED an Port D2 aus
52
  
53
    TCCR2 = (1<<CS21) | (1<<WGM21); // Timer 2: PreScale 8, CTC ein 
54
  TIMSK = (1<<OCIE2); //interrupts ein
55
  OCR2 = 125; //f_cpu=1MHz, Teiler 8, counter bis 125 ergibt 1ms
56
  
57
  sei();
58
  
59
    while (1) 
60
    {
61
  
62
    }
63
}

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.