Forum: Mikrocontroller und Digitale Elektronik Atmega8 Tmr1 Clear OC1B on Comp Match - Wie setzt man OC1B?


von Birne (Gast)


Lesenswert?

Hallo,

Ich versuche derzeit den Pin OC1B in einer genau festgelegten Zeit zu 
clearen (auf GND setzen).
Meine Idee war dazu den 16Bit Timer des Atmega8 zu benutzen:

- Pin OC1B (=PB2) in DDRB als Ausgang definieren.
- Timer konfigurieren für "Normal Mode": WGM13:10 = 0
- und dann den Compare Match an den Pin koppeln: COM1B1 = 1 (Clear bei 
Compare Match)
- Nach einem Compare Match will ich den Timer anhalten und zurücksetzen 
für das nächste Event
Starte ich nun den Timer, tritt der Compare Match ein (Geprüft über 
Compare match Interrupt)

Mein Problem ist, dass ich es nicht schaffe, den Pin OC1B zu setzen 
(also auf 5V zu legen). Dies macht dann auch irgendwie das Clearen 
überflüssig :P

Ich habe bereits probiert:
Erster Versuch:
1
PORTB = (1<<PB2)
=> Hat keinerlei Auswirkung

Zweiter Versuch:
pseudocode:
1
1. COM1B1:0 = 0 also den timer von OC1B abkoppeln
2
2. PORTB = (1<<PB2)
3
3. COM1B1 = 1 also clear on compare match weder aktivieren
=> dies hat auch keinerlei Auswirkung... OC1B ist immer auf GND (eine 
angeschlosssene LED leuchtet nicht)
Füge ich zwischen 2. und 3. ein kurzes delay ein, sehe ich die led kurz 
aufblinken. Aber sobald 3. ausgeführt wird ist die led wieder auf GND


Hier der komplette code:
Erwartetes Verhalten:
Taster (an INT0) wird gedrückt
LED an OC1B sollte leuchten
ca. 2sec später:
LED an OC1B sollte erlöschen
LED an PD6 wird per software getoggled

Beobachtetes Verhalten:
Taster (an INT0) wird gedrückt
LED an OC1B geht für 50ms an, dannach wieder aus
nach 2 sekunden:
LED an PD6 wechselt status
LED an OC1B bleibt aus
1
#include <avr/io.h>
2
#include <stdint.h>
3
#include <stdlib.h>
4
#define F_CPU 8000000UL
5
#include <util/delay.h>
6
#include <inttypes.h>
7
#include <avr/interrupt.h>
8
9
#define led2_an PORTD|=(1<<PD6)
10
#define led2_aus PORTD&=~(1<<PD6)
11
#define led2_toggle PORTD=PORTD ^ (1<<PD6)
12
13
#define starttimer TCCR1B = (1<<CS12) | (1<<CS10) // Timer/Counter 1 prescaler=1024 -> 0.12ms per timer update (8.38s until 16bit overflow)
14
#define stoptimer TCCR1B =0 //stop timer
15
void port_init(){
16
  DDRD = 0;
17
  DDRD = (1<<PD6); //signal led
18
  DDRB |= (1<<PB2); // timer controlled led
19
}
20
21
ISR(INT0_vect){
22
  TCCR1A &= ~( (1<<COM1B0) | (1<<COM1B1) ); //disconnect OCR1B (PB2) from timer
23
  _delay_ms(50);
24
  PORTB |= (1<<PB2); //enable PB2
25
  _delay_ms(50);
26
  TCCR1A |= (1<<COM1B1); //clear OCR1B on compare match //reconnect PB2 to timer
27
  starttimer;
28
}
29
30
ISR(TIMER1_COMPB_vect){
31
  stoptimer;
32
  TCNT1 = 0; //reset timer value to 0
33
  led2_toggle;
34
}
35
36
int main (void)
37
38
{
39
  port_init();
40
41
42
  MCUCR |= (1<<ISC01); //falling edge of INT0 generates interrupt request
43
  GICR |= (1<<INT0); //enale external pin interrupt INT0
44
45
  OCR1B = 0x3FFF; //load OCR1B (ca. 2sec)
46
47
  TIMSK = (1<<OCIE1B); //compare match with OCR1B generates interrupt request
48
49
  sei(); //enable interrupts
50
51
  led_aus;
52
53
  while (1) { 
54
    _delay_ms(10);
55
  }
56
}

