Forum: Mikrocontroller und Digitale Elektronik Timer-Interrupt und Delay


von Karsten S. (scottyrebel)


Lesenswert?

Hallo,
folgendes Vorhaben:
Ich möchte ein bis zu 40kHz Steuersignal an den Ausgängen eines ATMega32 
generieren. Der µC wird mit 14.7456MHz getaktet. 40kHz entsprechen einer 
Zeit von 25µs und demensprechend ca. 369 Taktzyklen des Prozessor. 
Während dieser Zeit muss sich der entsprechende Port 3 mal ändern, also 
alle 123 Taktzyklen. Dazu habe ich mir jetzt gedacht ich setze den 
Prescalar auf Eins und lade den Timer mit 256 - 123 = 133 vor. Damit 
sollte die Interrupt-Routine alle ca. 8,33µs ausgeführt werden. Müsste 
dann so aussehen:
1. Aufruf ;nach 0µs
Bit0 am Port setzen
2. Aufruf ;nach 8,33µs
Bit1 am Port setzen
3. Aufruf ;nach 16,67µs
Bit0 am Port rücksetzen
Bit1 am Port rücksetzen

4. Aufruf ;nach 25µs
Bit0 am Port setzen
5. Aufruf ;nach 33.33µs
Bit1 am Port setzen
6. Aufruf ;nach 41,67µs
Bit0 am Port rücksetzen
Bit1 am Port rücksetzen
...

Ich hoffe es ersichtlich, das sich hier 40kHz ergeben.

Nun aber zu meinem Problem. Zwischen jedem 3er Block muss ich ggf. eine 
Pause einlegen, damit ich die Frequenz varieren kann. Diese Pause sollte 
bis zu 65535 Prozessortakte betragen.
Nun kann ich aber keine Verzögerungsschleife innerhalb der 
Interrupt-Routine einbauen, da dann in der Zeit keine anderen Interrupts 
abgearbeitet werden, das muss aber gewährleistet sein.

Bin für jede Idee offen.
--
Gruß Karsten

von Karl H. (kbuchegg)


Lesenswert?

Karsten Sosna schrieb:

> alle 123 Taktzyklen. Dazu habe ich mir jetzt gedacht ich setze den
> Prescalar auf Eins und lade den Timer mit 256 - 123 = 133 vor.

In dem Moment, in dem jemand das Wort 'Vorladen' in den Mund nimmt (bei 
kritischem, schnellen Timing) stellen sich bei mir alle Nackenhaare auf.

Das Stichwort für dich lautet: CTC-Modus des Timers

> Während dieser Zeit muss sich der entsprechende Port 3 mal ändern

Hä?

Eine Rechteckschwingung mit einer gewissen Frequenz kann dir der Timer 
ganz von alleine, rein in Hardware, erzeugen. OK. Du willst 2 Pins mit 
bestimmten Frequenzen beaufschlagen.


> Nun aber zu meinem Problem. Zwischen jedem 3er Block muss ich ggf.
> eine Pause einlegen, damit ich die Frequenz varieren kann. Diese
> Pause sollte bis zu 65535 Prozessortakte betragen.

Schalte den Timer in den CTC-Modus. In der ISR schaltest du die 
Ausgangspins nach dem gewünschten Schema.
Die neue Frequenz wird ausserhalb der ISR durch Variation des Compare 
Registers und/oder des Vorteilers eingestellt.

Wobei ich beim nochmaligen Durchlesen fast sicher bin, dass sich dein 
Vorhaben mit den richtigen Pins auch durch einen PWM Modus durch Einsatz 
der Compare Register komplett in Hardware lösen lässt.

von spess53 (Gast)


Lesenswert?

Hi

>Dazu habe ich mir jetzt gedacht ich setze den Prescalar auf Eins und lade
>den Timer mit 256 - 123 = 133 vor.

Alle Timer des ATMega32 haben OC-Register. Da kannst du dir das Vorladen 
sparen.

MfG Spess

von Karsten S. (scottyrebel)


Lesenswert?

> Alle Timer des ATMega32 haben OC-Register. Da kannst du dir das Vorladen
> sparen.

Hallo,
Danke, das wusste ich nicht. Werde es gleich mal probieren.
--
Gruß Karsten

