PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Atmega 8 Timer



woodeye
16.09.2009, 10:45
Hallo Experten,
bin Neuling. Habe das Buch von Roland Walter gelesen (alles bis auf Timer auch verstanden).

Hier mein Problem:
Ich habe einen Atmega 8 mit 12 MHz Quarz zwischen PC und Steppertreiber
geschaltet (Parallelport).

Sinn??

Der PC liefert Takt und Richtung für X,Y,Z getrennt, Enable
jedoch nur einmal. Problem, wenn ein Motor läuft bekommen die anderen
beiden Strom und werden heiß.

Lösung:

Signal Takt über ATMEGA

Takt Eingang X=PD0; Y=PD3; Z=PD5
Takt Augang X=PD7; Y=PC1; Z=PC4

Enable X=PB0 ;Y=PC2; Z=PC4

Je ein Timer soll bei jedem Takt neu gestartet werden und Enable bis 200 mS nach dem letzten Takt auf H halten (aber nicht nach
jedem Takt abschalten, sondern nur, wenn nach 20 ms keinneuer Takt folgt).

Für einen Lösungsansatz mit Bascom wäre ich dankbar.

Gruß

Kalle

Sauerbruch
16.09.2009, 11:52
Habe das Buch von Roland Walter gelesen

Sehr gut - m.E. das Beste für den ersten Einstieg, was zur Zeit erhältlich ist :-)

Wenn Enable für 200 ms nach dem letzten Takt auf H gehalten werden soll, aber auf L gehen soll wenn nach 20 ms kein neuer Takt kommt - widerspricht sich das nicht? Vielleicht stehe ich ja auch nur auf der Leitung, denn ich hatte bisher noch keinen nennenswerten Kontakt zu Steppern...

Vielleicht kannst Du´s ja nochmal etwas genauer erklären - eine Lösung gibt´s bestimmt!

woodeye
16.09.2009, 13:43
Sorry, Tippfehler, natürlich muss es heissen "wenn nach 200 ms kein neuer Takt kommt"

Gruß

Kalle

Sauerbruch
16.09.2009, 17:32
Okay - also nochmal zusammengefasst:

Das Signal an D.0 soll an D.7 ausgegeben werden, D.3 an C.1 und D.5 an C.4.

Jedesmal wenn ein Ein- bzw. Ausgang H wird, soll der korrespondierende Enable-Pin (B.0, C.2 und C.4, der ist allerdings schonmal verwendet...) auf H gehen, und wieder auf L, wenn der dazugehörige Ein- bzw. Ausgang für 200ms L ist.

Korrekt?

woodeye
16.09.2009, 20:17
o.k. ich merke schon, das man beim programmieren genau sein muss und die Fragen nicht so nebenbei formuliert werden können.

Hier noch einmal die richtige Zuordnung (hat praktische Gründe, um beim Layout Kreuzungen zu vermeiden)

Takt Eingang X=D1;Y=D3;Z=D5

Takt Ausgang X= D7;Y=C1;Z=C4

Enable (Ausgang) X=B0;Y=C2;Z=C5

also keine Doppelbelegung.

die 200 ms sind zunächst ein Schätzwert, kann auch 500 ms oder 1 S sein.


Enable muss also so lane H sein, wie der Motor läuft. Den Ausgang wollte ich bei jedem Takt um n ms verzögern damit die Motortreiber aufgrund von Enable freigeschaltet werden.

Ich wollte für alle Richtungen folgendes ausprobieren:

If Eingang ungleich Ausgang dann Timer zurücksetzen und Ausgang gleich Eingang; nächste Abfrage. Danke , dass du dir Gedanken machst.

Gruß

Kalle

Sauerbruch
16.09.2009, 23:58
Okay - so langsam nimmt´s Formen an.

Sicherlich gibt´s viele Wege die nach Rom führen. Ich würde mir als Kernstück einen Timer so einstellen, dass er z.B. alle 10ms überläuft und ein Signalbit setzt. Also z.B. Timer 1 mit einem Prescaler von 16, d.h. einer Zählfrequenz von 1MHz, vorgeladen mit einem Wert von 55535.

Neben dem Signalbit wirst Du 3 Zeit-Variablen brauchen, die die vergangene Zeit seit dem letzten H-L-Wechsel zählen. Nennen wir sie ZeitX, ZeitY und ZeitZ, jeweils als Byte dimensioniert.


