Forum: Mikrocontroller und Digitale Elektronik Intervall softwaremässig realisieren


von Anfänger (Gast)


Lesenswert?

Ich möchte per Software mit einem Atmega88 ein Zeitintervall in C 
realisieren. Wird das Zeitintervall aktiviert, dann soll eine Aufgabe 
erledigt werden und der uC wieder ein neues Intervall abwarten.

Ich würde mein Lösungskonzept gerne kurz vorstellen und fragen, ob es so 
realisiert werden könnte oder ob noch was dran verändert oder gar ganz 
anders gemacht werden sollte.

Ich habs mir einfach mal so überlegt:

Taktfrequenz F_CPU = 1MHZ
Intervall I = 300 Sekunden (5 Minuten).
Verwendet wird 8bit-Timer
Prescaler 1024
MaxWert_8bit 256

Ich hab es mir so überlegt, bin mir aber nicht sicher obs richtig ist:

Der uC zählt 1Mio mal in der Sekunde. Durch den Prescaler verringert 
sich die Frequenz auf 1.000.000Hz/1024 = 976Hz. Da ich einen 8bit-Timer 
verwende und der nur bis 255 zählen kann, löst der Überlauf-Interrupt 
des Zählers 976/256=3,8 mal in der Sekunde aus. Diesen Wert 
multipliziere ich noch mit 300 Sekunden und komme auf 1144 Schritte. 
Diesen Wert möchte ich Zeitimpulsrate nennen.
1
#ifndef F_CPU
2
#define F_CPU 1000000L
3
#endif
4
5
#define Zeitabstand 300 // Sekunden
6
#define PRESCALER_WERT 1024
7
#define MAXWERT_8BIT 256
8
9
#define Zeitimpulsrate (F_CPU/(PRESCALER_WERT*MAXWERT_8BIT))*Zeitabstand    // bei diesen Werten: 1144,41
10
11
#include <avr/io.h>
12
#include <util/atomic.h>
13
14
volatile uint16_t Zeitzaehler;
15
16
17
// Programmeigenes Flag für verschiedene Aufgaben
18
uint8_t eigenes_Flag;
19
20
#define Zeitimpuls_Overflow 1
21
#define Zeitimpuls_Overflow_BIT (1<<Zeitimpuls_Overflow)
22
23
24
ISR (TIMER0_OVF_vect)
25
{
26
  Zeitzaehler ++;
27
  
28
    if (Zeitzaehler == Zeitimpulsrate) { 
29
    // IF-Abfrage erzeugt warning. Liegt hier ein Fehler vor? Kann ich einem uint16_t den Wert Zeitimpulsrate zuordnen?
30
    // die warnings sind: division by zero     integer overflow
31
    // integer overflow find ich komisch, weil eine 16bit-Variable bis 65536-1 wertemässig gehen müsste?
32
    Zeitzaehler = 0;
33
    eigenes_Flag |= Zeitimpuls_Overflow_BIT;
34
  }
35
} // Ende Timer0_Overflow Interrupt
36
37
int main(void)
38
{
39
    //Timer0 konfigurieren
40
  TCCR0B |= (1<<CS00) | (1<<CS02);    // Prescaler 1024
41
  TCCR0B &= ~(1<<CS01);          // Prescaler 1024
42
  TIMSK0 |= (1<<TOIE0);          // Interrupt TIMER_0OVF anschalten
43
  Zeitzaehler = 0;
44
  
45
  // Interrupts einschalten
46
  sei ();  
47
  
48
  while(1)
49
    {
50
        if (eigenes_Flag & Zeitimpuls_Overflow_BIT) {
51
      // tu was
52
      
53
      // Zeitimpuls_Overflow_BIT zurücksetzen
54
      ATOMIC_BLOCK (ATOMIC_RESTORESTATE) {
55
        eigenes_Flag &= ~Zeitimpuls_Overflow_BIT;
56
      }
57
      
58
    }
59
    }
60
}  // Ende main

von Anfänger (Gast)


Lesenswert?

// Programmeigenes Flag für verschiedene Aufgaben
uint8_t eigenes_Flag;

soll auch volatile sein:
volatile uint8_t eigenes_FLAG;

von Falk B. (falk)


Lesenswert?

