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 | }
|
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.
kann ich dem Simulator auch einen zeitlichen Input geben?
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.
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)
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 | }
|
@ 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.
@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.
- 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 | }
|
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.
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.
|