Hallo zusammen,
ich bin totaler Neuling was Mikrocontroller angeht. Ich habe versucht
eine LED mit meinem ATmega8-16PU im Sekundentakt blinken zu lassen, bin
daran aber jämmerlich gescheitert.
Die LED macht einfach überhaupt nichts.
Kann mir bitte einer sagen was in meinem Programm nicht stimmt.
(F_CPU = 4MHz)
#include <avr/io.h>
#include <avr/interrupt.h>
//Variable für die Zeit
volatile unsigned int bruchteile;
int main(void)
{
DDRC = (1<<PC5);
// Timer 0 konfigurieren
TCCR0 = (1<<CS00); // Prescaler 0
// Overflow Interrupt erlauben
TIMSK |= (1<<TOIE0);
// Global Interrupts aktivieren
sei();
while(1)
{
if(bruchteile == 0) PORTC ^= (1<<PC5);
}
return 0;
}
/*
Der Overflow Interrupt Handler
wird aufgerufen, wenn TCNT0 von
255 auf 0 wechselt (256 Schritte),
*/
ISR(TIMER0_OVF_vect)
{
/* Interrupt Aktion alle
4000000/256 Hz = 15625 Hz
bzw.
1/15625 = 0,064 ms
*/
bruchteile++;
if(bruchteile == 15625) bruchteile=0;
}
bruchteile ist 256 Takte lang Null. In dieser Zeit toggelst du laufend
(also mehrfach) die LED. Und wenn diese Toggel-Anzahl ganzzahlig ist,
hat sie hinterher den gleichen Zustand wie vorher, du siehst dann also
maximal ein sehr kurzes Aufblitzen.
Dann müsste ja, wenn ich es richtig verstanden habe, das Problem behoben
sein wenn ich schreibe:
ISR(TIMER0_OVF_vect)
{
/* Interrupt Aktion alle
4000000/256 Hz = 15625 Hz
bzw.
1/15625 = 0,064 ms
*/
bruchteile++;
if(bruchteile == 15625)
{
PORTC ^= (1<<PC5);
bruchteile=0;
}
}
(Meine while-Schleife ist jetzt leer)
Allerdings tut sich immernoch nichts.
Setze in Deinem Timer-Interrupt ein flag. Zum Beispiel so:
if(bruchteile == 15625)
{
bruchteile=0;
flag |= 1<<0
}
In der while(1) Schleife das Bit abfragen, und nach einmaligem toggeln
wieder zurücksetzen:
while(1)
{
if(bruchteile == 0) PORTC ^= (1<<PC5);
flag &= ~(1<<0)
}
return 0;
Hi!
Aus gegebenem Anlass (kam hier im Forum auch schon öffers mal vor) frage
ich:
Hast du die LED richtig herum in der Schaltung? Da hilft der beste Code
nix, wenn die LED mit der Kathode an + hängt... ;)
Hi
>Die Main bekommt es nicht mit, wenn sich die Variable "bruchteile" in>der ISR ändert, solange diese nicht als volatile definiert ist!
Wer Lesen kann....
MfG Spess
Joachim A. schrieb:
> Hast du die LED richtig herum in der Schaltung?
Ja, in anderen Schaltungen hat sie Funktioniert, es liegt wirklich nur
am Programm.
Magnetus schrieb:
> Die Main bekommt es nicht mit, wenn sich die Variable "bruchteile" in> der ISR ändert, solange diese nicht als volatile definiert ist!
Siehe oben, ist als volatile definiert.
1.8T-Passat schrieb:
> Setze in Deinem Timer-Interrupt ein flag.
Ich verstehe das nicht wirklich. Wie muss ich das flag definieren? Und
wie frag ich es später ab? Wie einen Pin oder anders?
Dein Code hat mehrere Probleme:
Wenn bruchteile gleich Null ist, ist das immer 256 Takte lang so. Das
ist ein Logikproblem beim Programmentwurf.
Und es wird in main() auf eine im Interrupt ggf. veränderte Variable
zugegriffen, die größer 1 Byte ist, d.h. der Zugriff ist nicht atomar.
Dieses Thema wird im AVR-GCC-Tutorial behandelt.
Eine mögliche Lösung für beide Probleme mit einem Kommunikations-Flag
zwischen ISR und main() sähe so aus:
1
#include<avr/io.h>
2
#include<avr/interrupt.h>
3
4
//Variable für die Zeit
5
volatileuint16_tbruchteile;
6
volatileuint8_taktion_ausfuehren;
7
8
intmain(void)
9
{
10
DDRC=(1<<PC5);
11
// Timer 0 konfigurieren
12
TCCR0=(1<<CS00);// No Prescaler
13
14
// Overflow Interrupt erlauben
15
TIMSK|=(1<<TOIE0);
16
17
// Global Interrupts aktivieren
18
sei();
19
20
while(1)
21
{
22
if(aktion_ausfuehren)
23
{
24
aktion_ausfuehren=0;
25
PORTC^=(1<<PC5);
26
}
27
}
28
return0;
29
}
30
31
/*
32
Der Overflow Interrupt Handler
33
wird aufgerufen, wenn TCNT0 von
34
255 auf 0 wechselt (256 Schritte),
35
*/
36
ISR(TIMER0_OVF_vect)
37
{
38
/* Interrupt Aktion alle
39
4000000/256 Hz = 15625 Hz
40
bzw.
41
1/15625 = 0,064 ms
42
*/
43
bruchteile++;
44
if(bruchteile==F_CPU/256)
45
{
46
bruchteile=0;
47
aktion_ausfuehren=1;
48
}
49
}
bruchteile bräuchte dafür nicht volatile zu sein, da es ausserhalb der
ISR nicht abgefragt oder geändert wird.
@ Stefan
Danke für deine Mühe, ich habe das Programm so eingefügt, aber es hat
auch nicht geklappt. Habe es bereits auch mit einem neuen ATmega8
versucht, aber auch keine blinkende LED.
Dann ist was oberfaul.
Ich habe das Programm auf dem Pollin Funk-AVR-Evaluationsboard am
laufen, allerdings weil ich die Onboard-LED nutzen will habe ich
DDRC/PORTC/PC5 auf DDRD/PORTD/PD5 angepasst. Es blinkt fröhlich im 1s
Hell/Dunkel Wechsel.
Dir ist es SICHER bereits gelungen, genau diese LED in genau dieser
Schaltung an PC5 ein- und auszuschalten?
Stefan B. schrieb:
> Dir ist es SICHER bereits gelungen, genau diese LED in genau dieser> Schaltung an PC5 ein- und auszuschalten?
Wenn das bereits funktioniert hat: Schalte die LED direkt nach der
DDR... Anweisung EIN. Die sollte dann 1s lang leuchten und dann im
Wechsel aus/an gehen.
Wenn - Spekulation - der AVR vor dem ersten Blinken ein RESET macht,
dann siehst du wenigstens, ob er mal angelaufen ist (LED kurz an). Beim
jetzigen Code siehst du in diesem Fall nix, Dunkel bleibt Dunkel. Sollte
ein RESET passieren, dann müssen wir den Watchdog kontrollieren und uns
näher mit der Schaltung befassen und besonders mit der
Spannungsversorgung.
Stefan B. schrieb:
> Dir ist es SICHER bereits gelungen, genau diese LED in genau dieser> Schaltung an PC5 ein- und auszuschalten?
Ja, soweit war ich schon. Hab es gerade eben auch nochmal mit einem
anderem Programm getestet. Also an der Schaltung liegt es sicher nicht.
Kann es sein das ich einen Fehler gemacht habe als ich die Frequenz
eingestellt habe? Ich habe dazu bei den Fuse-bits "Int. RC Osc. 4MHz;
Start-up time; 6 CK + 0ms" ausgewählt.
Denke das ist OK. Jedenfalls müsste in endlicher Zeit irgendwas
passieren. Deine Programm mit dem einfachen LED einschalten funktioniert
doch auch mit dieser Fuseeinstellung, oder?
Mit was arbeitest du - AVR Studio+WinAVR, WinAVR allein, AVR-GCC? Hast
du daran gedacht F_CPU in den Compileroptionen zu setzen? Stimmt der
Wert, besonders die Anzahl der Nullen :)
Was macht dieses Programm (mit -Os Optimierung übersetzen):
1
#include<avr/io.h>
2
#include<util/delay.h>
3
4
intmain(void)
5
{
6
DDRC=1<<PC5;
7
while(1)
8
{
9
PORTC^=1<<PC5;
10
_delay_ms(1000);
11
}
12
}
Wenn es blinken sollte, dann bitte Zählen wie oft in einer Minute.
Stefan B. schrieb:
> Mit was arbeitest du - AVR Studio+WinAVR, WinAVR allein, AVR-GCC? Hast> du daran gedacht F_CPU in den Compileroptionen zu setzen? Stimmt der> Wert, besonders die Anzahl der Nullen :)
Ich arbeite mit AVR Studio+WinAVR.
ÖÖÖÖHM, F_CPU setzen? Nein, habe ich nicht. Wär dir sehr dankbar wenn du
mir sagen könntest wie ich das mache.
Stefan B. schrieb:
> Was macht dieses Programm (mit -Os Optimierung übersetzen)
Die LED blinkt fröhlich vor sich hin.
Stefan B. schrieb:
> Wenn es blinken sollte, dann bitte Zählen wie oft in einer Minute.
Ich habe 120 mal gezählt. Und wie gesagt, habe 4MHz eingestellt.
Fabi schrieb:
> ÖÖÖÖHM, F_CPU setzen? Nein, habe ich nicht.
Dann müsste in deinem Build Fenster sowas stehen:
1
d:/winavr/bin/../avr/include/util/delay.h:85:3: warning: #warning "F_CPU not defined for <util/delay.h>"
Die util/delay.h setzt dann einen Defaultwert von 1000000UL. Und damit
werden die _delay_ms(1000) berechnet. In 1 Min. hättest du 30
Hellphasen.
> Ich habe 120 mal gezählt. Und wie gesagt, habe 4MHz eingestellt.
Du beobachtest 120, d.h. 4x30. Dein AVR läuft tatsächlich mit 4 MHz!
> Wär dir sehr dankbar wenn du> mir sagen könntest wie ich das mache.
In AVR Studio im Menü Projekt unter Configuration Options bei
Frequency 4000000 eintragen. Das neuübersetzte Einfachprogramm sollte
dann ein Ruhepuls von 30 haben...
Warum das Timer-Programm nicht läuft ist mir ein Rätsel. Hänge bitte die
HEX-Datei an, ich disassembliere das dann. Hat aber Zeit bis morgen,
weil ich gleich auf die Piste muss :)
Stefan B. schrieb:
> d:/winavr/bin/../avr/include/util/delay.h:85:3: warning: #warning "F_CPU not
defined for <util/delay.h>"
Stimmt! Ich hab nicht gewusst das man zusätzlich noch den MC auswählen
muss.
Jetzt funktioniert alles super! Also auch das Programm, dass du mir
verbessert hast.
Vielen Dank für diese super Hilfe.
Gruß Fabi
Hi Fabi
ich schreibe hier statt auf deine Email zu antworten, weil dann auch
andere ihren Senf^H^H^H Wissen dazugeben können und weil ich in der
Sache nicht 100% sicher bin.
Also in der Email schreibst du, dass du statt 30 Hellphasen in einer
Minute ca. 31-32 beobachtest und fragst, woher das kommen kann und wie
man das beheben kann.
Ich reime mir das mit der (Un)- Genauigkeit des verwendeten internen
RC-Oszillators zusammen.
Die Genauigkeit des internen RC-Oszillators ist von der
Versorgungsspannung und von der Temperatur und von Fertigungstoleranzen
abhängig. Atmel kalibriert die Chips lt. Datenblatt so, dass bei 5V 25°C
+- 3% erreicht werden.
Die 31 sind (31/30 - 1)*100 % = +3.3% (32 sind +6,7%) in der Taktrate.
Also in der Region, wo das die Kalibrierung in Betracht kommen kann.
Ein zweiter Effekt ist die Auflösung deiner Messung. Ich weiss nicht
wie du die 1 Minute misst, vielleiht mit dem Sekundenzeiger einer Uhr,
also das wäre eine Hell- oder Dunkelphase mehr oder weniger abhängig
davon wann du die Messung genau startest oder beendest. Und du zählst.
Der kleinste Zählschritt ist eine Hell- oder Dunkelphase. Schwankungen
in der Messung von +-1 Schritt wären also quasi vorprogrammiert. Bei
längeren Messungen sollte das besser werden.
Wie kann man die Genauigkeit erhöhen?
Atmel hat eine Appnote zum Rekalibrieren des internen RC-Oszillators
mithilfe des OSCCAL Registers. Laut Atmel sollen dabei +-1% machbar
sein. Ich selbst habe dazu aber keine praktische Erfahrung.
Oder du verwendest eine von Haus aus genauere Taktquelle wie z.B. einen
externen Ouarz mit wenigen hundert ppm (100 ppm = 0,1 o/oo = 0,01 %)
Fehler.
Wenn dich das Thema Genauigkeit und Auflösung weiter interessiert: Da
gibt es den schönen Artikel Auflösung und Genauigkeit