von Karsten S. (scottyrebel)


Angehängte Dateien:

Lesenswert?

>> alle 123 Taktzyklen. Dazu habe ich mir jetzt gedacht ich setze den
>> Prescalar auf Eins und lade den Timer mit 256 - 123 = 133 vor.
>
> In dem Moment, in dem jemand das Wort 'Vorladen' in den Mund nimmt (bei
> kritischem, schnellen Timing) stellen sich bei mir alle Nackenhaare auf.
>
> Das Stichwort für dich lautet: CTC-Modus des Timers

Ich glaube, wir verstehen uns noch nicht, siehe unten

>
>> Während dieser Zeit muss sich der entsprechende Port 3 mal ändern
>
> Hä?

Schau doch bitte mal das angehängte Diagramm an. Wie Du sicherlich 
erkennst ensteht dort eine Frequenz von ca 40kHz. Beachte nur ein Bit. 
Das Pulse-Pausen-Verhältnis interessiert nicht, das muss so sein.
Ich sehe nur eine Möglichkeit die 123 Takte zu erreichen und das ist in 
dem ich den Timer vorlade. Wenn es noch anders geht, wäre ein kleines 
Beispiel hilfreich.

> Eine Rechteckschwingung mit einer gewissen Frequenz kann dir der Timer
> ganz von alleine, rein in Hardware, erzeugen. OK. Du willst 2 Pins mit
> bestimmten Frequenzen beaufschlagen.

Es ist ein und dieselbe Frquenz, siehe oben.

>> Nun aber zu meinem Problem. Zwischen jedem 3er Block muss ich ggf.
>> eine Pause einlegen, damit ich die Frequenz varieren kann. Diese
>> Pause sollte bis zu 65535 Prozessortakte betragen.
>
> Schalte den Timer in den CTC-Modus. In der ISR schaltest du die
> Ausgangspins nach dem gewünschten Schema.
> Die neue Frequenz wird ausserhalb der ISR durch Variation des Compare
> Registers und/oder des Vorteilers eingestellt.
>
> Wobei ich beim nochmaligen Durchlesen fast sicher bin, dass sich dein
> Vorhaben mit den richtigen Pins auch durch einen PWM Modus durch Einsatz
> der Compare Register komplett in Hardware lösen lässt.

Nochmals zum Diagramm: Dort wo der 4.ISR-Aufruf ensteht muß eine 
variable Pause enstehen. Diese sollte von 0 bis 65535 Prozessortakten 
gehen. Bei 0 wären es wirklich die ca. 40kHz. Bei 65535 sind es dann 
65535+369=65904 Takte welches einer Frequenz von 1/(65904*(1/14745600))= 
ca. 224Hz entspricht.

Hoffe jetzt ist es deutlicher, was ich vorhabe.
--
Gruß Karsten

von Karl H. (kbuchegg)


Lesenswert?

Karsten Sosna schrieb:

> Ich glaube, wir verstehen uns noch nicht, siehe unten

Doch.
Nach der Hälfte deines Urpsrungspostings ist es klarer geworden.

> Ich sehe nur eine Möglichkeit die 123 Takte zu erreichen und das ist in
> dem ich den Timer vorlade.

Und ich sehe nach wie vor die Möglichkeit, den Timer so einzustellen, 
dass er sich ganz alleine um die 123 Takte kömmert.

Von dort Weg gibt es 2 Möglichkeiten

* entweder man macht eine ISR, in der die Pins geschaltet werden
  (mit dem CTC Modus, damit man den Timer eben nicht vorladen muss)
* oder aber man benutzt einen der PWM Modi bei einem Timer, dessen
  PWM
    + auf eine obere Grenze eingestellt werden kann
    + 2 Pins ansteuern kann

> Nochmals zum Diagramm: Dort wo der 4.ISR-Aufruf ensteht muß eine
> variable Pause enstehen.

Wozu brauchst du die Pause?
Das hab ich noch nicht verstanden.

von Karsten S. (scottyrebel)


Lesenswert?

Hallo Karl Heinz,

> * entweder man macht eine ISR, in der die Pins geschaltet werden
>   (mit dem CTC Modus, damit man den Timer eben nicht vorladen muss)

Das habe ich gerade probiert und damit kann arbeiten

