Forum: Compiler & IDEs Atmega 32, 8-Bit Timer(Timer 0)


von Benni D. (benni)


Lesenswert?

Hallo Zusammen,
ich bin relativ neu in der Mikrocontroller-Welt und wollte deswegen ein 
kleines Programm zum Verständnis der Timer und Interrupts schreiben.
Nach intensivem Studium des Datasheets komme ich aber hier leider nicht 
weiter. Das Problem:
Am gesammten Port C meines Atmega32 hängen 8 LEDs (LOW Aktiv PC0-PC7):
Der Sinn des folgenden Programms sollte einfach sein die LED an PC0 
mithilfe des 8-Bit Counters im CTC-Mode zum Blinken zu 
bringen.(Geschwindigkeit war mir erstmal egal).
Wenn ich das Programm jedoch starte leuchten alle LEDs an Port C auf und 
blinken auch nicht. Was mache ich falsch? Ich vermute, dass das Programm 
erst gar nicht in die ISR springt. Ist die Timer Initialisierung falsch?
Gruß

Benni
1
C-Code
2
3
#include <stdint.h>
4
#include <avr/io.h> 
5
#include <avr/interrupt.h>
6
7
//Interrupt Service Routine
8
ISR(TIMER0_COMP_vect)
9
{
10
static uint8_t c=0;
11
c=~c;
12
if (c==0){PORTC |= (1<<PC0);}    
13
14
if (c==1){PORTC &= ~(1<<PC0);} 
15
16
}
17
18
//Hauptprogramm
19
20
int main(int argc, char **argv) {
21
22
//Pin Konfiguration
23
DDRC = 0xFF; //PORT C als Ausgang
24
  
25
//Timer 0 initialisieren 
26
TCNT0 = 0x00; //Timer 0 mit Null initialisieren
27
OCR0 = 0xFE;  //Vergleichsregister initialisieren
28
TIMSK = (1<<OCIE0);    //Output Compare interrupt enable
29
sei();            //Enable Global Interrupt
30
31
//Timer Start
32
TCCR0 = ((1<<WGM01) | (1<<COM00) | (1<<CS02) |  (1<<CS00));
33
//         CTC Mode | Toggle OC0 | Prescaler clk/1024
34
            
35
while(1){};
36
  
37
}

von Johannes M. (johnny-m)


Lesenswert?

c kann bei der if-Abfrage nie "1" sein, weil Du c "0" setzt und danach 
das Bitkomplement bildest. c ist also zum Zeitpunkt der Abfrage immer 
0xff oder "0", jedoch nie "1".

von Johannes M. (johnny-m)


Lesenswert?

> int main(int argc, char **argv)
BTW:
Fällt mir grad so auf: Was soll der Blödsinn mit argc und argv denn da? 
main kann bei einem Mikrocontroller nicht woandersher aufgerufen werden 
und hat daher keine Parameter. Da kommt einfach ein "void" in die 
Klammer!

von Benni D. (benni)


Lesenswert?

Hi Johannes,
danke für die beiden Tipps!
Der "int main(int argc, char **argv)" Kram kam wohl von der 
Auto-Vervollständigen-Funktion von eclipse.
Der Fehler mit der zweiten If Abfrage ist mir auch schon aufgefallen...
Habe beides korrigiert, aber leider hat sich an dem Problem nichts 
geändert!
Ich suche weiter..
Danke aber trotzdem!
Gruß

Benni

von Johannes M. (johnny-m)


Lesenswert?

Benjamin D. wrote:
> Der "int main(int argc, char **argv)" Kram kam wohl von der
> Auto-Vervollständigen-Funktion von eclipse.
Aha...

> Der Fehler mit der zweiten If Abfrage ist mir auch schon aufgefallen...
> Habe beides korrigiert, aber leider hat sich an dem Problem nichts
> geändert!
WAS hast Du korrigiert? Schick doch mal den Code, wie er jetzt 
aussieht. Vielleicht haste das ja "verschlimmbessert"...

von Benni D. (benni)


Lesenswert?

