mikrocontroller.net

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


Autor: Benni D. (benni)
Datum:

Bewertung
0 lesenswert
nicht 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
C-Code

#include <stdint.h>
#include <avr/io.h> 
#include <avr/interrupt.h>

//Interrupt Service Routine
ISR(TIMER0_COMP_vect)
{
static uint8_t c=0;
c=~c;
if (c==0){PORTC |= (1<<PC0);}    

if (c==1){PORTC &= ~(1<<PC0);} 

}

//Hauptprogramm

int main(int argc, char **argv) {

//Pin Konfiguration
DDRC = 0xFF; //PORT C als Ausgang
  
//Timer 0 initialisieren 
TCNT0 = 0x00; //Timer 0 mit Null initialisieren
OCR0 = 0xFE;  //Vergleichsregister initialisieren
TIMSK = (1<<OCIE0);    //Output Compare interrupt enable
sei();            //Enable Global Interrupt

//Timer Start
TCCR0 = ((1<<WGM01) | (1<<COM00) | (1<<CS02) |  (1<<CS00));
//         CTC Mode | Toggle OC0 | Prescaler clk/1024
            
while(1){};
  
}


Autor: Johannes M. (johnny-m)
Datum:

Bewertung
0 lesenswert
nicht 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".

Autor: Johannes M. (johnny-m)
Datum:

Bewertung
0 lesenswert
nicht 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!

Autor: Benni D. (benni)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Johannes M. (johnny-m)
Datum:

Bewertung
0 lesenswert
nicht 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"...

Autor: Benni D. (benni)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi Johannes,
hier der Code wie ich ihn im Moment habe:
C-Code
#include <stdint.h>
#include <avr/io.h> 
#include <avr/interrupt.h>

//Interrupt Service Routine
ISR(TIMER0_COMP_vect)
{
static uint8_t c=0;
c=~c;
if (c==0){PORTC |= (1<<PC0);}    

if (c==255){PORTC &= ~(1<<PC0);} 

}

//Hauptprogramm

int main(void) {

//Pin Konfiguration
DDRC = 0xFF; //PORT C als Ausgang
  
//Timer 0 initialisieren 
TCNT0 = 0x00; //Timer 0 mit Null initialisieren
OCR0 = 0xFE;  //Vergleichsregister initialisieren
TIMSK = (1<<OCIE0);    //Output Compare interrupt enable
sei();            //Enable Global Interrupt

//Timer Start
TCCR0 = ((1<<WGM01) | (1<<COM00) | (1<<CS02) |  (1<<CS00));
//         CTC Mode | Toggle OC0 | Prescaler clk/1024
            
while(1){};
  
}

Gruß
Benni

Autor: Johannes M. (johnny-m)
Datum:

Bewertung
0 lesenswert
nicht 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
ISR(TIMER0_COMP_vect)
{
PORTC ^= 1<<PC0;    //PortC.0 umschalten
}
Das sollte genau das machen, was Du erreichen willst und ist absolut 
"idiotensicher" (und kürzer)...

Autor: Benni D. (benni)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi Johannes,
danke für deinen Verbesserugsvorschlag.
Habe das mal umgesetzt, sodass das Programm nun wie folgt aussieht.
C-Code

#include <stdint.h>
#include <avr/io.h> 
#include <avr/interrupt.h>

//Interrupt Service Routine
ISR(TIMER0_COMP_vect)
{
PORTC ^= 1<<PC0;    //PortC.0 umschalten
}

//Hauptprogramm

int main(void) {

//Pin Konfiguration
DDRC = 0xFF; //PORT C als Ausgang

//Timer 0 initialisieren 
TCNT0 = 0x00; //Timer 0 mit Null initialisieren
OCR0 = 0xFE;  //Vergleichsregister initialisieren
TIMSK = (1<<OCIE0);    //Output Compare interrupt enable
sei();            //Enable Global Interrupt

//Timer Start
TCCR0 = ((1<<WGM01) | (1<<COM00) | (1<<CS02) |  (1<<CS00));
//         CTC Mode | Toggle OC0 | Prescaler clk/1024
            
while(1){};
  
}
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

Autor: Johannes M. (johnny-m)
Datum:

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

Autor: Johannes M. (johnny-m)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

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

Autor: Johannes M. (johnny-m)
Datum:

Bewertung
0 lesenswert
nicht 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...

Autor: Benni D. (benni)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Sanny (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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()
{

Autor: Oliver (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>kann jemand die aufgaben lösen?

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

Das Tutorial ist oben links.

Oliver

Autor: Greg01 (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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:
#include <avr/io.h>
#include <stdint.h> 
#include <avr/interrupt.h>

unsigned int i;

// Initialisierung für TimerWarte (16-bit-Timer/Counter1)
void timer_warte_initial (void) {
  
  TCNT1 = 0x0000;  //Startwert 0 setzen
  OCR1A = 0x04E2;  //Vergleichsregister auf 1250
  TIMSK = (1<<OCIE1A);  //Output Compare Interrupt enable
  sei();                //Enable Global Interrupt 
  
  TCCR1A = 0x00;       
  TCCR1B = ((1<<WGM12)|(1<<CS11)|(1<<CS10));   //CTC-Mode, Prescaler 64


//Initialisierung des Timer 2 für PWM
void timer2_initial(void) {

  TCCR2 = 0x73;        //Mode 1, Signale invertieren, Prescaler 64  
  DDRD |= (1<<PD7);    //OC2 (Pin 21) als PWM-Ausgang setzen
}

//Interrupt-service- Routine
ISR(TIMER1_COMPA_vect) {     //Compare Match A - Vector 
  if (i>0) {
    i--;
    OCR2 = 0x71;         //PWM-Signal
  }
  else
    OCR2 = 0x9F;         //Anderes PWM-Signal
} 

int main(void) {

timer_warte_initial();

  while(1) {
  timer2_initial ();  //Timer 2 (für pwm) initialisieren

  unsigned int ADWert =300;       /*Wert soll eigendlich vom AD-Wandler    kommen,aber zu testzwecken, habe ich einen festen wert zugewiesen  */

  if (ADWert > 220) {    
     i = 300;      
   }
 
}
}

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

Autor: Stefan Ernst (sternst)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
volatile

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Greg01 (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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:
#include <avr/io.h>
#include <stdint.h> 
#include <avr/interrupt.h>

unsigned int i;

// Initialisierung für TimerWarte (16-bit-Timer/Counter1)
void timer_warte_initial (void) {
  
  TCNT1 = 0x0000;  //Startwert 0 setzen
  OCR1A = 0x04E2;  //Vergleichsregister auf 1250
  TIMSK = (1<<OCIE1A);  //Output Compare Interrupt enable
  sei();                //Enable Global Interrupt 
  TCCR1A = 0x00;       
  TCCR1B = ((1<<WGM12)|(1<<CS11)|(1<<CS10));   //CTC-Mode, Prescaler 64

//Initialisierung des Timer 2 für PWM
void timer2_initial(void) {

  TCCR2 = 0x73;        //Mode 1, Signale invertieren, Prescaler 64  
  DDRD |= (1<<PD7);    //OC2 (Pin 21) als PWM-Ausgang setzen
  i=200;
}

//Interrupt-service- Routine
ISR(TIMER1_COMPA_vect) {     //Compare Match A - Vector 
  if (i>0) {
    i--;
    OCR2 = 0x71;         //PWM-Signal
  }
  else
    OCR2 = 0x9F;         //Anderes PWM-Signal
} 

int main(void) {

timer_warte_initial();
timer2_initial ();

  while(1) {}  

}

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

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.