> * oder aber man benutzt einen der PWM Modi bei einem Timer, dessen
>   PWM
>     + auf eine obere Grenze eingestellt werden kann
>     + 2 Pins ansteuern kann

Tut mir leid, aber dort verstehe ich nur Bahnhof

>> Nochmals zum Diagramm: Dort wo der 4.ISR-Aufruf ensteht muß eine
>> variable Pause enstehen.
>
> Wozu brauchst du die Pause?
> Das hab ich noch nicht verstanden.

Das ist ganz einfach. Die ISR mit 3 Aufrufen steuert einen Schrittmotor. 
Die maximale Frequenz beträgt 40kHz(bei 16 Microsteps), das ist dann die 
maximale Verfahrgeschwindigkeit. Ich brauche aber eine 
Arbeitsgeschwindigkeit die einstellbar sein muss. Wenn ich jetzt also 
zwischen den einzelen 3er Gruppen Pausen einbaue veringert sich die 
Frequenz und damit sinkt die Verfahrgeschwindigkeit.
Welche Bits gesetzt werden müssen erhalte ich über die serielle 
Schnittstelle, genauso wie lang die Pause sein soll.
--
Gruß Karsten

von spess53 (Gast)


Lesenswert?

Hi

> Die ISR mit 3 Aufrufen steuert einen Schrittmotor.
>Die maximale Frequenz beträgt 40kHz(bei 16 Microsteps), das ist dann die
>maximale Verfahrgeschwindigkeit. Ich brauche aber eine
>Arbeitsgeschwindigkeit die einstellbar sein muss.

Also ich habe auch schon Schrittmotoren im Mikroschrittbetrieb 
angesteuert. Dein Ansatz kommt mir reichlich (vorsichtig ausgedrückt) 
schräg vor.

MfG Spess

von Karsten S. (scottyrebel)


Lesenswert?

> Das ist ganz einfach. Die ISR mit 3 Aufrufen steuert einen Schrittmotor.
> Die maximale Frequenz beträgt 40kHz(bei 16 Microsteps), das ist dann die
> maximale Verfahrgeschwindigkeit. Ich brauche aber eine
> Arbeitsgeschwindigkeit die einstellbar sein muss. Wenn ich jetzt also
> zwischen den einzelen 3er Gruppen Pausen einbaue veringert sich die
> Frequenz und damit sinkt die Verfahrgeschwindigkeit.
> Welche Bits gesetzt werden müssen erhalte ich über die serielle
> Schnittstelle, genauso wie lang die Pause sein soll.

Ich habe eine Idee:
Also den Timer0 benutze ich für meine Aufrufe mit ca. 139 Takten. Jetzt 
könnte ich doch daher gehen und den Interrupt für diesen Timer nach dem 
3. Aufruf disablen. Dafür enable ich den Aufruf des Timer1, dieser ist 
ja ein 16Bit-Timer, ergo könnte ich meine Pause von bis zu 65535 Takten 
einlegen. Löst der Timer1 den Interrupt aus, disable ich ihn und enable 
den Timer0 wieder. Damit könnte das Spiel von vorne beginnen. Müsste 
doch gehen, oder?
--
Gruß Karsten

von Karsten S. (scottyrebel)


Lesenswert?

> Also ich habe auch schon Schrittmotoren im Mikroschrittbetrieb
> angesteuert. Dein Ansatz kommt mir reichlich (vorsichtig ausgedrückt)
> schräg vor.

Dann erkläre mir doch mal, wie Du einen Satz an Steuersignalen via USB 
auf einem Controller ausgeben willst, wenn Du mit einer maximalen 
Frequenz von 40kHz arbeiten willst.
--
Gruß Karsten

von Karl H. (kbuchegg)


Lesenswert?

Karsten Sosna schrieb:

>> * oder aber man benutzt einen der PWM Modi bei einem Timer, dessen
>>   PWM
>>     + auf eine obere Grenze eingestellt werden kann
>>     + 2 Pins ansteuern kann
>
> Tut mir leid, aber dort verstehe ich nur Bahnhof

Dann musst du dich da einarbeiten.
Es geht letzten endes darum, dass du haben willst, dass dir die Hardware 
die Pulse ganz von alleine erzeugt.

