Forum: Compiler & IDEs Schnelles Schalten mit Interrupt 2


von patrick b. (calalex)


Lesenswert?

hallo, ich hab meinen vorherigen Code verändert [[Schnelles Schalten mit 
Interrupt]]  und bin auf Feedback gespannt, ob er vielleicht nun so 
funktionieren kann!
Bei jeder Flanke soll der Timer auf 0 gesetzt werden, anschließend
hochgezählt werden, sofern dann der compare Wert erreicht wird, soll PA0
aktiviert werden. Kommt vorher die nächste Flanke, wird der timer wieder
rechtzeitig zurückgesetzt. So meine Theorie.
Vielen Dank für nützliches Feedback!
1
#ifndef F_CPU
2
#define F_CPU 16000000UL
3
#endif
4
5
#define IN_PORT  PORTD
6
#define IN_DDR  DDRD
7
#define IN_PIN  PD6
8
9
#define OUT_PORT  PORTA
10
#define OUT_DDR    DDRA
11
#define OUT_PIN    PA0
12
13
#include <avr/io.h>
14
#include <util/delay.h>
15
#include <avr/interrupt.h>
16
17
18
ISR (TIMER1_COMPA_vect)
19
{
20
  PORTA |=(1<<PA0);          // falls kein Puls PA0 aktivieren
21
}
22
23
ISR(INT0_vect)
24
{
25
    TCCR1B |= (1 << WGM12)|(1 << CS10);    // timer setzen, ohne prescale
26
    TCNT1 = 0;                // counter init
27
    OCR1A = 3199;              // compare modus init: flck=16MHz => tclk=62.5ns,
28
                        // 200ns Pause + 20ns Toleranz = 220ns
29
                        // 220ns/62.5ns =3200 Zählschritte -1 = 3199
30
    TIMSK |= (1 << OCIE1A);          // compare modus aktivieren
31
  
32
}
33
34
int main(void)
35
{
36
  
37
  DDRD &= ~(1<<PD2);         //PD2 als Eingang
38
  PORTD|= (1<<PD2);         //Pullup aktivieren
39
  DDRA = (1<<PA0);        //PA0 als Ausgang
40
  
41
  sei();              //global interrupts
42
    
43
                
44
  MCUCR |= ((1<<ISC01)|(1<<ISC00));    //interrupt bei steigender Flanke
45
  GICR |= (1<<INT0);            //interrupt modus aktivieren
46
  
47
  
48
  
49
  
50
  while(1)
51
  {
52
    
53
  };
54
  
55
}

von Karl H. (kbuchegg)


Lesenswert?

patrick bateman schrieb:
> hallo, ich hab meinen vorherigen Code verändert [[Schnelles Schalten mit
> Interrupt]]  und bin auf Feedback gespannt, ob er vielleicht nun so
> funktionieren kann!

Das allerbeste Feedback gibt dir der µC selber.
Zur Not tuts auch der Simulator im AVR-Studio

Edit: Den Teil 'Zur Not' nehm ich zurück. Tatsächlich ist das in vielen 
Fällen eine sehr gute Möglichkeit um erst mal zu sehen, was im Programm 
warum passiert.

von patrick b. (calalex)


Lesenswert?

kann ich dem Simulator auch einen zeitlichen Input geben?

von Karl H. (kbuchegg)


Lesenswert?

patrick bateman schrieb:
> kann ich dem Simulator auch einen Input geben?

Natürlich.
Im Simulator siehst du alle µC-Register aller Hardware Komponenten. Zum 
Beispiel auch die PORT und PIN Register, aufgeschlüselt in die einzelnen 
Bits. Nichts und niemand hindert dich daran, da drauf zu klicken und dem 
µC auf seinem PIN Register einen Input von 1 (oder 0) zu liefern.

Mach dich ein bischen damit vertraut, das wirst du in deiner Karriere 
noch oft benötigen.

von Karl H. (kbuchegg)


Lesenswert?

Einen Tip gib ich dir. Denk mal ein bischen über Reihenfolgen nach. Es 
ist nicht egal, in welcher Reihenfolge man Dinge tut. Ein Timer beginnt 
dann zu laufen, sobald er einen Vorteiler bekommt. Es ist daher nicht 
egal, ob man gewisse Einstellungen erst macht NACHDEM der Timer schon 
losgelaufen ist, oder ob man die macht BEVOR der Timer läuft. Und: 
Compare-Matches werden IMMER geprüft! Egal ob du schon was ans OCR1A 
Register zugewiesen hast oder nicht. Denn die Register erwachen nicht 
zum Leben, wenn du das erste mal etwas zuweist. Die Register sind immer 
da und wenn du nichts zugewiesen hast, dann steht da eben 0 drinnen 
(oder welcher Wert auch immer beim Reset des µC von der Hardware 
eingestellt wird)

