Lieber Besucher, herzlich willkommen bei: RCLine Forum. Falls dies Ihr erster Besuch auf dieser Seite ist, lesen Sie sich bitte die Hilfe durch. Dort wird Ihnen die Bedienung dieser Seite näher erläutert. Darüber hinaus sollten Sie sich registrieren, um alle Funktionen dieser Seite nutzen zu können. Benutzen Sie das Registrierungsformular, um sich zu registrieren oder informieren Sie sich ausführlich über den Registrierungsvorgang. Falls Sie sich bereits zu einem früheren Zeitpunkt registriert haben, können Sie sich hier anmelden.

41

Donnerstag, 20. Januar 2011, 22:19

RE: Noch ne Variante....

Wie wärs, wenn Du die Impulsgenerierung unabhängig von der Impulsmessung machen würdest ?

Also in der ICP Routine die Impulslänge ermitteln. Im Hauptprogramm die Ausgabeimpulslänge berechnen.
In der Comparematch Routine den berechneten Wert ausgeben.

Das würde am Besten durch Verändern des Comparematch Registers funktionieren.

Also eine Zählvariable mitlaufen lassen, wenn der Wert 0 ist eine Pause generieren ( z.B. 18ms bei einem Kanal. Variable hochzählen.
Bei einem Kanal 1 aktivieren und die gewünschte Impulslänge + aktueller TCNT stand addieren und ins COMP Register schreiben.

Im nächsten Comparematch Ausgang 1 abschalten und Ausgang 2 einschalten.
Zählvariable hochzählen. Gewünschte Impulslänge für kan.2 + aktueller TCNT stand addieren und ins COMP Register schreiben. Und so weiter...

Wurden alle Impulse generiert ( auch wenns nur einer war ) die Zählvariable wieder auf 0 setzen. Dann gehts wieder von vorne los.

Der Vorteil der Methode ist, das sie recht einfach auf bis zu 8 Kanäle erweitert werden kann.

Bei meinen Basteleien hat das bis jetzt ohne Jitter am Servo funktioniert.
Der Nachteil ist natürlich, das im schlechtesten Fall 20ms vergehen, bis der neue Ausgabewert am Ausgang angelegt wird.

Ich Denke mal das Jittern kommt vom Ein- und Ausschalten der Comparematch Routine.

hsh

RCLine User

Wohnort: Österreich

  • Nachricht senden

42

Donnerstag, 20. Januar 2011, 22:25

Interruptflags werden automatisch gelöscht. TIFR1 |= 1 << ICF1; kann daher zu ungewollten Nebeneffekten führen. Wenn während der Interruptroutine der Interrupt neuerlich aufgetreten ist wird er von dir bewusst vergessen.

Zitat

The Input Capture Flag (ICF1) is set at the same system clock as the TCNT1 value is copied into ICR1 Register. If enabled (TICIE1 = 1), the Input Capture Flag generates an Input Capture interrupt. The ICF1 Flag is automatically cleared when the interrupt is executed. Alternatively the ICF1 Flag can be cleared by software by writing a logical one to its I/O bit location.


Ich muss jetzt gestehen, dass mir nicht ganz klar ist was du von deinem Programm erwartest. Kannst du das vielleicht in einem Flussdiagramm skizzieren?

Du setzt dir bei einer steigenden Flanke deinen neuen Output Compare Wert. ok.
Dann stellst du Input Capture auf fallende Flanken (Impulslänge messen?) um. ok.
Das Flag löschen solltest du dir sparen.

Bei fallender Flanke, setzt du den Servo Impuls high. ok.
Dann aktivierst du den Compare Match Interrupt. ok.
Dann stellst du Input Capture wieder zurück auf steigende Flanke (20ms). ok.

In der Compare Match Interrupt Routine setzt du den Servo Impuls low. ok.
Dann deaktivierst du den Compare Match Interrupt wieder. ok.
Die Tastenabfrage dürfte eigentlich auch nichts böses anstellen.

Das müsste als aktives Y-Kabel so eigentlich passen.
Als Reverser müsste man die Differenz der beiden Input Capture Werte noch entsprechend verrechnen. Ich könnte mir Vorstellen, dass es bei deinen ersten Versuchen vielleicht Probleme beim Überlauf gegeben hat. Jedenfalls wäre das ein naheliegender Fehler.
Was passiert eigentlich, wenn du negative Werte in OCR1A schreibst? das muss doch einen netten Hüpfer machen, oder? Wenn ich mich nicht ganz täusche, hast du diesen Punkt bei deinem code weiter unten nicht abgefangen.

Quellcode

1
2
start = ICR1;                                        //Startwert lesen
pulse = ICR1 - start;                          //Pulslänge = Capturewert - Startwert

Wenn dein Servo nur ganz leicht brummt, kann das durchaus auch am Servo liegen. Versuch es einmal mit leicht anderer Trimmstellung.

mfg Harald
mfg Harald

hsh

RCLine User

Wohnort: Österreich

  • Nachricht senden

43

Donnerstag, 20. Januar 2011, 22:42

Wilhelm hat recht, es wäre sauberer das OCF1A Flag zu löschen, bevor du den Output Compare Interrupt wieder aktivierst. Allerdings dürfte bei deinem Code da eigentlich nichts passieren.
mfg Harald

44

Freitag, 21. Januar 2011, 01:07

Hey Jungs,

na, das ist ja ein heiteres Rätselraten geworden... :)

