Forum: Mikrocontroller und Digitale Elektronik Timer atmega16, funktioniert nicht


von Andreas Horneff (Gast)


Lesenswert?

Hallo!

Nicht lachen, aber ich versuche jetzt schon den ganzen Tag einen Timer
auf einem atmega16 zum laufen zu bekommen (in C).
Kann mir mal bitte jm. ein Bsp für den 8-Bit und den 16-Bit Timer
posten? Wäre echt super nett, ich bekomme es einfach nicht hin. Hab
schon einiges dazu hier im Forum gelesen, klappt aber leider immer noch
nicht.

Vielleicht mal zu folgendem Beispiel. Ich habe an PortB 5 LED's
hängen, PINB0 - PINB4. Die sollen wie ein Lauflicht durchlaufen, mit
2sec Abstand. Sprich (1<<PINB0), 2sec an, dann aus und LED2 an.

Wäre super nett, wenn mir da mal jm. Bsp-Code mit vielen, vielen
Kommentaren posten könnte!!!

Danke schon mal,

Andreas

von crazy horse (Gast)


Lesenswert?

oder du stellst dein Programm hier rein und wir zerreissen dass dann
gnadenlos. Hinterher läufts aber :-)

von TravelRec. (Gast)


Lesenswert?

Ich will ja auch nicht klugsch... aber wär´s nicht einfacher, in
Assembler anzufangen? Beispiele dafür gibt´s im Datenblatt des
Controllers und in zahllosen Application-Notes und man braucht nur sehr
wenige Bytes Code dafür. Übersichtlicher für´s Anfängerauge ist´s
auch... Aber muß ja nicht....

von Christoph W. (christoph)


Lesenswert?

Also.

Zuerst musst du das TCCR? des jeweiligen Zählers beschreiben. Dann
läuft der Zähler, indem er TCNT? alle n Takte (n=Prescalerwert)
incrementiert. mit dem TimerInterruptMaskRegister kannst du den
OverFlow-Interrupt einschalten. Dann wird immer bei einem Überlauf der
Timer-Interrupt ausgeführt.

Am Anfang des Chips steht dann die Interrupt-Tabelle. Im
Reset-Interrupt steht dann z.B. rjmp RESET und im Timer0-interrupt rjmp
TIMER. in Reset initialisierst du den Chip (Stack unbedingt auf RAM-Ende
setzen), die Register, etc. und enablest die Interrupts (in Assembler :
sei). Am Ende eine Endlosschleife, in die sich der Prozessor einfängt.

Der Timer-Interrupt wird nun alle PRESCALE*TIMERBREITE Takte
ausgeführt. Timerbreite ist für einen 8bit von 1-256 und für einen
16bit 1-65536. Die Zeitspanne zwischen den Interrupts kannst du mit
(PRESCALE*TIMERBREITE)/TAKT ausrechnen.

Einstellen tust du die Timerbreite, indem du zu beginn des
Timerinterruptes die TCNT-Register analog dem Datenblatt auf einen
vordefinierten Wert setzt. Damit beginnt der Timer nicht jedesmal bei 0
mit dem Zählen. In ebendiesem Timer schreibst du dann noch eine Routine,
mit der deine LEDs gesetzt werden und fertig.

Als beispiel : mit einem 4MHz Takt stellst du Timer1 (16bit) auf einen
Prescaler von 256 ein und setzt TCNT1H und TCNT1L (reihenfolge des
Beschreibens beachten) auf (65536-31250), also 34286. Dann wird der
Timer/Counter Interrupt 1 exakt alle 2sec. ausgeführt.

Codebeispiel für die LEDs:

im RESET-Teil setzt du PORTB auf 0 (alle 5 LEDs aus)

in den TIMER-Teil kommt folgendes :

TEMP := PORTB; untere 5 Bits ausmasken
TEMP := TEMP shl 1 ; die Bits um 1 nach links schiften (0 in BIT 0
schiften)
TEMP := TEMP AND 0x1F ; die oberen 3 Bits ausmasken
if TEMP = 0 then TEMP = 1 ; wenn keine LED an (Bit ist ausmaskiert
worden, dann schalte PORTB.0 an.
PORTB := TEMP

So oder so ähnlich sollte es dann ablaufen.
Code ist nicht getestet und da ich nur Assembler programmiere auch
nicht in der C-Syntax.


Fragen, welche IO-Register wie zu verwenden und einzustellen sind,
beantworte ich mit einem Verweis auf www.atmel.com. Ziehe dir dort das
betreffende Datenblatt und lies es einfach nach.



Hoffe das hilft weiter

von Hannes L. (hannes)


Lesenswert?

> Ich will ja auch nicht klugsch... aber wär´s nicht einfacher, in
> Assembler anzufangen?

Mit der Meinung stehst du aber nicht alleine da... ;-)

Gruß von MD nach HBS...

...

von Andreas Horneff (Gast)


Lesenswert?