>>> Nochmals zum Diagramm: Dort wo der 4.ISR-Aufruf ensteht muß eine
>>> variable Pause enstehen.
>>
>> Wozu brauchst du die Pause?
>> Das hab ich noch nicht verstanden.
>
> Das ist ganz einfach. Die ISR mit 3 Aufrufen steuert einen Schrittmotor.
> Die maximale Frequenz beträgt 40kHz(bei 16 Microsteps), das ist dann die
> maximale Verfahrgeschwindigkeit. Ich brauche aber eine
> Arbeitsgeschwindigkeit die einstellbar sein muss. Wenn ich jetzt also
> zwischen den einzelen 3er Gruppen Pausen einbaue veringert sich die
> Frequenz und damit sinkt die Verfahrgeschwindigkeit.

OK.
Auch das kann man mit den PWM Modi immer noch erreichen. Ist letzten 
Endes nur eine Frage des Zusammenspiels des Top-Wertes und der Compare 
Werte.

> Welche Bits gesetzt werden müssen erhalte ich über die serielle
> Schnittstelle, genauso wie lang die Pause sein soll.

Die Serielle sollte bei deinen Timing Überlegungen keine Rolle spielen. 
Du kriegst einen Wert von irgendwo her: Welche Werte müssen TOP und die 
COmpare-Register des Timers haben, damit der Timer ganz alleine, völlig 
autonom das gewünschte Pin Muster erzeugt.

von Karsten S. (scottyrebel)


Lesenswert?

> Auch das kann man mit den PWM Modi immer noch erreichen. Ist letzten
> Endes nur eine Frage des Zusammenspiels des Top-Wertes und der Compare
> Werte.

Hallo Karl Heinz,
Also der Werte kann ich Dir keine Antwort geben, weil ich nicht weiß was 
Du darunter verstehst.

Wenn ich das mit 2 Timern lösen kann(siehe mein entsprechendes Posting), 
dann wäre der Compare-Wert für den Timer0 = 123 und für den Timer1 = 
Delay-Wert in Takten

Was Du mit den Top-Werten meinst weiß ich nicht.
--
Gruß Karsten

von spess53 (Gast)


Lesenswert?

Hi

>Dann erkläre mir doch mal, wie Du einen Satz an Steuersignalen via USB
>auf einem Controller ausgeben willst, wenn Du mit einer maximalen
>Frequenz von 40kHz arbeiten willst.

Was haben denn die Signale jetzt mit USB zu tun?

MfG Spess

von Karsten S. (scottyrebel)


Lesenswert?

>>Dann erkläre mir doch mal, wie Du einen Satz an Steuersignalen via USB
>>auf einem Controller ausgeben willst, wenn Du mit einer maximalen
>>Frequenz von 40kHz arbeiten willst.
>
> Was haben denn die Signale jetzt mit USB zu tun?

Der Postprozessor sitzt in der Anwendung auf dem PC. Wen Du über die USB 
übertragen willst musst Du soviel wie möglich übertragen. Es wird nicht 
funktionieren wenn Du 1 Byte zum Controller schickst und auf eine 
Antwort wartest. Der Treiber unter Windows für virtuelle Comports 
schiebt dann eine Pause von 1 Millisekunde für den Richtungswechsel ein. 
Damit sinkt Deine Übertragung auf unter 1000 Bytes pro Sekunde. Wenn Du 
aber 40000 Bytes brauchst kannst Du das Verfahren so vergessen. Es 
bleibt also nichts so vielwie möglich auch einen Schlag zu senden und 
dann eine Antwort zu senden. Denn sendest Du bspw. 512 Bytes und 
schickst dann eine Antwort, tritt diese Millisekunde erst nach den 512 
Bytes auf. Nur somit kann man auch genügend Daten an den Controller 
schicken, damit eine "fast synchrone" Abrbeitung gewährleistet ist.
--
Gruß Karsten

von Karl H. (kbuchegg)


Lesenswert?

Karsten Sosna schrieb:
> Nur somit kann man auch genügend Daten an den Controller
> schicken, damit eine "fast synchrone" Abrbeitung gewährleistet ist.

Schon klar.
Aber das alles hat erst mal nichts mit der Signalerzeugung zu tun!