@Wilhelm:
ich denke es ist hier jetzt ausreichend festgestellt worden, das es viele Wege nach Rom gibt. Die von mir vorgeschlagenen Wege gehen da wo ich das bisher so gemacht habe auch völlig jitterfrei (sprich das Ein/Ausschalten der CompareMatch geht gut, wo soll da auch Jitter herkommen). Helmut' Vorschlag bei ihm vermutlich auch, Thomas' bei ihm auch. Also gut, deiner bei dir auch. Aber irgendwie hilft das alles Stefan jetzt ziemlich wenig....

Zitat

Wie wärs, wenn Du die Impulsgenerierung unabhängig von der Impulsmessung machen würdest ? Also in der ICP Routine die Impulslänge ermitteln. Im Hauptprogramm die Ausgabeimpulslänge berechnen. In der Comparematch Routine den berechneten Wert ausgeben.
äh... lass mich fragen... und was macht Stefan in dem vorletzten Vorschlag?

Zitat

Bei einem Kanal 1 aktivieren und die gewünschte Impulslänge + aktueller TCNT stand addieren und ins COMP Register schreiben.
äh... lass mich fragen... und was macht Stefan da jetzt prinzipiel anders? ? ?

@Harald:

Zitat

Interruptflags werden automatisch gelöscht. TIFR1 |= 1 << ICF1; kann daher zu ungewollten Nebeneffekten führen. Wenn während der Interruptroutine der Interrupt neuerlich aufgetreten ist wird er von dir bewusst vergessen.
richtig, aber, so what? In den paar Takten bis dahin vergehen keinen 800us oder mehr.... (was nicht heisst dass ich's auch nicht machen würde)

Zitat

Wilhelm hat recht, es wäre sauberer das OCF1A Flag zu löschen, bevor du den Output Compare Interrupt wieder aktivierst.
richtig, aber... ah, da hast du's selber gemerkt :D (was nicht heisst dass ich's nicht auch machen würde)

Zitat

Ich muss jetzt gestehen, dass mir nicht ganz klar ist was du von deinem Programm erwartest.
zwei Infos hat er uns gegegeben: 1) er will einen Reverser bauen, 2) er hat die Vermutung dass das Problem bei so etwas wie "OCR1A = pulse; jetzt wegen der Drehrichtungsumkehr OCR1A = 2198 - pulse +900;" liegt, was lässt sich den nun daraus schliessen... was will er nur, was will er nur... grübel, grübel... :D

Zitat

dass es ... vielleicht Probleme beim Überlauf gegeben hat. Jedenfalls wäre das ein naheliegender Fehler. Was passiert eigentlich, wenn du negative Werte in OCR1A schreibst? das muss doch einen netten Hüpfer machen, oder? Wenn ich mich nicht ganz täusche, hast du diesen Punkt bei deinem code weiter unten nicht abgefangen.
das mit dem ICR haste noch nicht ganz kapiert, schau dir nochmal an warum gerade dieses Codestück genauso funktioniert wie es funktionieren soll, tatsächlich ist das bei ICP DER Standardtrick! (bei nen durchlaufenden Zähler, wie es Stefan oben anfänglich gemacht hat ists für nen "begrenzten" Zähler)

@All: ich denke Euch und mir geht es so das Stefan ein bischen die Neigung hat die Codestücke bzw Aufgaben an, sagen wir, "ungewöhnliche" Stellen auszuführen, aber bis dato ist's funktionell nicht erkennbar ein Problem, also, so what...

@Stefan: leider kann ich dir im Moment nicht weiterhelfen, aber wie gesagt werde ich mich am WoE mit Freude dranmachen... ich es würde mich sehr wundern wenn die letzten beiden Codes (mit den "komischen" Sachen weggelassen) nicht funktionieren wurden... d.h. im Moment frage ich mich ob das Problem auch nicht wo anders liegen könnte (Servo, Quali des Impulses am Eingang, Jitter des internen Oszis)... sind ja nur 2 Tage bis WoE...

ich habe mal nachgesehen, in meinem 4m1+2in1-Code erzeuge ich zwei Pulse so:

while(1){
:
if(doTask){
doTask=0;
ppmA_done= ppmB_done= 0;
ppmA_PORT |= (1<<ppmA_P);
ppmB_PORT |= (1<<ppmB_P);
cli();
OCR1A= (unsigned int)ppmA;
OCR1B= (unsigned int)ppmB;
T1_Reset;
sei();
T1_OutputCompareInterruptA_On;
T1_OutputCompareInterruptB_On;

if( ppmA_done && ppmB_done ){
ppmA_done= ppmB_done= 0;
T1_OutputCompareInterruptA_Off;
T1_OutputCompareInterruptB_Off;
test1_PORT &=~ (1<<test1_P);
}
:
}

ISR( TIMER1_COMPA_vect )
{
ppmA_PORT &=~ (1<<ppmA_P);
ppmA_done= 1;
}

ISR( TIMER1_COMPB_vect )
{
ppmB_PORT &=~ (1<<ppmB_P);
ppmB_done= 1;
}

Das Abschalten der COMP-ISRs mache ich also im Hauptpogram. Das könnte bei der vorletzten Variante ein Fehler sein, dass man in ner ISR die ISR nicht abschalten kann, zumindest bei C, ich habe was im Kopf klingeln das hier das Statusregister gepush/popt wird, schau mal ins .lst, obwohl das ja dann nur das allgemeine Interruptbit betreffen würde. Aber ich werde das nicht umsonst so (kompliziert) gemacht haben. In einer späteren Variante, beim Fp&KoaxGyroMixer, habe ich die beiden Comp-ISRs noch naked und in inline-asm gemacht, weil so gibts ein kleinen Jitter um ne us wenn die beiden Pulse exakt zur gleichen Zeit kommen, mit asm sind nur noch Bruchteile von us, ausserdem schalte ich die Comp-ISRs gar nicht mehr ab, weil ich da darauf bauen kann das bis der 16bit Timer einmal rum ist (65ms) schon noch irgendwas passiert. Sieht dort so aus

:
T1_OutputCompareInterruptB_On;
T1_OutputCompareInterruptA_On;
while(1){
:
//do the output of ppmA & ppmB,
cli();
OCR1A= OCR1B= TCNT1;
OCR1B+= (unsigned int)ppmA;
OCR1A+= (unsigned int)ppmB;
TIFR1 |= (1<<OCF1A)|(1<<OCF1B);
sei();
ppmA_PORT |= (1<<ppmA_P); //maybe 3us to late
ppmB_PORT |= (1<<ppmB_P);
:
}

ISR( TIMER1_COMPB_vect, ISR_NAKED) //used to generate the PPM for output A
{
ppmA_PORT &=~ (1<<ppmA_P); //cbi does not affect SREG
reti();
}

ISR( TIMER1_COMPA_vect, ISR_NAKED) //used to generate the PPM for output B
{
ppmB_PORT &=~ (1<<ppmB_P); //cbi does not affect SREG
reti();
}

Hier lösche ich als die Comp-Flags explizit, ich meine mich zu erinnern das es einen Grund dazu gab warum das da rein musste (d.h. dass es ohne nicht gut ging, kann mich aber nicht mehr genauer erinnern). Das mit dem naked bringts übrigens nur wenn man zwei (o. mehr) Pulse gleichzeitig und nicht hintereinander (wie man es wohl meistens machen würde) ausgeben will, sonst kann mans getrost vergessen.

Weiss nicht ob's irgenetwas hilft.

Olli

45

Freitag, 21. Januar 2011, 02:04

Grundsätzlich denke ich, dass es günstig ist in der ICP ISR eine Puls Validierung vorzunehmen und nur dann eine globale (volatile) Variable für PPM_in zu aktualisieren, wenn die Länge hinkommt.
Damit wäre das Thema "negative Werte" gegessen und auch sonst schadet es nicht (besonders bei 35Mhz).

Mir ist nicht so ganz klar, warum Output Compare verwendet wird, wenn dann der Output Wert doch manuell gesetzt wird, dann könnte man auch eine Schleife nehmen und warten bis der Timer den betreffenden Wert erreicht hat, Port setzen und weiter...

Ich würde Wilhelm zustimmen: genauer und einfacher wäre bei Output Compare Match den Ausgang toggeln zu lassen (TCCR1A setzen und den entsprechend Port beachten) und den Timer in Ruhe durchlaufen lassen.
Über Compare Register anpassen wird die Sache gesteuert. Das ist ausbaufähig und lässt sich notfalls auch synchronisieren ;) .

@Olli:... und da gibts auch keinen Jitter wenn beide Ausgänge zeitgleich gesteuert werden.

Z.B:

PHP-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
void TIM1_COMPA_vect(void//naked
{
    static uint16_t OUT_PULSE_LEN    PPM_PULSE_MIN_WIDTH;
    uint16_t OCR1A_tmp;
    
    if (bit_is_set(PINA,OUT_PIN))
    {
        //Pulse high started
        //Get calculated Value from RAM
        OUT_PULSE_LEN PPM_out_Value;
        //store to timer compare register
        
        OCR1A_tmp OCR1A OUT_PULSE_LEN;
        cli();
        OCR1A OCR1A_tmp;
        sei();
    }
    else
    {
        //Pulse low started
        //;set low level length (PPM_out_Frame_Len - PPM_out_Value)
    
        OCR1A_tmp=OCR1A+ (PPM_out_Frame_Len-OUT_PULSE_LEN);
        cli();
        OCR1A OCR1A_tmp;
        sei();
    }
}

Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von »amm« (21. Januar 2011, 02:15)


swessels

RCLine User

  • »swessels« ist der Autor dieses Themas

Wohnort: 31535 Neustadt, in der Woche HH

Beruf: Arbeitsplanung in der Luftfahzeugkomponenten - Instandsetzung

  • Nachricht senden

46

Freitag, 21. Januar 2011, 06:49

Hallo zusammen,

vielen Dank daß Ihr Euch so viel Mühe gebt mich zu unterstützen……. J

@Wilhelm:

Zitat


Wie wärs, wenn Du die Impulsgenerierung unabhängig von der Impulsmessung machen würdest ?

Das habe ich schon probiert. Siehe Beitrag http://www.rclineforum.de/forum/thread.p…824#post3776824

Da gab es andere für mich nicht nachvollziehbare Probleme und der Jitter war auch da sobald die ermittelte Impulslänge fürdie Drehrichtungsumkehr umgerechnet wurde.

Der Comparematch Interrupt könnte wohl an bleiben.


@Harald:

Zitat


Interruptflags werden automatisch gelöscht. TIFR1 |= 1 << ICF1; kann daher zu ungewollten Nebeneffekten führen. Wenn während der Interruptroutine der Interrupt neuerlich aufgetreten ist wird er von dir bewusst vergessen.

Wenn ich das Datenblatt richtig lese soll das aber nach Änderung des ICES1 Bits gemacht werden:

Aus dem Datenblatt, Kapitel 12.5.3

Changing the edge sensing must be done as early as possible after the ICR1
Register has been read. After a change of the edge, the Input Capture Flag (ICF1) must be cleared by software (writing a logical one to the I/O bit location).


Zitat


Ich muss jetzt gestehen, dass mir nicht ganz klar ist was du von deinem Programm erwartest. Kannst du das vielleicht in einem Flussdiagramm skizzieren?


Zur Wegumkehr gilt ja 2x Mittelstellung – Eingangsimpuls = Drehrichtung umgekehrt. Wenn ich also bei einer Mittelstellung von 1500µs einen Impuls von 1800µs messe, ergibt sich daraus ein Ausgangspuls von 3000µs – 1800µs = 1200µs.

- Warten auf steigende Flanke
- Capturewert + 2x Mittelstellung in das Comparematchregister schreiben
- Warten auf fallende Flanke
- Servoausgang high
- Comparematch Interrupt ein
- Auf Comparematch warten
- Servoausgang low
- Comparematch Interrupt aus
- von vorne…

Nach der oben genannten Berechnungsgrundlage ergibt sich die korrekte Impulslänge für die Wegumkehr. Es funktioniert auch bis auf den minimalen Jitter. Die Tastenabfrage hat darauf keinen Einfluß.

Zitat


Was passiert eigentlich, wenn du negative Werte in OCR1A schreibst? das muss doch einen netten Hüpfer machen, oder? Wenn ich mich nicht ganz täusche, hast du diesen Punkt bei deinem code weiter unten nicht abgefangen.

Quellcode

1
2
tart = ICR1;                                        //Startwert lesen
pulse = ICR1 - start;                          //Pulslänge = Capturewert – Startwert


Das sollte passen. Der Timer wird voll ausgenutzt und die verwendeten Variablen sind uint16_t. Bei einem Überlauf kommt trotzdem das korrekte Ergebnis raus.

Ich verwende den Servotester von Graupner und unterschiedliche Servos. Daran liegt es nicht.

Gruß,
Stefan

swessels

RCLine User

  • »swessels« ist der Autor dieses Themas

Wohnort: 31535 Neustadt, in der Woche HH

Beruf: Arbeitsplanung in der Luftfahzeugkomponenten - Instandsetzung

  • Nachricht senden

47

Freitag, 21. Januar 2011, 07:22

Weiter gehts.....

@Olli:

Das Abschalten des Comparematchinterrupts in einer ISR sollte funktionieren. Das Statusregister ist ja nicht betroffen. Das Compareflag werde ich noch mal explizit löschen.

@4712:

Zitat

Mir ist nicht so ganz klar, warum Output Compare verwendet wird,

Weil ich den Servoausgang zu einem definierten Zählerstand abschalten will:

Servo ein bei fallender Flanke des Eingansimpulses
Servo aus bei Zählerstand bei steigender Flanke des Eingansimpulses + 2x Mittelstellung

Zitat

genauer und einfacher wäre bei Output Compare Match den Ausgang toggeln zu lassen


Wenn ich das mache und der Eingangsimpuls bleibt aus gibt es warscheinlich Probleme.

Ganz nebenbei: Syntax Highlighting mit dem "php" Tag kannte ich noch nicht.....

@Alle:
Ich werde alles mal durchprobieren. Ich weiss aber nicht ob ich am WE dazu kpomme, die Kinder fordern Ihr Recht......

Vielleicht sollte ich das ganze mal mit nem anderen µC probieren. Nicht das es sich um ein Tiny24 - Problem handelt.

Gruß,
Stefan

48

Freitag, 21. Januar 2011, 09:25

@Achim:

Zitat

Mir ist nicht so ganz klar, warum Output Compare verwendet wird, wenn dann der Output Wert doch manuell gesetzt wird, dann könnte man auch eine Schleife nehmen und warten bis der Timer den betreffenden Wert erreicht hat, Port setzen und weiter...
da hast du natürlich recht dass man das in diesem Fall problemlos einfach durch pollen des Timers machen kann (genauso wie Helmut am Anfang auch recht hatte)(ich mache das bei mir mit ISRs weil ich keinen Nachteil sehe und die 2ms Hardware Resourcen nicht verschwenden wollte/konnte).

Zitat

und den Timer in Ruhe durchlaufen lassen.
oben gibt es doch genug Vorschläge mit durchlaufenden Timer :w (der Vorschlag mit TCNT1 auf Null zu setzen hat nur den "Vorteil" das es trivial wird ein Timeout einzubauen, mir fiele da jetzt nichts einfacheres ein)(ausser bei pollen natürlich).

Zitat

Ich würde Wilhelm zustimmen: genauer und einfacher wäre bei Output Compare Match den Ausgang toggeln zu lassen
das funktioniert nur wenn man innerhalb der 65 ms bleibt, aber dann ist mir meine letzte Lösung definitiv lieber, der Vorschlag mag um 2 Takte genauer sein, aber einfacher scheint er mir nicht (ist aber Geschmacksfrage)

ABER VORALLEM: das grosse "Problem" deines/eures Vorschlags liegt in dieser Zeile
OCR1A_tmp=OCR1A+ (PPM_out_Frame_Len-OUT_PULSE_LEN);
die uns zum Anfang der Diskussion zurückbringt, und in deren Licht fast ein Ausschlusskriterium ist :) (die Diskussion ob synchron oder asynchron könnte man jetzt natürlich wieder aufmachen...)

49

Freitag, 21. Januar 2011, 09:30

Zitat

Original von swessels
@4712:

Zitat

Mir ist nicht so ganz klar, warum Output Compare verwendet wird,

Weil ich den Servoausgang zu einem definierten Zählerstand abschalten will:

Servo ein bei fallender Flanke des Eingansimpulses
Servo aus bei Zählerstand bei steigender Flanke des Eingansimpulses + 2x Mittelstellung


Den Sinn verstehe ich nicht ganz...
Davon Abgesehen, das kannst Du natürlich auch ohne machen...

Zitat



Zitat

genauer und einfacher wäre bei Output Compare Match den Ausgang toggeln zu lassen


Wenn ich das mache und der Eingangsimpuls bleibt aus gibt es warscheinlich Probleme.

Warum? Ich sehe wenig Unterschied darin, ob das Servo den letzten Impuls wiederholt erhält oder gar keinen...

Zitat


...
Ich werde alles mal durchprobieren. Ich weiss aber nicht ob ich am WE dazu kpomme, die Kinder fordern Ihr Recht......

Recht geprochen!

50

Freitag, 21. Januar 2011, 10:06

Zitat

Original von OlliW
....
ABER VORALLEM: das grosse "Problem" deines/eures Vorschlags liegt in dieser Zeile
OCR1A_tmp=OCR1A+ (PPM_out_Frame_Len-OUT_PULSE_LEN);
die uns zum Anfang der Diskussion zurückbringt, und in deren Licht fast ein Ausschlusskriterium ist :) (die Diskussion ob synchron oder asynchron könnte man jetzt natürlich wieder aufmachen...)


Man kann Durchaus immer wieder eine neue Runde starten und bei fallender Flanke wieder abschalten. Das meinte ich mit synchronisieren.

Übrigens kannst Du reti auch weglassen, Du hast ja bei naked nicht die Interrupts abgeschaltet.

PHP-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
Main:

while(1)
{
// hier berechnen und auf neuen Impuls warten
....
//
cli();
OCR1ATCNT1+10//10 solten reichen
TIFR1 |= (1<<OCF1A);
sei();
}

void TIM1_COMPA_vect(void//ISR_NOBLOCK
{ 
    static uint16_t OUT_PULSE_LEN    PPM_PULSE_MIN_WIDTH; 
    uint16_t OCR1A_tmp; 
     
    if (bit_is_set(PINA,OUT_PIN)) 
    { 
        //Pulse high started 
        //Get calculated Value from RAM 
        OUT_PULSE_LEN PPM_out_Value; 
        //store to timer compare register 
         
        OCR1A_tmp OCR1A OUT_PULSE_LEN; 
        cli(); 
        OCR1A OCR1A_tmp; 
        sei(); 
    } 
    else 
    { 
        //Pulse low started 
      TIFR1 |= (0<<OCF1A);
     } 
} 


So wäre man synchron

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »amm« (21. Januar 2011, 14:24)


51

Freitag, 21. Januar 2011, 12:55

Zitat

Man kann Durchaus immer wieder eine neue Runde starten und bei fallender Flanke wieder abschalten. Das meinte ich mit synchronisieren.
jo, da hast du recht... aber die Lösung sieht dann ja gar nicht mehr sehr viel anders aus als andere :) (IMHO eher uneleganter, warum OCR1A nicht gleich in der main richtig setzen? was wenn es noch andere Interruts gibt und 1.2us nicht reichen? etc...)

Zitat

Übrigens kannst Du reti auch weglassen,
irgendetwas muss hin... könnte sein dass ein ret hier reicht, aber einfach weglassen würde "interessant" werden :)

Zitat

Du hast ja bei naked nicht die Interrupts abgeschaltet.
ich war immer der Meinung dass das automatisch hardwareseitig beim Einspringen geschiet

52

Freitag, 21. Januar 2011, 14:23

Stimmt Olli, ich hab mich vertan, bei ISR_NAKED muss man reti am Ende ausführen.
UNd die IR bleiben auch abgeschaltet beim Eintriitt.
Hab ich verwechselt mit ISR_NOBLOCK.

53

Freitag, 21. Januar 2011, 14:56

Zitat

bei ISR_NAKED muss man reti am Ende ausführen
ha, dazu gibt es in einigen Foren "interessante" Streitdiskussionen ob und wann nicht auch ein ret reicht... ich für meine Teil bin da altmodisch, ich nehme immer ein reti, und wenn ich nen Interrupt aus haben will ein cli...

Zitat

ISR_NOBLOCK
ah, siehste, dieser Befehl war mir jetzt gar nicht so bekannt... gleich mal am WoE checken was das wohl macht... :ok:

54

Samstag, 22. Januar 2011, 01:12

PHP-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
/*
Servoreverser - Variante 4712
MCU ATtiny 44
Takt: 16MHz
RC-In: ICP (PA7)
RC-Out: PA6
kein Taster bisher
*/



#ifndef F_CPU
    #define F_CPU 16000000UL
#endif

#include <avr/io.h>    
#include <stdint.h>
#include <stdlib.h>
#include <avr/interrupt.h>
#include <avr/eeprom.h>
#include <stdbool.h>
#include <util/delay.h>

#define OutPin PINA6
#define InPin  PINA7
#define DebugPin  PINA0

//PPM Impulswerte
//Nutzsignalbreite 1.2ms
#define CHANNEL_MIN_WIDTH        0U        //0 ms    = Minimale Kanalbreite    
#define    CHANNEL_MID_WIDTH         1200U    //0,6ms = Mittensignal 
#define CHANNEL_MAX_WIDTH        2400U   //1,2ms    = Maximale Kanalbreite 