von patrick b. (calalex)


Lesenswert?

das heißt, die zwei Zeilen
1
OCR1A = 3199;  
2
TIMSK |= (1 << OCIE1A);

pack ich in int main (void)?

Das Zurücksetzen des Timers klappt dann so, dass ich ihn zuerst auf 
TCNT1 = 0; initialisiere und mit der nächsten Zeile lasse ich in ja 
direkt starten.

Kann man dem Simulator auch eine Textdatei geben?
1
#ifndef F_CPU
2
#define F_CPU 16000000UL
3
#endif
4
5
#define IN_PORT  PORTD
6
#define IN_DDR  DDRD
7
#define IN_PIN  PD6
8
9
#define OUT_PORT  PORTA
10
#define OUT_DDR    DDRA
11
#define OUT_PIN    PA0
12
13
#include <avr/io.h>
14
#include <util/delay.h>
15
#include <avr/interrupt.h>
16
17
18
ISR (TIMER1_COMPA_vect)
19
{
20
  PORTA |=(1<<PA0);          // falls kein Puls PA0 aktivieren
21
}
22
23
ISR(INT0_vect)
24
{
25
    TCNT1 = 0;                // counter init
26
    TCCR1B |= (1 << WGM12)|(1 << CS10);    // timer setzen, ohne prescale
27
   
28
}
29
30
int main(void)
31
{
32
  
33
  DDRD &= ~(1<<PD2);         //PD2 als Eingang
34
  PORTD|= (1<<PD2);         //Pullup aktivieren
35
  DDRA = (1<<PA0);        //PA0 als Ausgang
36
  
37
  sei();              //global interrupts
38
    
39
                
40
  MCUCR |= ((1<<ISC01)|(1<<ISC00));    //interrupt bei steigender Flanke
41
  GICR |= (1<<INT0);            //interrupt modus aktivieren
42
  
43
      OCR1A = 3199;              // compare modus init: flck=16MHz => tclk=62.5ns,
44
                        // 200ns Pause + 20ns Toleranz = 220ns
45
                        // 220ns/62.5ns =3200 Zählschritte -1 = 3199
46
    TIMSK |= (1 << OCIE1A);          // compare modus aktivieren
47
  
48
  
49
  
50
  while(1)
51
  {
52
    
53
  };
54
  
55
}

von Falk B. (falk)


Lesenswert?

@ patrick bateman (calalex)

>hallo, ich hab meinen vorherigen Code verändert [[Schnelles Schalten mit
>Interrupt]]

Ist es so schwer, eine Link direkt einzufügen?

Beitrag "Schnelles Schalten mit Interrupt"

>und bin auf Feedback gespannt, ob er vielleicht nun so
>funktionieren kann!

Was ist das hier? Heiteres C-Code raten? Oder doch vielleicht mal 
nachdenken und was gescheites programmieren?

>Bei jeder Flanke soll der Timer auf 0 gesetzt werden,

Was selten sinnvoll ist. Meistens ist es besser, den Timer durchlaufen 
zu lassen.

> anschließend
>hochgezählt werden, sofern dann der compare Wert erreicht wird, soll PA0
>aktiviert werden. Kommt vorher die nächste Flanke, wird der timer wieder
>rechtzeitig zurückgesetzt.

Nennt sich Monoflop, hier in Software.

> So meine Theorie.

Wo sind die ZAHLEN dazu? Denn es ist ein Unterschied, ob wir von 1kHz 
oder 1 MHz reden.


>    TCCR1B |= (1 << WGM12)|(1 << CS10);    // timer setzen, ohne prescale

Man muss dem Timer nicht dauern neu initialisieren, schon gar nicht bei 
deinen Wunschpulsbreiten.

>    TCNT1 = 0;                // counter init

Naja.

>    OCR1A = 3199;              // compare modus init: flck=16MHz => >tclk=62.5ns,
>                        // 200ns Pause + 20ns Toleranz = 220ns
>                        // 220ns/62.5ns =3200 Zählschritte -1 = 3199

Fehlt hier ne Null? Oder zwei? Oder hast du Mikrosekunden mit 
Nanosekunden verwechselt? Die Zahen in den Kommentaren stimmen nicht.

>    TIMSK |= (1 << OCIE1A);          // compare modus aktivieren

Dito, das macht man EINMALIG am Programmanfang!