Die Hauptschleife würde ich immer damit beginnen, die Ausgänge gleich den Eingängen zu setzen, also


PORTD.7 = PIND.1
PORTC.1 = PIND.3
PORTC.4 = PIND.5


Dann müssen die Enables auf H gesetzt werden, wenn die Eingänge H führen:



If PIND.1 = 1 then
PORTB.0 = 1
End if

If PIND.3 = 1 then
PORTC.2 = 1
End if

If PIND.5 = 1 then
PORTC.5 = 1
End if



Alle 10ms, wird gecheckt, ob einer der drei Takte schon abgelaufen ist, der Enable-Ausgang aber noch H ist. In diesem Fall wird die dazugehörende Zeitzähl-Variable alle 10ms um 1 erhöht:



If Signalbit = 1 then
Signalbit = 0
If PIND.1 = 0 AND PORTB.0 = 1 then 'Takt ist vorbei, aber enable noch H
ZeitX = ZeitX + 1 'ZeitX erhöht sich alle 10ms um 1
End if

If PIND.3 = 0 AND PORTC.2 = 1 then
ZeitY = ZeitY + 1
End if

If PIND.5 = 0 AND PORTC.5 = 1 then
ZeitZ = ZeitZ + 1
End if

End if

Am Schluss der Hauptschleife checkst Du dann, ob eine der drei Zeitzähl-Variablen 20 erreicht hat (für 200ms, ansonsten entsprechend höhere Werte). Ist dies der fall, wird der Enable-Ausgang auf L gesetzt und die Zeitzähl-Variable auf 0 zurückgesetzt:



If ZeitX = 20 then
PORTB.0=0 'Enable-Ausgang auf L
ZeitX=0 'Zeitzähl-Variable rücksetzen
End if

If ZeitY = 20 then
PORTC.2 = 0
ZeitY = 0
End if

If ZeitZ = 20 then
PORTC.5 = 0
ZeitZ = 0
End if


Zu guter Letzt noch die Timer1-ISR:



Timer1_ISR:
Signalbit = 1
Timer1=55535
Return

Vorausgesetzt dass ich dein Problem richtig verstanden habe, könnte es so klappen. Ich hoffe, es ist nicht allzu verwirrend...

woodeye
17.09.2009, 09:23
Hallo und vielen Dank,
ich habe die Antwort überflogen und im Kopf sehe ich den Programmverlauf. Leider komme ich erst am WE dzu, das ganze auszuprobieren.

Ich gebe Montag eine Info, was das Programm mit den Treibern macht.

Gruß
und ein schönes WE

Kalle

Sauerbruch
17.09.2009, 12:30
War schon spät gestern Abend... Natürlich müssen die Zeitzähl-Variablen auch wieder auf 0 zurückgesetzt werden, wenn ein neuer Takt anfängt. Also am besten dort, wo die Enables auf H gesetzt werden, wenn der entsprechende Eingang H ist.

Schönes WE - bin gespannt, ob´s klappt :-)

woodeye
22.09.2009, 20:24
o.k.
da bin ich wieder. Leider hat die Herstellung der Platine länger als geplant gedauert. Klappt leider noch nicht. Ich sehe das Problem in der Zuordnung der Ausgänge. Die Enables müssen auf H gesetzt werden, wenn der Eingang von L auf H wechselt. Also die steigende Flanke ist entscheident. Gibt es einen Befehl, der die Flanke abfragt und dann die Enables auf H setzt.

Ich hoffe, wir kommen weiter.

Gruß

Kalle

Sauerbruch
22.09.2009, 20:41
Klar kommen wir weiter! :-)

Aber die Enables sollen doch (wenn ich alles richtig verstanden habe) nicht nur während der L/H-Flanke auf H gehen, sondern während der gesamten H-Phase des Eingangssignals und noch etwa 200 ms darüber hinaus H bleiben, oder?

Deshalb würde ich es qualitativ so machen:

1. Wenn Eingang=H, dann Ausgang=H, Enable=H und Anzahl der "Zeitticks"=0.
2. Wenn Eingang=L, dann Ausgang=L.
3. Wenn Eingang=L und Enable=H, dann 10ms-"Zeitticks" zählen
4. Wenn Eingang=L und Enable=H und 20 "Zeitticks" gezählt sind, dann Enable=L.

Kannst Du meine Gedanken ansatzweise nachvollziehen? :-)