//Gesamtimpuls länge
#define    PPM_PULSE_MIN_WIDTH     1800U                                    //0.92ms  min PPM Impulslänge  
#define    PPM_PULSE_MID_WIDTH     PPM_PULSE_MIN_WIDTH+CHANNEL_MID_WIDTH    //3040=1.52ms Mitte Futaba Standard
#define    PPM_PULSE_MAX_WIDTH     PPM_PULSE_MIN_WIDTH+CHANNEL_MAX_WIDTH    //4240=2.12ms max PPM Impulslänge 

//Konstanten für Impulslängen validierung
#define    PPM_GLITCH_TOLERANCE     300U    //0.15ms Throttle Impuls ungültig wenn    >2.27ms oder <0.77 ms

#define    PULSE_GOOD_THRESHOLD    5U


//Makros definieren für Port-Steuerung
#define SET_BIT(PORT, BITNUM)    ((PORT) |=  (1<<(BITNUM)))
#define CLEAR_BIT(PORT, BITNUM)  ((PORT) &= ~(1<<(BITNUM)))
#define TOGGLE_BIT(PORT, BITNUM) ((PORT) ^=  (1<<(BITNUM))) 


//ICP für Eingang
void TIM1_CAPT_vect(void)     __attribute__((interrupt));//alle Interrupts während Verarbeitung an (sei() vorneweg)
//Ausgange
void TIM1_COMPA_vect(void)     __attribute__((interrupt));//alle Interrupts während Verarbeitung an (sei() vorneweg) 


volatile     uint8_t     PPM_ValidPulse_Count        0volatile     uint16_t     PPM_in_Value        PPM_PULSE_MID_WIDTH;
volatile     uint16_t     PPM_out_Value        PPM_PULSE_MID_WIDTH;
volatile     bool         PPM_Out_Done        true;
volatile     bool         PPM_In_Done            falseint main () 
{
   
   //config I/O Pins
    DDRA =(1<<OutPin) | (0<<InPin)|(1<<DebugPin);  
    PORTA=(0<<OutPin) |(1<<InPin)|(0<<DebugPin);
    
       TIMSK1 = (1<<ICIE1);
       TCCR1B = (0<<CS12)|(1<<CS11)|(0<<CS10)|(1<<ICES1)|(0<<ICNC1);    //0 1 0 clkI/O/8 (From prescaler)
    //start ICES1=1 -> rising edge at ICP Input pin
       sei();
   
       while(1)
       {
         if (PPM_In_Done && PPM_Out_Done)
        {
            PPM_Out_Done=false;
            cli();
             PPM_out_Value=PPM_in_Value;
             sei();
            PPM_In_Done=false;
            PPM_out_Value=PPM_PULSE_MAX_WIDTH+PPM_PULSE_MIN_WIDTH-PPM_out_Value;
            
            OCR1ATCNT1+10//10 solten reichen 
             TCCR1A = (0<<COM1A1)| (1<<COM1A0);
            TIMSK1 |= (1<<OCIE1A); 
            
            
    
        }
   }
}