>int main(void)
>{

>  DDRD &= ~(1<<PD2);         //PD2 als Eingang
>  PORTD|= (1<<PD2);         //Pullup aktivieren
>  DDRA = (1<<PA0);        //PA0 als Ausgang

>  sei();              //global interrupts

Interrupts freigeben ohn vorher den Timer gescheit initialisiert zu 
haben? intessant.

>  MCUCR |= ((1<<ISC01)|(1<<ISC00));    //interrupt bei steigender Flanke
>  GICR |= (1<<INT0);            //interrupt modus aktivieren

Hier das Gleiche.

Solche Monoflops in Software sind nur für Zeiten von einigen 
Mikrosekunden oder mehr sinnvoll, für kleinere Zeiten macht man sowas in 
Hardware, siehe Monoflop.

von Falk B. (falk)


Lesenswert?

@patrick bateman (calalex)


>OCR1A = 3199;
>TIMSK |= (1 << OCIE1A);
>pack ich in int main (void)?

Ja.

>Das Zurücksetzen des Timers klappt dann so, dass ich ihn zuerst auf
>TCNT1 = 0; initialisiere

Ja.

> und mit der nächsten Zeile lasse ich in ja
>direkt starten.

Warum, er läuft doch schon. Und man kann ihm während des Betriebs neu 
laden, ohne ihn anzuhalten.

von patrick b. (calalex)


Lesenswert?

- Kommentarzeile hatte einen Fehler
- sei () steht jetzt am Ende
-
1
#ifndef F_CPU
2
#define F_CPU 16000000UL
3
#endif
4
5
#define IN_PORT  PORTD
6
#define IN_DDR  DDRD
7
#define IN_PIN  PD6
8
9
#define OUT_PORT  PORTA
10
#define OUT_DDR    DDRA
11
#define OUT_PIN    PA0
12
13
#include <avr/io.h>
14
#include <util/delay.h>
15
#include <avr/interrupt.h>
16
17
18
ISR (TIMER1_COMPA_vect)
19
{
20
  PORTA |=(1<<PA0);              // falls kein Puls PA0 aktivieren
21
}
22
23
ISR(INT0_vect)
24
{
25
    TCCR1B |= (1 << WGM12)|(1 << CS10);    // 16bit timer setzen, ohne prescale
26
    TCNT1 = 0;                // counter init
27
}
28
29
int main(void)
30
{
31
  
32
  DDRD &= ~(1<<PD2);               //PD2 als Eingang
33
  PORTD|= (1<<PD2);               //Pullup aktivieren
34
  DDRA = (1<<PA0);              //PA0 als Ausgang
35
  
36
  
37
                
38
  OCR1A = 3199;                // compare modus init: flck=16MHz => tclk=62.5ns,
39
                        // 200us Pause + 20us Toleranz = 220us
40
                        // 220us/62.5ns =3200 Zählschritte -1 = 3199
41
  TIMSK |= (1 << OCIE1A);            // compare modus aktivieren
42
  
43
                  
44
  MCUCR |= ((1<<ISC01)|(1<<ISC00));      //interrupt bei steigender Flanke
45
  GICR |= (1<<INT0);              //interrupt modus aktivieren
46
  
47
  sei();                    //global interrupts
48
  
49
  while(1)
50
  
51
  {
52
    
53
  };
54
  
55
}

von Karl H. (kbuchegg)


Lesenswert?

Was ist eigentlich, wenn die Anlage eingeschaltet wird und das 5Khz 
Signal auch dann schon nicht da ist?

Was ist, wenn das Signal ausfällt und wiederkommt?


Ist die Reaktion deines Programmes dann richtig?

Im übrigen:
Ab in den Simulator und testen.
Programm auf einen µC brennen und testen.

Sorry. Aber so ist das nun mal. Die eigentliche Arbeit des 
Programmschreibens  sind vielleicht 40% eines kompletten Projektes. 
Testen ist ein wesentlicher integraler Bestandteil eines jeden 
Projektes. Und das gehört genauso zur Arbeit eines SW-Entwicklers dazu, 
dass man lernt, wie man so testet, dass man das rausfindet was man 
wissen will.

von patrick b. (calalex)


Lesenswert?

Im Fall der Interrupt wird ausgelöst und der Port geschaltet...
1
ISR (TIMER1_COMPA_vect)
2
{
3
  PORTA |=(1<<PA0);          // falls kein Puls PA0 aktivieren
4
}

wie lange wird der Port geschaltet oder bleibt der dann aktiv?

kann man globale Interrupts mit sei(); auch erst nach einem Tastendruck 
aktivieren? oder ist das ungeschickt? Ich würde das Programm gern erst 
aktivieren, wenn man eine Button drückt.

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.