PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Timer und Interrupt



c-m-m
21.11.2005, 11:37
Hallo,

ich habe hier erstmals ein AtMega16 vorliegen und tue mich zur Zeit noch etwas schwer.
Ich habe in der Vergangenheit nur mit Mikroprozessoren der 8051Serie gearbeitet.

Und zwar möchte ich zur Einarbeitung erst mal eine ISR jede sekunde ausführen.

Ich habe mir dazu gedacht den 16Bit zähler hochzählen zu lassen und sobald der Wert mit meinem Errecheneten Wert übereinstimmt soll die ISR aufgerufen werden.

Nun meine erste frage, wie erechene ich den Wert.
Meine annahme: Vergleichswert = 8MHz * 1s / 1024
Wenn ich den Vorteiler auf 1024 setze.
Das macht bei mir 7812,5 -> 7812

Falls das richtig ist, was ist dann an meinem Code falsch bzw, was habe ich vergessen:


TCCR1B = (1<<WGM12) | (1<<CS12) | (1<<CS10);

OCR1AH = 30;
OCR1AL = 132; /*Representiert 7812*/

TIMSK |= (1<<OCIE1A); /*Interrupt bei gleichheit*/
sei();


Meine ISR wird angesprungen aber häufiger als jede sekunde.
CS12 und CS10 setzen den Vorteiler auf 1024.
WGM12 sorgt dafür das der Zähler geleert wird bei gleichheit.

Meine ISR sieht folgendermaßen aus:


SIGNAL(SIG_OUTPUT_COMPARE1A)
{
/*mach was...*/
}

mfg C-M-M

SprinterSB
21.11.2005, 12:00
Sieht doch ok aus, bis auf den Rundungsfehler. Der ini-Wert für OCR1A:

#define F_CPU 8000000
#define INTS_PER_SECOND 1
#define PRESCALE 1024

...
OCR1A = (uint16_t) ((uint32_t) F_CPU*INTS_PER_SECOND/PRESCALE-1);


Evtl. liegts an der Reihenfolge, in der OCR1AH und OCR1AL geschrieben werden. Das ist nicht egal. Lass das gcc machen mit OCR1A=..., der weiß die richtige Reihenfolge.

Mit Prescale = 256 gibt's auch keinen Rundungsfehler mehr.

c-m-m
21.11.2005, 20:21
Danke schon mal für die schnelle Antwort.
Leide hat es nicht geholfen.
Laut ATmega16 dukumentation hatte ich den Wert auch in richtiger Reihenfolge geschrieben(zufall O:) ).
Die ISR wird scheinbar ständig aufgerufen. Das interrupt flag muß doch in der SIGNAL routine nicht extra gelöscht werden oder? (hat jedenfalls auch nichts gebracht)

Könnte es sein das die Fusebits vieleicht falsch gesetzt sind?
Ich bin mit meinem Latain am Ende, ich wollte nur mal kurz ein kleines Programm schreiben um mich einzuarbeiten. Wenn das nicht mal klappt weiß ich nicht wie ich ein Funktionsfähiges Programm schreiben soll.....

mfg c-m-m

Andun
21.11.2005, 20:41
Also mein Problem, war mal, dass es einfach um das 8-fach zu langsam war und zwar weil ich halt die Fuses ncoh auf 1 Mhz internen Oszi hatte.

Das fiese war auch noch, dass es halt invertiert ist. D.h. ein Haken bedeutet, dass das Fuse NICHT gesetzt ist. Das ist allerdings nicht bei jedem µC gleich, aber bei nem Mega16 ist es so. (Bin ich mir ziemlich sicher)

Andun

SprinterSB
22.11.2005, 09:57
Die ISR wird scheinbar ständig aufgerufen. Das interrupt flag muß doch in der SIGNAL routine nicht extra gelöscht werden oder?
So ist es; das Flag muss nicht extra gelöscht werden, wenn man über IRQ geht.