von Karl H. (kbuchegg)


Lesenswert?

Karsten Sosna schrieb:
>> Auch das kann man mit den PWM Modi immer noch erreichen. Ist letzten
>> Endes nur eine Frage des Zusammenspiels des Top-Wertes und der Compare
>> Werte.
>
> Hallo Karl Heinz,
> Also der Werte kann ich Dir keine Antwort geben, weil ich nicht weiß was
> Du darunter verstehst.

Dann solltest du dir das mal näher ansehen.

> Wenn ich das mit 2 Timern lösen kann(siehe mein entsprechendes Posting),
> dann wäre der Compare-Wert für den Timer0 = 123 und für den Timer1 =
> Delay-Wert in Takten

1 Timer, der Timer1, reicht dazu völlig aus.
Der erzeugt dir völlig autonom deine gewünschten Signale.

> Was Du mit den Top-Werten meinst weiß ich nicht.

Lies das Datenblatt. Den Abschnitt im Timer 1 über die PWM.

Du verschenkst ja die Hälfte der Möglichkeiten, die dir dein Mega32 
bietet, wenn du dich nicht darüber informierst, was der alles kann.

von Karsten S. (scottyrebel)


Lesenswert?

> 1 Timer, der Timer1, reicht dazu völlig aus.
> Der erzeugt dir völlig autonom deine gewünschten Signale.

Hallo Karl Heinz,
meinst Du so was?

\\\

Timer1_Init:
  LDI    temp, (1<<CS10)|(1<<WGM01)
  OUT    TCCR1B, temp

  OUT    OCR1AH, NormalHigh
  OUT    OCR1AL, NormalLow

  ;Enable Timer1 overflow interrupt
  LDI     temp, (1<<OCIE1A)  ;TOIE0: Interrupt Timer0 Overflow
  OUT    TIMSK, temp

Main:

  SEI ;Enable Interupts

MainLoop:

RJMP MainLoop

TIMER1_Interrupt:
  INC  step

  CPI  step, 0x01
  BREQ  DoStep1
  CPI  step, 0x02
  BREQ  DoStep2
  CPI  step, 0x03
  BREQ  DoStep3
  CPI  step, 0x04
  BREQ  DoStep4

DoStep1:
  OUT  PORTC, step
  RJMP  Exit_TIMER0_Interrupt

DoStep2:
  OUT  PORTC, step
  RJMP  Exit_TIMER0_Interrupt

DoStep3:
  OUT  PORTC, step
  OUT    OCR1AH, DelayHigh ;Delaytime
  OUT    OCR1AL, DelayLow
  RJMP  Exit_TIMER0_Interrupt

DoStep4:
  OUT  PORTC, step
  OUT    OCR1AH, NormalHigh ;Normaltime
  OUT    OCR1AL, NormalLow
  LDI  step, 0x00
  RJMP  Exit_TIMER0_Interrupt

Exit_TIMER0_Interrupt:

RETI
///
Habe das auf das Wesentliche gekürzt, sollte aber zeigen wie ich das 
jetzt versucht habe zu lösen. Die Delaytime(DoStep3) wird später 
gesetzt.

So sollte das doch i.O. gehen, oder?
--
Gruß Karsten

von Karl H. (kbuchegg)


Lesenswert?

Ich würds so machen:

Zuallererst würd eich meine Zyklen umschaufeln. Das was du als Pause 
hintennach bezeichnest, würde ich als Pause davor auffassen.

Also so einen Signalverlauf

1
        -+                   +----------------------------+
2
Pin 1    |                   |                            |
3
         --------------------+                            +--
4
5
        -+                               +----------------+
6
Pin 2    |                               |                |
7
         --------------------------------+                +--
8
9
         ^                                                ^
10
         |           Eine Schwingung                      |
11
         +-- hier                              bis hier --+


Dazu nehme ich mir einen PWM Modus, bei dem ich die Frequenz mittels TOP 
Wert einstellen kann, aber noch die beiden Compare Register frei habe. 
Modus 14 bietet sich da an. Der Top Wert (und damit: wie weit zählt der 
Timer) kommt ins ICR1 Register und die OCR1x Register sind zur weiteren 
Benutzung verfügbar.