woodeye
23.09.2009, 09:21
Ja, kann ich nachvollziehen. Wobei nicht unbedingt 200 ms nachdem Takt auf L gegangen ist sondern 200 ms nach der letzten steigenden Flanke. Dürfte aber in der Realität keine Auswirkungen haben.

Ich werde das ganze heute abend mal zusammenlöten und die Enable auf H setzten. Dann kann ich überprüfen ob Takt und Richtung durchgereicht werden (Richtung hatte ich nicht erwähnt, weil in diesem Zusammenhang nicht relevant).

Danke und bis später.

PS: Aufgrund anderer Aktivitäten (Familie, Job, andere Hobbys) probiere ich einiges erst mit Verzögerung aus. Ich gebe aber auf jeden Fall ein Feedback.

Gruß

Kalle

woodeye
24.09.2009, 10:34
sooo, langsam kommen wir der Lösung näher.

Hier mein Entwurf (allerdings nur für die X-Achse. Wenn das läuft ziehe ich Y und Z nach).

Da der Takt im Ruhezustand auf H liegt, musste die Abfrage angepasst werden.

Meine Idee:

Wenn Ausgang auf L (von der letzten Schleife gleich Eingang gesetzt) und Eingang auf H dann liegt eine steigende Flanke also neuer Takt vor und Enable geht auf H und der Zähler für X wird auf 0 gesetzt.

Hier mein Programm. Bitte einmal den Syntax chekken. Da bin ich nicht Sattelfest, da ich mich im wesentlichen mit der Hardware beschäftge und nur im Problemfall zu einem Programm greife (kann sich aber ändern :-))

Gruß

Kalle

Hier das Programm

Sauerbruch
24.09.2009, 11:45
Moin Kalle!

Kannst du das Programm bitte nochmal reinstellen, und dabei mit "Code-Tags" kennzeichnen? (die kleinen Schaltflächen neben "Nachrichtehtext"...). Dann kann man auch ohne installiertes Bascom den Code lesen :-)

Gruß,

Daniel

woodeye
24.09.2009, 13:19
Ist ja genial,

Ich glaube, es liegt an den "If then" Abfragen. Besonders den Codeschnipsel "If Signalbit =1 then Signalbit = 0 habe ich nicht verstanden. Werden die weiteren Abfragen nur ausgeführt, wenn die bedingung erfüllt ist? (also If PIND.1 =0 usw. nur wenn die erste Bedingung erfüllt ist?

Hier der Code



'CNC Stepper.bas
'Stepperdriver
'____________________________________________
'Der Atmega wird zwischen Centronics und L 297 geschaltet, um je Richtung
' Enable (H aktiv) aus dem Takt zu generieren.
'Taklt wird bei Ansteigender Flanke aktiv
$regfile = "m8def.dat"
$crystal = 10000000 'Quarz 10 MHz
'


Dim Signalbit As Bit
Dim Zeitx As Byte

Config Timer1 = Timer , Prescale = 8


Timer1_isr:
Signalbit = 1
Timer1 = 55535
Return

Ddrd = &B11000000 'Port D-Pins 0-5 Eingang
Ddrb = &B11111111 'Port B Ausgang
Ddrc = &B11111111 'Port C Ausgang


Portc.3 = Pind.0 'Richtung X
Portc.0 = Pind.2 'Richtung Y
Portd.6 = Pind.4 'Richtung Z

Portd.7 = Pind.1 'Takt X
Portc.1 = Pind.3 'Takt Y
Portc.4 = Pind.5 'Takt Z

Main:

If Pind.1 = 0 And Portd.7 = 1 Then 'Wenn seit dem letzten Loop Ausgang noch auf 0 und Eingang auf 1 dann Steigende Flanke
Portb.0 = 1
ZeitX=0
End If

If Signalbit = 1 Then
Signalbit = 0

If Pind.1 = 1 And Portb.0 = 1 Then 'Takt vorbei, aber enable noch H
Zeitx = Zeitx + 1
End If
End If



Portd.7 = Pind.1


If Zeitx = 20 Then
Portb.0 = 0
Zeitx = 0
End If


Goto Main



Gruß und danke

Kalle

Sauerbruch
24.09.2009, 15:00
Genau so ist es: Alles zwischen "If...then" und "end if" wird nur ausgeführt, wenn die Bedingung erfüllt ist, und übersprungen, wenn sie es nicht ist.

Ein paar Fehlerchen sind mir noch aufgefallen:

1. Die Hauptschleife kennzeichnet man am besten mit "Do" und "Loop" anstatt mit Main und Goto main.

2. Diese Zeilen:


Portc.3 = Pind.0 'Richtung X
Portc.0 = Pind.2 'Richtung Y
Portd.6 = Pind.4 'Richtung Z

Portd.7 = Pind.1 'Takt X
Portc.1 = Pind.3 'Takt Y
Portc.4 = Pind.5 'Takt Z

müssen unbedingt in die Hauptschleife - sonst wird der Eingangs-Status ja nur ein einziges mal auf die Ausgänge weitergegeben, und alle Änderungen an den Eingängen würden ins leere laufen.

3. Interrupt-Routinen sollten eigentlich immer hinter dem "Loop" stehen

4. Damit der Timer-Überlauf-Interrupt ausgeführt werden kann, musst Du

a. definieren, wie die Timer-ISR heißt
b. den speziellen Timer-Interrupt freigeben, und
c. die Interrupts allgemein freigeben.

Und das ganze am Besten VOR der Do-Loop-Schleife. In Code sähe das etwa so aus:



Config Timer1 = timer, Prescaler = 8
On Timer1 Timer1_ISR 'so heißt die Timer1-ISR. Kann auch jeder andere Name sein!
Enable Timer1 'aktiviert NICHT den Timer, sondern den Timer-Interrupt!!
Enable Interrupts 'aktiviert Interrupts allgemein



Das ganze Schritt für Schritt zu programmieren ist sicher eine gute Idee - erstmal bei fest stehenden Enables die Eingangssignale auf den Ausgängen auszugeben, und dann Funktion für Funktion dazuprogrammieren.

Gutes Gelingen - bin gespannt auf feedback!

Gruß,

Daniel

woodeye
25.09.2009, 15:44
Sooo, langsam wird es was. Ich habe mir noch einmal das Buch zur Hand genommen und augehend von einem dort beschriebenen Programm das Ganze angepasst.

Wie gesagt nur für X (Yun Z folgen ggf noch am WE

Hier der Code:


'CNC Stepper.bas
'Stepperdriver
'____________________________________________
'Der Atmega wird zwischen Centronics und L 297 geschaltet, um je Richtung
' Enable (H aktiv) aus dem Takt zu generieren.
'Taklt wird bei Ansteigender Flanke aktiv
$regfile = "m8def.dat"
$crystal = 10000000 'Quarz 10 MHz
'


Dim Signalbit As Bit

Dim Zeitx As Word

On Timer0 Ontimer0 'Interrupt-Routine für Timer0-Overflow
Config Timer0 = Timer , Prescale = 1024 'Timer-Takt ist Quarz/1024
Enable Timer0 'Timer0-Overflow-Interrupt ein
Enable Interrupts 'Interrupts global zulassen


Ddrd = &B11000000 'Port D-Pins 0-5 Eingang
Ddrb = &B11111111 'Port B Ausgang
Ddrc = &B11111111 'Port C Ausgang


Portd.7 = Pind.1 'Takt X
Portc.1 = Pind.3 'Takt Y
Portc.4 = Pind.5 'Takt Z

Do




If Pind.1 = 1 And Portd.7 = 0 Then Zeitx = 0
If Pind.1 = 0 And Portd.7 = 1 Then Zeitx = 0
If Zeitx < 10 Then Portb.0 = 1 Else Portb.0 = 0
If Zeitx > 1000 Then Zeitx = 100

Portc.3 = Pind.0 'Richtung X
Portc.0 = Pind.2 'Richtung Y
Portd.6 = Pind.4 'Richtung Z

Portd.7 = Pind.1

Loop

Ontimer0:
Zeitx = Zeitx + 1 'Timer0-Overflow-Interrupt-Routine

Return


Ich setzte einfach bei unterschiedlichem Ein und Ausgang die Zeit auf Null.

Bei Zeit kleiner n (im moment 10) ist Enable auf H.

Damit nicht beim Überlauf Enable auf H geht setze ich einfach bei Zeitx größer 1000 die Zeit wieder auf 100.

Es geht bestimmt auch schöner, aber es klappt.

Nur die Richtugsänderung wird noch nicht durchgereicht. Der Stepper läuft immer in die selbe Richtung. Den Fehler werde ich aber noch finden.

Erst einmal Schluss für Heute

Gruß

Kalle