Im Prinzi ist das OK, die kleinen, gemeinen Details dazu findet man im 
Artikel Interrupt.

Die Fehlermeldung kann so nicht stimmen, denn dividiert wird dort rein 
gar nichts.

von Karl H. (kbuchegg)


Lesenswert?

Deine Warnungen bzw. Fehler hängen hiermit zusammen
1
#define Zeitabstand 300 // Sekunden
2
#define PRESCALER_WERT 1024
3
#define MAXWERT_8BIT 256
4
5
#define Zeitimpulsrate (F_CPU/(PRESCALER_WERT*MAXWERT_8BIT))*Zeitabstand

Ersetz mal im Makro die Zahlenwerte, dann steht da


   ( 1000000L / ( 1024 * 256 ) ) * 300


richte dein Augenmerk auf 1024 * 256

1024 ist ein int
256 ist ein int

Also wird diese Multiplikation als int * int durchgeführt und liefert 
als solches wieder ein int Ergebnis.

1024*256 ist aber rein rechnerisch 262144 und damit über dem Limit von 
32767 für ein 16-Bit int Ergebnis. Daher die Overflow-Warnung.


>    // integer overflow find ich komisch, weil eine 16bit-Variable
>       bis 65536-1 wertemässig gehen müsste?

Es sind nur 32767. Du sprichst von unsigned int, da sinds dann 65535. 
Nur hast du keine unsigned Zahlen geschrieben und damit sind die dann 
signed int.

Aber abgesehen davon: Es reicht nicht, wenn das Endergebnis im gültigen 
Bereich liegt. Es müssen auch alle Zwischenergebnisse im gültigen 
Bereich sein.

Und da dir die Multiplikation überläuft, kommt durch diesen Überlauf 
eine 0 zu stande (kann man sich leicht überlegen, wenn man sich das 
Bitmuster für 1024 ansieht und erkennt dass eine Multiplikation mit 256 
einfach nur ein linksschieben um 8 Bit ist. Bei einem 16 Bit int hat man 
dadurch alle 1-Bits von 1024 aus dem 16 Bit Bereich rausgeschoben und es 
bleibt eine glatte 0 übrig.

Oder anders ausgedrückt:
Das Bitmuster für 262144 lautet
 0b1000000000000000000
und wenn man das auf die von int geforderten 16 Bit einschränkt, dann 
bleibt
    0b0000000000000000
übrig. Und das ist nun mal eine glatte dezimale 0

von Falk B. (falk)


Lesenswert?

Ach ja, Prescalernutzung ist OK, aber man wird sich meist eine 
brauchbarere Zeitbasis einrichten, so 1-10ms. Die kann man nämlich auch 
für viele andere wichtige DInge nutzen, wie Tastenentprellung, Timeouts 
etc. Und man sollte einen ganzzahligen Teiler von 1s nehemen, sonst wird 
es unschön mit der Rechnerei.

von Anfänger (Gast)


Lesenswert?

Falk Brunner schrieb:
> Die Fehlermeldung kann so nicht stimmen, denn dividiert wird dort rein
> gar nichts.

Danke fuer die Antwort. Kann es sein, dass bei #define Zeitimpulsrate 
was schief läuft?

Schreib ich dort ne andere Zahl rein, zB. Festwert 1144 dann kommt keine 
Warnmeldung, schreib ich rein 1144.41 auch kein warning. Ich habs ein 
paar mal mitm Taschenrechner nachgerechnet, Punkt-vor-Strichrechnung 
gibts nicht ... Klammern sind meiner Meinung nach richtig gesetzt?

von Karl H. (kbuchegg)


Lesenswert?

Anfänger schrieb:
> Falk Brunner schrieb:
>> Die Fehlermeldung kann so nicht stimmen, denn dividiert wird dort rein
>> gar nichts.
>
> Danke fuer die Antwort. Kann es sein, dass bei #define Zeitimpulsrate
> was schief läuft?

NUr für den Fall, dass du es überlesen hast

Beitrag "Re: Intervall softwaremässig realisieren"

von Karl H. (kbuchegg)


Lesenswert?

Und nachdem du die Problemerklärung gelesen hast, liest du dir das hier 
mal durch
FAQ: Datentypen in Operationen
Scheinbar hast du das in deinem C-Buch überlesen bzw. ihm nicht die 
Beachtung geschenkt, die es verdient.

von Anfänger (Gast)


Lesenswert?

Hallo Karl Heinz Buchegger,

manchmal ist hier drinnen in dem Forum das Problem, dass man eine 
Antwort schreibt, dafür einiges an Zeit braucht und inzwischen die 
Antwort auf die Frage, die erst später gestellt wird, schon da ist 
(12:46 Uhr) - wie bei mir, ob was an dem #define falsch gemacht (12:52 
Uhr) hab, Du mich aber darauf hingewiesen hast. Das bitte ich zu 
entschuldigen.