So, danke erst mal für die Antworten, wobei nicht alle produktiv waren.
Ich habe mich daran gemacht, AVR's mit C zu programmieren, da ich von
C ein bisschen Ahnung habe, von Assembler aber gar keinen.
In den Datenblättern gibt es unter jedem Assembler Beispiel-Code auch
den C-Code. Zumindest in denen, in denen ich bis jetzt geschaut hab.
Aber das soll ja jetzt auch erst mal nebensächlich sein, kann ja jeder
machen wie er will. Ich will das auf jeden Fall in C lernen.

Ich poste jetzt einfach mal meinen Code, ist ein bisschen umfangreich.
Kann mir jm. sagen, was da falsch ist, bzw. das nicht funktioniert???

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

int counter = 0;
int led_on = 0;

static void io_init(void)
{

  PORTA =0xff;  //eingänge
  DDRA = 0x0;
  // PortB
  PORTB = 0x0;  //ausgänge, an 0, 1, 2, 3, 4, hängen die LED's
  DDRB = 0xff;
  // PortC
  PORTC = 0x0;  //ausgänge
  DDRC = 0xff;
  // PortD
  PORTD = 0x0;  //ausgänge
  DDRD = 0xff;

  ACSR = 0x80;

}
void timerinit(void){
  //jetzt kommt die sache mit dem Timer
  TCCR0 |= (1<<CS00)|(1<<CS02);
  sei();
  //sollte doch heißen, dass der CPU-Takt / 1024 geteilt wird und dann
der
  //counter immer um eins hochgezählt wird.
  //bei 16MHz wären das dann immer noch 15,625KHz

}

  //in TCNT0 wird doch jetzt immer um eins hoch gezählt, wenn 1024
Prozessor-Takte
  //vorbei sind. Wenn der TCNT0 = 0xFF ist doch der Überlauf und es wird
ein
  //INTERRUPT ausgelöst, was zur folge hat, dass diese Funktion
aufgerufen wird, oder?
SIGNAL(SIG_OVERFLOW0)/* signal handler for tcnt0 overflow interrupt */
  {
    counter++;  //eine Fariable um 1 hochzählen
  }

int main(void)
{

  io_init();
  timerinit();


  while(1)
  {
    if (counter == 60){  //wenn der counter 60 Mal um eins hochgezählt
wurde,
              //ist in etwa eine Minute vorbei
      counter = 0;    //counter wieder auf null setzen
      PORTB = (1 << PINB1);

      if(led_on == 0){
        PORTB = (1 << PINB0);  //led0-anschalten
        led_on++;
        return;
      }
      if(led_on == 1){
        PORTB = (1 << PINB1);  //led1-anschalten
        led_on++;
        return;
      }
      if(led_on == 2){
        PORTB = (1 << PINB2);  //led2-anschalten
        led_on++;
        return;
      }
      if(led_on == 3){
        PORTB = (1 << PINB3);  //led3-anschalten
        led_on++;
        return;
      }
      if(led_on == 4){
        PORTB = (1 << PINB4);  //led4-anschalten
        led_on = 0;
        return;
      }


    }


  }
  return(0);
}

Wäre nett, wenn mir jm. weiterhelfen könnte. Ich weiß, das ist ne
Anfängerfrage, aber ich bekomme es halt nicht hin. Wenn also mal jm.
drüber schauen könnte, der sich damit besser auskennt als ich und mir
nicht nur sagen will, das ich besser mit Assembler programmieren
sollte, wäre ich euch echt dankbar!

mfg Andreas

von crazy horse (Gast)


Lesenswert?

TIMSK fehlt noch.

von Thomas K. (thkais)


Lesenswert?

Den counter musst Du volatile definieren, sonst könnte es sein, dass der
Compiler das innerhalb des Hauptprogramms in ein Register
hineinoptimiert.
Also:
volatile int counter;

von Andreas Horneff (Gast)


Lesenswert?

Hallo!

Vielen Dank für eure Hilfe!
Ich hab das jetzt mal so abgeändert, habe die Variable counter mit
volatile definiert.

Meine init_counter sieht jetzt so aus:
  TIMSK=0x01;  //Timer/Counter Interrupt Mask
  TCCR0=0x02;  //Timer/Counter Control Register auf CK/8
  sei();    //All Interrupt enable

Wenn ich mir das jetzt simulieren lasse, zählt der counter auch, also
TCNT0 zählt hoch. Wenn es jetzt zu einem überlauf kommt, sprich, wenn
TCNT0 = 0xFF, dann wird einfach meine main - Funktion neu aufgerufen,
sprich init_io in meinem Programm. Warum springt das Programm nicht in
meine SIGNAL(SIG_OVERFLOW0) Funktion? Ist die überhaupt richtig
angelegt so, oder hab ich da was falsch gemacht?

DANKE!!!!!!

von Karl H. (kbuchegg)


Lesenswert?

Wenn du SIGNAL verwendest, dann musst du auch
signal.h includieren

#include <avr/signal.h>

sonst kommts zu diesem Effekt.

von Andreas Horneff (Gast)


Lesenswert?

freu

Vielen Dank euch allen, ihr seit super! :) Funktioniert jetzt endlich.

Danke noch mal!!!!

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.