Über jede Hilfe bzw. Tipps würde ich mich sehr freuen.

von high (Gast)


Lesenswert?

Bin mir nicht sicher: Es könnte helfen den OC1B Wert auf den 
"Extremwert" 0 für die Zeit zu setzen, die der OC1B high sein soll.

von high (Gast)


Lesenswert?

high schrieb:
> Es könnte helfen den OC1B Wert auf den "Extremwert" 0

Natürlich OCR1B auf 0

von Birne (Gast)


Lesenswert?

Danke für die Antwort :)
Ich verstehe noch nicht so genau was du damit meinst.
Wo genau soll ich OCR1B auf 0 setzen?

Ich dachte dort schreibe ich den Wert rein, bei welchem der Compare 
Match auftritt. So wie ich das verstanden habe bestimmt OCR1B die Zeit, 
welche der Pin auf High steht und nach einem Compare Match sollte der 
Pin geCleared werden

Wenn Ich OCR1B auf 0 setze dann hat das nur die Auswirkung, dass der 
Compare Match erst nach 0xFFFF Timerschritten auftritt. Der Pin wird 
trotzdem nicht High

von high (Gast)


Lesenswert?

In manchen Timer Modi bewirkt das Setzen von Extremwerten in den Compare 
Registern ein dauerhaftes high oder low auf den OC Pins. Im Normalen 
Modus, in der dein Timer läuft, konnte ich so schnell keine Angabe 
darüber finden.

Birne schrieb:
> Wenn Ich OCR1B auf 0 setze dann hat das nur die Auswirkung, dass der
> Compare Match erst nach 0xFFFF Timerschritten auftritt. Der Pin wird
> trotzdem nicht High

Wenn du das ausprobiert hast, geht diese Methode anscheinend in dem 
eingestellten Timermode nicht. Sorry.

von Stefan E. (sternst)


Lesenswert?

Birne schrieb:
> Mein Problem ist, dass ich es nicht schaffe, den Pin OC1B zu setzen

COM-Bits auf "Set on Comapre Match" setzen, und dann ein "Force Compare 
Match" machen.

von Birne (Gast)


Lesenswert?

Habe das Problem gelöst :)

Das wird im Datenblatt seeeehr schlecht erklärt:

Nach langem lesen habe ich herausgefunden, dass man das man mit dem 
FOC1A/B Bit Im Register TCCR1A einen Compare Match erzwingen kann, 
welcher keinen Interrupt auslöst, sondern nur die Pins updated, wie wenn 
ein Compare Match auftreten würde.

D.h. um den Pin auf high zu setzen und beim nächsten (echten) Compare 
Match wieder zu clearen mache ich folgendes:
1
  TCCR1A = (1<<COM1B1) | (1<<COM1B0); //set OCR1B on compare match (only temporary)
2
  TCCR1A |= (1<<FOC1B); //force compare match (to set pin high)
3
  TCCR1A = (1<<COM1B1); //clear OCR1B on compare match //reconnect PB2 to timer
Ich vermute stark, dass das der von ATMEL vorgesehene standart weg ist 
so etwas zu realisieren. Leider wurde dies im Datenblatt nicht 
beschrieben :(

Auf jeden Fall funktioniert es jetzt :)

von Birne (Gast)


Lesenswert?

Stefan Ernst schrieb:
> Birne schrieb:
>> Mein Problem ist, dass ich es nicht schaffe, den Pin OC1B zu setzen
>
> COM-Bits auf "Set on Comapre Match" setzen, und dann ein "Force Compare
> Match" machen.


Ahh habe deine Antwort gerade erst gesehen o.O
Vielen Dank... Bin quasi gleichzeitig von selbst auf die Lösung gekommen

von spess53 (Gast)


Lesenswert?

Hi

>D.h. um den Pin auf high zu setzen und beim nächsten (echten) Compare
>Match wieder zu clearen mache ich folgendes:

>  TCCR1A = (1<<COM1B1) | (1<<COM1B0); //set OCR1B on compare match (only 
>temporary)
>  TCCR1A |= (1<<FOC1B); //force compare match (to set pin high)
>  TCCR1A = (1<<COM1B1); //clear OCR1B on compare match //reconnect PB2 to >timer

Irgentwie von hinten durch die Brust ins Auge. Da ist es einfacher, den 
OC-Pin nicht mit dem Timer zu verbinden und den Pin einfach im 
COMPB-Interrupt per Hand zu setzen.

MfG Spess

von Peter D. (peda)


Lesenswert?

Birne schrieb:
> TCCR1A |= (1<<FOC1B); //force compare match (to set pin high)

Durch das manuelle Setzen des Ausgangs verliert man natürlich wieder den 
Vorteil des zyklusgenauen Setzens.
Die Pulslänge hat also einen Jitter.

Ich benutze daher die Force-Bits nicht, sondern triggere mit 
entsprechendem Reload des OCR1B.


Peter

von spess53 (Gast)


Lesenswert?

Hi

>Durch das manuelle Setzen des Ausgangs verliert man natürlich wieder den
>Vorteil des zyklusgenauen Setzens.

Scheint hier eh nicht so wichtig zu sein:

>OCR1B = 0x3FFF; //load OCR1B (ca. 2sec) -> 0x3D08 wären 2 Sekunden

MfG Spess

von Birne (Gast)


Lesenswert?

Ich sehe diesen Thread (und mein Problem) zwar als gelöst an, aber um 
Missverständnisse aufzuklären hier nochmal Kommentare zu den letzten 
Posts:

spess53 schrieb:
> Hi
>
>>Durch das manuelle Setzen des Ausgangs verliert man natürlich wieder den
>>Vorteil des zyklusgenauen Setzens.
>
> Scheint hier eh nicht so wichtig zu sein:
>
>>OCR1B = 0x3FFF; //load OCR1B (ca. 2sec) -> 0x3D08 wären 2 Sekunden
>
> MfG Spess

Hehe...
Dieser Wert und die Taktung sind hier nur zum Testen.

Im der finalen Version kommt es auf sehr exaktes Timing an. Dabei habe 
ich allerdings viel Zeit zum Vorbereiten meines Timers. Wichtig ist nur, 
dass nach einem externen Interrupt innerhalb einer sehr exakten 
Zeitspanne der Pin auf LOW geht. Diese Zeitspanne soll einstellbar sein 
und auch quasi 0 sein können. Deshalb werde ich mir auch den Interrupt 
handler direkt in assembler selbst schreiben, um hier möglichst wenig 
Verzögerung zu haben.

spess53 schrieb:
> Irgentwie von hinten durch die Brust ins Auge. Da ist es einfacher, den
> OC-Pin nicht mit dem Timer zu verbinden und den Pin einfach im
> COMPB-Interrupt per Hand zu setzen.
>
> MfG Spess

Das wäre die primitivste Lösung, aber leider nicht genau genug :P

von Peter D. (peda)


Lesenswert?

Birne schrieb:
> Diese Zeitspanne soll einstellbar sein
> und auch quasi 0 sein können.

0 geht nicht. Gewöhn Dich mal daran, realistische Angaben zu machen.

Birne schrieb:
> Deshalb werde ich mir auch den Interrupt
> handler direkt in assembler selbst schreiben, um hier möglichst wenig
> Verzögerung zu haben.

Das ist ne Milchmädchenrechnung, Assembler hilft dabei in keinster 
Weise.
Schon der Interrupteintritt wird verzögert, z.B. durch den längsten 
anderen Interrupt bzw. den längsten atomic Block.

Wenn Du es zyklusgenau haben willst, geht das nur über Input-Capture und 
dann darauf addiert die Verzögerung als Compare-Wert.
Die längst mögliche Interruptlatenz bestimmt die minimale Zeit.


Peter

von spess53 (Gast)


Lesenswert?

Hi

Außerdem sollte man den Prescaler berücksichtigen. Bei obigen Beispiel 
kann die eingetellte Zeit im ungünstgsten Fall auch mal 1023 CPU-Takte 
kürzer sein.

MfG Spess

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.