Bist du sicher, daß die ISR immer wieder aufgerufen wurd? Oder landest du immer wieder im RESET? In letzterem Falle tritt warscheinlich eine nicht implementierte IRQ auf, was nach __bad_interrupt abgebildet wird und nen Warmstart macht.

Poste man den kompletten Code, ist ja net so viel.

c-m-m
22.11.2005, 11:19
Hier mal mein Programm.
Ich weiß das es schlecht programmiert ist und der Interrupt eigentlich wenig Sinn macht weil der Contoller aktiv wartet.
Das programm stellt ein Lauflicht dar, welches funktioniert hat als ich in der wait() funktion noch mit Schleifen gewartet habe.


#include <inttypes.h>
#include <avr/io.h>
#include <avr/wdt.h>
#include <avr/signal.h>
#include <avr/interrupt.h>

#define F_CPU 8000000
#define INTS_PER_SECOND 1
#define PRESCALE 256

void wait(void);

volatile uint8_t wasinterrupted;

main()
{
wdt_disable();

TCCR1B = (1<<WGM12) | (1<<CS11) | (1<<CS10);

OCR1A = (uint16_t) ((uint32_t)F_CPU*INTS_PER_SECOND/PRESCALE);

TIMSK |= (1<<OCIE1A);

wasinterrupted = 0;
sei();


DDRC = 0xff;
PORTC = 0xFE;
while(1)
{
PORTC = 0xFE;
wait();
PORTC = 0xFD;
wait();
PORTC = 0xFB;
wait();
PORTC = 0xF7;
wait();
PORTC = 0xEF;
wait();
PORTC = 0xDF;
wait();
PORTC = 0xBF;
wait();
PORTC = 0x7F;
wait();
}
}

void wait()
{
while(!wasinterrupted);
wasinterrupted = 0;
}


SIGNAL(SIG_OUTPUT_COMPARE1A)
{
wasinterrupted++;
}

SprinterSB
22.11.2005, 12:30
Da gehen mir langsam die Ideen aus...
Mit dem WDT hab ich noch nicht gearbeitet, evtl schlägt er zu, bevor du in main() ankommst. In diesem Falle musst du das WDT-Disable früh machen, also vor der Initialisierung und bevor du nach main kommst.

Einige kleine Änderungen, die aber nicht groß was zur Sache tun. Aber man weiß ja nie ;-)
Ist jetzt ein 7-stelliges Lauflicht und die 8. LED blinkt.


#include <avr/io.h>
#include <avr/wdt.h>
#include <avr/signal.h>
#include <avr/interrupt.h>

#define F_CPU 8000000
#define INTS_PER_SECOND 1
#define PRESCALE 256

void wait(void);

volatile uint8_t wasinterrupted = 0;

main()
{
wdt_disable();

DDRC = 0xff;
PORTC = 0xbf;

TCCR1A = 0;
TCCR1B = (1<<WGM12) | (1<<CS11) | (1<<CS10);

OCR1A = (uint16_t) ((uint32_t)F_CPU*INTS_PER_SECOND/PRESCALE);

TIFR = (1<<OCF1A);
TIMSK = (1<<OCIE1A);

sei();

while(1)
{
PORTC ^= 0x41;
wait();
PORTC ^= 3<<0;
wait();
PORTC ^= 3<<1;
wait();
PORTC ^= 3<<2;
wait();
PORTC ^= 3<<3;
wait();
PORTC ^= 3<<4;
wait();
PORTC ^= 3<<5;
wait();
}
}

void wait()
{
wasinterrupted = 0;
while (!wasinterrupted);
}


SIGNAL(SIG_OUTPUT_COMPARE1A)
{
wasinterrupted = 1;
PORTC ^= 0x80;
}

c-m-m
23.11.2005, 11:03
Schönen dank an alle, ich habe mein Problem gelöst.
Es hatte nichts mit der Programmierung zu tun.
Ich habe die Fuse bits nun so gesetzt das ich die interne Clock verwende.
Jetzt muß ich nur noch herausfinden ob ich die Fusebits falsch gesetzt hatte oder ob ich ein Fehler auf meine Board habe.

mfg c-m-m