Forum: Mikrocontroller und Digitale Elektronik Blinklicht in Assembler


von Ricardo Herrklotz (Gast)


Lesenswert?

Ich bin ein absoluter newbie in sachen mikrocontroller und assembler.

Ich möchte nun ein ganz einfaches Prog. schreiben, welches zwei led's
abwechselnd an PD0 und PD1 blinken lässt. und das ganze mit 1Hz.

Das Problem das ich hab ist, dass ich keinen Ahnung hab wie ich den
internen Timer meines ATmega8 (4MHz) verwenden kann.

Was ich bis jetzt herausgefunden habe ist, dass man irgendwelches Bits
setzten muss um einen "Multiplikator" (oder so ähnlich) einzustellen,
der dann sagt wie oft der Timer in der sek. zählt. Was ich auch schon
herausgefunden hab ist, das ich den Timer1 nehmen muss, weil ein 8bit
Timer nicht für so eine "große" Zeitspanne ausreicht.

Bei jedem Überlauf des Timers soll dann ein Interrupt ausgelöst
werden.

Doch wie konfiguriere ich den Timer und starte ihn? welche bits muss
ich setzten??

Könnt ihr mir vielleicht einen Link nennen wo das verständlich
beschrieben wird?

danke

von Chris (Gast)


Lesenswert?

Hallo,

Du setzt das Bit für globale Interruptfreigabe, setzt das Bit, damit
Timer1 einen Überlauf-Interrupt auslöst, stellst den Vorteiler ein, so
das die Quarzfrequenz geteilt durch den Wert des Vorteilers, z.B. 1024,
in die Nähe Deiner gewünschten Frequenz kommt. Takt  Vorteiler  65535
= Interruptfrequenz.
Welche Bits in welchen Registern das genau sind, steht im Datenblatt in
den Kapiteln zum Interrupt und zu dem Timer1. Dann kannst Du in der
Interruptroutine irgend ein Bit setzen, und dieses im Hauptprogramm
abfragen und an den Port weiter geben oder Du schiebst in der
Interruptroutine ein Register nach links und gibst dieses im
Hauptprogramm am Port aus.
Auf der Homepage von Scott-Falk Huehn ist ein Beispiel, wie einen LED
blinkt, allerdings ohne Interrupt.
oder hier, allerdings nicht für den Mega:
www.avr-asm-tutorial.net/avr_de/test4.html

Gruß

von Ricardo Herrklotz (Gast)


Lesenswert?

Danke ich werd mir das mal anschauen

von Ricardo Herrklotz (Gast)


Lesenswert?

aus den verschiedensten Seiten hab ich nun folgenden Code gebastelt:

.include "m8def.inc"


.org 0x000
         rjmp start
.org 0x08
         rjmp overflow      ; Timer1 Overflow Handler
;--------------------------------------
start:  ;-- stapel initialisieren
  ldi r16,HIGH(RAMEND)
  out SPH,r16
  ldi r16,LOW(RAMEND)
  out SPL,r16

  ;-- LED's initialisieren:
  ldi r16,0xFF
  out DDRB,r16
  ldi r16,0x00
  out PORTB,r16

  ldi r17,0b01010101

  sei
  rcall load_count
  ;--------------------------------
  ;prescaler auf 256 setzen
  ldi r16,0x04
  out TCCR1B,r16
  ;--------------------------------

  loop:  rjmp loop

;-----------------------------------------´
overflow:
  ;LED's ändern
  inc r17
  out PORTB,r17

  rcall load_count
  reti
;------------------------------------------
load_count:
  push r16

  ;Timer Counter mit 49911 vorladen
  ldi r16,HIGH(49911)
  out TCNT1H,r16
  ldi r16,LOW(49911)
  out TCNT1L,r16

  pop r16
  ret
;------------------------------------------


nur er funzt nicht und ich hab keine ahnung worans liegt.
Wenn ich den proz starte tut sich nichts. alle LEDs tot!
obwohl die LEDs in der Hauptroutine einmal alle angeschaltet werden!
sozusagen als funktionstest!

Aber da geht nichts!!
Woran könnte das liegen??

von Andi (Gast)


Lesenswert?

Du mußt den Timer1-IRQ noch aktivieren.
Nach dem rcall load_count setz mal folgendes ein:
"sbi TIMSK,TOIE1"

Gruß
Andi

von ...HanneS... (Gast)


Lesenswert?

sbi im oberen I/O-Bereich??

Ich habe jetzt zwar nicht konkret im Datenblatt nachgeschaut, aber ich
denke, dass die Register der Timer im oberen I/O-Bereich liegen.
SBI/CBI geht doch aber nur im unteren Bereich, oder??

...HanneS...

von Tobi (Gast)


Lesenswert?