Damit kann ich schon mal die Frequenz der kompletten Schwingung 
einstellen.

Die Übergänge von 0 auf 1 an jedem der beiden Pins macht mir der Compare 
Match, repektive die OCR1A bzw. OCR1B Register. Dazu muss aber der 
Compare Match im jeweiligen Kanal auf Modus

  COM1A0 / COM1A1   =  1 / 1 = Set OC1A on compare match, clear at 
BOTTOM
  COM1B0 / COM1B1   =  1 / 1 = Set OC1B on compare match, clear at 
BOTTOM

eingestellt werden.

Hier nochmal, welches Register für welche Flanke zuständig ist:
1
        -+                   +----------------------------+
2
Pin 1    |                   |                            |
3
         --------------------+                            +--
4
5
        -+                               +----------------+
6
Pin 2    |                               |                |
7
         --------------------------------+                +--
8
9
         0                   ^           ^                ^
10
                             |           |                |
11
                            OCR1A       OCR1B           ICR1


Der Rest ist jetzt einfach nur noch die Werte für ICR1, OCR1A bzw OCR1B 
korrekt berechnen und die Hardware erzeugt die Pulse ohne weiteres Zutun 
des Programmes völlig von alleine. Das Programm stellt einfach nur die 
Modi ein, stellt die Registerwerte ein und ist ansonsten von der 
Pulsgenerierung befreit und kann Däumchen drehen (oder eine serielle 
Schnittstelle überwachen)

Die Pause soll länger werden? Kein Problem. OCR1A, OCR1A und ICR1 
rutschen nach rechts (werden größer)

OK. Ich bin auf die Benutzung der speziellen Pins OC1A bzw OC1B 
angewiesen. Die müsste man hardwaremässig freischaufeln. Aber das wäre 
es mir wert.

von Karsten Sosna (Gast)


Lesenswert?

Hallo Karl Heinz,
erst mal entschuldige ich mich dafür das ich mich erst jetzt melde, 
musste noch ein anderes Projekt abschließen.

So, Dein Vorschlag gefällt mir gut und ich versuche ihn gerade 
umzusetzen. Leider bekomme ich das mit dem Timer nicht geregelt. :=(

Ich habe das jetzt so initialisiert:
\\\
.org 0x00
RJMP Reset
.org ICP1addr
RJMP TIMER1_Capt
.org OC1Aaddr
RJMP TIMER1_CompA
.org OC1Baddr
RJMP TIMER1_CompB

;...

Timer1_Init:
;Mode 14, no prescaling
LDI     temp, 1<<WGM10 | 1<<COM1A0 | 1<<COM1A1 | 1<<COM1B0 | 1<<COM1B1
OUT     TCCR1A, temp
LDI     Temp, 1<<WGM13 | 1<<WGM12 | 1<<CS10
OUT     TCCR1B, temp

LDI     NormalLow, 0x40
OUT     OCR1AL, NormalLow

LDI     NormalLow, 0x60
OUT     OCR1BL, NormalLow

LDI     NormalLow, 0x80
OUT     ICR1L, NormalLow

;Enable Timer1 interrupts
LDI     temp, 1<<TICIE1 | 1<<OCIE1A  | 1<<OCIE1B
OUT     TIMSK, temp

SEI

MainLoop:
RJMP MainLoop
///

Nur ist es so, das nur ein Interrupt(TIMER1_CompA) ausgelöst wird, und 
das gleich beim Start, d.h. so wie Anwendung in die MainLoop läuft. Dort 
wird auch OC1A gesetzt. Zu diesem Zeitpunkt steht der Timer auf 0x0013. 
Danach wird keine Interruptroutine mehr aufgerufen. Erreicht der Timer 
dann 0x0060 wird auch OC1B gesetzt, jedoch kein Interrupt ausgelöst. Bei 
0x0080 sollte der Timer dann doch eigentlich wieder zurückgesetzt 
werden, doch auch das passiert nicht, na ja und der Interrupt wird auch 
nicht ausgelöst.
Ich gehe mal davon aus, dass meine Initialisierung fehlerhaft ist. Wäre 
nett wenn Du oder irgendjemand anderes das mal prüfen könnte.

Bin für jeden Hinweis/ Tipp dankbar.
--
Gruß Scotty

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.