void TIM1_COMPA_vect(void//ISR_NOBLOCK 
{  
    static uint16_t OUT_PULSE_LEN    PPM_PULSE_MIN_WIDTH;  
    uint16_t OCR1A_tmp;  
      
    if (bit_is_set(PINA,OutPin))  
    {  
        TOGGLE_BIT(PORTADebugPin);
        //Pulse high started  
        //Get calculated Value from RAM  
        OUT_PULSE_LEN PPM_out_Value;  
        //store to timer compare register  
          
        OCR1A_tmp OCR1A OUT_PULSE_LEN;  
        cli();  
        OCR1A OCR1A_tmp;  
        sei();  
    }  
    else  
    {  
     //Pulse low started  
     TCCR1A = (0<<COM1A1)| (0<<COM1A0);
     TIMSK1 |= (0<<OCIE1A);
      PPM_Out_Done=true; 
     }  
} 



void TIM1_CAPT_vect(void)//ISR_NOBLOCK 
{
    static uint16_t PPM_in_Timer_High     0;
    uint16_t         ICR_tmp;
    
    ICR_tmp=ICR1;
    
    if(bit_is_set(TCCR1B ,ICES1))//falling comparator output edge
    {
        PPM_in_Timer_High ICR_tmp;
        CLEAR_BIT(TCCR1B,ICES1);//TCCR1B&=~(1<<ICES1); //prepare for falling edge
    }
    else
    {
        TOGGLE_BIT(PORTADebugPin);
        ICR_tmp=ICR_tmp-PPM_in_Timer_High;
        //falling edge
        if ((ICR_tmp< (PPM_PULSE_MAX_WIDTH PPM_GLITCH_TOLERANCE)) && 
            (ICR_tmp>(PPM_PULSE_MIN_WIDTH PPM_GLITCH_TOLERANCE)))
        {
            if (PPM_ValidPulse_Count<=PULSE_GOOD_THRESHOLDPPM_ValidPulse_Count++;
            if (PPM_ValidPulse_Count PULSE_GOOD_THRESHOLD) 
            {
                PPM_in_Value=ICR_tmp;
                PPM_In_Done=true; 
            }
        }
        else PPM_ValidPulse_Count--;
        SET_BIT(TCCR1B,ICES1);//TCCR1B&=~(1<<ICES1); //prepare for rising edge
    }
}


So mal dahin geschludert.... ist aber ausbaufähig und funktioniert ohne Zicken.
Werte muss man für 8Mhz Quarz ändern´.
Kein Output, wenn kein Input und synchron angekoppelt; Impulsausgabe erfolgt 12-13µS nach Ende des Eingangsimpulses (bei 16Mhz Takt).
»4712« hat folgendes Bild angehängt:
  • Test.png

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »amm« (22. Januar 2011, 01:32)


swessels

RCLine User

  • »swessels« ist der Autor dieses Themas

Wohnort: 31535 Neustadt, in der Woche HH

Beruf: Arbeitsplanung in der Luftfahzeugkomponenten - Instandsetzung

  • Nachricht senden

55

Samstag, 22. Januar 2011, 13:14

Hallo zusammen,

was soll ich sagen, es funktioniert. Ich habe das Programm auf einen Mega8 portiert, 8MHz externer Takt, kein Jitter.

Es scheint also am internen Takt des Tinys zu liegen.

Zitat


Zitat

4712
genauer und einfacher wäre bei Output Compare Match den Ausgang toggeln zu lassen

Zitat

swessels
Wenn ich das mache und der Eingangsimpuls bleibt aus gibt es warscheinlich Probleme.

Warum? Ich sehe wenig Unterschied darin, ob das Servo den letzten Impuls wiederholt erhält oder gar keinen...


Wenn ich den Ausgang per Comparematch toggle kann ich je nachdem wo der letzte Matchwert liegt Impulse von 0 - 65ms bekommen. Vielleicht hab ich da aber auch nen Denkfehler....

Weiteres nächste Woche.

Gruß,
Stefan

56

Samstag, 22. Januar 2011, 13:17

Hey Stefan,

ich glaube, wie ich oben schon mal angedeutet hatte, du musst mal alles Mögliche auchjenseits des Codes nachchecken... ich habe mal eben schnell die allereinfachste Variante, pollen des Eingangpins wie von Helmut vorgeschlag, ausprobiert, und funktioniert bei mir auf nem Atmega8 mit internal 8MHz absolut perfekt, ich kann da null Jitter sehen (man muss nur mit der Bedingung zum detektieren des Endes etwas aufpassen).

Werde heute Abend noch die ISR-Varianten testen.
Olli

PHP-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
/*********************************************************/
// Program 
// (c) Oliver Waldmann
/*********************************************************/
#include <stdlib.h>
#include <string.h>
#include <util/delay.h>
#include <avr/io.h>
#include <avr/interrupt.h>

/*********************************************************/
version v0.01 ATmega internal 8MHz  
v22.01.2011
FUSESChkopt1SUT1:010CKSEL3:00100
STATUSworking
COMMENT:
  polling variant
/*********************************************************/

#define in_P        PB0
#define in_DDR      DDRB
#define in_PORT     PORTB
#define in_PIN      PINB

#define out_P       PB2
#define out_DDR     DDRB
#define out_PORT    PORTB

int main(void)
{
unsigned char state//0: wait for up, 1: wait for down, 2: wait for end of out
unsigned int start;

  //init PPM out at OCR1B/PB2
  out_DDR |= (1<<out_P); //pin as output
  out_PORT &=~ (1<<out_P); //pin = 0

  //init PPM in at ICP/PB0
  in_DDR &=~ (1<<in_P); //pin as input
  in_PORT |= (1<<in_P); //pullup on

  //init Timer1
  //waveform normal
  TCCR1B&=~ (3<<WGM12); TCCR1A&=~ (3<<WGM10); //WGM1 3:0 = 0000
  //prescale 8
  TCCR1B&=~ (7<<CS10); TCCR1B|= (2<<CS10); //CS1 2:0 = 010
  
  statestart0;
  while( ){

    if( state==){
      if( in_PIN&(1<<in_P) ){
        startTCNT1;
        state++; //state= 1
      }
    }else if( state==){
      if( !(in_PIN&(1<<in_P)) ){
        out_PORT|= (1<<out_P);
        state++; //state= 2
      }
    }else{
      if( TCNT1-start>3000 ){ //its important to do handle it this way, for 3000 choose what you want
        out_PORT&=~ (1<<out_P);
        state0;
      }
    }

  }
  return 0;
}


EDIT: hups, beim schreiben deinen Post nicht gesehen :)
EDIT II: im Code switch durch if's ersetzt, wird so kürzer

Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von »OlliW« (22. Januar 2011, 21:13)


wurpfel1

RCLine User

Wohnort: CH-rheintal

  • Nachricht senden

57

Samstag, 22. Januar 2011, 13:25

hi leutz

fehlersuche ohne werkzeug ist sehr mühsam. um servoimpulse anzuschauen müss es kein textronic sein, ne PC-soundkarte reicht.

oder man baut konsequent DIY und nimmt das arduscope. wenn was passiert ist es nicht teuer, das scope lässt sich zudem als logic-analyzer verwenden.
die anzeige lässt sich auch auf ein LCDshield umlenken, schon hat man ein tragbares vielkanal-oszi mit LA :evil:
für ernsthaften betrieb würde ich die eingänge noch etwas schützen und tiefpassfilter anbringen. am besten aktive tiefpass mit einem OP ;)

http://code.google.com/p/arduinoscope/wiki/Usage






übrigends:
ich verwende multiplexer-bausteine um servoimpulse "herzustellen".
damit können mehrere servos gleichzeitig bewegt werden, gerne genommen für 3D-hubies oder klappen mit mehreren servos.
PWM ist nicht wirklich notwendig, ein zähler mit compare-ISR ist absolut ausreichend.
bin schon zu alt zum spielen.. macht aber gleichwohl spass ;-)

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »wurpfel1« (22. Januar 2011, 13:25)