Vielen Dank aber nochmal auch für Deine Erklärungen und den Hinweis zu 
dem Link Datentypen und Operationen.

von xfr (Gast)


Lesenswert?

Beachte auch, dass Du auch mit den richtigen Integertypen bei der 
Rechnung einen starken Rundungsfehler erhälst:
1
  (1.000.000 / (1024 * 256)) * 300
2
= (1.000.000 / 262.144) * 300
3
= 3 * 300
4
= 900

Also besser erst alles multiplizieren und dann dividieren. Aber darauf 
achten, dass es bei der Multiplikation nicht wieder zum Überlauf kommt. 
In diesem Fall passen 1.000.000 * 300 allerdings in ein int32_t bzw. 
uint32_t.

von Falk B. (falk)


Lesenswert?


von Anfänger (Gast)


Lesenswert?

Praktisch wäre es jetzt noch, wenn ich herausbekäme, wie man mit dem 
AVR-Studio beim Debuggen Variablenwerte beobachten könnte, die z.B. in 
einem separaten Fenster angezeigt und bei jedem Schritt aktualisiert 
werden.

von Anfänger (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Scheinbar hast du das in deinem C-Buch überlesen bzw. ihm nicht die
> Beachtung geschenkt, die es verdient.

Danke für diesen Hinweis. Das besondere Augenmerk muss man in diesem 
Fall wohl auf die #defines legen, erst durch Deinen Link bin ich darauf 
gestoßen:

"Welche Datentypen haben Konstanten?"

Dass haben wir in der Programmiervorlesung nicht behandelt und in dem 
Script steht zu den Datentypen bei Konstanten nix drinnen, ich lese hier 
erst zum ersten mal davon. Aber wir hatten das Programmieren auch nur 
als 2stündige Vorlesung im Nebenfach, von daher bitte ich meine 
Unkenntnis an manchen Stellen noch zu entschuldigen.

An einer anderen Stelle kam auch mal der Hinweis, dass man erst C 
richtig lernen sollte und nicht parallel mit den Mikrocontrollern. das 
Problem ist allerdings nur - man braucht eine oder mehrere kleine 
Programmieraufgaben, wo man solche Sachen ausprobieren könnte, was nicht 
immer so einfach ist und sich alles immer merken, funktioniert nicht 
immer. Manchmal lernt man eben auch aus Fehlern - so wie bei mir, dass 
ich bestimmten Sachen vorher nicht die notwendige Beachtung geschenkt 
hab, wie ich vielleicht hätte tun sollen. Aber Euch allen vielen Dank 
für die ganzen Hilfen, Links, Gedankenimpulse ....

von Anfänger (Gast)


Lesenswert?

Anfänger schrieb:
> und in dem
> Script steht zu den Datentypen bei Konstanten nix drinnen,

C-Skript von der Uni!!!

von Falk B. (falk)


Lesenswert?

@  Anfänger (Gast)

>An einer anderen Stelle kam auch mal der Hinweis, dass man erst C
>richtig lernen sollte und nicht parallel mit den Mikrocontrollern.

Ist auch so. Dazu reicht ein einfacher 0815 Compiler mit printf/scanf 
für Ein- und Ausgabe.

von Karl H. (kbuchegg)


Lesenswert?

Anfänger schrieb:

> An einer anderen Stelle kam auch mal der Hinweis, dass man erst C
> richtig lernen sollte und nicht parallel mit den Mikrocontrollern. das
> Problem ist allerdings nur - man braucht eine oder mehrere kleine
> Programmieraufgaben, wo man solche Sachen ausprobieren könnte,


Der springende Punkt ist nicht, dass du keine Übungen machen sollst, 
sondern mit welchem System du diese Übungen machst.

Auf einem µC der AVR Klasse ist das nicht so einfach, weil ein paar 
grundlegende Dinge einfach nicht so funktionieren wie in den Büchern. 
Kaufst du dir zb den Klassiker K&R und probierst du das erste Programm, 
das da drinnen enthalten ist aus
1
#include <stdio.h>
2
3
int main()
4
{
5
  printf( "Hello world!" );
6
}

dann funktioniert das auf jedem Nasenpopel-PC. Auf der Konsole (oder in 
einem Fenster, das sich öffnet) erscheint der Text.

Auf einem nackigen AVR funktioniert dieses Programm aber nicht. Denn 
dort musst du erst mal das Ausgabe-Subsystem lauffähig machen. Du 
brauchst erst mal Code, um die Ausgabe von printf entweder über eine 
UART oder zb auf ein LCD ausgeben zu können. Das was du auf dem PC von 
Anfang an zur Verfügung hast, nämlich eine funktionierende 
Infrastruktur, das hast du auf dem AVR nicht.

Die Empfehlung lautet NICHT deswegen auf Übungen zu verzichten, sondern 
die Empfehlung lautet: Mach deine ersten C-Schritte mit einem C-Compiler 
auf einem PC! Denn dort hast du genau die Infrastruktur zur Verfügung, 
die Debugumgebung etc. die im Buch vorausgesetzt wird. D.h. du 
strampelst dich da nicht erst mal enorm ab, um überhaupt die 
grundlegenden Dinge zum Laufen zu bekommen - die funktionieren auf dem 
PC einfach so aus dem Stand.

von Anfänger (Gast)


Lesenswert?

Ich hab nochmal eine Frage zu diesem Abschnitt:
1
ISR (TIMER0_OVF_vect)
2
{
3
  Zeitzaehler ++;
4
  
5
    if (Zeitzaehler == Zeitimpulsrate) { 
6
    Zeitzaehler = 0;
7
    eigenes_Flag |= Zeitimpuls_Overflow_BIT;
8
  }
9
} // Ende Timer0_Overflow Interrupt

Ich bin mir etwas unsicher - dass was ich hier gemacht hab - der 
Timer-OVF-Interrupt zählt einen Zeitzähler und vergleicht diesen Wert 
mit "Impulsrate" und setzt beim Erreichen eines bestimmten Wertes mein 
"eigenes_Flag" - ist das ähnlich mit dem CTC-Modus?

Aus dem GCC-Tutorial:
1
int main(void)
2
{
3
  // Timer 0 konfigurieren
4
  TCCR0A = (1<<WGM01); // CTC Modus
5
  TCCR0B |= (1<<CS01); // Prescaler 8
6
  // ((1000000/8)/1000) = 125
7
  OCR0A = 125-1;
8
 
9
  // Compare Interrupt erlauben
10
  TIMSK |= (1<<OCIE0A);

Entspricht hier OCR0A = XXX meinem if==Zeitimpulsrate? Und wenn 
Bedingung erfüllt, dann wird der Compare-Interrupt aufgerufen? Und wo 
ich bei mir mit "Zeitzaehler" die Anzahl der ausgelösten 
Overflow-Interrupts zähle, so kann ich mir das mit einer Variable 
sparen, weil das der uC durch den Compare-Interrupt "automatisch-fest 
eingebaut" für mich erledigt?

von Anfänger (Gast)


Lesenswert?

Anfänger schrieb:
> Entspricht hier OCR0A = XXX meinem if==Zeitimpulsrate? Und wenn
> Bedingung erfüllt, dann wird der Compare-Interrupt aufgerufen? Und wo
> ich bei mir mit "Zeitzaehler" die Anzahl der ausgelösten
> Overflow-Interrupts zähle, so kann ich mir das mit einer Variable
> sparen, weil das der uC durch den Compare-Interrupt "automatisch-fest
> eingebaut" für mich erledigt?

nur das dort auch die Variablen eher alle 8bit sind, wo ich bei 
"Impulsrate" eine 16bit-Variable hab, um auf 300 Sekunden zu kommen?

von Peter D. (peda)


Lesenswert?

Vielleicht könnte Dich das interessieren:

Beitrag "Wartezeiten effektiv (Scheduler)"

Peter

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.