Hi Johannes,
hier der Code wie ich ihn im Moment habe:
1
C-Code
2
#include <stdint.h>
3
#include <avr/io.h> 
4
#include <avr/interrupt.h>
5
6
//Interrupt Service Routine
7
ISR(TIMER0_COMP_vect)
8
{
9
static uint8_t c=0;
10
c=~c;
11
if (c==0){PORTC |= (1<<PC0);}    
12
13
if (c==255){PORTC &= ~(1<<PC0);} 
14
15
}
16
17
//Hauptprogramm
18
19
int main(void) {
20
21
//Pin Konfiguration
22
DDRC = 0xFF; //PORT C als Ausgang
23
  
24
//Timer 0 initialisieren 
25
TCNT0 = 0x00; //Timer 0 mit Null initialisieren
26
OCR0 = 0xFE;  //Vergleichsregister initialisieren
27
TIMSK = (1<<OCIE0);    //Output Compare interrupt enable
28
sei();            //Enable Global Interrupt
29
30
//Timer Start
31
TCCR0 = ((1<<WGM01) | (1<<COM00) | (1<<CS02) |  (1<<CS00));
32
//         CTC Mode | Toggle OC0 | Prescaler clk/1024
33
            
34
while(1){};
35
  
36
}

Gruß
Benni

von Johannes M. (johnny-m)


Lesenswert?

Sag mal, warum machst Du es so kompliziert? c kann genau zwei Zustände 
annehmen, 0 und FF. Sowas kann man in dem Fall besser mit if...else 
erschlagen und nicht mit zwei if hintereinander.

Allerdings ist die Umschalterei so eh unsinnig. Die Variable c ist 
überflüssig. Schreib die ISR mal um zu
1
ISR(TIMER0_COMP_vect)
2
{
3
PORTC ^= 1<<PC0;    //PortC.0 umschalten
4
}
Das sollte genau das machen, was Du erreichen willst und ist absolut 
"idiotensicher" (und kürzer)...

von Benni D. (benni)


Lesenswert?

Hi Johannes,
danke für deinen Verbesserugsvorschlag.
Habe das mal umgesetzt, sodass das Programm nun wie folgt aussieht.
1
C-Code
2
3
#include <stdint.h>
4
#include <avr/io.h> 
5
#include <avr/interrupt.h>
6
7
//Interrupt Service Routine
8
ISR(TIMER0_COMP_vect)
9
{
10
PORTC ^= 1<<PC0;    //PortC.0 umschalten
11
}
12
13
//Hauptprogramm
14
15
int main(void) {
16
17
//Pin Konfiguration
18
DDRC = 0xFF; //PORT C als Ausgang
19
20
//Timer 0 initialisieren 
21
TCNT0 = 0x00; //Timer 0 mit Null initialisieren
22
OCR0 = 0xFE;  //Vergleichsregister initialisieren
23
TIMSK = (1<<OCIE0);    //Output Compare interrupt enable
24
sei();            //Enable Global Interrupt
25
26
//Timer Start
27
TCCR0 = ((1<<WGM01) | (1<<COM00) | (1<<CS02) |  (1<<CS00));
28
//         CTC Mode | Toggle OC0 | Prescaler clk/1024
29
            
30
while(1){};
31
  
32
}
Da das Ergebnis immer noch das Selbe ist (Alle LED's leuchten permanent)
heißt das doch eigentlich, dass die ISR aus irgendeinem Grund erst gar 
nicht ausgeführt wird, oder?
Woran könnte das liegen? Müssen bestimmte FUSE BITS gesetzt werden um 
Interrupts ausführen zu können? Ist nicht doch vielleicht die Timer 
Initialiserung falsch?
Gruß
Benni

von Johannes M. (johnny-m)


Lesenswert?

Mit welcher Taktfrequenz läuft der Mega32 denn überhaupt? Und bist Du 
sicher, dass die LED wirklich an PortC.0 hängt?

von Johannes M. (johnny-m)


Lesenswert?

BTW:
Hast Du die Möglichkeit, die LED (oder von mir aus auch eine andere LED) 
an den OC0-Pin zu hängen? Wenn ja, dann mach das mal. Schließlich hast 
Du den Pin ja schon auf toggeln konfiguriert. Wenn sich da auch nichts 
tut, dann läuft irgendwas nicht richtig.

von Karl H. (kbuchegg)


Lesenswert?

Wichtig wär aber zunächst mal die Taktfrequenz, wie Johannes M.
schon sagte!

von Johannes M. (johnny-m)


Lesenswert?

Naja, wenn er den Mega32 nicht grad übertaktet, sollte selbst bei der 
maximalen Frequenz von 16 MHz eine "Blinkfrequenz" von ca. 30 Hz 
rauskommen, was eigentlich zumindest noch als Flimmern wahrzunehmen sein 
sollte...

von Benni D. (benni)


Lesenswert?

Hallo zusammen,
danke für eure zahlreichen Hilfen! Ich habe das Problem endlich gelöst.
Es lag alles nur an der von mir verwendeten Enwicklungsumgebung!
Ich benutzte das auf dieser Seite vorgestellte AVR-Plugin für Eclipse 
http://www.mikrocontroller.net/articles/AVR_Eclipse
den folgenden Hinweis habe ich jedoch übersehen:

WARNUNG: Bei mir funktionierten Timer-Interrupts mit dem Plugin nicht 
(die jedoch tadellos mit der WinAVR Makefile fuktionierten). Vielleicht 
habe ich nur eine Option übersehen, seid aber auf der Hut. Wenn ihr 
Unregelmäßigkeiten bei IRQs feststellt, versucht's erstmal ohne das 
Eclipse-Plugin (bevor ihr stundenlang an eurem Code und euch selbst 
zweifelt :-) ).