haschenk

RCLine User

Beruf: Dipl. Ing.

  • Nachricht senden

58

Samstag, 22. Januar 2011, 18:16

Hallo,

für diejenigen, die´s interessiert:
Servo-Reverser mit Arduino.....
"Entwicklungszeit" ca. 2 Stunden, wobei der Großteil für eine widerspenstige LCD-Ausgabe der Impulslängen notwendig war (die lcd-lib kommt mit einem 4-zeiligen LCD nicht ganz klar). Im Listing unten ist diese wieder entfernt.

Gruß,
Helmut



/************* Servo_Reverser_kurz ***************/

int center = 1500;
int pulse_in, pulse_out;

void setup()
{
pinMode(9, OUTPUT);
}

void loop()
{
pulse_in = pulseIn(7, HIGH);
pulse_out = 2*center - pulse_in;
if (pulse_in > 0) //Bei fehlendem Eingangsimpuls
//liefert 'pulse_in' 0 zurück
{
digitalWrite(9, HIGH);
delayMicroseconds(pulse_out);
digitalWrite(9, LOW);
}
}

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »haschenk« (22. Januar 2011, 18:17)


59

Samstag, 22. Januar 2011, 21:08

Hallo Zusammen,

auch die Interruptvariante von hier funktioniert einwandfrei und jitterfrei (wieder ATmega8 intern 8MHz).

PS: vom Erstellen des Projekts bis ersten Flash 21 min, hat auf Anhieb funktioniert (:D), es geht also auch "altmodisch" zügig :)

Stefan, ich denke es ist klar dass du irgendetwas nicht unbedingt Codebezogenes falsch machst oder ein Problem hast. Ich glaube dabei NICHT dass das Problem wirklich am Attiny24 liegt, es ist zwar bekannt dass der interne Oszillator mehr jittert als man das gerne hätte, aber wir alle haben schon einige Projekte mit den AttinyX4 gemacht, und wenn es da ein ernsthaftes Probleme gäbe mit einer stabilen PPM-Ausgabe dann wäre das aufgefallen (ich zitiere hier jetzt einfach mal Achim's Mischer, basiert auf Attiny84). Der ISR Code hat wirklich ungelogen von scratch funktioniert. Und die beiden Codes springen immer sofort an, und nicht nur 70% der Zeit. Ich vermute dass bei dir der Oszi nicht richtig läuft. Vielleicht einfach Fuses nicht passend gesetzt?

Olli

PHP-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
/*********************************************************/
// Program 
// (c) Oliver Waldmann
/*********************************************************/
#include <stdlib.h>
#include <string.h>
#include <util/delay.h>
#include <avr/io.h>
#include <avr/interrupt.h>

/*********************************************************/
version v0.02 ATmega internal 8MHz  
v22.01.2011
FUSESChkopt1SUT1:010CKSEL3:00100
STATUSworking
COMMENT:  ISR variant #1
/*********************************************************/
#define in_P        PB0 //ICP
#define in_DDR      DDRB
#define in_PORT     PORTB
#define in_PIN      PINB

#define out_P       PB2 //OCR1B
#define out_DDR     DDRB
#define out_PORT    PORTB

ISRTIMER1_CAPT_vect )
{
  if( TCCR1B&(1<<ICES1) ){
    OCR1B3000 ICR1;
    TIFR |= (1<<OCF1B); //OCR1B interupt flag off
    TIMSK |= (1<<OCIE1B); //OCR1A interrupt on
  }else
    out_PORT |= (1<<out_P); //pin = 1
  TCCR1B ^= (1<<ICES1); //ICP edge select toggle
}

ISRTIMER1_COMPB_vect )  
{
  out_PORT &=~ (1<<out_P); //pin = 0
  TIMSK &=~ (1<<OCIE1B); //OCR1B interrupt off
}

int main(void)
{
  //init PPM out at OCR1B/PB2
  out_DDR |= (1<<out_P); //pin as output
  out_PORT &=~ (1<<out_P); //pin = 0

  //init PPM in at ICP/PB0
  in_DDR &=~ (1<<in_P); //pin as input
  in_PORT |= (1<<in_P); //pullup on

  //init ICP
  TIMSK |= (1<<TICIE1); //ICP interrupt on
  TCCR1B |= (1<<ICES1); //ICP edge select up

  //init Timer1
  //waveform normal
  TCCR1B&=~ (3<<WGM12); TCCR1A&=~ (3<<WGM10); //WGM1 3:0 = 0000
  //prescale 8
  TCCR1B&=~ (7<<CS10); TCCR1B|= (2<<CS10); //CS1 2:0 = 010
  
  sei();   
  while(1){}
  return 0;
}

swessels

RCLine User

  • »swessels« ist der Autor dieses Themas

Wohnort: 31535 Neustadt, in der Woche HH

Beruf: Arbeitsplanung in der Luftfahzeugkomponenten - Instandsetzung

  • Nachricht senden

60

Montag, 24. Januar 2011, 13:35

Hallo zusammen,

vieln Dank dass Ihr alles ausprobiert. Ich werde mir alles in Ruhe zu Gemüte führen, aber nicht mehr Heute. Ich bin seit gestern 06:00 Uhr auf den Beinen und habe derzeit überhaupt keinen Kopf dafür.

Ihr hört aber auf jeden Fall wieder von mir.

Gruß,
Stefan