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
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.
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
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.
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?
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.
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.
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.
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.
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 ....
@ 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.
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
intmain()
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.
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
intmain(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?
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?