sbi/cbi geht bis 0x20, timsk ist an 0x39

von Andi (Gast)


Lesenswert?

Ups, sorry, habe nur versucht, zu helfen!
Sich über andere Leute beschweren aber selber keinen Lösungsvorschlag
bringen, das ham ma gern ;-)
Habe bei mir gerade nachgesehen und da ist es auch mit out.
Aber das hasse ich wirklich am meisten bei den AVR´s. Dies und das geht
einfach mit diesem oder jenem Befehl ABER...
Ansonsten jibt es nix auszusetzen über die AVR´s.
Das nur mal so am Rande.

@Ricardo:
Dann füge folgendes, am besten lieber doch vor der Marke "loop",
ein:

 ldi r16,0b00000100
 out TIMSK,r16

oder

 ldi r16,1<<TOIE1
 out TIMSK,r16

Gruß
Andi

von Ricardo Herrklotz (Gast)


Lesenswert?

Danke für den Tip.

Leider muss sonst noch ein Fehler drinen sein!
Ich hab den Code nun ein bisschen geändert:

.include "m8def.inc"

.org 0x000
         rjmp start
.org 0x08
         rjmp overflow      ; Timer1 Overflow Handler
;--------------------------------------
start:  ;-- stapel initialisieren
  ldi r16,HIGH(RAMEND)
  out SPH,r16
  ldi r16,LOW(RAMEND)
  out SPL,r16

  ;-- LED's initialisieren:
  ldi r16,0xFF
  out DDRB,r16


  sei
  rcall load_count

  ldi r17,0b01010101
  out PORTB,r17

  ;--------------------------------
  ;prescaler auf 256 setzen
  ldi r16,0x00000100
  out TCCR1B,r16
  ;--------------------------------

  ;--------------------------------
  ;timer_1 overflow interrupt enable
   ldi r16,0b00000100
   out TIMSK,r16
   ;--------------------------------

  loop:  rjmp loop

;-----------------------------------------´
overflow:
  ;LED's ändern
  ror r17
  out PORTB,r17

  rcall load_count
  reti
;------------------------------------------
load_count:
  push r16

  ;Timer Counter mit 49911 vorladen
  ldi r16,HIGH(49911)
  out TCNT1H,r16
  ldi r16,LOW(49911)
  out TCNT1L,r16

  pop r16
  ret
;------------------------------------------

ich komm einfach nicht drauf woran es liegt!
der timer dürfte einfach nicht zu zählen beginnen oder kein interrupt
auslösen! Muss ich da noch irgend ein Bit setzen??

von Andi (Gast)


Lesenswert?

Du könntest ja mal zur Sicherheit die Liste für die Interrup-Vektoren
verfollständigen:

.org 0x000
         rjmp start         ; Reset-Vektor
         reti
         reti
         reti
         reti
         reti
         reti
         reti
         rjmp overflow      ; Timer1 Overflow Handler ($008)
         reti
         reti
         reti
         reti
         reti
         reti
         reti
         reti
         reti
         reti
         reti

Wenn von Haus aus ein IRQ aktiv ist, wird durch ein RETI verhindert,
das irgend was unvorhergesehenes passiert und Dein Prog.-Code ist nicht
innerhalb der Vektor-Liste.

Ansonsten lies Dir im PDF für den ATMega8 ab Seite 44 alles über
Interrups durch.
http://www.atmel.com/dyn/resources/prod_documents/doc2486.pdf

Vielleicht mußt Du noch irgend was wegen dem Bootloader machen, weiß
nur nicht was.
Aber da hilft nur Büffeln im PDF.

Gruß
Andi

von Ricardo Herrklotz (Gast)


Lesenswert?

ich hatte auch schon prob. alle interrupts zu deklarieren!
hatte aber nichts gebracht! Habs trotzdem nochmal probiert. Aber kein
Erfolg!

Naja dann packen wir mal usere Englischkenntnisse aus!

von Ricardo Herrklotz (Gast)


Lesenswert?

Das studieren des Datenblatts hat mich leider auch nicht wirklich
weitergebracht!

von Andreas Hesse (Gast)


Lesenswert?

Hallo,

Du kannst den Timer so konfigurieren, das Du ihn nicht jedesmal neu
laden muss (siehe CTC Clear Timer On Compare).

Laut Kommentar aktivierst Du den OverFlow Interrupt, verwendest aber
einen Compare Match Interrupt. Ich denke Du muesstest Bit 3 (also
0b00001000) nehmen.

Gruss
Andreas

von Jörg (Gast)


Lesenswert?

Hallo Ricardo,

du hast einen Fehler in Deiner Prescaler-Routine

  ;--------------------------------
  ;prescaler auf 256 setzen
  ldi r16,0x00000100
  out TCCR1B,r16
  ;--------------------------------