Habe mich dann nach Alternativen umgeguckt und bin hier fündig geworden:
http://sourceforge.net/projects/avr-eclipse

Damit funktioniert alles einwandfrei!
Vielen Dank vorallen an Johannes

Gruß

Benni

von Sanny (Gast)


Lesenswert?

Eine Lichtkette besteht aus 8 Luchtdioden.Diese werden von einem 
Mikrocontroller des Typs AVR ATMega8 angesteuert.Sie müssen verschiedene 
Aufgaben zur Programmierung des eingebetteten Prozessors lösen.Die 
Lichtkette sollzeitlich hochgenaueLichtwechsel ausführen.Sie benötigen 
dazu eine Zeitbasisvon 500ms.Initialisieren den Timer0 so,dass 
genaueinmal aller 500ms eine Interrupt SERVICE ROUTINE nebenläufig 
aufgerufen wird.Die Quarzfrequenz beträgt 0.032768 MHZ.
kann jemand die aufgaben lösen?Das wäre dankbar.
void initTimer0()
{

von Oliver (Gast)


Lesenswert?

>kann jemand die aufgaben lösen?

Kann denn heutzutage überhaupt niemand mehr seine Hausaufgaben alleine 
machen?

Das Tutorial ist oben links.

Oliver

von Greg01 (Gast)


Lesenswert?

Hallo an alle,

ich weiß jetzt nicht ob ich hier einfach mein problem reinschreiben 
darf,aber es trifft genau dasselbe Thema, deswegen denke ich, kann es 
nicht so verkehrt sein...

also ich habe auch den Atmega32, läuft mit 8 MHz, und möchte mit dem 
16-bit-timer (Timer1) und einer Interrupt-service-routine Wartezeiten 
erzeugen.

Hier mein Code:
1
#include <avr/io.h>
2
#include <stdint.h> 
3
#include <avr/interrupt.h>
4
5
unsigned int i;
6
7
// Initialisierung für TimerWarte (16-bit-Timer/Counter1)
8
void timer_warte_initial (void) {
9
  
10
  TCNT1 = 0x0000;  //Startwert 0 setzen
11
  OCR1A = 0x04E2;  //Vergleichsregister auf 1250
12
  TIMSK = (1<<OCIE1A);  //Output Compare Interrupt enable
13
  sei();                //Enable Global Interrupt 
14
  
15
  TCCR1A = 0x00;       
16
  TCCR1B = ((1<<WGM12)|(1<<CS11)|(1<<CS10));   //CTC-Mode, Prescaler 64
17
18
19
//Initialisierung des Timer 2 für PWM
20
void timer2_initial(void) {
21
22
  TCCR2 = 0x73;        //Mode 1, Signale invertieren, Prescaler 64  
23
  DDRD |= (1<<PD7);    //OC2 (Pin 21) als PWM-Ausgang setzen
24
}
25
26
//Interrupt-service- Routine
27
ISR(TIMER1_COMPA_vect) {     //Compare Match A - Vector 
28
  if (i>0) {
29
    i--;
30
    OCR2 = 0x71;         //PWM-Signal
31
  }
32
  else
33
    OCR2 = 0x9F;         //Anderes PWM-Signal
34
} 
35
36
int main(void) {
37
38
timer_warte_initial();
39
40
  while(1) {
41
  timer2_initial ();  //Timer 2 (für pwm) initialisieren
42
43
  unsigned int ADWert =300;       /*Wert soll eigendlich vom AD-Wandler    kommen,aber zu testzwecken, habe ich einen festen wert zugewiesen  */
44
45
  if (ADWert > 220) {    
46
     i = 300;      
47
   }
48
 
49
}
50
}
Der Timer soll mit 100 Hz einen Interrupt erzeugen. Dies habe ich mit 
dem Prescaler = 64 und Dem Vergleichsregister OCR1A = 1250 realisiert 
(CPU läuft mit 8 MhZ)

In der ISR soll dann ein pwm-signal => OCR2 = 0x71 anliegen bis i = 0 
ist.
i wird in der ISR runtergezählt. i ist zuständig für die Länge der 
Wartezeit. je höher der Wert desto länger ist die Wartezeit, in meinem 
Fall sind das 3 sekunden.
Nach diesen 3 sekunden soll dann das andere pwm-signal (also OCR2 = 
0x9F) ausgegeben werden.

Mein Problem ist allerdings, dass OCR2 immer 0x9F ist und bleibt.

Ich hoffe jemand sieht den fehler im Quelltext, ich finde meinen Fehler 
nicht, ich vermute allerdings, dass ein Fehler in der ISR und deren 
deklaration liegt.

also ich bin für jeden Hinweis dankbar,
und sorry, wenn es falsch war bein Problem ist diesen alten Beitrag 
reinzuschrieben.

viele Grüße aus Bochum
Greg

von Stefan E. (sternst)


Lesenswert?

volatile

von Karl H. (kbuchegg)


Lesenswert?

Greg01 schrieb:

> also ich bin für jeden Hinweis dankbar,

Dein i wird nie 0 werden.
Grund: In der Hauptschleife wird immer wieder auf 300 gesetzt, da ja 
ADWert immer größer als 220 ist.

So sehr sich die ISR auch abmüht, kaum hat sie i um 1 verringert, setzt 
die Hauptschleife es sofort wieder auf 300

Weiter hab ich nicht geschaut.

Edit: i volatile zu machen ist sowieso eine gute Idee.

von Greg01 (Gast)


Lesenswert?

Hallo,

vielen Dank für die Hinweise, mit diesen funktioniert es jetzt.
Allerdings möchte ich jetzt mein Programm so verändern, dass nur beim 
ersten Start des Programms das erste pwm-signal 2 sekunden anliegt und 
es dann auf das zweite umspringt.
Ich verstehe jetzt nur nicht, wo ich dann i = 200 (für  2 sekunden das 
erste pwm-signal) festlegen muss,damit es nicht immer i-- überschreibt.
also nochmal ein quelltext, ein bisschen verändert:
1
#include <avr/io.h>
2
#include <stdint.h> 
3
#include <avr/interrupt.h>
4
5
unsigned int i;
6
7
// Initialisierung für TimerWarte (16-bit-Timer/Counter1)
8
void timer_warte_initial (void) {
9
  
10
  TCNT1 = 0x0000;  //Startwert 0 setzen
11
  OCR1A = 0x04E2;  //Vergleichsregister auf 1250
12
  TIMSK = (1<<OCIE1A);  //Output Compare Interrupt enable
13
  sei();                //Enable Global Interrupt 
14
  TCCR1A = 0x00;       
15
  TCCR1B = ((1<<WGM12)|(1<<CS11)|(1<<CS10));   //CTC-Mode, Prescaler 64
16
17
//Initialisierung des Timer 2 für PWM
18
void timer2_initial(void) {
19
20
  TCCR2 = 0x73;        //Mode 1, Signale invertieren, Prescaler 64  
21
  DDRD |= (1<<PD7);    //OC2 (Pin 21) als PWM-Ausgang setzen
22
  i=200;
23
}
24
25
//Interrupt-service- Routine
26
ISR(TIMER1_COMPA_vect) {     //Compare Match A - Vector 
27
  if (i>0) {
28
    i--;
29
    OCR2 = 0x71;         //PWM-Signal
30
  }
31
  else
32
    OCR2 = 0x9F;         //Anderes PWM-Signal
33
} 
34
35
int main(void) {
36
37
timer_warte_initial();
38
timer2_initial ();
39
40
  while(1) {}  
41
42
}

ich dachte, indem ich timer2_initial() vor die while-schleife setze, 
wird i nur einmal auf 200 gesetzt und die ISR- zählt dann bis 0 runter 
und wechselt dann auf das 2.pwm-signal. Das funktioniert aber nicht, das 
pwm-signal bleibt immer auf OCR2= 0x71 stehen.
kan mir bei diesem Problem nch jemand helfen?
vielen Dank wie immer für die Unterstützung,
Greg

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.