du musst ldi r16,0b0000100 eingeben. x bedeutet Hexadezimalwert wird
übergeben und der ist mit 00000100 out of range.

Vielleicht solltest Du dir AVR-Studio von der Atmel Seite laden und
Deine Programme darin simulieren. Habe ich auch mit Deinem Programm
gemacht und der Fehler wurde sofort angezeigt.

Gruß Jörg

von Jörg (Gast)


Lesenswert?

Nochmals ich,

falls die LED's abwechselnd blinken sollen würde ich statt

  ;LED's ändern
  ror r17
  out PORTB,r17

  ;LED's ändern
  com r17
  out PORTB,r17

schreiben. Also das 1ner Complement bilden (heißt nur alles was 0 ist
wird 1 und umgekehrt). ROR rotiert nach rechts und fügt ganz links das
Carry-Bit ein und da Du es beim ersten mal nicht gesetzt hast schiebt
er eine 0 ein.

Dazu empfiehlt sich das AVR Instruction Manual (auch bei Atmel).

Gruß Jörg

von Jörg (Gast)


Lesenswert?

Nochmals zum 3.,

falls Deine Programme später mal zeitkritischer werden, würde ich die
load_count-Routine in die Timer1-Overflow-Routine setzen.
Der Aufruf der Unterroutine load_count plus dem pusch und pop plus
Rücksprung kosten mal eben 11 Taktzyclen.
Weiterhin kann es sein, dass es zu Laufzeitfehlern kommt, wenn z.B. die
load_count-Routine durch einen anderen Interrupt unterbrochen wird.

Gruß Jörg

von Andi (Gast)


Lesenswert?

Hallo Jörg!
Wenn eine IRQ-Routine gestartet wurde, wird automatisch das I-Bit in
SREG (Global Interrupt Flag) gelöscht, d. h. das während des Ablaufs
einer IRQ-Routine keine andere IRQ-Routine gestartet wird.
Beim Rücksprung aus einer IRQ-Routine mit RETI wird das I-Bit wieder
automatisch gesetzt auch wenn vorher kein Hardware-Call zu einer
IRQ-Routine war.
Man kann das I-Bit mit SEI innerhalb einer IRQ-Routine wieder setzen
wodurch dann ein Interrupt innerhalb einer IRQ-Routine aufgerufen
werden darf aber so was sollte man dann mit eigenen Flag-Variablen
handlen.
Desweiteren kommt es im Normalfall bei einer IRQ-Auslösung während eine
IRQ-Routine läuft quasi zu einer zeitlichen verzögerung.
Z. B. wird für Timer1 das Flag Timer 1 Overflow Interruptflag gesetzt
aber die dazu gehörende IRQ-Routine erst gestartet, wenn die vorher
ausgeführte IRQ-Routine mit RETI beendet wurde.
Will damit nur sagen, das die Unterroutine load_count welche mit rcall
von der IRQ-Routine Timer1_Overflow aufgerufen wurde nicht unterbrochen
werden kann da währenddessen das I-Bit gelöscht ist und erst durch RETI
wieder gesetzt wird.

Gruß
Andi

von Jörg (Gast)


Lesenswert?

Hallo Andi,

stimmt hab ich nicht dran gedacht.

Was passiert denn, wenn wärend der Abarbeitung eines Timer 1 Overflow
Interrupts ein weiterer Timer 1 Overflow Interrupt auftritt?

Gruß Jörg

von Andi (Gast)


Lesenswert?

Wenn der Timer1 einen IRQ auslöst während Timer1 läuft wird so gut wie
kein Befehl mehr von dem normal ablaufenden Programm abgearbeitet.
Desweiteren stimmt dann die Frequenz nicht mehr für Timer1 und man
sollte die Frequenz verringern oder die Routine optimieren bzw. auf
weniger Takte bringen.

Gruß
Andi

von Jens (Gast)


Lesenswert?

hi

guck mal auf..


http://paste.phpfi.com/31595


nur als ansatz, da wird eine led immer abwechselnt an / ausgeschaltet

von Denis Gérard (Gast)


Lesenswert?

läuft der AVR überhaupt bzw. hast du mit einem kurzem testprogramm
mal geschaut ob irgendeine LED überhaupt an (aus) geht?

von Ricardo Herrklotz (Gast)


Lesenswert?

Danke für alle Tipps!

ich hatte eigentlich schon wenige Stunden nach der 1. Antwort gepostet
das der Fehler mit dem x schuld war!

der thread scheint aber gar nicht auf!?! Wahrscheinlich war irgend ein
kleiner Fehler mit er Verbindung zum Server oder sonst was!

Danke aber trotzdem für die vielen Antworten und Tips

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.