PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Mehrere RC Signale einlesen und mehrere Servos ausgeben



R2D2 Bastler
13.12.2013, 10:25
Hallo zusammen,

ich möchte mehrere RC Signale einlesen und auf mehrere Servos ausgeben. Leider ist es mir das nach 2 Wochen noch immer nicht gelungen. :(
Das ganze ist für eine Bagger Steuerung. Das fertige Programm soll 3 RC-Eingänge und 2 Poti-Eingänge einlesen, sowie 4 Servoausgänge ausgeben.

Ein RC-Eingang fragt den Betriebsmode (Fahrbetrieb/Baggerbetrieb) ab.
Zwei weitere RC-Eingänge bekommen die Signale von den Knüppelstellungen der Fernbedienung.
Die Potis dienen der Servowegbegrenzung (vergleichbar mit DualRate).
2 Servos gehen zum Baggerarm (je nach Betriebsmode aktiv).
2 Servos zum Kettenanrieb (je nach Betriebsmode aktiv).

Eine funktionierende ISR für die Servoausgabe habe ich bereits realisieren können, doch leider verträgt sich diese nicht mit dem Pulsein Befehl (die „romanartigen“ Kommentare im Programm sind nur für mich Voll-NOP, damit ich immer nachlesen kann, was das Programm gerade macht).
Im folgenden Programm ist die Routine für 4 Servos ersichtlich. Zu Testzwecken wollte ich nur mal ein RC-Signal einlesen und ein Servo damit speisen. Ein weiteres Servo wurde ebenfalls angeschlossen und mit einem Festwert (hier 1500 für Mittelstellung) gespeist. Die ISR wird dabei offensichtlich korrekt abgearbeitet (erkennbar an dem Servo, welches nur die Mittelstellung halten soll). Allerdings zuckt das andere Servo erheblich hin und her. Es reagiert zwar auf die Eingaben am RC-Eingang (Servo folgt „grob“ dem RC-Signal), ist aber ansonsten nicht zu gebrauchen. Ich „vermute“, das die Springerei in die ISR den Pulsein Befehl stört, dadurch falsche Werte eingelesen werden und in folge davon das Servo zuckt. Schade, denn die Funktion dieses Programms hätte ich ausnahmsweise mal nachvollziehen können. :cry:


'================================================= ==============================
'Baggersteuerung (ACHTUNG FUNKTIONIERT NICHT, SERVO RUCKELT EXTREM)

'RC Empfangskanal 1 an Pin 6 (PA7)
'RC Empfangskanal 2 an Pin 7 (PA6)
'RC Empfangskanal 3 (Mode-Schalter) an Pin 5 (PB2)

'Werte zwischen 100 und 200 entsprechen dem kompletten Knüppelweg
'Der Wert 150 entspricht dabei ca. Mittelstellung, der Wert 200 ca. Vollausschlag

'Poti Eingang 1 Pin 9 (PA4)
'Poti Eingang 1 Pin 8 (PA5)

'Servoausgang 1 Pin13 (PA0)
'Servoausgang 2 Pin12 (PA1)
'Servoausgang 3 Pin11 (PA2)
'Servoausgang 4 Pin10 (PA3)

'Made by Robert (www.roberts-r2d2-bau.de)
'================================================= ==============================


$regfile = "attiny24.dat"
$crystal = 8000000 'FuseBit CKDIV8 deaktivieren


$hwstack = 32
$swstack = 20
$framesize = 40



'-------------------------------------------------------------------------------------------------------------
'Variablen definieren
'-------------------------------------------------------------------------------------------------------------


Dim Empfangskanal(3) As Word
Dim Servo1_signal As Word

Dim Kanal As Byte
Dim Servo(4) As Word


'-------------------------------------------------------------------------------------------------------------
'Ein- und Ausgang festlegen
'-------------------------------------------------------------------------------------------------------------

Ddra = &B00001111 'PA0, PA1, PA2 und PA3 werden Ausgänge, der restliche Port A bleibt Eingang
Ddrb = &B00000000 'PortB bleibt Eingang


'-------------------------------------------------------------------------------------------------
'Timer und Interrupt-Service-Routinen konfigurieren und freigeben
'-------------------------------------------------------------------------------------------------

Config Timer1 = Timer , Prescale = 8 'Timer für Servos, läuft mit 1MHz, ergibt 1000 Schritte Auflösung pro ms
Enable Timer1 'schaltet den Timer1 Overflow-interrupt ein

On Timer1 Servoausgabe 'Sprigt bei Timer1 Überlauf in die ISR
Enable Interrupts ' Interrupts zulassen


'-------------------------------------------------------------------------------------------------------------
'Einigen Variablen Werte zuweisen
'-------------------------------------------------------------------------------------------------------------

Kanal = 1


'-------------------------------------------------------------------------------------------------------------
'Hauptprogramm starten
'-------------------------------------------------------------------------------------------------------------

Do

Pulsein Empfangskanal(1) , Pinb , 2 , 1 'Zählung läuft, solange der Empfänger Pegel auf 1 ist
' Pulsein Empfangskanal(2) , Pina , 7 , 1
' Pulsein Empfangskanal(3) , Pina , 6 , 1


Servo1_signal = Empfangskanal(1) * 10


Servo(1) = Servo1_signal
Servo(2) = 1500
Servo(3) = 1500
Servo(4) = 1500


Loop

End




Servoausgabe:


'(

Info
Bei jedem Timer1 Überlauf wird in die ISR(Interrupt Service Routine) gesprungen und
eine EINZIGE If -Bedingung erfüllt und abgearbeitet.
Ablauf:
Pin des ersten Servoausgangs einschalten. Gleichzeitig wird der Timer1 so vorgeladen,
dass es nach einer bestimmten Zeit(Wert der Variable "Servo(1)") erneut zu einem
Überlauf kommt. Da sonst keine weitere If -Bedingung erfüllt wird , gehts gleich wieder
zurück ins Hauptprogramm.
Beim folgenden Überlauf ist der erste Servoausgang bereits eingeschaltet , sodass
nun die Else -Bedingung greift und der Ausgang wieder ausgeschaltet wird (somit ist das
erste Servosignal (je nach Variablenwert 1ms -2ms) gesendet. Gleichzeitig
wird die Variable "Kanal" um eins erhöht.
Nun trifft die If -Bedingung für den zweiten Servoausgang zu (da die Variable "Kanal"
ja nun auf 2 steht) und dieser wird eingeschaltet.
Das Spiel wiederholt sich so lange , bis alle Kanäle durch sind. Die letzte If -Bedingung
in dieser ISR füllt dann nur noch die verbleibende Zeit auf, um auf eine
Wiederholungsfrequenz von ca 20ms zu kommen.

')

If Kanal = 1 Then
If Porta.0 = 0 Then 'wenn der Ausgangspin aus ist
Load Timer1 , Servo(1) 'wird der Timer1 mit dem Wert der Variable "Servo(1)" vorgeladen
Porta.0 = 1 'und der Ausgangspin eingeschaltet
Else 'erst beim nächsten Timer1 Überlauf landen wir hier
Porta.0 = 0 'Ausgangspin wird wieder ausgeschaltet
Incr Kanal 'und der nächsten Kanal bearbeitet
End If
End If


If Kanal = 2 Then
If Porta.1 = 0 Then
Load Timer1 , Servo(2)
Porta.1 = 1
Else
Porta.1 = 0
Incr Kanal
End If
End If


If Kanal = 3 Then
If Porta.2 = 0 Then
Load Timer1 , Servo(3)
Porta.2 = 1
Else
Porta.2 = 0
Incr Kanal
End If
End If


If Kanal = 4 Then
If Porta.3 = 0 Then
Load Timer1 , Servo(4)
Porta.3 = 1
Else
Porta.3 = 0
Incr Kanal
End If
End If

'(

Pausenauffüllung
Jeder der oben angesteuerten Kanälen benötigt zwischen 1 -2ms. Die verbleibende Zeit
wird nun hier eingegeben, um auf eine durchschnittliche Periodendauer von ca. 20ms
zu kommen. Der eingegebene Zahlenwert entspricht dabei us,
z.b. 13000 entspricht 13 ms.

')

If Kanal = 5 Then
Load Timer1 , 13000 'der Zahlenwert entspricht us
Kanal = 1
End If

Return




Eine weitere Möglichkeit wäre Hardware-PWM. Allerdings habe ich da noch praktisch keine Erfahrung. Hier im Forum fand ich ein Programm mit HW-PWM, verstehe aber nicht wirklich, was bestimmte einzelne Zeilen bewirken. Ich war ehrlich gesagt überrascht, dass es nach nur wenigen Änderungen auf meinem Attiny24 lauffähig war. Ich habe zwar Erklärungen dieser Abkürzungen in Datenblätter und einigen Seiten im www gefunden, aber zwischen lesen und verstehen ist leider ein erheblicher Unterschied.



'================================================= ==============================
'Servo an Pin 7 (PA6, OC1A)
'RC Empfangskanal an Pin 5 (PB2)
'================================================= ==============================


$regfile = "attiny24.dat"
$crystal = 8000000 'FuseBit CKDIV8 deaktivieren


$hwstack = 32
$swstack = 20
$framesize = 40


Ddra.6 = 1 'PA6 (Ausgang vom OC1A) wird Ausgang
Ddrb = &B00000000 'PortB bleibt Eingang

Tccr1a = &B10000000
Tccr1b = &B00010001

Icr1h = &B11111111
Icr1l = &B11111111


Dim Empfangskanal As Word


Do

Pulsein Empfangskanal , Pinb , 2 , 1

Empfangskanal = Empfangskanal * 40

Ocr1ah = High(empfangskanal)
Ocr1al = Low(empfangskanal)

Loop

Kann mir jemand erklären, was genau die folgenden Zeilen im Detail machen (bin noch Anfänger mit begrenzten Englischkenntnissen ohne Abi/ Studium, daher bitte nicht zu viel Fachwissen voraussetzen).


Tccr1a = &B10000000
Tccr1b = &B00010001

Icr1h = &B11111111
Icr1l = &B11111111

Gibt es dafür auch andere Schreibweisen in Bascom (wie z.B. Config Timer1 = PWM etc), die ich eher nachvollziehen kann? :-k

Ich bin auch für andere Ideen offen (solange sie in irgendeiner Weise nachvollziehbar bleiben). Im ersten Schritt geht es mir zunächst „nur“ um eine ruckelfreie Ausgabe der Servoimpulse bei gleichzeitigem Einlesen von 3 RC-Signalen.


Mit freundlichen Grüßen
Robert

radbruch
13.12.2013, 11:39
Timer für Servos, läuft mit 1MHz, ergibt 1000 Schritte Auflösung pro msWelche Auflösung benötigst du wirklich für deine Anwendung?

R2D2 Bastler
13.12.2013, 17:31
hallo radbruch,

ehrlich gesagt weiß ich nicht genau, bis zu welche Auflösung man runter gehen kann. Hängt mit Sicherheit auch von den verwendeteten Servos (Modellflug) oder Drehzahlreglern ab. Da ich die Routinen evt auch in Flugmodellen benutzen werde, müssen sie eben "einigermaßen" flüssig und ruckelfrei laufen.
Bei meinen verwendetet Werten hatte ich den Vorteil, dass ich nicht lange umrechnen muss, da 1000 einer ms entspricht (Anschlag auf der einen Seite) und 2000 zwei ms (Anschlag auf der anderen Seite) .

mfg
Robert

oberallgeier
13.12.2013, 23:41
... weiß ich nicht genau, bis zu welche Auflösung man runter gehen kann ...Bei wenigen verschiedenen Servos hatte ich Auflösung im Bereich rund 4 µs festgestellt, wobei ich die Servos in Schritten von 0,4 µs angesteuert (https://www.roboternetz.de/community/threads/62904-Servomesstester-(Servotester-und-–messaufbau)?p=585511&viewfull=1#post585511) hatte, siehe hier für nen schlechten Billigservo (https://www.roboternetz.de/community/threads/61379-Kopfsache-und-ein-m1284-etliche-Servos-viel-Alu?p=579801&viewfull=1#post579801) und hier für nen billigen Guten. (https://www.roboternetz.de/community/threads/61379-Kopfsache-und-ein-m1284-etliche-Servos-viel-Alu?p=583165&viewfull=1#post583165) Das macht dann aber wirklich nur Sinn, wenn Du das irgendwie nutzen kannst - ich habe es so ausgelegt, damit ich kleinste, zusätzlich auch langsam(st)e Bewegungen fahren kann. (https://www.roboternetz.de/community/threads/61379-Kopfsache-und-ein-m1284-etliche-Servos-viel-Alu?p=577672&viewfull=1#post577672)

Wie das in Basic geht, kann ich aber nicht sagen.

radbruch
20.12.2013, 13:41
Ungetestet:


'================================================= ==============================
'Baggersteuerung (ACHTUNG FUNKTIONIERT NICHT, SERVO RUCKELT EXTREM)

'RC Empfangskanal 1 an Pin 6 (PA7)
'RC Empfangskanal 2 an Pin 7 (PA6)
'RC Empfangskanal 3 (Mode-Schalter) an Pin 5 (PB2)

'Werte zwischen 100 und 200 entsprechen dem kompletten Knüppelweg
'Der Wert 150 entspricht dabei ca. Mittelstellung, der Wert 200 ca. Vollausschlag

'Poti Eingang 1 Pin 9 (PA4)
'Poti Eingang 1 Pin 8 (PA5)

'Servoausgang 1 Pin13 (PA0)
'Servoausgang 2 Pin12 (PA1)
'Servoausgang 3 Pin11 (PA2)
'Servoausgang 4 Pin10 (PA3)

'Made by Robert (www.roberts-r2d2-bau.de)

'Angepasst von radbruch 21.12.2013
'https://www.roboternetz.de/community/threads/63750-Mehrere-RC-Signale-einlesen-und-mehrere-Servos-ausgeben?
'================================================= ==============================


$regfile = "attiny24.dat"
$crystal = 8000000 'FuseBit CKDIV8 deaktivieren


$hwstack = 32
$swstack = 20
$framesize = 40



'-------------------------------------------------------------------------------------------------------------
'Variablen definieren
'-------------------------------------------------------------------------------------------------------------


Dim Empfangskanal(3) As Word
Dim Empfangskanal_temp_ein(3) As Word
Dim Empfangskanal_temp_aus(3) As Word

Dim Kanal As Byte
Dim Servo(4) As Word


'-------------------------------------------------------------------------------------------------------------
'Ein- und Ausgang festlegen
'-------------------------------------------------------------------------------------------------------------

Ddra = &B00001111 'PA0, PA1, PA2 und PA3 werden Ausgänge, der restliche Port A bleibt Eingang
Ddrb = &B00000000 'PortB bleibt Eingang


'-------------------------------------------------------------------------------------------------
'Timer und Interrupt-Service-Routinen konfigurieren und freigeben
'-------------------------------------------------------------------------------------------------

Config Timer1 = Timer , Prescale = 8 'Timer für Servos, läuft mit 1MHz, ergibt 1000 Schritte Auflösung pro ms
Compare1a = 1000 ' Nach 1ms wird die ISR initialisiert
Timer1 = 1
Enable Compare1a 'schaltet den Timer1 Compare1a Match-Interrupt ein

On Compare1a Servoausgabe 'Springt bei Timer1 Compare1a Match in die ISR
Enable Interrupts ' Interrupts zulassen


'-------------------------------------------------------------------------------------------------------------
'Einigen Variablen Werte zuweisen
'-------------------------------------------------------------------------------------------------------------

Kanal = 0 ' Kanal startet jetzt bei 0 wegen porta.kanal in der ISR!

Servo(1) = 1500
Servo(2) = 1500
Servo(3) = 1500
Servo(4) = 1500
Empfangskanal(1) = 0
Empfangskanal(2) = 0
Empfangskanal(3) = 0
Empfangskanal_temp_ein(1) = 0
Empfangskanal_temp_ein(2) = 0
Empfangskanal_temp_ein(3) = 0
Empfangskanal_temp_aus(1) = 0
Empfangskanal_temp_aus(2) = 0
Empfangskanal_temp_aus(3) = 0


'-------------------------------------------------------------------------------------------------------------
'Hauptprogramm starten
'-------------------------------------------------------------------------------------------------------------

Do

If Pina.7 = 1 And Empfangskanal_temp_ein(1) = 0 Then
Empfangskanal_temp_ein(1) = Timer1
End If

If Pina.7 = 0 And Empfangskanal_temp_ein(1) <> 0 Then
Empfangskanal_temp_aus(1) = Timer1
If Empfangskanal_temp_aus(1) < Empfangskanal_temp_ein(1) Then
Empfangskanal_temp_aus(1) = Empfangskanal_temp_aus(1) + 20000
End If
Empfangskanal(1) = Empfangskanal_temp_aus(1) - Empfangskanal_temp_ein(1)
Empfangskanal_temp_ein(1) = 0
End If

If Pina.6 = 1 And Empfangskanal_temp_ein(2) = 0 Then
Empfangskanal_temp_ein(2) = Timer1
End If

If Pina.6 = 0 And Empfangskanal_temp_ein(2) <> 0 Then
Empfangskanal_temp_aus(2) = Timer1
If Empfangskanal_temp_aus(2) < Empfangskanal_temp_ein(2) Then
Empfangskanal_temp_aus(2) = Empfangskanal_temp_aus(2) + 20000
End If
Empfangskanal(2) = Empfangskanal_temp_aus(2) - Empfangskanal_temp_ein(2)
Empfangskanal_temp_ein(2) = 0
End If

If Empfangskanal(1) <> 0 Then Servo(1) = Empfangskanal(1) End If
If Empfangskanal(2) <> 0 Then Servo(2) = Empfangskanal(2) End If

Loop

End

'(

Der Timer startet beim ersten Servo mit dem Zählwert 1, Das Compare-Register wird auf den Wert
für die Impulslänge des ersten Servo gesetzt.In der Compere-ISR (wenn der Zählwert den Wert im
Compareregister erreicht hat) wird dann ausgehend vom aktuelen Timerwert der Vergleichswert für
die Impulslänge des nächsten Servo berechnet und gesetzt. Nach dem letzten Servo wird 20000 für
die Wiederholfrequenz des Signals gesetzt

')

Servoausgabe:

Porta.0 = 0 'alle Servosignale ausschalten
Porta.1 = 0
Porta.2 = 0
Porta.3 = 0

If Kanal < 4 Then ' Zeitpunkt für nächstes Servo setzen
If Kanal = 0 Then
Timer1 = 1 ' beim ersten Servo Timer Zähler zurücksetzen
End If
Compare1a = Timer1 + Servo(kanal + 1) ' aktueller Timerwert + Servo Impulslänge
Porta.kanal = 1 ' Impuls ausgeben
Incr Kanal ' nächstes Servo vormerken
Else
Compare1a = 20000 ' 50Hz Wiederholfrequenz setzen
Kanal = 0
End If

Return

R2D2 Bastler
21.12.2013, 11:18
Hallo radbruch,

habe Deinen Vorschlag mal gestestet, funktioniert ähnlich gut wie der Code, den ich in der Zwischenzeit mit Hilfe eines weiteren Forummitglieds gestrickt habe. Mit "ähnlich gut" meine ich, dass ein geringes, aber deutlich sichtbares Servozucken an den Servos vorhanden ist, welche mit den eingelesenen RC-Signalen versorgt werden. Servos, denen ich im Programm einen Festwert zuweise, zucken nicht. Daraus schliese ich, dass die Servoausgabe ISR korrekt und zuverlässig arbeitet, aber sich die Interrupts der RC Einleserei in die Quere kommen. Der folgende Code liest 3 RC-Signale ein und gibt 2 davon an 2 Servos weiter, während die anderen beiden Servos einen Festwert zugewiesen bekommen haben. Ich benutze TIMER1 für das Einlesen (tolle Auflösung) und TIMER0 für die Ausgabe (leider nur noch sehr bescheidene Auflösung).

Inzwischen ist die Auswertung der Poti's auch eingebaut und getestet (hier aber nicht gepostet, da es den Code nur unnötig unübersichtlich macht). Allerdings habe ich die Speicherkapazität des Attiny24 mittlerweile geprengt und warte nun auf die bestellten Attiny84.

Mein momentanes Problem ist das nicht ganz saubere Einlesen der RC Signale. Ich habe auch versucht, immer nur einen der drei Interrupts zuzulassen (z.B. mit "disable INT0" in der entsprechenden ISR) und die RC Signale hintereinander einzulesen, führte aber zu keiner Verbesserung.

mfg
Robert



'================================================= ==============================
'RC Eingang 1 an Pin 3 (PB1, PCINT9)
'RC Eingang 2 an Pin 7 (PA7, PCINT7)
'RC Eingang 3 an Pin 5 (PB2, INT0)
'Servo 1 an Pin 13 (PA0)
'Servo 2 an Pin 12 (PA1)
'Servo 3 an Pin 11 (PA2)
'Servo 4 an Pin 10 (PA3)
'================================================= ==============================

$regfile = "attiny24.dat"
$crystal = 8000000 'FuseBit CKDIV8 deaktivieren

$hwstack = 32
$swstack = 20
$framesize = 40


'-------------------------------------------------------------------------------------------------
'Timer und konfigurieren
'-------------------------------------------------------------------------------------------------

Config Timer1 = Timer , Prescale = 8 'Timer für Einlesen RC Signale
Start Timer1

Config Timer0 = Timer , Prescale = 64 'Timer für Servoausgabe, Wert 125 entspricht 1ms, Wert 250 entspricht 2ms
Enable Timer0
On Timer0 Servoausgabe


'-------------------------------------------------------------------------------------------------------------
'Variablen definieren
'-------------------------------------------------------------------------------------------------------------

'Variablen fürs RC Einlesen
Dim Rc_signal_1_start As Word
Dim Rc_signal_2_start As Word
Dim Rc_signal_3_start As Word

Dim Impulslaenge_1 As Word
Dim Impulslaenge_2 As Word
Dim Impulslaenge_3 As Word



'Variablen für Berechnungen
Dim Berechnung_1 As Word
Dim Berechnung_2 As Word



'Variablen für Servoausgabe
Dim Kanal As Byte
Dim Servoausgabe_1 As Byte
Dim Servoausgabe_2 As Byte
Dim Servoausgabe_3 As Byte
Dim Servoausgabe_4 As Byte
Dim Pausen_variable As Byte


'-------------------------------------------------------------------------------------------------
'Einigen Variablen Werte zuweisen
'-------------------------------------------------------------------------------------------------

Kanal = 1
Pausen_variable = 0

'-------------------------------------------------------------------------------------------------------------
'Ein- und Ausgang festlegen
'-------------------------------------------------------------------------------------------------------------

Ddra = &B00001111 'PA0 - PA3 werden Ausgänge
Ddrb = &B00000000 'PortB bleibt Eingang


'-------------------------------------------------------------------------------------------------
'Interrupt-Service-Routinen konfigurieren und freigeben
'-------------------------------------------------------------------------------------------------

'Info:
'Alle Porta Pinchangeinterrupts sind in Bascom "PCINT0" zugeordnet.
'Alle Portb Pinchangeinterrupts sind in Bascom "PCINT1" zugeordnet.


Pcmsk1.pcint9 = 1 'beim Flankenwechsel an PB1/PCINT9 (RC Eingang 1) Pinchangeinterrupt1 auslösen und in die Subroutine springen
Enable Pcint1 'Pinchangeinterrupt1 (1 weil auf PortB) zulassen
On Pcint1 Rc_eingang_1


Pcmsk0.pcint7 = 1 'beim Flankenwechsel an PA7/PCINT6 (RC Eingang 2) Pinchangeinterrupt0 auslösen und in die Subroutine springen
Enable Pcint0 'Pinchangeinterrupt0 (0 weil auf PortA) zulassen
On Pcint0 Rc_eingang_2


Config Int0 = Change 'beim Flankenwechsel an PB2/INT0 (RC Eingang 3) Int0 auslösen und in die Subroutine springen
Enable Int0
On Int0 Rc_eingang_3

Enable Interrupts



'================================================= =====
'Hauptprogramm
'================================================= =====

Do


'Umrechnung erstes RC Signal auf 8-Bit
Berechnung_1 = Impulslaenge_1 / 8 'ergibt Werte zwischen 125 und 250

If Berechnung_1 > 255 Then 'zu hohe Werte abfangen
Berechnung_1 = 255
End If

If Berechnung_1 < 120 Then 'zu kleine Werte abfangen
Berechnung_1 = 120
End If


'Umrechnung zweites RC Signal auf 8-Bit
Berechnung_2 = Impulslaenge_2 / 8 'ergibt Werte zwischen 125 und 250

If Berechnung_2 > 255 Then 'zu hohe Werte abfangen
Berechnung_2 = 255
End If

If Berechnung_2 < 120 Then 'zu kleine Werte abfangen
Berechnung_2 = 120
End If



Servoausgabe_1 = Berechnung_1
Servoausgabe_2 = Berechnung_2
Servoausgabe_3 = 190
Servoausgabe_4 = 190


Loop



'================================================= =====
'ISR
'================================================= =====


Rc_eingang_1:
If Pinb.1 = 1 Then
Rc_signal_1_start = Timer1
Else
Impulslaenge_1 = Timer1 - Rc_signal_1_start
End If
Return


Rc_eingang_2:
If Pina.7 = 1 Then
Rc_signal_2_start = Timer1
Else
Impulslaenge_2 = Timer1 - Rc_signal_2_start
End If
Return


Rc_eingang_3:
If Pinb.2 = 1 Then
Rc_signal_3_start = Timer1
Else
Impulslaenge_3 = Timer1 - Rc_signal_3_start
End If
Return




Servoausgabe:

If Kanal = 1 Then
If Porta.0 = 0 Then 'wenn der Ausgangspin aus ist
Load Timer0 , Servoausgabe_1 'wird der Timer0 mit dem Wert der Variable "Servoausgabe_1" vorgeladen
Porta.0 = 1 'und der Ausgangspin eingeschaltet
Else 'erst beim nächsten Timer0 Überlauf landen wir hier
Porta.0 = 0 'Ausgangspin wird wieder ausgeschaltet
Incr Kanal 'und der nächsten Kanal bearbeitet
End If
End If


If Kanal = 2 Then
If Porta.1 = 0 Then
Load Timer0 , Servoausgabe_2
Porta.1 = 1
Else
Porta.1 = 0
Incr Kanal
End If
End If


If Kanal = 3 Then
If Porta.2 = 0 Then
Load Timer0 , Servoausgabe_3
Porta.2 = 1
Else
Porta.2 = 0
Incr Kanal
End If
End If


If Kanal = 4 Then
If Porta.3 = 0 Then
Load Timer0 , Servoausgabe_4
Porta.3 = 1
Else
Porta.3 = 0
Incr Kanal
End If
End If



'Pausenauffüllung


If Kanal = 5 Then
Timer0 = 0 '8-Bit Timer auf 0, Überlauf alle 2,048ms
If Pausen_variable < 7 Then '2,048ms * 6 = 12,288ms Pausenfüllzeit
Incr Pausen_variable
Else
Pausen_variable = 0
Kanal = 1
End If
End If


Return

PICture
21.12.2013, 11:38
Hallo!


Mein momentanes Problem ist das nicht ganz saubere Einlesen der RC Signale. Ich habe auch versucht, immer nur einen der drei Interrupts zuzulassen (z.B. mit "disable INT0" in der entsprechenden ISR) und die RC Signale hintereinander einzulesen, führte aber zu keiner Verbesserung.

Eben, warum (fast) alle alles mit nur einem µC realisieren versuchen (Billiger ? :confused: ).

for_ro
21.12.2013, 13:24
Ich habe auch versucht, immer nur einen der drei Interrupts zuzulassen (z.B. mit "disable INT0" in der entsprechenden ISR) und die RC Signale hintereinander einzulesen, führte aber zu keiner Verbesserung.

Hallo Robert,
wenn die RC Signale wie üblich nacheinander ankommen, dann kannst du das ganze doch mit dem Timer1 alleine hinkriegen. Erst die drei Kanäle einlesen, dann die beiden Potis auswerten und schließlich alles nacheinander an die Servos ausgeben.
3 x RC dauert max 6ms, Potis einlesen < 1ms und die Ausgabe an 4 Servos dauert maximal 8ms, also zusammen keine 15ms. Wenn dem so ist, wäre dies definitiv mein Weg, wie ich vorgehen würde. Und wenn du dann noch alle 3 RC Kanäle auf einen Eingang - vorzugsweise den ICP (PA.7) - legen kannst, ist das Einlesen auch eine saubere, weil in Hardware realisierte Lösung.
Alles würde dann synchronisiert durch den ersten RC Eingang. Genauer bekommst du die Intervalle sonst auf keinen Fall realisiert und du hättest wieder 1000 Schritte bei den Servos und nicht wie jetzt nur 125.
Solange du nicht synchron arbeitest, wird es dir immer wieder passieren, dass gerade eine ISR abgearbeitet wird, wenn ein RC Signal beginnt oder endet.
Und wenn du die Arbeit auf zwei Controller verteilst, müssen die eingelesenen Werte übertragen werden, was dann zu ähnlichen Problemen führt.

PICture
21.12.2013, 13:32
Und wenn du die Arbeit auf zwei Controller verteilst, müssen die eingelesenen Werte übertragen werden, was dann zu ähnlichen Problemen führt.

Vielleicht geht es mit mehr als zwei µC einfacher. :confused:

R2D2 Bastler
21.12.2013, 16:51
Hallo zusammen,

mit "hintereinander Einlesen" meinte ich in meinem letzten Posting das Einlesen von 3 RC Signalen, und zwar unabhängig davon, WANN sie der Empfänger sendet (wir reden nicht von einem Summensignal, sondern von 3 getrennten Anschlüssen am Empfänger).
Es gibt Empänger, die ein Signal zu Ende senden, dann mit dem nächsten beginnen (am nächsten Ausgang des Empfängers) usw bis alle 4, 6 oder 8 Kanäle durch sind. Andere Empänger setzen die Kanäle nahezu zeitgleich auf 1 und wieder andere beginnen den nächsten Kanal nach einer vordefinierten Zeit. Manche käufliche V-Mischer hatten dann das Problem, dass sie nicht mehr funktionierten, wenn man die Reihenfolge der RC Kanäle veränderte.

Ich möchte meinen Code so gestalten, dass es egal ist, welchen Empfänger ich dran hänge. Wenn ich also vom Fall ausgehe, dass die einzelnen RC Signale nahezu gleichzeitig beginnen auf 1 (high) zu gehen, und ich sie dann HINTEREINANDER einlese, so dauert das Einlesen 3 x 20ms. Das kann ich nicht während der Pause der Servoausgabe einbauen, da diese ja spätestens nach 20ms wieder bedient werden möchten (müssen).

@ PICture
Das Problem liegt daran, das ich mehr als 1 Servosignal einlesen muss. Genauergesagt mindestens 2. Eines ist der Betriebsmode (also Baggerbetrieb oder Fahrbetrieb) und das zweite die eigentliche Steuereingabe. Deshalb versuche ich alles auf einen Chip zu bekommen.
Alternativ ginge es mit 3 Chips: Einer wertet nur den Betriebsmode aus und setzt einen Ausgang auf high oder low, die anderen steuern die Servos und werten nur den Ausgang des "Betriebsmode-Chips" aus.
Mal sehen, wie es weitergeht ;)

mfg
Robert

mfg
Robert

Searcher
21.12.2013, 20:43
Hallo Robert,
wenn es in Bascom zeitkritisch wird, kann man es mit Inlineassembler versuchen.

Ich habe nix weiter gemacht als in Deinem letzten geposteten Programm versucht, die Impulsmeß-ISRs mit ASM zu ersetzten mit manuellem Registersichern (On interrupt label Nosave) und hoffe keinen Bug eingebaut zu haben:-k.
Die max. Ausführungszeit einer ISR sollte von ca. 17µs auf ca. knapp 5µs gesunken sein. Vielleicht bessert es das Verhalten:confused:.

Ungetestet und ohne Gewähr :(

Gruß
Searcher



'================================================= ==============================
'RC Eingang 1 an Pin 3 (PB1, PCINT9)
'RC Eingang 2 an Pin 7 (PA7, PCINT7)
'RC Eingang 3 an Pin 5 (PB2, INT0)
'Servo 1 an Pin 13 (PA0)
'Servo 2 an Pin 12 (PA1)
'Servo 3 an Pin 11 (PA2)
'Servo 4 an Pin 10 (PA3)
'================================================= ==============================

$regfile = "attiny24.dat"
$crystal = 8000000 'FuseBit CKDIV8 deaktivieren

$hwstack = 32
$swstack = 20
$framesize = 40


'-------------------------------------------------------------------------------------------------
'Timer und konfigurieren
'-------------------------------------------------------------------------------------------------

Config Timer1 = Timer , Prescale = 8 'Timer für Einlesen RC Signale
Start Timer1

Config Timer0 = Timer , Prescale = 64 'Timer für Servoausgabe, Wert 125 entspricht 1ms, Wert 250 entspricht 2ms
Enable Timer0
On Timer0 Servoausgabe


'-------------------------------------------------------------------------------------------------------------
'Variablen definieren
'-------------------------------------------------------------------------------------------------------------

'Variablen fürs RC Einlesen
Dim Rc_signal_1_start As Word
Dim Rc_signal_2_start As Word
Dim Rc_signal_3_start As Word

Dim Impulslaenge_1 As Word
Dim Impulslaenge_2 As Word
Dim Impulslaenge_3 As Word



'Variablen für Berechnungen
Dim Berechnung_1 As Word
Dim Berechnung_2 As Word



'Variablen für Servoausgabe
Dim Kanal As Byte
Dim Servoausgabe_1 As Byte
Dim Servoausgabe_2 As Byte
Dim Servoausgabe_3 As Byte
Dim Servoausgabe_4 As Byte
Dim Pausen_variable As Byte


'-------------------------------------------------------------------------------------------------
'Einigen Variablen Werte zuweisen
'-------------------------------------------------------------------------------------------------

Kanal = 1
Pausen_variable = 0

'-------------------------------------------------------------------------------------------------------------
'Ein- und Ausgang festlegen
'-------------------------------------------------------------------------------------------------------------

Ddra = &B00001111 'PA0 - PA3 werden Ausgänge
Ddrb = &B00000000 'PortB bleibt Eingang


'-------------------------------------------------------------------------------------------------
'Interrupt-Service-Routinen konfigurieren und freigeben
'-------------------------------------------------------------------------------------------------

'Info:
'Alle Porta Pinchangeinterrupts sind in Bascom "PCINT0" zugeordnet.
'Alle Portb Pinchangeinterrupts sind in Bascom "PCINT1" zugeordnet.


Pcmsk1.pcint9 = 1 'beim Flankenwechsel an PB1/PCINT9 (RC Eingang 1) Pinchangeinterrupt1 auslösen und in die Subroutine springen
Enable Pcint1 'Pinchangeinterrupt1 (1 weil auf PortB) zulassen
On Pcint1 Rc_eingang_1 Nosave 'Register werden manuel in der ISR gesichert


Pcmsk0.pcint7 = 1 'beim Flankenwechsel an PA7/PCINT6 (RC Eingang 2) Pinchangeinterrupt0 auslösen und in die Subroutine springen
Enable Pcint0 'Pinchangeinterrupt0 (0 weil auf PortA) zulassen
On Pcint0 Rc_eingang_2 Nosave 'Register werden manuel in der ISR gesichert


Config Int0 = Change 'beim Flankenwechsel an PB2/INT0 (RC Eingang 3) Int0 auslösen und in die Subroutine springen
Enable Int0
On Int0 Rc_eingang_3 Nosave 'Register werden manuel in der ISR gesichert

Enable Interrupts



'================================================= =====
'Hauptprogramm
'================================================= =====

Do


'Umrechnung erstes RC Signal auf 8-Bit
Berechnung_1 = Impulslaenge_1 / 8 'ergibt Werte zwischen 125 und 250

If Berechnung_1 > 255 Then 'zu hohe Werte abfangen
Berechnung_1 = 255
End If

If Berechnung_1 < 120 Then 'zu kleine Werte abfangen
Berechnung_1 = 120
End If


'Umrechnung zweites RC Signal auf 8-Bit
Berechnung_2 = Impulslaenge_2 / 8 'ergibt Werte zwischen 125 und 250

If Berechnung_2 > 255 Then 'zu hohe Werte abfangen
Berechnung_2 = 255
End If

If Berechnung_2 < 120 Then 'zu kleine Werte abfangen
Berechnung_2 = 120
End If



Servoausgabe_1 = Berechnung_1
Servoausgabe_2 = Berechnung_2
Servoausgabe_3 = 190
Servoausgabe_4 = 190


Loop



'================================================= =====
'ISR
'================================================= =====


'Rc_eingang_1:
' If Pinb.1 = 1 Then
' Rc_signal_1_start = Timer1
' Else
' Impulslaenge_1 = Timer1 - Rc_signal_1_start
' End If
'Return

Rc_eingang_1:
$asm
push r16 'Register auf Stack sichern
in r16,sreg 'Statusregister holen und halten
push r17 'Register auf Stack sichern
push r18 'Register auf Stack sichern
sbis pinb , 1 'Skip next Instr if PINBx = 1
rjmp pulslaenge1 'Spring zur Berechnung von Impulslaenge
in r17 , tcnt1l 'Timer1 low Byte holen
sts {Rc_signal_1_start} , r17 'Speichere Timer1 low Byte nach Rc_signal low Byte
in r17 , tcnt1h 'Timer1 high Byte holen
sts {Rc_signal_1_start} + 1 , r17 'Speichere Timer1 high Byte nach Rc_signal high Byte
rjmp ende1 'Springe zum Ende
Pulslaenge1:
in r17 , tcnt1l 'Timer1 low Byte holen
lds r18 , {Rc_signal_1_start} 'Hole Rc_signal low Byte
Sub R17 , R18 'Subtrahiere Rc_signal low Byte von Timer1 low Byte
sts {Impulslaenge_1} , r17 'Speichere Resultat low Byte nach Impulslaenge low Byte
in r17 , tcnt1h 'Timer1 high Byte holen
lds r18 , {Rc_signal_1_start} + 1 'Hole Rc_signal high Byte
sbc r17 , r18 'Subtrahiere Rc_signal high Byte von Timer1 high Byte
sts {Impulslaenge_1} + 1 , r17 'Speichere Resultat high Byte nach Impulslaenge high Byte
Ende1:
pop r18 'Register vom Stack zurückholen
pop r17 'Register vom Stack zurückholen
Out Sreg , R16 'Statusregister zurückspeichern
pop r16 'Register vom Stack zurückholen
$end Asm
Return

'Rc_eingang_2:
' If Pina.7 = 1 Then
' Rc_signal_2_start = Timer1
' Else
' Impulslaenge_2 = Timer1 - Rc_signal_2_start
' End If
'Return

Rc_eingang_2:
$asm
push r16 'Register auf Stack sichern
in r16,sreg 'Statusregister holen und halten
push r17 'Register auf Stack sichern
push r18 'Register auf Stack sichern
sbis pina , 7 'Skip next Instr if PINBx = 1
rjmp pulslaenge2 'Spring zur Berechnung von Impulslaenge
in r17 , tcnt1l 'Timer1 low Byte holen
sts {Rc_signal_2_start} , r17 'Speichere Timer1 low Byte nach Rc_signal low Byte
in r17 , tcnt1h 'Timer1 high Byte holen
sts {Rc_signal_2_start} + 1 , r17 'Speichere Timer1 high Byte nach Rc_signal high Byte
rjmp ende2 'Springe zum Ende
Pulslaenge2:
in r17 , tcnt1l 'Timer1 low Byte holen
lds r18 , {Rc_signal_2_start} 'Hole Rc_signal low Byte
Sub R17 , R18 'Subtrahiere Rc_signal low Byte von Timer1 low Byte
sts {Impulslaenge_2} , r17 'Speichere Resultat low Byte nach Impulslaenge low Byte
in r17 , tcnt1h 'Timer1 high Byte holen
lds r18 , {Rc_signal_2_start} + 1 'Hole Rc_signal high Byte
sbc r17 , r18 'Subtrahiere Rc_signal high Byte von Timer1 high Byte
sts {Impulslaenge_2} + 1 , r17 'Speichere Resultat high Byte nach Impulslaenge high Byte
Ende2:
pop r18 'Register vom Stack zurückholen
pop r17 'Register vom Stack zurückholen
Out Sreg , R16 'Statusregister zurückspeichern
pop r16 'Register vom Stack zurückholen
$end Asm
Return

'Rc_eingang_3:
' If Pinb.2 = 1 Then
' Rc_signal_3_start = Timer1
' Else
' Impulslaenge_3 = Timer1 - Rc_signal_3_start
' End If
'Return

Rc_eingang_3:
$asm
push r16 'Register auf Stack sichern
in r16,sreg 'Statusregister holen und halten
push r17 'Register auf Stack sichern
push r18 'Register auf Stack sichern
sbis pinb , 2 'Skip next Instr if PINBx = 1
rjmp pulslaenge3 'Spring zur Berechnung von Impulslaenge
in r17 , tcnt1l 'Timer1 low Byte holen
sts {Rc_signal_3_start} , r17 'Speichere Timer1 low Byte nach Rc_signal low Byte
in r17 , tcnt1h 'Timer1 high Byte holen
sts {Rc_signal_3_start} + 1 , r17 'Speichere Timer1 high Byte nach Rc_signal high Byte
rjmp ende3 'Springe zum Ende
Pulslaenge3:
in r17 , tcnt1l 'Timer1 low Byte holen
lds r18 , {Rc_signal_3_start} 'Hole Rc_signal low Byte
Sub R17 , R18 'Subtrahiere Rc_signal low Byte von Timer1 low Byte
sts {Impulslaenge_3} , r17 'Speichere Resultat low Byte nach Impulslaenge low Byte
in r17 , tcnt1h 'Timer1 high Byte holen
lds r18 , {Rc_signal_3_start} + 1 'Hole Rc_signal high Byte
sbc r17 , r18 'Subtrahiere Rc_signal high Byte von Timer1 high Byte
sts {Impulslaenge_3} + 1 , r17 'Speichere Resultat high Byte nach Impulslaenge high Byte
Ende3:
pop r18 'Register vom Stack zurückholen
pop r17 'Register vom Stack zurückholen
Out Sreg , R16 'Statusregister zurückspeichern
pop r16 'Register vom Stack zurückholen
$end Asm
Return



Servoausgabe:

If Kanal = 1 Then
If Porta.0 = 0 Then 'wenn der Ausgangspin aus ist
Load Timer0 , Servoausgabe_1 'wird der Timer0 mit dem Wert der Variable "Servoausgabe_1" vorgeladen
Porta.0 = 1 'und der Ausgangspin eingeschaltet
Else 'erst beim nächsten Timer0 Überlauf landen wir hier
Porta.0 = 0 'Ausgangspin wird wieder ausgeschaltet
Incr Kanal 'und der nächsten Kanal bearbeitet
End If
End If


If Kanal = 2 Then
If Porta.1 = 0 Then
Load Timer0 , Servoausgabe_2
Porta.1 = 1
Else
Porta.1 = 0
Incr Kanal
End If
End If


If Kanal = 3 Then
If Porta.2 = 0 Then
Load Timer0 , Servoausgabe_3
Porta.2 = 1
Else
Porta.2 = 0
Incr Kanal
End If
End If


If Kanal = 4 Then
If Porta.3 = 0 Then
Load Timer0 , Servoausgabe_4
Porta.3 = 1
Else
Porta.3 = 0
Incr Kanal
End If
End If



'Pausenauffüllung


If Kanal = 5 Then
Timer0 = 0 '8-Bit Timer auf 0, Überlauf alle 2,048ms
If Pausen_variable < 7 Then '2,048ms * 6 = 12,288ms Pausenfüllzeit
Incr Pausen_variable
Else
Pausen_variable = 0
Kanal = 1
End If
End If


Return

radbruch
22.12.2013, 07:48
Hallo


Mit "ähnlich gut" meine ich, dass ein geringes, aber deutlich sichtbares Servozucken an den Servos vorhanden ist, welche mit den eingelesenen RC-Signalen versorgt werden. Servos, denen ich im Programm einen Festwert zuweise, zucken nicht.Die Verwendung des (NoSave-) Pinchange-Interrupts zum Einlesen halte ich auch für richtig, ich war nur zu faul es umzusetzen.

Was ihr weder in der Bascom- noch in der ASM-Version beachtet ist der Überlauf des Timers während der Impulsmessung. Hier schlummert möglicherweise auch mein Denkfehler:


'-------------------------------------------------------------------------------------------------------------
'Hauptprogramm starten
'-------------------------------------------------------------------------------------------------------------

Do

If Pina.7 = 1 And Empfangskanal_temp_ein(1) = 0 Then
Empfangskanal_temp_ein(1) = Timer1
End If

If Pina.7 = 0 And Empfangskanal_temp_ein(1) <> 0 Then
Empfangskanal_temp_aus(1) = Timer1
If Empfangskanal_temp_aus(1) > Empfangskanal_temp_ein(1) Then ' Wert nur gültig wenn kein Timerüberlauf
Empfangskanal(1) = Empfangskanal_temp_aus(1) - Empfangskanal_temp_ein(1)
End If
Empfangskanal_temp_ein(1) = 0
End If

If Empfangskanal(1) <> 0 Then Servo(1) = Empfangskanal(1) End If

Loop

End

Gruß

mic

for_ro
22.12.2013, 10:16
Was ihr weder in der Bascom- noch in der ASM-Version beachtet ist der Überlauf des Timers während der Impulsmessung.

Inder Bascom Version funktioniert dies auch bei Timer Überlauf, solange Impulslaenge_1 nicht überläuft. Da hier nur Werte bis max 2000 kommen dürften, geht das auch so:

Rc_eingang_1:
If Pinb.1 = 1 Then
Rc_signal_1_start = Timer1
Else
Impulslaenge_1 = Timer1 - Rc_signal_1_start
End If
Return

Du brauchst also keine Vorkehrungen zu treffen, ob der aktuelle Wert größer als der vorherige ist
If Empfangskanal_temp_aus(1) > Empfangskanal_temp_ein(1) Then ' Wert nur gültig wenn kein Timerüberlauf
Ob searcher das in ASM auch so umgesetzt hat, kann ich leider nicht beurteilen.

R2D2 Bastler
22.12.2013, 11:48
Hallo zusammen,

ich habe mir gerade die Zeiten angesehen, die Ihr für die Ausführung der ISR vermutet. Als Anfänger und Laie weiß ich natürlich nicht, wie lange so ein Sprung in die ISR und wieder zurück dauert. Aber selbst wenn ich von rund 20 µs ausgehen, sind das "nur" 2% der Gesamtmessdauer bei einem 1ms Signal (bei 2ms wäre es nur noch 1 % Abweichung). Da die Ausgabewerte durch die Verwendung des 8-Bit-Timers dann auch noch durch 8 geteilt werden, "scheinen" mir die Sprungzeiten kaum noch relevant, oder rechne ich da falsch :confused:. Ist wie gesagt nur eine Vermutung.

mfg
Robert

for_ro
22.12.2013, 11:57
Hallo Robert,
rechne mal so:
Du hast auf der Ausgabeseite eine Auflösung von 125 Schritten für die Spanne zwischen 1ms und 2ms, also für 1ms. D.h. ein Schritt hat die Dauer von 8µs. Da machen 20µs natürlich schon einen Unterschied.

Searcher
22.12.2013, 12:04
Du brauchst also keine Vorkehrungen zu treffen, ob der aktuelle Wert größer als der vorherige ist
If Empfangskanal_temp_aus(1) > Empfangskanal_temp_ein(1) Then ' Wert nur gültig wenn kein Timerüberlauf
Ob searcher das in ASM auch so umgesetzt hat, kann ich leider nicht beurteilen.

Hallo, die ASM Teile sollen eine 1:1 Umsetzung der entsprechenden Bascom ISRs sein; also keine Gültigkeitsüberprüfung. Falls der Timer überläuft würde das Ergebnis der Subtraktion negativ sein. Die Impulslaenge Variable ist vom Typ Word mit 16Bit wie auch TCNT1 16Bit, kann keine negativen Zahlen enthalten, "läuft mit über" und enthält dann den gewollten Abstand von Rc_signal_start und aktuellem Timerstand. Soll auch so in der ASM funktionieren.

@radbruch: Hab noch keine Zeit gefunden, mich in Deine Version reinzudenken.

Die ASM Teile könnten noch etwas optimiert werden, wenn die Push und Pop nur da stehen, wo sie wirklich gebraucht werden. Falls PIN high ist, spart man an der Stelle fast nochmal ca 1µs.

Mit der "Servoausgabe ISR" hab ich mich auch noch nicht beschäftigt :(

Gerade überlege ich, ob man nicht ein Polling in einer einzigen ISR zum Pulsmessen durchführen könnte.
Also irgendeiner der drei Interrupts tritt auf und in der einzigen ISR, die allen drei Interrupts zugeordnet ist werden dann alle drei Eingänge auf ihren Zustand gepollt und über Flagsteuerung Falschberechnungen verhindert werden. Keine Ahnung, ob das schneller werden könnte ...

Gruß
Searcher


Rc_eingang_1:
$asm
sbis pinb , 1 'Skip next Instr if PINBx = 1
rjmp pulslaenge1 'Spring zur Berechnung von Impulslaenge
push r17 'Register auf Stack sichern
in r17 , tcnt1l 'Timer1 low Byte holen
sts {Rc_signal_1_start} , r17 'Speichere Timer1 low Byte nach Rc_signal low Byte
in r17 , tcnt1h 'Timer1 high Byte holen
sts {Rc_signal_1_start} + 1 , r17 'Speichere Timer1 high Byte nach Rc_signal high Byte
pop r17 'Register vom Stack zurückholen
rjmp ende1 'Springe zum Ende
Pulslaenge1:
push r16 'Register auf Stack sichern
in r16,sreg 'Statusregister holen und halten
push r17 'Register auf Stack sichern
push r18 'Register auf Stack sichern
in r17 , tcnt1l 'Timer1 low Byte holen
lds r18 , {Rc_signal_1_start} 'Hole Rc_signal low Byte
Sub R17 , R18 'Subtrahiere Rc_signal low Byte von Timer1 low Byte
sts {Impulslaenge_1} , r17 'Speichere Resultat low Byte nach Impulslaenge low Byte
in r17 , tcnt1h 'Timer1 high Byte holen
lds r18 , {Rc_signal_1_start} + 1 'Hole Rc_signal high Byte
sbc r17 , r18 'Subtrahiere Rc_signal high Byte von Timer1 high Byte
sts {Impulslaenge_1} + 1 , r17 'Speichere Resultat high Byte nach Impulslaenge high Byte
pop r18 'Register vom Stack zurückholen
pop r17 'Register vom Stack zurückholen
Out Sreg , R16 'Statusregister zurückspeichern
pop r16 'Register vom Stack zurückholen
Ende1:
$end Asm
Return

R2D2 Bastler
27.12.2013, 12:50
@Searcher
Nach dem ganzen "Weihnachts- und Familienfeierstreß" habe ich mich wieder dem Programmieren zugewand. Ich habe Deinen ASM-Code mal drauf gespielt. Ist schwer zu sagen, ob es gewirkt hat: Die Servos haben vorher leicht gezuckt, und zucken hinterher auch leicht (subjektiv etwas weniger). Ich denke es liegt daran, dass wir bisher "nur" bei den 3 ISRs vom RC-Signal Einlesen Zeit gespart haben. Bei der Ausgabe wird allerding viel öfter in die ISR gesprungen (alleine 6-7 mal nur für die Pausenzeit).

Ich habe die letzten Stunden damit verbacht, Deinen ASM Code zu verstehen. Meine Güte, wer hat sich den sowas ausgedacht ](*,) Obwohl ich versucht habe, mich auch hier http://www.avr-asm-tutorial.net/avr_de/index.html einzulesen, gebe ich nun offen und ehrlich zu: ASM werd ich wohl nie kapieren :(

Ich habe den neuen Code nochmal angehängt. Deinen Code, der die Register nur im Bedarfsfall sichert/holt, konnte ich anpassen und einfügen (war nach Deiner tollen Vorarbeit auch nicht so schwer).
Nur die Ausgabe ISR habe ich nicht realisieren können :(

mfg
Robert



'================================================= ==============================
'RC Eingang 1 an Pin 3 (PB1, PCINT9)
'RC Eingang 2 an Pin 6 (PA7, PCINT7)
'RC Eingang 3 an Pin 5 (PB2, INT0)
'Servo 1 an Pin 13 (PA0)
'Servo 2 an Pin 12 (PA1)
'Servo 3 an Pin 11 (PA2)
'Servo 4 an Pin 10 (PA3)
'================================================= ==============================

$regfile = "attiny84.dat"
$crystal = 8000000 'FuseBit CKDIV8 deaktivieren

$hwstack = 50
$swstack = 50
$framesize = 50


'-------------------------------------------------------------------------------------------------
'Timer und konfigurieren
'-------------------------------------------------------------------------------------------------

Config Timer1 = Timer , Prescale = 8 'Timer für Einlesen RC Signale
Start Timer1

Config Timer0 = Timer , Prescale = 64 'Timer für Servoausgabe, Wert 125 entspricht 1ms, Wert 250 entspricht 2ms
Enable Timer0
On Timer0 Servoausgabe


'-------------------------------------------------------------------------------------------------------------
'Variablen definieren
'-------------------------------------------------------------------------------------------------------------

'Variablen fürs RC Einlesen
Dim Rc_signal_1_start As Word
Dim Rc_signal_2_start As Word
Dim Rc_signal_3_start As Word

Dim Impulslaenge_1 As Word
Dim Impulslaenge_2 As Word
Dim Impulslaenge_3 As Word



'Variablen für Berechnungen
Dim Berechnung_1 As Word
Dim Berechnung_2 As Word



'Variablen für Servoausgabe
Dim Kanal As Byte
Dim Servoausgabe_1 As Byte
Dim Servoausgabe_2 As Byte
Dim Servoausgabe_3 As Byte
Dim Servoausgabe_4 As Byte
Dim Pausen_variable As Byte


'-------------------------------------------------------------------------------------------------
'Einigen Variablen Werte zuweisen
'-------------------------------------------------------------------------------------------------

Kanal = 1
Pausen_variable = 0

'-------------------------------------------------------------------------------------------------------------
'Ein- und Ausgang festlegen
'-------------------------------------------------------------------------------------------------------------

Ddra = &B00001111 'PA0 - PA3 werden Ausgänge
Ddrb = &B00000000 'PortB bleibt Eingang


'-------------------------------------------------------------------------------------------------
'Interrupt-Service-Routinen konfigurieren und freigeben
'-------------------------------------------------------------------------------------------------

'Info:
'Alle Porta Pinchangeinterrupts sind in Bascom "PCINT0" zugeordnet.
'Alle Portb Pinchangeinterrupts sind in Bascom "PCINT1" zugeordnet.


Pcmsk1.pcint9 = 1 'beim Flankenwechsel an PB1/PCINT9 (RC Eingang 1) Pinchangeinterrupt1 auslösen und in die Subroutine springen
Enable Pcint1 'Pinchangeinterrupt1 (1 weil auf PortB) zulassen
On Pcint1 Rc_eingang_1 Nosave 'Register werden manuel in der ISR gesichert


Pcmsk0.pcint7 = 1 'beim Flankenwechsel an PA7/PCINT6 (RC Eingang 2) Pinchangeinterrupt0 auslösen und in die Subroutine springen
Enable Pcint0 'Pinchangeinterrupt0 (0 weil auf PortA) zulassen
On Pcint0 Rc_eingang_2 Nosave 'Register werden manuel in der ISR gesichert


Config Int0 = Change 'beim Flankenwechsel an PB2/INT0 (RC Eingang 3) Int0 auslösen und in die Subroutine springen
Enable Int0
On Int0 Rc_eingang_3 Nosave 'Register werden manuel in der ISR gesichert

Enable Interrupts



'================================================= =====
'Hauptprogramm
'================================================= =====

Do


'Umrechnung erstes RC Signal auf 8-Bit
Berechnung_1 = Impulslaenge_1 / 8 'ergibt Werte zwischen 125 und 250

If Berechnung_1 > 255 Then 'zu hohe Werte abfangen
Berechnung_1 = 255
End If

If Berechnung_1 < 120 Then 'zu kleine Werte abfangen
Berechnung_1 = 120
End If


'Umrechnung zweites RC Signal auf 8-Bit
Berechnung_2 = Impulslaenge_2 / 8 'ergibt Werte zwischen 125 und 250

If Berechnung_2 > 255 Then 'zu hohe Werte abfangen
Berechnung_2 = 255
End If

If Berechnung_2 < 120 Then 'zu kleine Werte abfangen
Berechnung_2 = 120
End If



Servoausgabe_1 = Berechnung_1
Servoausgabe_2 = Berechnung_2
Servoausgabe_3 = 190
Servoausgabe_4 = 190


Loop



'================================================= =====
'ISR
'================================================= =====


'Rc_eingang_1:
' If Pinb.1 = 1 Then
' Rc_signal_1_start = Timer1
' Else
' Impulslaenge_1 = Timer1 - Rc_signal_1_start
' End If
'Return

Rc_eingang_1:
$asm
sbis pinb , 1 'Skip next Instr if PINBx = 1
rjmp pulslaenge1 'Spring zur Berechnung von Impulslaenge
push r17 'Register auf Stack sichern
in r17 , tcnt1l 'Timer1 low Byte holen
sts {Rc_signal_1_start} , r17 'Speichere Timer1 low Byte nach Rc_signal low Byte
in r17 , tcnt1h 'Timer1 high Byte holen
sts {Rc_signal_1_start} + 1 , r17 'Speichere Timer1 high Byte nach Rc_signal high Byte
pop r17 'Register vom Stack zurückholen
rjmp ende1 'Springe zum Ende
Pulslaenge1:
push r16 'Register auf Stack sichern
in r16,sreg 'Statusregister holen und halten
push r17 'Register auf Stack sichern
push r18 'Register auf Stack sichern
in r17 , tcnt1l 'Timer1 low Byte holen
lds r18 , {Rc_signal_1_start} 'Hole Rc_signal low Byte
Sub R17 , R18 'Subtrahiere Rc_signal low Byte von Timer1 low Byte
sts {Impulslaenge_1} , r17 'Speichere Resultat low Byte nach Impulslaenge low Byte
in r17 , tcnt1h 'Timer1 high Byte holen
lds r18 , {Rc_signal_1_start} + 1 'Hole Rc_signal high Byte
sbc r17 , r18 'Subtrahiere Rc_signal high Byte von Timer1 high Byte
sts {Impulslaenge_1} + 1 , r17 'Speichere Resultat high Byte nach Impulslaenge high Byte
pop r18 'Register vom Stack zurückholen
pop r17 'Register vom Stack zurückholen
Out Sreg , R16 'Statusregister zurückspeichern
pop r16 'Register vom Stack zurückholen
Ende1:
$end Asm
Return

'Rc_eingang_2:
' If Pina.7 = 1 Then
' Rc_signal_2_start = Timer1
' Else
' Impulslaenge_2 = Timer1 - Rc_signal_2_start
' End If
'Return

Rc_eingang_2:
$asm
sbis pinA , 7 'Skip next Instr if PINBx = 1
rjmp pulslaenge2 'Spring zur Berechnung von Impulslaenge
push r17 'Register auf Stack sichern
in r17 , tcnt1l 'Timer1 low Byte holen
sts {Rc_signal_2_start} , r17 'Speichere Timer1 low Byte nach Rc_signal low Byte
in r17 , tcnt1h 'Timer1 high Byte holen
sts {Rc_signal_2_start} + 1 , r17 'Speichere Timer1 high Byte nach Rc_signal high Byte
pop r17 'Register vom Stack zurückholen
rjmp ende2 'Springe zum Ende
Pulslaenge2:
push r16 'Register auf Stack sichern
in r16,sreg 'Statusregister holen und halten
push r17 'Register auf Stack sichern
push r18 'Register auf Stack sichern
in r17 , tcnt1l 'Timer1 low Byte holen
lds r18 , {Rc_signal_2_start} 'Hole Rc_signal low Byte
Sub R17 , R18 'Subtrahiere Rc_signal low Byte von Timer1 low Byte
sts {Impulslaenge_2} , r17 'Speichere Resultat low Byte nach Impulslaenge low Byte
in r17 , tcnt1h 'Timer1 high Byte holen
lds r18 , {Rc_signal_2_start} + 1 'Hole Rc_signal high Byte
sbc r17 , r18 'Subtrahiere Rc_signal high Byte von Timer1 high Byte
sts {Impulslaenge_2} + 1 , r17 'Speichere Resultat high Byte nach Impulslaenge high Byte
pop r18 'Register vom Stack zurückholen
pop r17 'Register vom Stack zurückholen
Out Sreg , R16 'Statusregister zurückspeichern
pop r16 'Register vom Stack zurückholen
Ende2:
$end Asm
Return

'Rc_eingang_3:
' If Pinb.2 = 1 Then
' Rc_signal_3_start = Timer1
' Else
' Impulslaenge_3 = Timer1 - Rc_signal_3_start
' End If
'Return

Rc_eingang_3:
$asm
sbis pinb , 2 'Skip next Instr if PINBx = 1
rjmp pulslaenge3 'Spring zur Berechnung von Impulslaenge
push r17 'Register auf Stack sichern
in r17 , tcnt1l 'Timer1 low Byte holen
sts {Rc_signal_3_start} , r17 'Speichere Timer1 low Byte nach Rc_signal low Byte
in r17 , tcnt1h 'Timer1 high Byte holen
sts {Rc_signal_3_start} + 1 , r17 'Speichere Timer1 high Byte nach Rc_signal high Byte
pop r17 'Register vom Stack zurückholen
rjmp ende3 'Springe zum Ende
Pulslaenge3:
push r16 'Register auf Stack sichern
in r16,sreg 'Statusregister holen und halten
push r17 'Register auf Stack sichern
push r18 'Register auf Stack sichern
in r17 , tcnt1l 'Timer1 low Byte holen
lds r18 , {Rc_signal_3_start} 'Hole Rc_signal low Byte
Sub R17 , R18 'Subtrahiere Rc_signal low Byte von Timer1 low Byte
sts {Impulslaenge_3} , r17 'Speichere Resultat low Byte nach Impulslaenge low Byte
in r17 , tcnt1h 'Timer1 high Byte holen
lds r18 , {Rc_signal_3_start} + 1 'Hole Rc_signal high Byte
sbc r17 , r18 'Subtrahiere Rc_signal high Byte von Timer1 high Byte
sts {Impulslaenge_3} + 1 , r17 'Speichere Resultat high Byte nach Impulslaenge high Byte
pop r18 'Register vom Stack zurückholen
pop r17 'Register vom Stack zurückholen
Out Sreg , R16 'Statusregister zurückspeichern
pop r16 'Register vom Stack zurückholen
Ende3:
$end Asm
Return



Servoausgabe:

If Kanal = 1 Then
If Porta.0 = 0 Then 'wenn der Ausgangspin aus ist
Load Timer0 , Servoausgabe_1 'wird der Timer0 mit dem Wert der Variable "Servoausgabe_1" vorgeladen
Porta.0 = 1 'und der Ausgangspin eingeschaltet
Else 'erst beim nächsten Timer0 Überlauf landen wir hier
Porta.0 = 0 'Ausgangspin wird wieder ausgeschaltet
Incr Kanal 'und der nächsten Kanal bearbeitet
End If
End If


If Kanal = 2 Then
If Porta.1 = 0 Then
Load Timer0 , Servoausgabe_2
Porta.1 = 1
Else
Porta.1 = 0
Incr Kanal
End If
End If


If Kanal = 3 Then
If Porta.2 = 0 Then
Load Timer0 , Servoausgabe_3
Porta.2 = 1
Else
Porta.2 = 0
Incr Kanal
End If
End If


If Kanal = 4 Then
If Porta.3 = 0 Then
Load Timer0 , Servoausgabe_4
Porta.3 = 1
Else
Porta.3 = 0
Incr Kanal
End If
End If



'Pausenauffüllung


If Kanal = 5 Then
Timer0 = 0 '8-Bit Timer auf 0, Überlauf alle 2,048ms
If Pausen_variable < 7 Then '2,048ms * 6 = 12,288ms Pausenfüllzeit
Incr Pausen_variable
Else
Pausen_variable = 0
Kanal = 1
End If
End If


Return

Searcher
27.12.2013, 17:32
Hallo,

ich nutze ASM auch nur, wenn es zeitkritisch wird. Die Anfänge sind eigentlich nicht so schwer aber um ASM richtig auszunutzen bedarf es schon einiger Praxis (die mir auch noch fehlt).

Ich habe im Augenblick nicht die Ruhe mich eingehend mit der Aufgabe zu beschäftigen aber trotzdem noch ein paar Gedanken, weil ich bisher nur auf die 1:1 Umsetzung der ISR nach ASM geschaut habe:

Die ASM-ISRs zum Messen der Impulse dauern im ungünstigen Fall 5µs. Liegen die asynchron ankommenden Impulse zeitmäßig ungünstig (alle drei fallende Flanken zum gleichen Zeitpunk) wird die letzte ISR schon mit 10µs Verzögerung aufgerufen, da ja die ersten beiden Flanken abgearbeitet werden. Die 10µs Ungenauigkeit (Jitter in der Pulsweite) veranlassen die meisten Servos schon zu einer Bewegung ohne die Ausgaberoutine genauer unter die Lupe genommen zu haben.

Neue Idee ist nun, den Puls an einem Eingang komplett zu messen und eventuell weitere Pulse an den anderen Eingängen erstmal zu ignorieren. Der gemessene Eingang wird "gesperrt" um den nächsten Eingang zu komplett zu messen. Das Spiel geht weiter, bis der letzte Pulseingang gemessen wurde und dann kann mit dem ersten wieder begonnen werden.

Vorteil ist erstmal eine genaue Pulslängenmessung. Die Pulse treffen ja alle 20ms ein. Im schlechten Fall würden also 3 oder 4 aufeinander folgende Impulse eines Einganges nicht gemessen, also 60ms bis 80ms würde keine Veränderung in der Pulsweite eines Eingangs festgestellt werden - würd ich als vernachlässigbar bei der Baggersteuerung ansehen!?

Wahrscheinlich vielleicht käme man dann auch ohne ASM aus. :-)

In der Zeit, in der die Pulseeingänge nacheinander gemessen werden, müssen natürlich alle 20ms die Servos mit den Steuerimpulsen versorgt werden. Kann auch, da die Meßergebnisse zwischengespeichert werden und wenn ein Eingang mit Messen dran ist, aktualisiert wird. - Wie gesagt, soweit bin ich noch nicht - Vielleicht läßt sich radbruchs Methode verwenden oder Deine? Wird halt eine weitere Aufgabe...


Gruß
Searcher

R2D2 Bastler
27.12.2013, 19:22
Neue Idee ist nun, den Puls an einem Eingang komplett zu messen und eventuell weitere Pulse an den anderen Eingängen erstmal zu ignorieren. Der gemessene Eingang wird "gesperrt" um den nächsten Eingang komplett zu messen.


Sowas habe ich bereits versucht umzusetzen, indem ich entsprechende Interrupts disabled und das entsprechende Flag im Timer/Counter Interrupt Flag Register unmittelbar vor Verlassen der Einlese-ISR gesetzt habe. Dadurch wurden (EINLESE-)Interrupts, die wärend des Einlesens eines Kanals aufgetreten sind, gelöscht. Die einzelnen Eingänge sind dann nacheinander eingelesen worden. Dass ich dafür länger brauche, bis alle Daten der 3 Eingänge aktualisiert sind, ist kein Problem.

Ich durfte dabei aber nicht die Flags der Ausgabe ISR löschen, da sich sonst die Servos überschlugen (war ja auch zu erwarten). Ich denke, dass nur noch eine Beschleunigung der (zeitaufwendigen) Springerei in die Ausgabe ISR etwas vom Servozitten nehmen kann. Mit meinem ASM-Kenntnissen (Gesamtlerndauer 10 Stunden, am Stück, heute) komme ich aber defenitiv nicht weiter.

Könnte mir jemand Schritt für Schritt erklären, wie ich einen passenden ASM Code basteln kann, ohne dafür 3 Jahre an die Uni zu müssen? :(

Schönes Wochenende noch,
Robert

Searcher
27.12.2013, 23:20
Sowas habe ich bereits versucht umzusetzen, ....

Gut, doch keine neue Idee :( Egal, dann hab ich was zum Testen für Dich. Eine Übersetzung der Ausgabe ISR nach ASM. Ohne Gewähr (wie immer) da ich außer Kompilieren nicht getestet habe.
Ist wieder nichts weiter als eine fast 1:1 Übersetzung von der Funktion her. In der Bascom ISR wird mit "Load Timer" gearbeitet. Das hab ich zur "Servoausgabe_x" Berechnung in der Hauptschleife vorweggenommen und nicht weiter überprüft, ob das so hinhauen kann. Sollte eigentlich nach der Bascom Hilfe zu LOAD. - nosave - bei der ON Timer0 label Anweisung. Viel Muße bei der Durchsicht :-)


Könnte mir jemand Schritt für Schritt erklären, wie ich einen passenden ASM Code basteln kann, ohne dafür 3 Jahre an die Uni zu müssen? :(

Das wär zu viel für mich :lol: Hab versucht gut zu kommentieren.

Gruß
Searcher



'================================================= ==============================
'RC Eingang 1 an Pin 3 (PB1, PCINT9)
'RC Eingang 2 an Pin 6 (PA7, PCINT7)
'RC Eingang 3 an Pin 5 (PB2, INT0)
'Servo 1 an Pin 13 (PA0)
'Servo 2 an Pin 12 (PA1)
'Servo 3 an Pin 11 (PA2)
'Servo 4 an Pin 10 (PA3)
'================================================= ==============================

$regfile = "attiny84.dat"
$crystal = 8000000 'FuseBit CKDIV8 deaktivieren

$hwstack = 50
$swstack = 50
$framesize = 50


'-------------------------------------------------------------------------------------------------
'Timer und konfigurieren
'-------------------------------------------------------------------------------------------------

Config Timer1 = Timer , Prescale = 8 'Timer für Einlesen RC Signale
Start Timer1

Config Timer0 = Timer , Prescale = 64 'Timer für Servoausgabe, Wert 125 entspricht 1ms, Wert 250 entspricht 2ms
Enable Timer0
On Timer0 Servoausgabe Nosave 'Register werden manuell in der ISR gesichert


'-------------------------------------------------------------------------------------------------------------
'Variablen definieren
'-------------------------------------------------------------------------------------------------------------

'Variablen fürs RC Einlesen
Dim Rc_signal_1_start As Word
Dim Rc_signal_2_start As Word
Dim Rc_signal_3_start As Word

Dim Impulslaenge_1 As Word
Dim Impulslaenge_2 As Word
Dim Impulslaenge_3 As Word



'Variablen für Berechnungen
Dim Berechnung_1 As Word
Dim Berechnung_2 As Word



'Variablen für Servoausgabe
Dim Kanal As Byte
Dim Servoausgabe_1 As Byte
Dim Servoausgabe_2 As Byte
Dim Servoausgabe_3 As Byte
Dim Servoausgabe_4 As Byte
Dim Pausen_variable As Byte


'-------------------------------------------------------------------------------------------------
'Einigen Variablen Werte zuweisen
'-------------------------------------------------------------------------------------------------

Kanal = 1
Pausen_variable = 0

'-------------------------------------------------------------------------------------------------------------
'Ein- und Ausgang festlegen
'-------------------------------------------------------------------------------------------------------------

Ddra = &B00001111 'PA0 - PA3 werden Ausgänge
Ddrb = &B00000000 'PortB bleibt Eingang


'-------------------------------------------------------------------------------------------------
'Interrupt-Service-Routinen konfigurieren und freigeben
'-------------------------------------------------------------------------------------------------

'Info:
'Alle Porta Pinchangeinterrupts sind in Bascom "PCINT0" zugeordnet.
'Alle Portb Pinchangeinterrupts sind in Bascom "PCINT1" zugeordnet.


Pcmsk1.pcint9 = 1 'beim Flankenwechsel an PB1/PCINT9 (RC Eingang 1) Pinchangeinterrupt1 auslösen und in die Subroutine springen
Enable Pcint1 'Pinchangeinterrupt1 (1 weil auf PortB) zulassen
On Pcint1 Rc_eingang_1 Nosave 'Register werden manuel in der ISR gesichert


Pcmsk0.pcint7 = 1 'beim Flankenwechsel an PA7/PCINT6 (RC Eingang 2) Pinchangeinterrupt0 auslösen und in die Subroutine springen
Enable Pcint0 'Pinchangeinterrupt0 (0 weil auf PortA) zulassen
On Pcint0 Rc_eingang_2 Nosave 'Register werden manuel in der ISR gesichert


Config Int0 = Change 'beim Flankenwechsel an PB2/INT0 (RC Eingang 3) Int0 auslösen und in die Subroutine springen
Enable Int0
On Int0 Rc_eingang_3 Nosave 'Register werden manuel in der ISR gesichert

Enable Interrupts



'================================================= =====
'Hauptprogramm
'================================================= =====

Do


'Umrechnung erstes RC Signal auf 8-Bit
Berechnung_1 = Impulslaenge_1 / 8 'ergibt Werte zwischen 125 und 250

If Berechnung_1 > 255 Then 'zu hohe Werte abfangen
Berechnung_1 = 255
End If

If Berechnung_1 < 120 Then 'zu kleine Werte abfangen
Berechnung_1 = 120
End If


'Umrechnung zweites RC Signal auf 8-Bit
Berechnung_2 = Impulslaenge_2 / 8 'ergibt Werte zwischen 125 und 250

If Berechnung_2 > 255 Then 'zu hohe Werte abfangen
Berechnung_2 = 255
End If

If Berechnung_2 < 120 Then 'zu kleine Werte abfangen
Berechnung_2 = 120
End If



Servoausgabe_1 = 256 - Berechnung_1 'Servoausgabe_ zum direkten Laden in TCNT0 vorbereiten (Cmd Load umgangen)
Servoausgabe_2 = 256 - Berechnung_2 'Servoausgabe_ zum direkten Laden in TCNT0 vorbereiten (Cmd Load umgangen)
Servoausgabe_3 = 190
Servoausgabe_4 = 190


Loop



'================================================= =====
'ISR
'================================================= =====


'Rc_eingang_1:
' If Pinb.1 = 1 Then
' Rc_signal_1_start = Timer1
' Else
' Impulslaenge_1 = Timer1 - Rc_signal_1_start
' End If
'Return

Rc_eingang_1:
$asm
sbis pinb , 1 'Skip next Instr if PINBx = 1
rjmp pulslaenge1 'Spring zur Berechnung von Impulslaenge
push r17 'Register auf Stack sichern
in r17 , tcnt1l 'Timer1 low Byte holen
sts {Rc_signal_1_start} , r17 'Speichere Timer1 low Byte nach Rc_signal low Byte
in r17 , tcnt1h 'Timer1 high Byte holen
sts {Rc_signal_1_start} + 1 , r17 'Speichere Timer1 high Byte nach Rc_signal high Byte
pop r17 'Register vom Stack zurückholen
rjmp ende1 'Springe zum Ende
Pulslaenge1:
push r16 'Register auf Stack sichern
in r16,sreg 'Statusregister holen und halten
push r17 'Register auf Stack sichern
push r18 'Register auf Stack sichern
in r17 , tcnt1l 'Timer1 low Byte holen
lds r18 , {Rc_signal_1_start} 'Hole Rc_signal low Byte
Sub R17 , R18 'Subtrahiere Rc_signal low Byte von Timer1 low Byte
sts {Impulslaenge_1} , r17 'Speichere Resultat low Byte nach Impulslaenge low Byte
in r17 , tcnt1h 'Timer1 high Byte holen
lds r18 , {Rc_signal_1_start} + 1 'Hole Rc_signal high Byte
sbc r17 , r18 'Subtrahiere Rc_signal high Byte von Timer1 high Byte
sts {Impulslaenge_1} + 1 , r17 'Speichere Resultat high Byte nach Impulslaenge high Byte
pop r18 'Register vom Stack zurückholen
pop r17 'Register vom Stack zurückholen
Out Sreg , R16 'Statusregister zurückspeichern
pop r16 'Register vom Stack zurückholen
Ende1:
$end Asm
Return

'Rc_eingang_2:
' If Pina.7 = 1 Then
' Rc_signal_2_start = Timer1
' Else
' Impulslaenge_2 = Timer1 - Rc_signal_2_start
' End If
'Return

Rc_eingang_2:
$asm
sbis pinA , 7 'Skip next Instr if PINBx = 1
rjmp pulslaenge2 'Spring zur Berechnung von Impulslaenge
push r17 'Register auf Stack sichern
in r17 , tcnt1l 'Timer1 low Byte holen
sts {Rc_signal_2_start} , r17 'Speichere Timer1 low Byte nach Rc_signal low Byte
in r17 , tcnt1h 'Timer1 high Byte holen
sts {Rc_signal_2_start} + 1 , r17 'Speichere Timer1 high Byte nach Rc_signal high Byte
pop r17 'Register vom Stack zurückholen
rjmp ende2 'Springe zum Ende
Pulslaenge2:
push r16 'Register auf Stack sichern
in r16,sreg 'Statusregister holen und halten
push r17 'Register auf Stack sichern
push r18 'Register auf Stack sichern
in r17 , tcnt1l 'Timer1 low Byte holen
lds r18 , {Rc_signal_2_start} 'Hole Rc_signal low Byte
Sub R17 , R18 'Subtrahiere Rc_signal low Byte von Timer1 low Byte
sts {Impulslaenge_2} , r17 'Speichere Resultat low Byte nach Impulslaenge low Byte
in r17 , tcnt1h 'Timer1 high Byte holen
lds r18 , {Rc_signal_2_start} + 1 'Hole Rc_signal high Byte
sbc r17 , r18 'Subtrahiere Rc_signal high Byte von Timer1 high Byte
sts {Impulslaenge_2} + 1 , r17 'Speichere Resultat high Byte nach Impulslaenge high Byte
pop r18 'Register vom Stack zurückholen
pop r17 'Register vom Stack zurückholen
Out Sreg , R16 'Statusregister zurückspeichern
pop r16 'Register vom Stack zurückholen
Ende2:
$end Asm
Return

'Rc_eingang_3:
' If Pinb.2 = 1 Then
' Rc_signal_3_start = Timer1
' Else
' Impulslaenge_3 = Timer1 - Rc_signal_3_start
' End If
'Return

Rc_eingang_3:
$asm
sbis pinb , 2 'Skip next Instr if PINBx = 1
rjmp pulslaenge3 'Spring zur Berechnung von Impulslaenge
push r17 'Register auf Stack sichern
in r17 , tcnt1l 'Timer1 low Byte holen
sts {Rc_signal_3_start} , r17 'Speichere Timer1 low Byte nach Rc_signal low Byte
in r17 , tcnt1h 'Timer1 high Byte holen
sts {Rc_signal_3_start} + 1 , r17 'Speichere Timer1 high Byte nach Rc_signal high Byte
pop r17 'Register vom Stack zurückholen
rjmp ende3 'Springe zum Ende
Pulslaenge3:
push r16 'Register auf Stack sichern
in r16,sreg 'Statusregister holen und halten
push r17 'Register auf Stack sichern
push r18 'Register auf Stack sichern
in r17 , tcnt1l 'Timer1 low Byte holen
lds r18 , {Rc_signal_3_start} 'Hole Rc_signal low Byte
Sub R17 , R18 'Subtrahiere Rc_signal low Byte von Timer1 low Byte
sts {Impulslaenge_3} , r17 'Speichere Resultat low Byte nach Impulslaenge low Byte
in r17 , tcnt1h 'Timer1 high Byte holen
lds r18 , {Rc_signal_3_start} + 1 'Hole Rc_signal high Byte
sbc r17 , r18 'Subtrahiere Rc_signal high Byte von Timer1 high Byte
sts {Impulslaenge_3} + 1 , r17 'Speichere Resultat high Byte nach Impulslaenge high Byte
pop r18 'Register vom Stack zurückholen
pop r17 'Register vom Stack zurückholen
Out Sreg , R16 'Statusregister zurückspeichern
pop r16 'Register vom Stack zurückholen
Ende3:
$end Asm
Return



Servoausgabe:

'If Kanal = 1 Then
' If Porta.0 = 0 Then 'wenn der Ausgangspin aus ist
' Load Timer0 , Servoausgabe_1 'wird der Timer0 mit dem Wert der Variable "Servoausgabe_1" vorgeladen
' Porta.0 = 1 'und der Ausgangspin eingeschaltet
' Else 'erst beim nächsten Timer0 Überlauf landen wir hier
' Porta.0 = 0 'Ausgangspin wird wieder ausgeschaltet
' Incr Kanal 'und der nächsten Kanal bearbeitet
' End If
'End If


$asm
push r16 'Register auf Stack sichern
in r16,sreg 'Statusregister holen und halten
push r17 'Register auf Stack sichern
push r18 'Register auf Stack sichern
lds r17 , {kanal} 'hole Kanalnummer
cpi r17 , 1 'check Kanal und ...
brne LABEL_KANAL_2 '... "wenn nicht gleich" verzweige zum nächsten Kanal
sbic porta , 0 'Skip next instr. wenn PORTA.0 = 0 ist
rjmp label_1 'Springe zum LOW-setzen des Servosignals
lds r18 , {Servoausgabe_1} 'Hole aufbereiteten Pulslängenwert für Timer0
Out Tcnt0 , R18 'Setze Timer0 mit Pulslängenwert
sbi porta , 0 'Setze Servosignal HIGH
rjmp Ende_isr 'Springe zum Ende der ISR
Label_1:
cbi porta , 0 'Setze Servosignal nach LOW
inc r17 'Erhöhe Kanalnummer
sts {kanal} , r17 'Sichere Kanalnummer

'If Kanal = 2 Then
' If Porta.1 = 0 Then
' Load Timer0 , Servoausgabe_2
' Porta.1 = 1
' Else
' Porta.1 = 0
' Incr Kanal
' End If
'End If

Label_kanal_2: 'Bearbeitung von Kanal 2
cpi r17 , 2 'check Kanal und ...
brne LABEL_KANAL_3 '... "wenn nicht gleich" verzweige zum nächsten Kanal
sbic porta , 1 'Skip next instr. wenn PORTA.1 = 0 ist
rjmp label_2 'Springe zum LOW-setzen des Servosignals
lds r18 , {Servoausgabe_2} 'Hole aufbereiteten Pulslängenwert für Timer0
Out Tcnt0 , R18 'Setze Timer0 mit Pulslängenwert
sbi porta , 1 'Setze Servosignal HIGH
rjmp Ende_isr 'Springe zum Ende der ISR
Label_2:
cbi porta , 1 'Setze Servosignal nach LOW
inc r17 'Erhöhe Kanalnummer
sts {kanal} , r17 'Sichere Kanalnummer

'If Kanal = 3 Then
' If Porta.2 = 0 Then
' Load Timer0 , Servoausgabe_3
' Porta.2 = 1
' Else
' Porta.2 = 0
' Incr Kanal
' End If
'End If

Label_kanal_3: 'Bearbeitung von Kanal 3
cpi r17 , 3 'check Kanal und ...
brne LABEL_KANAL_4 '... "wenn nicht gleich" verzweige zum nächsten Kanal
sbic porta , 2 'Skip next instr. wenn PORTA.2 = 0 ist
rjmp label_3 'Springe zum LOW-setzen des Servosignals
lds r18 , {Servoausgabe_3} 'Hole aufbereiteten Pulslängenwert für Timer0
Out Tcnt0 , R18 'Setze Timer0 mit Pulslängenwert
sbi porta , 2 'Setze Servosignal HIGH
rjmp Ende_isr 'Springe zum Ende der ISR
Label_3:
cbi porta , 2 'Setze Servosignal nach LOW
inc r17 'Erhöhe Kanalnummer
sts {kanal} , r17 'Sichere Kanalnummer

'If Kanal = 4 Then
' If Porta.3 = 0 Then
' Load Timer0 , Servoausgabe_4
' Porta.3 = 1
' Else
' Porta.3 = 0
' Incr Kanal
' End If
'End If

Label_kanal_4: 'Bearbeitung von Kanal 4
cpi r17 , 4 'check Kanal und ...
brne LABEL_KANAL_5 '... "wenn nicht gleich" verzweige zum nächsten Kanal
sbic porta , 3 'Skip next instr. wenn PORTA.3 = 0 ist
rjmp label_4 'Springe zum LOW-setzen des Servosignals
lds r18 , {Servoausgabe_4} 'Hole aufbereiteten Pulslängenwert für Timer0
Out Tcnt0 , R18 'Setze Timer0 mit Pulslängenwert
sbi porta , 3 'Setze Servosignal HIGH
rjmp Ende_isr 'Springe zum Ende der ISR
Label_4:
cbi porta , 3 'Setze Servosignal nach LOW
inc r17 'Erhöhe Kanalnummer
sts {kanal} , r17 'Sichere Kanalnummer

'Pausenauffüllung

'If Kanal = 5 Then
' Timer0 = 0 '8-Bit Timer auf 0, Überlauf alle 2,048ms
' If Pausen_variable < 7 Then '2,048ms * 6 = 12,288ms Pausenfüllzeit
' Incr Pausen_variable
' Else
' Pausen_variable = 0
' Kanal = 1
' End If
'End If

Label_kanal_5: 'Bearbeitung von Kanal 5
cpi r17 , 5 'check Kanal und ...
brne Ende_isr '... "wenn nicht gleich" verzweige zum Ende der ISR
clr r18 'Setze r18 auf 0
Out Tcnt0 , R18 'Setze Timer0 auf 0
lds r18 , {pausen_variable} 'Hole Pausenvariable
cpi r18 , 7 'Vergleiche mit 7
brlo label_incr_pause 'Ist Pausenvariable (r18) kleiner 7 verzweige
clr r18 'Setze r18 auf 0
sts {pausen_variable} , r18 'Setze Pausenvariable auf 0
ldi r17 , 1 'Setze r17 auf 1
sts {kanal} , r17 'Setze Kanal auf 1 (Speichere r17 nach Kanal)
rjmp ende_isr 'Springe
Label_incr_pause:
inc r18 'Erhöhe r18 um 1
sts {pausen_variable} , r18 'Speichere r18 nach Pausenvariable
Ende_isr:
pop r18 'Register vom Stack zurückholen
pop r17 'Register vom Stack zurückholen
Out Sreg , R16 'Statusregister zurückspeichern
pop r16 'Register vom Stack zurückholen
$end Asm
Return

R2D2 Bastler
28.12.2013, 11:49
Hallo Searcher,

der Code ließ sich ohne Probleme drauf spielen und brachte diesmal eine merkliche Verbesserung.

SUPER \\:D/

Die Ausschläge des Servozucken gingen zurück, oft ist es auch nur noch hör- und fühlbar, ohne sichtbare Ausschläge. Ich denke, auf der Grundlage läßt sich das weitere Programm aufbauen. Werde weiter berichten.

mfg
Robert

Searcher
28.12.2013, 17:44
Die Ausschläge des Servozucken gingen zurück, oft ist es auch nur noch hör- und fühlbar, ohne sichtbare Ausschläge. Ich denke, auf der Grundlage läßt sich das weitere Programm aufbauen.
Prima, dann kann man schon fast anfangen kaputtzuoptimieren :-)

Mir gefiel nicht, daß bei der Umrechnung auf 8-Bit der Wert der Wordvariable "Impulslänge_" während des Zugriffs im Hauptprogramm durch die ISR verändert werden könnte. Bascom muß ja da auch immer Low-Byte und High-Byte in verschiedenen Schritten holen, die von einem Interrupt unterbrochen werden können.

Ist eine potentielle Zuckquelle, denke ich. Andere Zuckquellen sind wahrscheinlich die Interrups, die sich gegenseitig aufhalten und deshalb zu Ungenauigkeiten bei Messung und Ausgabe führen.
Ist der Einsatz eines oder mehrerer weiterer µC eine Option?

Statt im Hauptprogramm für die Zeit des Zugriffs auf Impulslänge die Interrupts zu disablen, wird nun in der ISR schon auf 8 Bit-Wert umgerechnet und steht schon als Bytewert in der Hauptschleife zur Verfügung.

Dadurch ist die ISR wieder um ca 1µs länger geworden aber das oben genannte mögliche Problem empfinde ich als schwerwiegender.

Hab diesmal nur die wichtigsten Teile eingefügt. Die Variablen Impulslänge_x sollten als Byte Variable deklariert werden. Berechnung_x können auch Byte sein. Rest des ungeposteten Programms bleibt unberührt gegenüber der letzten Version.

Gruß
Searcher


'================================================= =====
'Hauptprogramm
'================================================= =====

Do

'Umrechnung erstes RC Signal auf 8-Bit
'Berechnung_1 = Impulslaenge_1 / 8 'ergibt Werte zwischen 125 und 250

Berechnung_1 = Impulslaenge_1 'Berechnung kann und Impulslaenge SOLLTE als Byte declariert werden

'If Berechnung_1 > 255 Then 'zu hohe Werte abfangen 'kann nicht mehr höher als 255 werden
' Berechnung_1 = 255
'End If

If Berechnung_1 < 120 Then 'zu kleine Werte abfangen
Berechnung_1 = 120
End If


'Umrechnung zweites RC Signal auf 8-Bit
'Berechnung_2 = Impulslaenge_2 / 8 'ergibt Werte zwischen 125 und 250

Berechnung_2 = Impulslaenge_2 'Berechnung kann und Impulslaenge SOLLTE als Byte declariert werden

'If Berechnung_2 > 255 Then 'zu hohe Werte abfangen 'kann nicht mehr höher als 255 werden
' Berechnung_2 = 255
'End If

If Berechnung_2 < 120 Then 'zu kleine Werte abfangen
Berechnung_2 = 120
End If

Servoausgabe_1 = 256 - Berechnung_1 'Servoausgabe_ zum direkten Laden in TCNT0 vorbereiten (Cmd Load umgangen)
Servoausgabe_2 = 256 - Berechnung_2 'Servoausgabe_ zum direkten Laden in TCNT0 vorbereiten (Cmd Load umgangen)
Servoausgabe_3 = 190
Servoausgabe_4 = 190

Loop




'================================================= =====
'ISR
'================================================= =====

Rc_eingang_1:
$asm
sbis pinb , 1 'Skip next Instr if PINBx = 1
rjmp pulslaenge1 'Spring zur Berechnung von Impulslaenge
push r17 'Register auf Stack sichern
in r17 , tcnt1l 'Timer1 low Byte holen
sts {Rc_signal_1_start} , r17 'Speichere Timer1 low Byte nach Rc_signal low Byte
in r17 , tcnt1h 'Timer1 high Byte holen
sts {Rc_signal_1_start} + 1 , r17 'Speichere Timer1 high Byte nach Rc_signal high Byte
pop r17 'Register vom Stack zurückholen
rjmp ende1 'Springe zum Ende
Pulslaenge1:
push r16 'Register auf Stack sichern
in r16,sreg 'Statusregister holen und halten
push r17 'Register auf Stack sichern
push r18 'Register auf Stack sichern
push r19 'Register auf Stack sichern
in r17 , tcnt1l 'Timer1 low Byte holen
lds r18 , {Rc_signal_1_start} 'Hole Rc_signal low Byte
Sub R17 , R18 'Subtrahiere Rc_signal low Byte von Timer1 low Byte
in r18 , tcnt1h 'Timer1 high Byte holen
lds r19 , {Rc_signal_1_start} + 1 'Hole Rc_signal high Byte
sbc r18 , r19 'Subtrahiere Rc_signal high Byte von Timer1 high Byte
'divison durch 8 durch dreimaliges rechts shiften des 16bit ergebnisses
lsr r18 'logical shift right von ergebnis high byte, lsb wandert ins carry flag
ror r17 'rechts rollen von ergebnis low byte, msb kommt aus carry flag
lsr r18 'logical shift right von ergebnis high byte, lsb wandert ins carry flag
ror r17 'rechts rollen von ergebnis low byte, msb kommt aus carry flag
lsr r18 'logical shift right von ergebnis high byte, lsb wandert ins carry flag
ror r17 'rechts rollen von ergebnis low byte, msb kommt aus carry flag
sts {Impulslaenge_1} , r17 'Speichere durch 8 dividiertes Resultat in Impulslaenge
pop r19 'Register vom Stack zurückholen
pop r18 'Register vom Stack zurückholen
pop r17 'Register vom Stack zurückholen
Out Sreg , R16 'Statusregister zurückspeichern
pop r16 'Register vom Stack zurückholen
Ende1:
$end Asm
Return


Rc_eingang_2:
$asm
sbis pinA , 7 'Skip next Instr if PINBx = 1
rjmp pulslaenge2 'Spring zur Berechnung von Impulslaenge
push r17 'Register auf Stack sichern
in r17 , tcnt1l 'Timer1 low Byte holen
sts {Rc_signal_2_start} , r17 'Speichere Timer1 low Byte nach Rc_signal low Byte
in r17 , tcnt1h 'Timer1 high Byte holen
sts {Rc_signal_2_start} + 1 , r17 'Speichere Timer1 high Byte nach Rc_signal high Byte
pop r17 'Register vom Stack zurückholen
rjmp ende2 'Springe zum Ende
Pulslaenge2:
push r16 'Register auf Stack sichern
in r16,sreg 'Statusregister holen und halten
push r17 'Register auf Stack sichern
push r18 'Register auf Stack sichern
push r19 'Register auf Stack sichern
in r17 , tcnt1l 'Timer1 low Byte holen
lds r18 , {Rc_signal_2_start} 'Hole Rc_signal low Byte
Sub R17 , R18 'Subtrahiere Rc_signal low Byte von Timer1 low Byte
in r18 , tcnt1h 'Timer1 high Byte holen
lds r19 , {Rc_signal_2_start} + 1 'Hole Rc_signal high Byte
sbc r18 , r19 'Subtrahiere Rc_signal high Byte von Timer1 high Byte
'Divison durch 8 durch dreimaliges rechts shiften des 16bit ergebnisses
lsr r18 'logical shift right von ergebnis high byte, lsb wandert ins carry flag
ror r17 'rechts rollen von ergebnis low byte, msb kommt aus carry flag
lsr r18 'logical shift right von ergebnis high byte, lsb wandert ins carry flag
ror r17 'rechts rollen von ergebnis low byte, msb kommt aus carry flag
lsr r18 'logical shift right von ergebnis high byte, lsb wandert ins carry flag
ror r17 'rechts rollen von ergebnis low byte, msb kommt aus carry flag
sts {Impulslaenge_2} , r17 'Speichere durch 8 dividiertes Resultat in Impulslaenge
pop r19 'Register vom Stack zurückholen
pop r18 'Register vom Stack zurückholen
pop r17 'Register vom Stack zurückholen
Out Sreg , R16 'Statusregister zurückspeichern
pop r16 'Register vom Stack zurückholen
Ende2:
$end Asm
Return


Rc_eingang_3:
$asm
sbis pinb , 2 'Skip next Instr if PINBx = 1
rjmp pulslaenge3 'Spring zur Berechnung von Impulslaenge
push r17 'Register auf Stack sichern
in r17 , tcnt1l 'Timer1 low Byte holen
sts {Rc_signal_3_start} , r17 'Speichere Timer1 low Byte nach Rc_signal low Byte
in r17 , tcnt1h 'Timer1 high Byte holen
sts {Rc_signal_3_start} + 1 , r17 'Speichere Timer1 high Byte nach Rc_signal high Byte
pop r17 'Register vom Stack zurückholen
rjmp ende3 'Springe zum Ende
Pulslaenge3:
push r16 'Register auf Stack sichern
in r16,sreg 'Statusregister holen und halten
push r17 'Register auf Stack sichern
push r18 'Register auf Stack sichern
push r19 'Register auf Stack sichern
in r17 , tcnt1l 'Timer1 low Byte holen
lds r18 , {Rc_signal_3_start} 'Hole Rc_signal low Byte
Sub R17 , R18 'Subtrahiere Rc_signal low Byte von Timer1 low Byte
in r18 , tcnt1h 'Timer1 high Byte holen
lds r19 , {Rc_signal_3_start} + 1 'Hole Rc_signal high Byte
sbc r18 , r19 'Subtrahiere Rc_signal high Byte von Timer1 high Byte
'divison durch 8 durch dreimaliges rechts shiften des 16bit ergebnisses
lsr r18 'logical shift right von ergebnis high byte, lsb wandert ins carry flag
ror r17 'rechts rollen von ergebnis low byte, msb kommt aus carry flag
lsr r18 'logical shift right von ergebnis high byte, lsb wandert ins carry flag
ror r17 'rechts rollen von ergebnis low byte, msb kommt aus carry flag
lsr r18 'logical shift right von ergebnis high byte, lsb wandert ins carry flag
ror r17 'rechts rollen von ergebnis low byte, msb kommt aus carry flag
sts {Impulslaenge_3} , r17 'Speichere durch 8 dividiertes Resultat in Impulslaenge
pop r19 'Register vom Stack zurückholen
pop r18 'Register vom Stack zurückholen
pop r17 'Register vom Stack zurückholen
Out Sreg , R16 'Statusregister zurückspeichern
pop r16 'Register vom Stack zurückholen
Ende3:
$end Asm
Return

Searcher
31.12.2013, 13:16
Hallo R2D2 Bastler.
ich weiß ja, daß Du fleißig beim Testen bist. Mich hat das Mikrosekundenjagdfieber gepackt und ohne das Grobkonzept zu ändern hier noch eine Variante.

Um die ISRs kurz zu halten werden jetzt Berechnungen in die Hauptschleife gelegt.
Der Timer0 wir zur Pausenfüllung langsamer laufen gelassen, damit er weniger Overflow Interrupts erzeugt. (Die hab ich selbst noch nicht ausprobiert und weis nicht genau, was passiert, wenn der Prescaler während des Timerlaufs umgeschaltet wird.)


'================================================= ==============================
'RC Eingang 1 an Pin 3 (PB1, PCINT9)
'RC Eingang 2 an Pin 6 (PA7, PCINT7)
'RC Eingang 3 an Pin 5 (PB2, INT0)
'Servo 1 an Pin 13 (PA0)
'Servo 2 an Pin 12 (PA1)
'Servo 3 an Pin 11 (PA2)
'Servo 4 an Pin 10 (PA3)
'================================================= ==============================

$regfile = "attiny84.dat"
$crystal = 8000000 'FuseBit CKDIV8 deaktivieren

$hwstack = 50
$swstack = 50
$framesize = 50


'-------------------------------------------------------------------------------------------------
'Timer und konfigurieren
'-------------------------------------------------------------------------------------------------

Config Timer1 = Timer , Prescale = 8 'Timer für Einlesen RC Signale
Start Timer1

Config Timer0 = Timer , Prescale = 64 'Timer für Servoausgabe, Wert 125 entspricht 1ms, Wert 250 entspricht 2ms
Enable Timer0
On Timer0 Servoausgabe Nosave 'Register werden manuell in der ISR gesichert


'-------------------------------------------------------------------------------------------------------------
'Variablen definieren
'-------------------------------------------------------------------------------------------------------------

'Variablen fürs RC Einlesen
Dim Rc_signal_1_start As Word
Dim Rc_signal_2_start As Word
Dim Rc_signal_3_start As Word

Dim Rc_signal_1_stop As Word
Dim Rc_signal_2_stop As Word
Dim Rc_signal_3_stop As Word

Dim Rc_signal_1_stop_flag As Byte
Dim Rc_signal_2_stop_flag As Byte
Dim Rc_signal_3_stop_flag As Byte


Dim Impulslaenge_1 As Byte
Dim Impulslaenge_2 As Byte
Dim Impulslaenge_3 As Byte



'Variablen für Berechnungen
Dim Berechnung_1 As Word
Dim Berechnung_2 As Word



'Variablen für Servoausgabe
Dim Kanal As Byte
Dim Servoausgabe_1 As Byte
Dim Servoausgabe_2 As Byte
Dim Servoausgabe_3 As Byte
Dim Servoausgabe_4 As Byte
Dim Pausen_variable As Byte


'-------------------------------------------------------------------------------------------------
'Einigen Variablen Werte zuweisen
'-------------------------------------------------------------------------------------------------

Kanal = 1
Pausen_variable = 0

'-------------------------------------------------------------------------------------------------------------
'Ein- und Ausgang festlegen
'-------------------------------------------------------------------------------------------------------------

Ddra = &B00001111 'PA0 - PA3 werden Ausgänge
Ddrb = &B00000000 'PortB bleibt Eingang


'-------------------------------------------------------------------------------------------------
'Interrupt-Service-Routinen konfigurieren und freigeben
'-------------------------------------------------------------------------------------------------

'Info:
'Alle Porta Pinchangeinterrupts sind in Bascom "PCINT0" zugeordnet.
'Alle Portb Pinchangeinterrupts sind in Bascom "PCINT1" zugeordnet.


Pcmsk1.pcint9 = 1 'beim Flankenwechsel an PB1/PCINT9 (RC Eingang 1) Pinchangeinterrupt1 auslösen und in die Subroutine springen
Enable Pcint1 'Pinchangeinterrupt1 (1 weil auf PortB) zulassen
On Pcint1 Rc_eingang_1 Nosave 'Register werden manuel in der ISR gesichert


Pcmsk0.pcint7 = 1 'beim Flankenwechsel an PA7/PCINT6 (RC Eingang 2) Pinchangeinterrupt0 auslösen und in die Subroutine springen
Enable Pcint0 'Pinchangeinterrupt0 (0 weil auf PortA) zulassen
On Pcint0 Rc_eingang_2 Nosave 'Register werden manuel in der ISR gesichert


Config Int0 = Change 'beim Flankenwechsel an PB2/INT0 (RC Eingang 3) Int0 auslösen und in die Subroutine springen
Enable Int0
On Int0 Rc_eingang_3 Nosave 'Register werden manuel in der ISR gesichert

Enable Interrupts


'================================================= =====
'Hauptprogramm
'================================================= =====

Do

If Rc_signal_1_stop_flag = 1 Then 'Bearbeitung nur, wenn ISR Pulsende gefunden hat
Disable Pcint1 'Mögliche Korruption durch ISR vorbeugen
Impulslaenge_1 = Rc_signal_1_stop - Rc_signal_1_start
Enable Pcint1
Rc_signal_1_stop_flag = 0
Berechnung_1 = Impulslaenge_1
Shift Berechnung_1 , Right , 3 'Division von Berechnung_1 durch 8

If Berechnung_1 > 255 Then 'zu hohe Werte abfangen
Berechnung_1 = 255
End If

If Berechnung_1 < 120 Then 'zu kleine Werte abfangen
Berechnung_1 = 120
End If
Servoausgabe_1 = 256 - Berechnung_1 'Servoausgabe_ zum direkten Laden in TCNT0 vorbereiten (Cmd Load umgangen)
End If


If Rc_signal_2_stop_flag = 1 Then 'Bearbeitung nur, wenn ISR Pulsende gefunden hat
Disable Pcint0 'Mögliche Korruption durch ISR vorbeugen
Impulslaenge_2 = Rc_signal_2_stop - Rc_signal_2_start
Enable Pcint0
Rc_signal_2_stop_flag = 0
Berechnung_2 = Impulslaenge_2
Shift Berechnung_2 , Right , 3 'Division von Berechnung_1 durch 8

If Berechnung_2 > 255 Then 'zu hohe Werte abfangen
Berechnung_2 = 255
End If

If Berechnung_2 < 120 Then 'zu kleine Werte abfangen
Berechnung_2 = 120
End If
Servoausgabe_2 = 256 - Berechnung_2 'Servoausgabe_ zum direkten Laden in TCNT0 vorbereiten (Cmd Load umgangen)
End If

Servoausgabe_3 = 190
Servoausgabe_4 = 190

Loop



'================================================= =====
'ISR
'================================================= =====

Rc_eingang_1:
$asm
push r17 'Register auf Stack sichern
sbis pinb , 1 'Skip next Instr if PINBx = 1
rjmp Puls_ende1 'Springe Puls_ende
in r17 , tcnt1l 'Timer1 low Byte holen
sts {Rc_signal_1_start} , r17 'Speichere Timer1 low Byte nach Rc_signal_x_start low Byte
in r17 , tcnt1h 'Timer1 high Byte holen
sts {Rc_signal_1_start} + 1 , r17 'Speichere Timer1 high Byte nach Rc_signal_x_start high Byte
rjmp ende1 'Springe zum Ende
Puls_ende1:
in r17 , tcnt1l 'Timer1 low Byte holen
sts {Rc_signal_1_stop} , r17 'Speichere Timer1 low Byte nach Rc_signal_x_stop low Byte
in r17 , tcnt1h 'Timer1 high Byte holen
sts {Rc_signal_1_stop} + 1 , r17 'Speichere Timer1 high Byte nach Rc_signal_x_stop high Byte
ldi r17 , 1
sts {Rc_signal_1_stop_flag} , r17 'Setze Flag zur Bearbeitung von Impulslaenge in Hauptschleife
Ende1:
pop r17 'Register vom Stack zurückholen
$end Asm
Return

Rc_eingang_2:
$asm
push r17 'Register auf Stack sichern
sbis pinA , 7 'Skip next Instr if PINBx = 1
rjmp Puls_ende2 'Springe Puls_ende
in r17 , tcnt1l 'Timer1 low Byte holen
sts {Rc_signal_2_start} , r17 'Speichere Timer1 low Byte nach Rc_signal_x_start low Byte
in r17 , tcnt1h 'Timer1 high Byte holen
sts {Rc_signal_2_start} + 1 , r17 'Speichere Timer1 high Byte nach Rc_signal_x_start high Byte
rjmp ende2 'Springe zum Ende
Puls_ende2:
in r17 , tcnt1l 'Timer1 low Byte holen
sts {Rc_signal_2_stop} , r17 'Speichere Timer1 low Byte nach Rc_signal_x_stop low Byte
in r17 , tcnt1h 'Timer1 high Byte holen
sts {Rc_signal_2_stop} + 1 , r17 'Speichere Timer1 high Byte nach Rc_signal_x_stop high Byte
ldi r17 , 1
sts {Rc_signal_2_stop_flag} , r17 'Setze Flag zur Bearbeitung von Impulslaenge in Hauptschleife
Ende2:
pop r17 'Register vom Stack zurückholen
$end Asm
Return

Rc_eingang_3:
$asm
push r17 'Register auf Stack sichern
sbis pinB , 2 'Skip next Instr if PINBx = 1
rjmp Puls_ende3 'Springe Puls_ende
in r17 , tcnt1l 'Timer1 low Byte holen
sts {Rc_signal_3_start} , r17 'Speichere Timer1 low Byte nach Rc_signal_x_start low Byte
in r17 , tcnt1h 'Timer1 high Byte holen
sts {Rc_signal_3_start} + 1 , r17 'Speichere Timer1 high Byte nach Rc_signal_x_start high Byte
rjmp ende3 'Springe zum Ende
Puls_ende3:
in r17 , tcnt1l 'Timer1 low Byte holen
sts {Rc_signal_3_stop} , r17 'Speichere Timer1 low Byte nach Rc_signal_x_stop low Byte
in r17 , tcnt1h 'Timer1 high Byte holen
sts {Rc_signal_3_stop} + 1 , r17 'Speichere Timer1 high Byte nach Rc_signal_x_stop high Byte
ldi r17 , 1
sts {Rc_signal_3_stop_flag} , r17 'Setze Flag zur Bearbeitung von Impulslaenge in Hauptschleife
Ende3:
pop r17 'Register vom Stack zurückholen
$end Asm
Return



Servoausgabe:

$asm
push r16 'Register auf Stack sichern
in r16,sreg 'Statusregister holen und halten
push r17 'Register auf Stack sichern
lds r17 , {kanal} 'hole Kanalnummer

cpi r17 , 1 'check Kanal und ...
brne LABEL_KANAL_2 '... "wenn nicht gleich" verzweige zum nächsten Kanal
sbic porta , 0 'Skip next instr. wenn PORTA.0 = 0 ist
rjmp label_1 'Springe zum LOW-setzen des Servosignals
sts {kanal} , r17 'Sichere Kanalnummer
lds r17 , {Servoausgabe_1} 'Hole aufbereiteten Pulslängenwert für Timer0
Out Tcnt0 , R17 'Setze Timer0 mit Pulslängenwert
sbi porta , 0 'Setze Servosignal HIGH
rjmp Ende_isr 'Springe zum Ende der ISR
Label_1:
cbi porta , 0 'Setze Servosignal nach LOW
inc r17 'Erhöhe Kanalnummer


Label_kanal_2: 'Bearbeitung von Kanal 2
cpi r17 , 2 'check Kanal und ...
brne LABEL_KANAL_3 '... "wenn nicht gleich" verzweige zum nächsten Kanal
sbic porta , 1 'Skip next instr. wenn PORTA.1 = 0 ist
rjmp label_2 'Springe zum LOW-setzen des Servosignals
sts {kanal} , r17 'Sichere Kanalnummer
lds r17 , {Servoausgabe_2} 'Hole aufbereiteten Pulslängenwert für Timer0
Out Tcnt0 , R17 'Setze Timer0 mit Pulslängenwert
sbi porta , 1 'Setze Servosignal HIGH
rjmp Ende_isr 'Springe zum Ende der ISR
Label_2:
cbi porta , 1 'Setze Servosignal nach LOW
inc r17 'Erhöhe Kanalnummer


Label_kanal_3: 'Bearbeitung von Kanal 3
cpi r17 , 3 'check Kanal und ...
brne LABEL_KANAL_4 '... "wenn nicht gleich" verzweige zum nächsten Kanal
sbic porta , 2 'Skip next instr. wenn PORTA.2 = 0 ist
rjmp label_3 'Springe zum LOW-setzen des Servosignals
sts {kanal} , r17 'Sichere Kanalnummer
lds r17 , {Servoausgabe_3} 'Hole aufbereiteten Pulslängenwert für Timer0
Out Tcnt0 , R17 'Setze Timer0 mit Pulslängenwert
sbi porta , 2 'Setze Servosignal HIGH
rjmp Ende_isr 'Springe zum Ende der ISR
Label_3:
cbi porta , 2 'Setze Servosignal nach LOW
inc r17 'Erhöhe Kanalnummer


Label_kanal_4: 'Bearbeitung von Kanal 4 mit anschließendem Pausenanfang
cpi r17 , 4 'check Kanal und ...
brne LABEL_KANAL_5 '... "wenn nicht gleich" verzweige zum nächsten Kanal
sbic porta , 3 'Skip next instr. wenn PORTA.3 = 0 ist
rjmp label_4 'Springe zum LOW-setzen des Servosignals
sts {kanal} , r17 'Sichere Kanalnummer
lds r17 , {Servoausgabe_4} 'Hole aufbereiteten Pulslängenwert für Timer0
Out Tcnt0 , R17 'Setze Timer0 mit Pulslängenwert
sbi porta , 3 'Setze Servosignal HIGH
rjmp Ende_isr 'Springe zum Ende der ISR
Label_4:
cbi porta , 3 'Setze Servosignal nach LOW

'Pausenauffüllung

'Pausenanfang
inc r17
sts {kanal} , r17 'Sichere Kanalnummer
ldi r17 , &B00000101 'CS00 und CS02 für prescaler 1024 in Timer0 vorbereiten
Out Tccr0b , R17 'Timer0 läuft mit 128µs Auflösung
ldi r17 , 162 'Wert für ca 12ms bis zum nächsten OVF (162=256-94) bei prescaler 1024
Out Tcnt0 , R17
rjmp Ende_isr 'Springe zum Ende der ISR

'Pausenende
Label_kanal_5: 'Bearbeitung von Kanal 5 (Pausenende)
ldi r17 , &B00000011 'CS00 und CS01 für prescaler 64 in Timer0 vorbereiten
Out Tccr0b , R17 'Timer0 läuft wieder mit 8µs Auflösung
ldi r17 , 1 'Kanalnummer auf 1 setzen
sts {kanal} , r17 'Sichere Kanalnummer
Ende_isr:
pop r17 'Register vom Stack zurückholen
Out Sreg , R16 'Statusregister zurückspeichern
pop r16 'Register vom Stack zurückholen
$end Asm
Return

R2D2 Bastler
31.12.2013, 15:48
Hallo Searcher,

die Ausgabe ISR funtioniert super, nur die Eingabe scheint nun alles durcheinander zu bringen. Die Servos fahren auf Anschlag und reagieren nicht auf RC Eingaben. Beim Testen der Ausgabe-ISR mit den "alten" Eingabe-ISRs scheint sich das Zucken ganz minimal verbessert zu haben.

Dir und allen anderen einen guten Rutsch ins neue Jahr :Strahl

Robert

Sauerbruch
31.12.2013, 17:58
Hallo Robert,

jetzt melde ich mich doch mal auf diesem Weg :-)

Ich habe den Thread natürlich aufmerksam mitverfolgt, und auch den Ansatz, am Ende einer ISR die Interrupt-Flags von den anderen Mess-Interrupts zu löschen. So weit, so gut - aber von den ca. 120 (!) Taktzyklen, die so ein Sprung in eine ISR und wieder zurück braucht, vergehen fast die Hälfte NACH dem Return, weil eben alle Register wieder zurückgeschrieben werden müssen. Und wenn in dieser Zeit ein neuer Flanken-Interrupt ausgelöst wird, wird die ISR halt auch erst mit ein paar -zig Takten Verzögerung ausgeführt, d.h. mit Messfehlern beim Ablesen des Timers.

Ich würde es tatsächlich mal so versuchen, dass in der Hauptschleife (!) immer nur einer der drei Interrupts freigegeben wird. Hat der Controller einen Impuls fertig gemessen, setzt er ein Flag, und daran erkennt das Hauptprogramm, dass jetzt der nächste Interrupt freigegeben werden muss, usw. So kannst Du m.E. zuverlässig verhindern, dass bei zwei sich überschneidenden Impulsen dadurch Messfehler entstehen, dass die Interrupts nur nacheinander ablaufen können. Und dass Dir durch mal ab und zu ein Impuls entgeht, dürfte bei einer Wiederholungsrate von ca. 20/Sekunde glaube ich keine Rolle spielen.

Aber das ist vielleicht ein Projekt für´s nächste Jahr, in das ich Dir einen guten Rutsch wünsche!!

Daniel

Searcher
01.01.2014, 07:53
Allen ein Frohes Neues Jahr!

Hallo Robert,

die Ausgabe ISR funtioniert super, nur die Eingabe scheint nun alles durcheinander zu bringen. Die Servos fahren auf Anschlag und reagieren nicht auf RC Eingaben. Beim Testen der Ausgabe-ISR mit den "alten" Eingabe-ISRs scheint sich das Zucken ganz minimal verbessert zu haben.

Das Fehler hat mich nicht schlafen lassen ;) und mit dem neuen Jahr hat sich mir mindestens ein Fehler offenbart :-)
Die Variablen für Impulslaenge müssen wieder als Word deklariert werden. Also in dem letzten Code diese drei wieder als Word deklarieren.


Dim Impulslaenge_1 As Byte
Dim Impulslaenge_2 As Byte
Dim Impulslaenge_3 As Byte


Die Hoffnung bei der µs/ns-Jagd ist, den Meß- und Ausgabefehler so gering zu kriegen, daß er unter die Ansprechschwelle der Servos fällt, also in die Totzeit fällt, so daß er sich nicht mehr rührt. Sicher vermeiden läßt er sich so wie bisher nicht.

Man könnte noch versuchen eine SW-Hysterese in der Hauptschleife unterzubringen. Da die Auflösung des Timer0 8µs ist, muß sich der Ausgabewert (Impulslaenge) eines Servos gegenüber dem vorherigen Puls um mindestens 16µs in die entgegengesetzte Richtung geändert haben, bevor es zur Ausgabe an den Servo kommt. Betrifft die Änderung 8µs die gleiche Drehrichtung wie vorher, könnte man diese unverändert weitergeben. Bedeutet natürlich einen etwas größeren Sprung bei Drehrichtungsänderung und ist noch nicht ganz durchdacht...

Auch wie Sauerbruch nochmal vorgeschlagen hat, die Eingänge nacheinander zu messen könnte Erfolg bringen - müßte man mal in Code umsetzen.

Letzer Ausweg: Drei µC mit ICP die drei Eingänge messen lassen und falls notwendig die Meßdaten zum einem Haupcontroller übertragen, bearbeiten und ausgeben oder gleich an Ort und Stelle bearbeiten. Bischen aufwändig und nicht so herausfordernd :lol:

Gruß
Searcher

for_ro
01.01.2014, 15:40
Hallo zusammen,
von mir auch die besten Wünsche für 2014 an alle.



Letzer Ausweg: Drei µC mit ICP die drei Eingänge messen lassen und falls notwendig die Meßdaten zum einem Haupcontroller übertragen, bearbeiten und ausgeben oder gleich an Ort und Stelle bearbeiten.

Wir hatten ja schon festgestellt, dass es beim Einlesen der Signale nicht notwendig ist, die Länge jedes Impulses zu messen.
Dann kannst du auch drei Eingänge über den Multiplexer nacheinander auf den ICP legen. Ich glaube immer noch, dass dies die erfolgversprechendste Variante wäre, da die HW die Zeitdauer des Impulses in das Register schreibt.
Da auch die Ausgabe der Servo Signale in HW geschieht, sehe ich dabei gute Chancen für konstante Werte an den Servos.

Searcher
01.01.2014, 18:02
Hallo,

Dann kannst du auch drei Eingänge über den Multiplexer nacheinander auf den ICP legen. Ich glaube immer noch, dass dies die erfolgversprechendste Variante wäre, da die HW die Zeitdauer des Impulses in das Register schreibt.

@for_ro: sowas ähnliches geht mir auch schon eine Weile im Kopf rum und wollte schon Diodengatter bauen. Hab jetzt aber nochmal ins Datenblatt vom ATtiny84 geschaut. Der ICP Interrupt kann vom Ausgang des Analog Comparators ausgelöst werden und ein Eingang des Analog Comparators kann über den ADC-Multiplexer auf verschiedene Eingänge gelegt werden. Ist es das, was Du meinst?

Würde wieder eine üble Bitfieselei werden um alles einzustellen und umzuschalten. Das nacheinander Messen wäre wohl wirklich eine verläßliche Methode um die "zufälligen" Interrupts in den Griff zu kriegen.

Gruß
Searcher

R2D2 Bastler
01.01.2014, 18:59
Das Fehler hat mich nicht schlafen lassen ;) und mit dem neuen Jahr hat sich mir mindestens ein Fehler offenbart :-)
Die Variablen für Impulslaenge müssen wieder als Word deklariert werden. Also in dem letzten Code diese drei wieder als Word deklarieren.


Dim Impulslaenge_1 As Byte
Dim Impulslaenge_2 As Byte
Dim Impulslaenge_3 As Byte



Das Jahr fängt definitiv gut an. :cheesy:

DAS war der Fehler. Nachdem ich (wie Du gesagt hast) die Deklaration geändert habe, funktionierts. Ich konnte heute nur kurz testen, aber es sieht im Moment echt gut aus. Nur noch ganz selten hörbare und fühlbare Zuckungen, sichtbare Zuckungen waren praktisch nicht vorhanden. Werde morgen nochmal ausgiebig testen und den Code anschließend in mein Programm aufnehmen.

Fortsetzung folgt... \\:D/

mfg
Robert

for_ro
01.01.2014, 20:34
Der ICP Interrupt kann vom Ausgang des Analog Comparators ausgelöst werden und ein Eingang des Analog Comparators kann über den ADC-Multiplexer auf verschiedene Eingänge gelegt werden. Ist es das, was Du meinst?
Ja, genau das.


Würde wieder eine üble Bitfieselei werden um alles einzustellen und umzuschalten.
Das ist gar nicht so wild.

Du setzt den Bandgap auf 1, dann brauchst du keinen externen positiven Eingang mehr.
Du stoppst den ADC und schaltest den Multiplexer auf den negativen Input.
Du verbindest den AC mit dem ICP von Timer1
Du selektierst den gewünschten Flankenwechsel des AC und auch für den Input Capture

Einige dieser Bits kannst du wahrscheinlich auch über Bascom Config Befehle setzen.

Zum Umschalten der Eingänge musst du dann nur noch den Multiplexer umschalten und sicherheitshalber das ICP Flag löschen.
Im schlechtesten Fall kann es dann 60ms dauern, bis alle 3 kanäle eingelesen wurden.

Searcher
02.01.2014, 13:23
Nur noch ganz selten hörbare und fühlbare Zuckungen, sichtbare Zuckungen waren praktisch nicht vorhanden. Werde morgen nochmal ausgiebig testen und den Code anschließend in mein Programm aufnehmen.
Hört sich schon gut an, aber leider noch nicht semiperfekt. :)



Das ist gar nicht so wild.


Du setzt den Bandgap auf 1, dann brauchst du keinen externen positiven Eingang mehr.
Du stoppst den ADC und schaltest den Multiplexer auf den negativen Input.
Du verbindest den AC mit dem ICP von Timer1
Du selektierst den gewünschten Flankenwechsel des AC und auch für den Input Capture


Das reizt mich, aber bevor ich/wir daran gehen noch eine Symptombehandlung. Jitterunterdrückung per SW-Hysterese nach diesem Muster (https://www.roboternetz.de/community/threads/60698-Softwarehysterese-bei-ADC-Messungen) zum Testen.

Hab in der Hauptschleife nun eine SW-Hysterese untergebracht. Die Hysterese kann über die Konstante "Jitterausgleich" in Vielfachen von von 8µs eingestellt werden. Testweise auch kleinere Stufen, aber ich glaube, daß dann das Servo "weglaufen" :) könnte. Müßte man mal testen.

Gruß
Searcher


'================================================= ==============================
'RC Eingang 1 an Pin 3 (PB1, PCINT9)
'RC Eingang 2 an Pin 6 (PA7, PCINT7)
'RC Eingang 3 an Pin 5 (PB2, INT0)
'Servo 1 an Pin 13 (PA0)
'Servo 2 an Pin 12 (PA1)
'Servo 3 an Pin 11 (PA2)
'Servo 4 an Pin 10 (PA3)
'================================================= ==============================

$regfile = "attiny84.dat"
$crystal = 8000000 'FuseBit CKDIV8 deaktivieren

$hwstack = 50
$swstack = 50
$framesize = 50


'-------------------------------------------------------------------------------------------------
'Timer und konfigurieren
'-------------------------------------------------------------------------------------------------

Config Timer1 = Timer , Prescale = 8 'Timer für Einlesen RC Signale
Start Timer1

Config Timer0 = Timer , Prescale = 64 'Timer für Servoausgabe, Wert 125 entspricht 1ms, Wert 250 entspricht 2ms
Enable Timer0
On Timer0 Servoausgabe Nosave 'Register werden manuell in der ISR gesichert


'-------------------------------------------------------------------------------------------------------------
'Variablen definieren / Konstanten
'-------------------------------------------------------------------------------------------------------------

Const Jitterausgleich = 8 'Wert in µs um den sich gemessene Impulslaenge aendern darf/muss, bevor Servo gestellt wird
'Nur in Vielfachen von 8 veraendern (8, 16, 32, ..)
'Variablen fürs RC Einlesen
Dim Rc_signal_1_start As Word
Dim Rc_signal_2_start As Word
Dim Rc_signal_3_start As Word

Dim Rc_signal_1_stop As Word
Dim Rc_signal_2_stop As Word
Dim Rc_signal_3_stop As Word

Dim Rc_signal_1_stop_flag As Byte
Dim Rc_signal_2_stop_flag As Byte
Dim Rc_signal_3_stop_flag As Byte


Dim Impulslaenge_1 As Word
Dim Impulslaenge_2 As Word
Dim Impulslaenge_3 As Word

Dim Impulslaenge_1_alt As Word
Dim Impulslaenge_2_alt As Word
Dim Impulslaenge_3_alt As Word
Dim Impulslaengenaenderung As Integer 'wird nur einmal gebraucht, da nur Hilfswariable


'Variablen für Berechnungen
Dim Berechnung_1 As Word
Dim Berechnung_2 As Word



'Variablen für Servoausgabe
Dim Kanal As Byte
Dim Servoausgabe_1 As Byte
Dim Servoausgabe_2 As Byte
Dim Servoausgabe_3 As Byte
Dim Servoausgabe_4 As Byte
'Dim Pausen_variable As Byte


'-------------------------------------------------------------------------------------------------
'Einigen Variablen Werte zuweisen
'-------------------------------------------------------------------------------------------------

Kanal = 1
'Pausen_variable = 0

Impulslaenge_1_alt = 1500 'mit Servomittelstellung initialisieren (nicht essentiell)
Impulslaenge_2_alt = 1500 'mit Servomittelstellung initialisieren (nicht essentiell)
Impulslaenge_3_alt = 1500 'mit Servomittelstellung initialisieren (nicht essentiell)

'-------------------------------------------------------------------------------------------------------------
'Ein - Und Ausgang Festlegen
'-------------------------------------------------------------------------------------------------------------

Ddra = &B00001111 'PA0 - PA3 werden Ausgänge
Ddrb = &B00000000 'PortB bleibt Eingang


'-------------------------------------------------------------------------------------------------
'Interrupt-Service-Routinen konfigurieren und freigeben
'-------------------------------------------------------------------------------------------------

'Info:
'Alle Porta Pinchangeinterrupts sind in Bascom "PCINT0" zugeordnet.
'Alle Portb Pinchangeinterrupts sind in Bascom "PCINT1" zugeordnet.


Pcmsk1.pcint9 = 1 'beim Flankenwechsel an PB1/PCINT9 (RC Eingang 1) Pinchangeinterrupt1 auslösen und in die Subroutine springen
Enable Pcint1 'Pinchangeinterrupt1 (1 weil auf PortB) zulassen
On Pcint1 Rc_eingang_1 Nosave 'Register werden manuel in der ISR gesichert


Pcmsk0.pcint7 = 1 'beim Flankenwechsel an PA7/PCINT6 (RC Eingang 2) Pinchangeinterrupt0 auslösen und in die Subroutine springen
Enable Pcint0 'Pinchangeinterrupt0 (0 weil auf PortA) zulassen
On Pcint0 Rc_eingang_2 Nosave 'Register werden manuel in der ISR gesichert


Config Int0 = Change 'beim Flankenwechsel an PB2/INT0 (RC Eingang 3) Int0 auslösen und in die Subroutine springen
Enable Int0
On Int0 Rc_eingang_3 Nosave 'Register werden manuel in der ISR gesichert

Enable Interrupts


'================================================= =====
'Hauptprogramm
'================================================= =====

Do

If Rc_signal_1_stop_flag = 1 Then 'Bearbeitung nur, wenn ISR Pulsende gefunden hat
Disable Pcint1 'Mögliche Korruption durch ISR vorbeugen
Impulslaenge_1 = Rc_signal_1_stop - Rc_signal_1_start
Enable Pcint1
Rc_signal_1_stop_flag = 0

Impulslaengenaenderung = Impulslaenge_1_alt - Impulslaenge_1 'SW-Hysterese
If Abs(impulslaengenaenderung) >= Jitterausgleich Then
Impulslaenge_1_alt = Impulslaenge_1

Berechnung_1 = Impulslaenge_1
Shift Berechnung_1 , Right , 3 'Division von Berechnung_1 durch 8
If Berechnung_1 > 255 Then Berechnung_1 = 255 'zu hohe Werte abfangen
If Berechnung_1 < 120 Then Berechnung_1 = 120 'zu kleine Werte abfangen
Servoausgabe_1 = 256 - Berechnung_1 'Servoausgabe_ zum direkten Laden in TCNT0 vorbereiten (Cmd Load umgangen)
End If
End If


If Rc_signal_2_stop_flag = 1 Then 'Bearbeitung nur, wenn ISR Pulsende gefunden hat
Disable Pcint0 'Mögliche Korruption durch ISR vorbeugen
Impulslaenge_2 = Rc_signal_2_stop - Rc_signal_2_start
Enable Pcint0
Rc_signal_2_stop_flag = 0

Impulslaengenaenderung = Impulslaenge_2_alt - Impulslaenge_2 'SW-Hysterese
If Abs(impulslaengenaenderung) >= Jitterausgleich Then
Impulslaenge_2_alt = Impulslaenge_2

Berechnung_2 = Impulslaenge_2
Shift Berechnung_2 , Right , 3 'Division von Berechnung_1 durch 8
If Berechnung_2 > 255 Then Berechnung_2 = 255 'zu hohe Werte abfangen
If Berechnung_2 < 120 Then Berechnung_2 = 120 'zu kleine Werte abfangen
Servoausgabe_2 = 256 - Berechnung_2 'Servoausgabe_ zum direkten Laden in TCNT0 vorbereiten (Cmd Load umgangen)
End If
End If

Servoausgabe_3 = 190
Servoausgabe_4 = 190

Loop



'================================================= =====
'ISR
'================================================= =====

Rc_eingang_1:
$asm
push r17 'Register auf Stack sichern
sbis pinb , 1 'Skip next Instr if PINBx = 1
rjmp Puls_ende1 'Springe Puls_ende
in r17 , tcnt1l 'Timer1 low Byte holen
sts {Rc_signal_1_start} , r17 'Speichere Timer1 low Byte nach Rc_signal_x_start low Byte
in r17 , tcnt1h 'Timer1 high Byte holen
sts {Rc_signal_1_start} + 1 , r17 'Speichere Timer1 high Byte nach Rc_signal_x_start high Byte
rjmp ende1 'Springe zum Ende
Puls_ende1:
in r17 , tcnt1l 'Timer1 low Byte holen
sts {Rc_signal_1_stop} , r17 'Speichere Timer1 low Byte nach Rc_signal_x_stop low Byte
in r17 , tcnt1h 'Timer1 high Byte holen
sts {Rc_signal_1_stop} + 1 , r17 'Speichere Timer1 high Byte nach Rc_signal_x_stop high Byte
ldi r17 , 1
sts {Rc_signal_1_stop_flag} , r17 'Setze Flag zur Bearbeitung von Impulslaenge in Hauptschleife
Ende1:
pop r17 'Register vom Stack zurückholen
$end Asm
Return

Rc_eingang_2:
$asm
push r17 'Register auf Stack sichern
sbis pinA , 7 'Skip next Instr if PINBx = 1
rjmp Puls_ende2 'Springe Puls_ende
in r17 , tcnt1l 'Timer1 low Byte holen
sts {Rc_signal_2_start} , r17 'Speichere Timer1 low Byte nach Rc_signal_x_start low Byte
in r17 , tcnt1h 'Timer1 high Byte holen
sts {Rc_signal_2_start} + 1 , r17 'Speichere Timer1 high Byte nach Rc_signal_x_start high Byte
rjmp ende2 'Springe zum Ende
Puls_ende2:
in r17 , tcnt1l 'Timer1 low Byte holen
sts {Rc_signal_2_stop} , r17 'Speichere Timer1 low Byte nach Rc_signal_x_stop low Byte
in r17 , tcnt1h 'Timer1 high Byte holen
sts {Rc_signal_2_stop} + 1 , r17 'Speichere Timer1 high Byte nach Rc_signal_x_stop high Byte
ldi r17 , 1
sts {Rc_signal_2_stop_flag} , r17 'Setze Flag zur Bearbeitung von Impulslaenge in Hauptschleife
Ende2:
pop r17 'Register vom Stack zurückholen
$end Asm
Return

Rc_eingang_3:
$asm
push r17 'Register auf Stack sichern
sbis pinB , 2 'Skip next Instr if PINBx = 1
rjmp Puls_ende3 'Springe Puls_ende
in r17 , tcnt1l 'Timer1 low Byte holen
sts {Rc_signal_3_start} , r17 'Speichere Timer1 low Byte nach Rc_signal_x_start low Byte
in r17 , tcnt1h 'Timer1 high Byte holen
sts {Rc_signal_3_start} + 1 , r17 'Speichere Timer1 high Byte nach Rc_signal_x_start high Byte
rjmp ende3 'Springe zum Ende
Puls_ende3:
in r17 , tcnt1l 'Timer1 low Byte holen
sts {Rc_signal_3_stop} , r17 'Speichere Timer1 low Byte nach Rc_signal_x_stop low Byte
in r17 , tcnt1h 'Timer1 high Byte holen
sts {Rc_signal_3_stop} + 1 , r17 'Speichere Timer1 high Byte nach Rc_signal_x_stop high Byte
ldi r17 , 1
sts {Rc_signal_3_stop_flag} , r17 'Setze Flag zur Bearbeitung von Impulslaenge in Hauptschleife
Ende3:
pop r17 'Register vom Stack zurückholen
$end Asm
Return



Servoausgabe:

$asm
push r16 'Register auf Stack sichern
in r16,sreg 'Statusregister holen und halten
push r17 'Register auf Stack sichern
lds r17 , {kanal} 'hole Kanalnummer

cpi r17 , 1 'check Kanal und ...
brne LABEL_KANAL_2 '... "wenn nicht gleich" verzweige zum nächsten Kanal
sbic porta , 0 'Skip next instr. wenn PORTA.0 = 0 ist
rjmp label_1 'Springe zum LOW-setzen des Servosignals
sts {kanal} , r17 'Sichere Kanalnummer
lds r17 , {Servoausgabe_1} 'Hole aufbereiteten Pulslängenwert für Timer0
Out Tcnt0 , R17 'Setze Timer0 mit Pulslängenwert
sbi porta , 0 'Setze Servosignal HIGH
rjmp Ende_isr 'Springe zum Ende der ISR
Label_1:
cbi porta , 0 'Setze Servosignal nach LOW
inc r17 'Erhöhe Kanalnummer


Label_kanal_2: 'Bearbeitung von Kanal 2
cpi r17 , 2 'check Kanal und ...
brne LABEL_KANAL_3 '... "wenn nicht gleich" verzweige zum nächsten Kanal
sbic porta , 1 'Skip next instr. wenn PORTA.1 = 0 ist
rjmp label_2 'Springe zum LOW-setzen des Servosignals
sts {kanal} , r17 'Sichere Kanalnummer
lds r17 , {Servoausgabe_2} 'Hole aufbereiteten Pulslängenwert für Timer0
Out Tcnt0 , R17 'Setze Timer0 mit Pulslängenwert
sbi porta , 1 'Setze Servosignal HIGH
rjmp Ende_isr 'Springe zum Ende der ISR
Label_2:
cbi porta , 1 'Setze Servosignal nach LOW
inc r17 'Erhöhe Kanalnummer


Label_kanal_3: 'Bearbeitung von Kanal 3
cpi r17 , 3 'check Kanal und ...
brne LABEL_KANAL_4 '... "wenn nicht gleich" verzweige zum nächsten Kanal
sbic porta , 2 'Skip next instr. wenn PORTA.2 = 0 ist
rjmp label_3 'Springe zum LOW-setzen des Servosignals
sts {kanal} , r17 'Sichere Kanalnummer
lds r17 , {Servoausgabe_3} 'Hole aufbereiteten Pulslängenwert für Timer0
Out Tcnt0 , R17 'Setze Timer0 mit Pulslängenwert
sbi porta , 2 'Setze Servosignal HIGH
rjmp Ende_isr 'Springe zum Ende der ISR
Label_3:
cbi porta , 2 'Setze Servosignal nach LOW
inc r17 'Erhöhe Kanalnummer


Label_kanal_4: 'Bearbeitung von Kanal 4 mit anschließendem Pausenanfang
cpi r17 , 4 'check Kanal und ...
brne LABEL_KANAL_5 '... "wenn nicht gleich" verzweige zum nächsten Kanal
sbic porta , 3 'Skip next instr. wenn PORTA.3 = 0 ist
rjmp label_4 'Springe zum LOW-setzen des Servosignals
sts {kanal} , r17 'Sichere Kanalnummer
lds r17 , {Servoausgabe_4} 'Hole aufbereiteten Pulslängenwert für Timer0
Out Tcnt0 , R17 'Setze Timer0 mit Pulslängenwert
sbi porta , 3 'Setze Servosignal HIGH
rjmp Ende_isr 'Springe zum Ende der ISR
Label_4:
cbi porta , 3 'Setze Servosignal nach LOW

'Pausenauffüllung

'Pausenanfang
inc r17
sts {kanal} , r17 'Sichere Kanalnummer
ldi r17 , &B00000101 'CS00 und CS02 für prescaler 1024 in Timer0 vorbereiten
Out Tccr0b , R17 'Timer0 läuft mit 128µs Auflösung
ldi r17 , 162 'Wert für ca 12ms bis zum nächsten OVF (162=256-94) bei prescaler 1024
Out Tcnt0 , R17
rjmp Ende_isr 'Springe zum Ende der ISR

'Pausenende
Label_kanal_5: 'Bearbeitung von Kanal 5 (Pausenende)
ldi r17 , &B00000011 'CS00 und CS01 für prescaler 64 in Timer0 vorbereiten
Out Tccr0b , R17 'Timer0 läuft wieder mit 8µs Auflösung
ldi r17 , 1 'Kanalnummer auf 1 setzen
sts {kanal} , r17 'Sichere Kanalnummer
Ende_isr:
pop r17 'Register vom Stack zurückholen
Out Sreg , R16 'Statusregister zurückspeichern
pop r16 'Register vom Stack zurückholen
$end Asm
Return

R2D2 Bastler
02.01.2014, 17:13
Müßte man mal testen.


Na dafür bin ich ja da :cheesy:

Ich hab den Code mal eingespielt, leider kam es zu Errors.

Die Zeilen...

If Abs(impulslaengenaenderung) >= Jitterausgleich Then
...mag mein Bascom (1.11.9.8.) gar nicht.
Keine Ahnung, ob's an meiner Version liegt. :-k


Habe den Code dann geändert in...


Dim A As Word
.
.
.
A = Abs(impulslaengenaenderung)
If A >= Jitterausgleich Then
... und schon klappte es.

Die Servos würde ich nun als "jitterfrei" bezeichnen. Ich habe allerdings nicht die Möglichkeit, die tatsächlichen Werte zu betrachten (z.B. Oszi usw). Als Servos habe ich die billigsten HXT500 vom bösen Chinamann, sowie HS-82MG und HS-85MG dranhängen (Digitalservos hab ich keine zum Testen).

Die Konstante "Jitterausgleich" habe ich auch mal auf 4 verkleinert, konnte aber keine Veränderung feststellen.

mfg
Robert

Searcher
02.01.2014, 18:26
Die Zeilen...

If Abs(impulslaengenaenderung) >= Jitterausgleich Then
...mag mein Bascom (1.11.9.8.) gar nicht.
Keine Ahnung, ob's an meiner Version liegt. :-k

Ja, liegt an der Bascom Version. Ich nutze die 2.0.7.5 Demo Version und es gibt, glaub ich schon eine neuere.


Die Servos würde ich nun als "jitterfrei" bezeichnen. Ich habe allerdings nicht die Möglichkeit, die tatsächlichen Werte zu betrachten (z.B. Oszi usw).
Nichts mehr zu hören? Nein? Super! Das würd mir erstmal reichen! Werd aber sicher nochmal die MUX Methode versuchen. Bedeutet dann aber auch eine Änderung der HW, da die drei RC Eingänge auf ADC Eingänge gelegt werden müßten.


Die Konstante "Jitterausgleich" habe ich auch mal auf 4 verkleinert, konnte aber keine Veränderung feststellen.
Ich verlier da so langsam den Überblick und man müßte das Ganze mal am Simulator oder händisch mit verschiedenen nah beieinander liegenden Impulslaengen durchspielen. Dadurch, daß der Timer0 ja nur eine Auflösung von 8µs hat und der Wert für Timer0 durch eine Division durch 8 (mit implizitem Abschneiden der Nachkommastellen durch Nichtverwendung von Fließkommazahlen) entsteht, sollten kleinere Werte als 8 keine oder negative Auswirkungen haben. Wenn 8 funktioniert würd ich zunächst 8 drinlassen.

Dann weiter frohes Testen :)
Gruß
Searcher

R2D2 Bastler
02.01.2014, 20:29
Ich habe Deine ISRs nun in mein Programm mit eingebaut, Servozucken scheint nun kein Problem mehr zu sein. Leider hänge ich an einer anderen Stelle noch fest.

Die Steuerung ist ja wie gesagt für einen Bagger.


Folgende Funktionalitäten klappen einwandfrei:

- Die Baggerarmservos bewegen sich nur, wenn der Knüppel aus der Mittelstellung genommen wird.
- Wird der Knüppel losgelassen, verbleiben die Servos an der eben erreichten Stelle.
- Sie fahren nur bis zu einer festgelegten Position (per Potis und SW festgelegter Endanschlag).


Folgendes Feature funktioniert nur bedingt:

-Die Servogeschwindigkeit hängt von der Größe des Steuerknüppelauschlags ab.


Je weiter ich den Knüppel also aus der Mittelstellung auslenke, umso schneller fährt das Servo. Hier der Codeschnipsel, der das bewerkstelligt (die Variable Berechnung_1a wird am Programmende dann "Servoausgabe_1 = 256 - Berechnung_1a") :



'Erstes Baggerarmservo
If Berechnung_1 < 182 Then 'Info: Todband (Mittelstellung) liegt zwischen 182 und 193
Servospeed = Berechnung_1 - 120 'ergibt Werte zwischen 0 (schnell) und 61 (langsam)
Servospeed = Servospeed * 100 'Werte hochscalieren (Faktor 30 bis 100, höherer Faktor = langsamerer minimal Speed)
For I = 1 To Servospeed 'Werte von 0 (schnell) bis max 6100 (langsam) sinnvoll
Next I
Decr Berechnung_1a
End If

If Berechnung_1 > 193 Then
Servospeed = 255 - Berechnung_1
Servospeed = Servospeed * 100
For I = 1 To Servospeed
Next I
Incr Berechnung_1a
End If


Leider tritt bei meinem Code folgendes Phänomen auf: Wenn ich den Knüppel aus der Todzone bewege (das Servo fährt langsam in die entsprechende Richtung) und dann schlagartig Vollausschlag mache, fährt das Servo manchmal nur mit reduzierter Geschwindigkeit weiter.

Meine Vermutung ist nun folgende: Wenn das Servo langsam losfährt, ist die Variable "Servospeed" in der For-Next Schleife sehr groß. Auch wenn ich den Knüppel nun schlagartig auf Endandanschlag bringe, findet ja keine neue Berechnung der Variable "Servospeed" statt. Wenn also Servospeed sehr groß ist und ich den Knüppel nicht wieder in Mittelstellung bringe (um aus der ganzen IF Bedingung raus zu kommen), fährt das Servo nur mit Minimalgeschwindigkeit bis zum Anschlag.

Das Programm besteht eigenlich nur aus einer großen If-Bedingung: Wenn Bertriebsmode "Fahrbetrieb", dann läuft es über den Deltamischer, ansonsten ist Betriebsmode "Baggerbetrieb". Der Teil mit dem Deltamischer funktioniert anstandslos, egal wie schnell ich die Knüppel vergewaltige. Daher gehe ich davon aus, dass "nur" der Teil mit der Servogeschwindigkeit während des Baggerbetriebs noch Veränderungen braucht.

Hier das ganze Programm.



'================================================= ==============================
'BAGGER Steuerung
'
'RC Eingang 1 an Pin 3 (PB1, PCINT9) Stick 1 (Aileron)
'RC Eingang 2 an Pin 6 (PA7, PCINT7) Stick 2 (Elevator)
'RC Eingang 3 an Pin 5 (PB2, INT0) Betriebsmode
'Poti 1a an Pin 10 (PA3, ADC3)
'Poti 1b an Pin 9 (PA4, ADC4)
'Poti 2a an Pin 8 (PA5, ADC5)
'Poti 2b an Pin 7 (PA6, ADC6)
'Servo 1 an Pin 2 (PB0) Baggerarmservo
'Servo 2 an Pin 13 (PA0) Baggerarmservo
'Servo 3 an Pin 12 (PA1) Kettenantrieb
'Servo 4 an Pin 11 (PA2) Kettenantrieb
'
'================================================= ==============================

$regfile = "attiny84.dat"
$crystal = 8000000 'FuseBit CKDIV8 deaktivieren

$hwstack = 50
$swstack = 50
$framesize = 50


'-------------------------------------------------------------------------------------------------
'Timer konfigurieren
'-------------------------------------------------------------------------------------------------

Config Timer1 = Timer , Prescale = 8 'Timer für Einlesen RC Signale
Start Timer1

Config Timer0 = Timer , Prescale = 64 'Timer für Servoausgabe, Wert 125 entspricht 1ms, Wert 250 entspricht 2ms
Enable Timer0
On Timer0 Servoausgabe


'-------------------------------------------------------------------------------------------------
'Poti-Eingänge konfigurieren
'-------------------------------------------------------------------------------------------------

Config Adc = Single , Prescaler = Auto
Start Adc


'-------------------------------------------------------------------------------------------------------------
'Variablen definieren
'-------------------------------------------------------------------------------------------------------------

Const Jitterausgleich = 8 'Wert in µs um den sich gemessene Impulslaenge aendern darf/muss, bevor Servo gestellt wird
'Nur in Vielfachen von 8 veraendern (8, 16, 32, ..)


'Variablen fürs RC Einlesen
Dim Rc_signal_1_start As Word
Dim Rc_signal_2_start As Word
Dim Rc_signal_3_start As Word

Dim Rc_signal_1_stop As Word
Dim Rc_signal_2_stop As Word
Dim Rc_signal_3_stop As Word

Dim Rc_signal_1_stop_flag As Byte
Dim Rc_signal_2_stop_flag As Byte
Dim Rc_signal_3_stop_flag As Byte

Dim Impulslaenge_1 As Word
Dim Impulslaenge_2 As Word
Dim Impulslaenge_3 As Word

Dim Impulslaenge_1_alt As Word
Dim Impulslaenge_2_alt As Word
Dim Impulslaenge_3_alt As Word
Dim Impulslaengenaenderung As Integer 'wird nur einmal gebraucht, da nur Hilfswariable


'Variablen für Servowegsbegrenzung
Dim Poti_1a As Word
Dim Poti_1b As Word
Dim Poti_2a As Word
Dim Poti_2b As Word

Dim Limit_1a As Byte
Dim Limit_1b As Byte
Dim Limit_2a As Byte
Dim Limit_2b As Byte


'Variablen für Berechnungen
Dim Berechnung_1 As Word
Dim Berechnung_2 As Word
Dim Berechnung_3 As Word
Dim Berechnung_4 As Word

Dim Berechnung_1a As Word
Dim Berechnung_2a As Word
Dim Berechnung_3a As Word
Dim Berechnung_4a As Word

Dim I As Word
Dim Servospeed As Word

'Variable für Deltamischer
Dim A1 As Integer
Dim A2 As Integer
Dim B1 As Integer
Dim B2 As Integer

'Variablen für Servoausgabe
Dim Kanal As Byte
Dim Servoausgabe_1 As Byte
Dim Servoausgabe_2 As Byte
Dim Servoausgabe_3 As Byte
Dim Servoausgabe_4 As Byte


'-------------------------------------------------------------------------------------------------
'Einigen Variablen Werte zuweisen
'-------------------------------------------------------------------------------------------------

Kanal = 1

Berechnung_1a = 187 'alle Servos erst mal auf Mittelstellung
Berechnung_2a = 187
Berechnung_3a = 187
Berechnung_4a = 187


'-------------------------------------------------------------------------------------------------------------
'Ein- und Ausgang festlegen
'-------------------------------------------------------------------------------------------------------------

Ddra = &B00000111 'PA0 - PA2 werden Ausgänge, restlicher PortA bleibt Eingang
Ddrb = &B00000001 'PB0 wird Ausgang, restlicher PortB bleibt Eingang


'-------------------------------------------------------------------------------------------------
'Interrupt-Service-Routinen konfigurieren und freigeben
'-------------------------------------------------------------------------------------------------

'Info:
'Alle Porta Pinchangeinterrupts sind "PCINT0" zugeordnet.
'Alle Portb Pinchangeinterrupts sind "PCINT1" zugeordnet.


Pcmsk1.pcint9 = 1 'beim Flankenwechsel an PB1/PCINT9 (RC Eingang 1) Pinchangeinterrupt1 auslösen und in die Subroutine springen
Enable Pcint1 'Pinchangeinterrupt1 (1 weil auf PortB) zulassen
On Pcint1 Rc_eingang_1 Nosave 'Register werden manuel in der ISR gesichert


Pcmsk0.pcint7 = 1 'beim Flankenwechsel an PA7/PCINT6 (RC Eingang 2) Pinchangeinterrupt0 auslösen und in die Subroutine springen
Enable Pcint0 'Pinchangeinterrupt0 (0 weil auf PortA) zulassen
On Pcint0 Rc_eingang_2 Nosave 'Register werden manuel in der ISR gesichert


Config Int0 = Change 'beim Flankenwechsel an PB2/INT0 (RC Eingang 3) Int0 auslösen und in die Subroutine springen
Enable Int0
On Int0 Rc_eingang_3 Nosave 'Register werden manuel in der ISR gesichert

Enable Interrupts



'================================================= =====
'Hauptprogramm
'================================================= =====

Do



'RC Impulslänge berechnen, auf 8-Bit umrechnen und Grenzwerte festlegen

If Rc_signal_1_stop_flag = 1 Then 'Bearbeitung nur, wenn ISR Pulsende gefunden hat
Disable Pcint1 'Mögliche Korruption durch ISR vorbeugen
Impulslaenge_1 = Rc_signal_1_stop - Rc_signal_1_start
Enable Pcint1
Rc_signal_1_stop_flag = 0

Impulslaengenaenderung = Impulslaenge_1_alt - Impulslaenge_1 'SW-Hysterese
If Abs(impulslaengenaenderung) >= Jitterausgleich Then
Impulslaenge_1_alt = Impulslaenge_1

Berechnung_1 = Impulslaenge_1
Shift Berechnung_1 , Right , 3 'Division von Berechnung_1 durch 8
If Berechnung_1 > 255 Then Berechnung_1 = 255
If Berechnung_1 < 120 Then Berechnung_1 = 120
End If
End If


If Rc_signal_2_stop_flag = 1 Then 'Bearbeitung nur, wenn ISR Pulsende gefunden hat
Disable Pcint0 'Mögliche Korruption durch ISR vorbeugen
Impulslaenge_2 = Rc_signal_2_stop - Rc_signal_2_start
Enable Pcint0
Rc_signal_2_stop_flag = 0

Impulslaengenaenderung = Impulslaenge_2_alt - Impulslaenge_2 'SW-Hysterese
If Abs(impulslaengenaenderung) >= Jitterausgleich Then
Impulslaenge_2_alt = Impulslaenge_2

Berechnung_2 = Impulslaenge_2
Shift Berechnung_2 , Right , 3 'Division von Berechnung_1 durch 8
If Berechnung_2 > 255 Then Berechnung_2 = 255
If Berechnung_2 < 120 Then Berechnung_2 = 120
End If
End If


If Rc_signal_3_stop_flag = 1 Then 'Bearbeitung nur, wenn ISR Pulsende gefunden hat
Disable Int0 'Mögliche Korruption durch ISR vorbeugen
Impulslaenge_3 = Rc_signal_3_stop - Rc_signal_3_start
Enable Int0
Rc_signal_3_stop_flag = 0
Impulslaengenaenderung = Impulslaenge_3_alt - Impulslaenge_3 'SW-Hysterese
If Abs(impulslaengenaenderung) >= Jitterausgleich Then
Impulslaenge_3_alt = Impulslaenge_3

Berechnung_3 = Impulslaenge_3
Shift Berechnung_3 , Right , 3 'Division von Berechnung_1 durch 8
If Berechnung_3 > 255 Then Berechnung_3 = 255
If Berechnung_3 < 120 Then Berechnung_3 = 120
End If
End If





'--------------------------------------------------
'Start der größten IF Bedingung (Betriebsmodewahl)
'FAHRBETRIEB
'--------------------------------------------------

If Berechnung_3 < 187 Then 'falls der Betriebswahlkanal unter Mittelstellung ist, ist Fahrbetrieb
'(
'Hier wird durchgeschleift, NUR für Testzwecke!
Berechnung_3a = Berechnung_1 'Werte vom ersten RC Kanal gehen zu Fahrservo 1
Berechnung_4a = Berechnung_2 'Werte vom zweiten RC Kanal gehen zu Fahrservo 2
'Ende durchschleifen
')

'Hier wird gemischt (Deltamischer)
A1 = Berechnung_1 - 187 ' Mittelstellung nun bei 0, Auschläge von -67 bis +67
A2 = Berechnung_2 - 187

A1 = A1 / 2 ' Werte halbieren (Mischanteile)
'A2 = A2 / 2

B1 = A1 + A2
B2 = A1 - A2

B1 = 185 + B1 ' Unterschiedliche Ausgangswerte wegen Nullpunktjustierung!
B2 = 188 + B2

Berechnung_3a = B1
Berechnung_4a = B2

'Ende der Mischer Berechnung


If Berechnung_3a < 120 Then 'Werte begrenzen
Berechnung_3a = 120
End If
If Berechnung_3a > 255 Then
Berechnung_3a = 255
End If

If Berechnung_4a < 120 Then
Berechnung_4a = 120
End If
If Berechnung_4a > 255 Then
Berechnung_4a = 255
End If



'--------------------------------------------------
'BAGGERETRIEB
'--------------------------------------------------

Else 'falls Betriebswahlschalter über Mittelstellung ist, ist Baggerbetrieb

Berechnung_3a = 187 ' Fahrservos in Mittelstellung
Berechnung_4a = 187 ' Fahrservos in Mittelstellung


'Berechnung der Servogeschwindigkeit und Servostellung

'Erstes Baggerarmservo
If Berechnung_1 < 182 Then 'Info: Todband (Mittelstellung) liegt zwischen 182 und 193
Servospeed = Berechnung_1 - 120 'ergibt Werte zwischen 0 (schnell) und 61 (langsam)
Servospeed = Servospeed * 100 'Werte hochscalieren (Faktor 30 bis 100, höherer Faktor = langsamere minimal Speed)
For I = 1 To Servospeed 'Werte von 0 (schnell) bis max 6100 (langsam) sinnvoll
Next I
Decr Berechnung_1a
End If

If Berechnung_1 > 193 Then
Servospeed = 255 - Berechnung_1
Servospeed = Servospeed * 100
For I = 1 To Servospeed
Next I
Incr Berechnung_1a
End If

'Zweites Baggerarmservo
If Berechnung_2 < 182 Then 'Info: Todband (Mittelstellung) liegt zwischen 182 und 193
Servospeed = Berechnung_2 - 120 'ergibt Werte zwischen 0 (schnell) und 61 (langsam)
Servospeed = Servospeed * 100 'Werte hochscalieren (Faktor 30 bis 100, höherer Faktor = langsamere minimal Speed)
For I = 1 To Servospeed 'Werte von 0 (schnell) bis max 6100 (langsam) sinnvoll
Next I
Decr Berechnung_2a
End If

If Berechnung_2 > 193 Then
Servospeed = 255 - Berechnung_2
Servospeed = Servospeed * 100
For I = 1 To Servospeed
Next I
Incr Berechnung_2a
End If


'Potis abfragen und Werte für Servolimits berechnen

Poti_1a = Getadc(3)
Poti_1a = Poti_1a / 16 '1024 / 16 = 62,5 ergibt ca 25% Wegbegrenzung
Limit_1a = 255 - Poti_1a

Poti_1b = Getadc(4)
Poti_1b = Poti_1b / 16
Limit_1b = 120 + Poti_1b

Poti_2a = Getadc(5)
Poti_2a = Poti_2a / 16
Limit_2a = 255 - Poti_2a

Poti_2b = Getadc(6)
Poti_2b = Poti_2b / 16
Limit_2b = 120 + Poti_2b


'RC Signal auf Limitwerte begrenzen (nur Baggerarmservos)

If Berechnung_1a > Limit_1a Then 'zu hohe Werte abfangen
Berechnung_1a = Limit_1a
End If
If Berechnung_1a < Limit_1b Then 'zu kleine Werte abfangen
Berechnung_1a = Limit_1b
End If


If Berechnung_2a > Limit_2a Then
Berechnung_2a = Limit_2a
End If
If Berechnung_2a < Limit_2b Then
Berechnung_2a = Limit_2b
End If


' Ende der größten IF Bedingung
End If

'Finale Berechnungen an Servoausgabe übergeben, Servoausgabe_x zum direkten Laden in TCNT0 vorbereiten (Cmd Load umgangen)

Servoausgabe_1 = 256 - Berechnung_1a 'Baggerarmservo 1,
Servoausgabe_2 = 256 - Berechnung_2a 'Baggerarmservo 2
Servoausgabe_3 = 256 - Berechnung_3a 'Fahrservo oder ESC 1
Servoausgabe_4 = 256 - Berechnung_4a 'Fahrservo oder ESC 2


Loop



'================================================= =====
'ISR
'================================================= =====


Rc_eingang_1:
$asm
push r17 'Register auf Stack sichern
sbis pinb , 1 'Skip next Instr if PINBx = 1
rjmp Puls_ende1 'Springe Puls_ende
in r17 , tcnt1l 'Timer1 low Byte holen
sts {Rc_signal_1_start} , r17 'Speichere Timer1 low Byte nach Rc_signal_x_start low Byte
in r17 , tcnt1h 'Timer1 high Byte holen
sts {Rc_signal_1_start} + 1 , r17 'Speichere Timer1 high Byte nach Rc_signal_x_start high Byte
rjmp ende1 'Springe zum Ende
Puls_ende1:
in r17 , tcnt1l 'Timer1 low Byte holen
sts {Rc_signal_1_stop} , r17 'Speichere Timer1 low Byte nach Rc_signal_x_stop low Byte
in r17 , tcnt1h 'Timer1 high Byte holen
sts {Rc_signal_1_stop} + 1 , r17 'Speichere Timer1 high Byte nach Rc_signal_x_stop high Byte
ldi r17 , 1
sts {Rc_signal_1_stop_flag} , r17 'Setze Flag zur Bearbeitung von Impulslaenge in Hauptschleife
Ende1:
pop r17 'Register vom Stack zurückholen
$end Asm
Return

Rc_eingang_2:
$asm
push r17 'Register auf Stack sichern
sbis pinA , 7 'Skip next Instr if PINBx = 1
rjmp Puls_ende2 'Springe Puls_ende
in r17 , tcnt1l 'Timer1 low Byte holen
sts {Rc_signal_2_start} , r17 'Speichere Timer1 low Byte nach Rc_signal_x_start low Byte
in r17 , tcnt1h 'Timer1 high Byte holen
sts {Rc_signal_2_start} + 1 , r17 'Speichere Timer1 high Byte nach Rc_signal_x_start high Byte
rjmp ende2 'Springe zum Ende
Puls_ende2:
in r17 , tcnt1l 'Timer1 low Byte holen
sts {Rc_signal_2_stop} , r17 'Speichere Timer1 low Byte nach Rc_signal_x_stop low Byte
in r17 , tcnt1h 'Timer1 high Byte holen
sts {Rc_signal_2_stop} + 1 , r17 'Speichere Timer1 high Byte nach Rc_signal_x_stop high Byte
ldi r17 , 1
sts {Rc_signal_2_stop_flag} , r17 'Setze Flag zur Bearbeitung von Impulslaenge in Hauptschleife
Ende2:
pop r17 'Register vom Stack zurückholen
$end Asm
Return

Rc_eingang_3:
$asm
push r17 'Register auf Stack sichern
sbis pinB , 2 'Skip next Instr if PINBx = 1
rjmp Puls_ende3 'Springe Puls_ende
in r17 , tcnt1l 'Timer1 low Byte holen
sts {Rc_signal_3_start} , r17 'Speichere Timer1 low Byte nach Rc_signal_x_start low Byte
in r17 , tcnt1h 'Timer1 high Byte holen
sts {Rc_signal_3_start} + 1 , r17 'Speichere Timer1 high Byte nach Rc_signal_x_start high Byte
rjmp ende3 'Springe zum Ende
Puls_ende3:
in r17 , tcnt1l 'Timer1 low Byte holen
sts {Rc_signal_3_stop} , r17 'Speichere Timer1 low Byte nach Rc_signal_x_stop low Byte
in r17 , tcnt1h 'Timer1 high Byte holen
sts {Rc_signal_3_stop} + 1 , r17 'Speichere Timer1 high Byte nach Rc_signal_x_stop high Byte
ldi r17 , 1
sts {Rc_signal_3_stop_flag} , r17 'Setze Flag zur Bearbeitung von Impulslaenge in Hauptschleife
Ende3:
pop r17 'Register vom Stack zurückholen
$end Asm
Return




Servoausgabe:

$asm
push r16 'Register auf Stack sichern
in r16,sreg 'Statusregister holen und halten
push r17 'Register auf Stack sichern
lds r17 , {kanal} 'hole Kanalnummer

cpi r17 , 1 'check Kanal und ...
brne LABEL_KANAL_2 '... "wenn nicht gleich" verzweige zum nächsten Kanal
sbic portB , 0 'Skip next instr. wenn PORTA.0 = 0 ist
rjmp label_1 'Springe zum LOW-setzen des Servosignals
sts {kanal} , r17 'Sichere Kanalnummer
lds r17 , {Servoausgabe_1} 'Hole aufbereiteten Pulslängenwert für Timer0
Out Tcnt0 , R17 'Setze Timer0 mit Pulslängenwert
sbi portB , 0 'Setze Servosignal HIGH
rjmp Ende_isr 'Springe zum Ende der ISR
Label_1:
cbi portB , 0 'Setze Servosignal nach LOW
inc r17 'Erhöhe Kanalnummer


Label_kanal_2: 'Bearbeitung von Kanal 2
cpi r17 , 2 'check Kanal und ...
brne LABEL_KANAL_3 '... "wenn nicht gleich" verzweige zum nächsten Kanal
sbic porta , 0 'Skip next instr. wenn PORTA.1 = 0 ist
rjmp label_2 'Springe zum LOW-setzen des Servosignals
sts {kanal} , r17 'Sichere Kanalnummer
lds r17 , {Servoausgabe_2} 'Hole aufbereiteten Pulslängenwert für Timer0
Out Tcnt0 , R17 'Setze Timer0 mit Pulslängenwert
sbi porta , 0 'Setze Servosignal HIGH
rjmp Ende_isr 'Springe zum Ende der ISR
Label_2:
cbi porta , 0 'Setze Servosignal nach LOW
inc r17 'Erhöhe Kanalnummer


Label_kanal_3: 'Bearbeitung von Kanal 3
cpi r17 , 3 'check Kanal und ...
brne LABEL_KANAL_4 '... "wenn nicht gleich" verzweige zum nächsten Kanal
sbic porta , 1 'Skip next instr. wenn PORTA.2 = 0 ist
rjmp label_3 'Springe zum LOW-setzen des Servosignals
sts {kanal} , r17 'Sichere Kanalnummer
lds r17 , {Servoausgabe_3} 'Hole aufbereiteten Pulslängenwert für Timer0
Out Tcnt0 , R17 'Setze Timer0 mit Pulslängenwert
sbi porta , 1 'Setze Servosignal HIGH
rjmp Ende_isr 'Springe zum Ende der ISR
Label_3:
cbi porta , 1 'Setze Servosignal nach LOW
inc r17 'Erhöhe Kanalnummer


Label_kanal_4: 'Bearbeitung von Kanal 4
cpi r17 , 4 'check Kanal und ...
brne LABEL_KANAL_5 '... "wenn nicht gleich" verzweige zum nächsten Kanal
sbic porta , 2 'Skip next instr. wenn PORTA.3 = 0 ist
rjmp label_4 'Springe zum LOW-setzen des Servosignals
sts {kanal} , r17 'Sichere Kanalnummer
lds r17 , {Servoausgabe_4} 'Hole aufbereiteten Pulslängenwert für Timer0
Out Tcnt0 , R17 'Setze Timer0 mit Pulslängenwert
sbi porta , 2 'Setze Servosignal HIGH
rjmp Ende_isr 'Springe zum Ende der ISR
Label_4:
cbi porta , 2 'Setze Servosignal nach LOW
inc r17 'Erhöhe Kanalnummer

'Pausenauffüllung

'Pausenanfang
inc r17
sts {kanal} , r17 'Sichere Kanalnummer
ldi r17 , &B00000101 'CS00 und CS02 für prescaler 1024 in Timer0 vorbereiten
Out Tccr0b , R17 'Timer0 läuft mit 128µs Auflösung
ldi r17 , 162 'Wert für ca 12ms bis zum nächsten OVF (162=256-94) bei prescaler 1024
Out Tcnt0 , R17
rjmp Ende_isr 'Springe zum Ende der ISR

'Pausenende
Label_kanal_5: 'Bearbeitung von Kanal 5 (Pausenende)
ldi r17 , &B00000011 'CS00 und CS01 für prescaler 64 in Timer0 vorbereiten
Out Tccr0b , R17 'Timer0 läuft wieder mit 8µs Auflösung
ldi r17 , 1 'Kanalnummer auf 1 setzen
sts {kanal} , r17 'Sichere Kanalnummer
Ende_isr:
pop r17 'Register vom Stack zurückholen
Out Sreg , R16 'Statusregister zurückspeichern
pop r16 'Register vom Stack zurückholen
$end Asm

Return



Ich experimentier dann auch mal weiter.

mfg
Robert

Searcher
03.01.2014, 12:57
Meine Vermutung ist nun folgende: Wenn das Servo langsam losfährt, ist die Variable "Servospeed" in der For-Next Schleife sehr groß. Auch wenn ich den Knüppel nun schlagartig auf Endandanschlag bringe, findet ja keine neue Berechnung der Variable "Servospeed" statt. Wenn also Servospeed sehr groß ist und ich den Knüppel nicht wieder in Mittelstellung bringe (um aus der ganzen IF Bedingung raus zu kommen), fährt das Servo nur mit Minimalgeschwindigkeit bis zum Anschlag.

Ich habe leider keine rechte Vorstellung, wie der Bagger reagiert, vielleicht auch weil ich selbst keine Modellbaufernbedienung habe.
Auf jeden Fall darf die Zeit zwischen den Abfragen der Rc_signal_stop_flags nicht länger sein als der Abstand zweier Impulse an dem Eingang. Anders: die Laufzeit einer Runde in der Hauptschleife darf nicht länger sein als der Abstand zweier Impulse, da es sonst zu Fehlmessungen kommen kann (Flag wird nicht zurückgesetzt und Rc_signal_start wird neu gesetzt und Rc_signal_stop noch nicht. Dazwischen bis zu 2ms Zeit Fehler zu produzieren. Gefahr könnte man auf die Schnelle durch Maßnahmen in den ISR vermindern aber nicht ganz ausschließen. Einfacher und besser ist es die Hauptschleife schneller zu machen.

Die For-Next Schleifen können das Hauptprogramm bis zu 70ms aufhalten - grob im Simulator getestet.

Das sollte erstmal eliminiert werden. Hoffe mein Vorschlag funktioniert wenigstens ansatzweise.
Er basiert auf der zusätzlichen Nutzung von Timer1 zur Zeitmessung, so daß das Hauptprogramm nicht aufgehalten wird.

Sobald Timer1 läuft, werden auch nach spätestens 66ms die OCF1A und OCF1B Comparematch Interruptflags gesetzt und bleiben gesetzt, da ja da kein Interrupt drauf aktiviert ist.

Die werden in meinem Vorschlag abgefragt, gelöscht, wenn sie gesetzt sind und die OCR1x Register mit Wartezeiten geladen. Damit werden zusätzlich Interrupts zur Zeitmessung vermieden, allerdings sind Wartezeiten etwas abhängig von Programmverzweigungen (kann ich nicht überblicken, sollte aber im Verhältnis kaum ins Gewicht fallen?)

Die INCR und DECR der "Berechnung_xA" findet sofort ohne Wartezeit statt. Kann man später noch mit Flagsteuerung ändern - möchte nur erstmal wissen ob es im Prinzip so akzeptabel ist (und zuallererst ob es überhaupt funktioniert - wenn vielleicht auch das Knüppelsteuerungsreißproblem noch nicht behoben wird :-) )

Dies hier also mit dem weiter unten stehenden ersetzten.



'Berechnung der Servogeschwindigkeit und Servostellung

'Erstes Baggerarmservo
If Berechnung_1 < 182 Then 'Info: Todband (Mittelstellung) liegt zwischen 182 und 193
Servospeed = Berechnung_1 - 120 'ergibt Werte zwischen 0 (schnell) und 61 (langsam)
Servospeed = Servospeed * 100 'Werte hochscalieren (Faktor 30 bis 100, höherer Faktor = langsamere minimal Speed)
For I = 1 To Servospeed 'Werte von 0 (schnell) bis max 6100 (langsam) sinnvoll
Next I
Decr Berechnung_1a
End If

If Berechnung_1 > 193 Then
Servospeed = 255 - Berechnung_1
Servospeed = Servospeed * 100
For I = 1 To Servospeed
Next I
Incr Berechnung_1a
End If

'Zweites Baggerarmservo
If Berechnung_2 < 182 Then 'Info: Todband (Mittelstellung) liegt zwischen 182 und 193
Servospeed = Berechnung_2 - 120 'ergibt Werte zwischen 0 (schnell) und 61 (langsam)
Servospeed = Servospeed * 100 'Werte hochscalieren (Faktor 30 bis 100, höherer Faktor = langsamere minimal Speed)
For I = 1 To Servospeed 'Werte von 0 (schnell) bis max 6100 (langsam) sinnvoll
Next I
Decr Berechnung_2a
End If

If Berechnung_2 > 193 Then
Servospeed = 255 - Berechnung_2
Servospeed = Servospeed * 100
For I = 1 To Servospeed
Next I
Incr Berechnung_2a
End If





Hab grob versucht die gleiche Wartzeit hinzukriegen. Original for-next mit servospeed=61*100=6100 -> maximal 35ms. jetzt 61*500 = 30500µs = 30,5ms


If Tifr1.ocf1a = 1 And Berechnung_1 < 182 Then
Set Tifr1.ocf1a 'Interruptflag löschen
Servospeed = Berechnung_1 - 120
Servospeed = Servospeed * 500 'eine Einheit in Servospeed macht 1µs Wartezeit
'sicherheitszeit 1000µs damit bei kleinen Werten kein kompletter timer rundlauf bis zum nächsten
'comparematch abgewartet werden muß. Länger sollte auch eine Runde in der Hauptschleife nicht dauern.
If Servospeed < 1000 Then Servospeed = 1000 'Sicherheitszeit
Ocr1a = Tcnt1 + Servospeed 'Setzen von OCR1A damit nach Servospeed ein Compare Match auftritt
Decr Berechnung_1a
End If

If Tifr1.ocf1a = 1 And Berechnung_1 > 193 Then
Set Tifr1.ocf1a
Servospeed = 255 - Berechnung_1
Servospeed = Servospeed * 500
If Servospeed < 1000 Then Servospeed = 1000 'Sicherheitszeit
Ocr1a = Tcnt1 + Servospeed
Incr Berechnung_1a
End If

If Tifr1.ocf1b = 1 And Berechnung_2 < 182 Then
Set Tifr1.ocf1b
Servospeed = Berechnung_2 - 120
Servospeed = Servospeed * 500
If Servospeed < 1000 Then Servospeed = 1000 'Sicherheitszeit
Ocr1b = Tcnt1 + Servospeed
Decr Berechnung_2a
End If

If Tifr1.ocf1b = 1 And Berechnung_2 > 193 Then
Set Tifr1.ocf1b
Servospeed = 255 - Berechnung_2
Servospeed = Servospeed * 500
If Servospeed < 1000 Then Servospeed = 1000 'Sicherheitszeit
Ocr1b = Tcnt1 + Servospeed
Incr Berechnung_2a
End If


Gruß
Searcher

R2D2 Bastler
03.01.2014, 13:33
Hallo zusammen,

habs gerade probiert, wurde leider noch schlimmer :(

Wenn der Knüppel schnell bewegt wird, fährt das Servo oft wieder nur langsam. Wird in dieser Zeit auch noch der andere Küppel für das zweite Servo bewegt, läuft plötzlich das erste Servo schneller. Beeinflussen sich also beide gegenseitig.

Meine Codeumsetzung war diese (funktioniert aber auch nicht!!!)

'Erstes Baggerarmservo
If Berechnung_1 < 182 Then 'Info: Todband (Mittelstellung) liegt zwischen 182 und 193
Servospeed = Berechnung_1 - 120 'ergibt Werte zwischen 0 (schnell) und 61 (langsam)
Servospeed = Servospeed * 1000 'Werte hochscalieren 0 (schnell) und 61000 (langsam)

Ocr1a = Timer1 + Servospeed
Tifr1.ocf1a = 1 'OCF1A Flag löschen (set to 1)
If Tifr1.ocf1a = 0 Then Incr Zeit_1 'wenn OCF1A erreicht ist (OCF1A Flag = 0), dann Zeit_1 um 1 erhöhen
If Zeit_1 = 50 Then 'zusätzliche Zeitbasis, da Timer1 nur bis 65535 zählen kann
Decr Berechnung_1a
Zeit_1 = 0
End If
End If

@Searcher

Du frägst bei den IF Bedingungen "Tifr1.ocf1a = 1" ab um in die Schleife zu kommen und benutzt dann den Befehl "Set Tifr1.ocf1a". Wird damit nicht das entsprechende Bit (Tifr1.ocf1a) auf 1 gesetzt (welches Du ja bereits vorher zur Bedingung zum Eintritt in die Schleife ohnehin auf 1 gebraucht hast) :confused: :confused: :confused:


mfg
Robert

Searcher
03.01.2014, 13:52
Hallo,

habs gerade probiert, wurde leider noch schlimmer :(
Wenn mein Code zur Verkürzung der Hauptschleife im Prinzip funktioniert, die Auswirkungen aber schlimmer werden, laß ihn drin (bis eindeutig feststeht, daß er weitere Fehler produziert). Die "Sicherheitszeit kann man auch auf 2000 oder 3000 µs erhöhen.

Muß man also weiter nach Bugs suchen. Bin im Augenblick ratlos :(


@Searcher
Du frägst bei den IF Bedingungen "Tifr1.ocf1a = 1" ab um in die Schleife zu kommen und benutzt dann den Befehl "Set Tifr1.ocf1a". Wird damit nicht das entsprechende Bit (Tifr1.ocf1a) auf 1 gesetzt (welches Du ja bereits vorher zur Bedingung zum Eintritt in die Schleife ohnehin auf 1 gebraucht hast) :confused: :confused: :confused:

Ja, ist was Besonderes bei den Interruptflags allgemein. Auszug aus Datenblatt:
"OCF1A is automatically cleared when the Output Compare Match A Interrupt Vector is exe-cuted. Alternatively, OCF1A can be cleared by writing a logic one to its bit location."

Gruß
Searcher

R2D2 Bastler
03.01.2014, 14:00
Hallo Searcher,

vergiss mein letztes Posting, ich glaube mein Attiny ist jetzt den Flash-Tod gestorben. Ich habs jetzt mit einem anderen probiert, scheint zu funktionieren. Einzig was mir noch auffällt ist, dass die Servos in eine Richtung schneller unterwegs sind, als in die andere. Ich bin grad dabei heraus zu finden, welche Passage im Code die "schnellen" bzw "langsamen" sind.

Melde mich dann wieder.

mfg
Robert

Searcher
03.01.2014, 14:05
@R2D2 Bastler: Ohhh, ich seh gerade, daß bei Dir bei "On Timer0 ..." ganz oben das "nosave" fehlt!

Gruß
Searcher

R2D2 Bastler
03.01.2014, 16:27
@R2D2 Bastler: Ohhh, ich seh gerade, daß bei Dir bei "On Timer0 ..." ganz oben das "nosave" fehlt!


Danke, wir sind ja mittlerweile bei Version 23 und unzähligen Experimenten zusätzlich, ist mir durch die Lappen gegangen :oops:

Habe nun weiter getestet.

Die unterschiedlichen Geschwindigkeiten liegen offensichtlich daran, dass bei Mittelstellung der Knüppel die RC-Signale nicht exakt bei 1,5 ms liegen. Ob das nun von der Fernbedienung, vom Empfänger oder vom Programm "verschludert" wird, kann ich nicht rausmessen. Ist aber kein Problem, dafür gibt es die Funktion "Subtrimm" bei nahezu jeder Funke. Es fehlt ja nur wenige Klicks.

Des weiteren werde ich wohl die Grenzwerte noch etwas enger stecken müssen, also für den einen Anschlag 125 anstelle von 120, für den anderen Anschlag 250 anstelle von 255. Meine Fernbedienung erreicht sonst teilweise nicht die Werte für die Endanschläge. Ich verliere dabei zwar 10 Schritte der Auflösung, aber die Ausschläge meiner Fernbedienung stehen schon bei 125%, weiter kann ich sie nicht hochstellen.

Werde dann nochmal berichten,

mfg
Robert

- - - Aktualisiert - - -

Ich glaub das Ding geht, es geeeeeehhhhhhhhhhht \\:D/\\:D/\\:D/

Ich hab noch mit einigen Zahlen gespielt, hier und da ein paar kleine Veränderungen vorgenommen und das ganze ausgiebig getestet. Grinse gerade von einem Ohr bis zum anderen :cheesy:

Dank zahlreicher Hilfe hier im Forum, sowie unzähligen PM, läuft das Teil nun besser als erwartet.

Das Programm wird auf jeden Fall meine Codegrundlage für weitere RC-Geschichten, bei denen mehrere RC Signale auf einem Chip zeitkritisch ein- bzw ausgegeben werden müssen. Mal sehen, ob und wie sich eine "Mehr-Chip-Variante" entwickelt (bessere Auflösung etc).

Bastle noch an einer Single-Baggerarm Steuerung (1 Eingang, 1 Ausgang auf einem kleineren Attiny45). Ergebnisse werd ich hier noch nachreichen.

Hänge den fertigen Code nochmal mit dran.

mfg
Robert



'================================================= ==============================
'BAGGER Steuerung V23
'
'RC Eingang 1 an Pin 3 (PB1, PCINT9) Stick 1 (Aileron)
'RC Eingang 2 an Pin 6 (PA7, PCINT7) Stick 2 (Elevator)
'RC Eingang 3 an Pin 5 (PB2, INT0) Betriebsmode
'Poti 1a an Pin 10 (PA3, ADC3)
'Poti 1b an Pin 9 (PA4, ADC4)
'Poti 2a an Pin 8 (PA5, ADC5)
'Poti 2b an Pin 7 (PA6, ADC6)
'Servo 1 an Pin 2 (PB0) Baggerarmservo
'Servo 2 an Pin 13 (PA0) Baggerarmservo
'Servo 3 an Pin 12 (PA1) Kettenantrieb
'Servo 4 an Pin 11 (PA2) Kettenantrieb
'
'================================================= ==============================

$regfile = "attiny84.dat"
$crystal = 8000000 'FuseBit CKDIV8 deaktivieren

$hwstack = 50
$swstack = 50
$framesize = 50


'-------------------------------------------------------------------------------------------------
'Timer konfigurieren
'-------------------------------------------------------------------------------------------------

Config Timer1 = Timer , Prescale = 8 'Timer für Einlesen RC Signale
Start Timer1

Config Timer0 = Timer , Prescale = 64 'Timer für Servoausgabe, Wert 125 entspricht 1ms, Wert 250 entspricht 2ms
Enable Timer0
On Timer0 Servoausgabe Nosave


'-------------------------------------------------------------------------------------------------
'Poti-Eingänge konfigurieren
'-------------------------------------------------------------------------------------------------

Config Adc = Single , Prescaler = Auto
Start Adc


'-------------------------------------------------------------------------------------------------------------
'Konstanten definieren
'-------------------------------------------------------------------------------------------------------------
Const Jitterausgleich = 8 'Wert in µs um den sich gemessene Impulslaenge aendern darf/muss, bevor Servo gestellt wird
'Nur in Vielfachen von 8 veraendern (8, 16, 32, ..)
Const Rc_min = 122 'Minimaler RC-Wert für die Berechnungen (Werte zwischen 120 - 125 sinvoll)
Const Rc_max = 253 'Maximaler RC-Wert für die Berechnungen (Werte zwischen 250 - 255 sinvoll)

'-------------------------------------------------------------------------------------------------------------
'Variablen definieren
'-------------------------------------------------------------------------------------------------------------

'Variablen fürs RC Einlesen
Dim Rc_signal_1_start As Word
Dim Rc_signal_2_start As Word
Dim Rc_signal_3_start As Word

Dim Rc_signal_1_stop As Word
Dim Rc_signal_2_stop As Word
Dim Rc_signal_3_stop As Word

Dim Rc_signal_1_stop_flag As Byte
Dim Rc_signal_2_stop_flag As Byte
Dim Rc_signal_3_stop_flag As Byte

Dim Impulslaenge_1 As Word
Dim Impulslaenge_2 As Word
Dim Impulslaenge_3 As Word

Dim Impulslaenge_1_alt As Word
Dim Impulslaenge_2_alt As Word
Dim Impulslaenge_3_alt As Word

Dim Impulslaengenaenderung As Integer 'wird nur einmal gebraucht, da nur Hilfswariable


'Variablen für Servowegsbegrenzung
Dim Poti_1a As Word
Dim Poti_1b As Word
Dim Poti_2a As Word
Dim Poti_2b As Word

Dim Limit_1a As Byte
Dim Limit_1b As Byte
Dim Limit_2a As Byte
Dim Limit_2b As Byte


'Variablen für Berechnungen
Dim Berechnung_1 As Word
Dim Berechnung_2 As Word
Dim Berechnung_3 As Word
Dim Berechnung_4 As Word

Dim Berechnung_1a As Word
Dim Berechnung_2a As Word
Dim Berechnung_3a As Word
Dim Berechnung_4a As Word

Dim Servospeed As Word

'Variable für Deltamischer
Dim A1 As Integer
Dim A2 As Integer
Dim B1 As Integer
Dim B2 As Integer

'Variablen für Servoausgabe
Dim Kanal As Byte

Dim Servoausgabe_1 As Byte
Dim Servoausgabe_2 As Byte
Dim Servoausgabe_3 As Byte
Dim Servoausgabe_4 As Byte


'-------------------------------------------------------------------------------------------------
'Einigen Variablen Werte zuweisen
'-------------------------------------------------------------------------------------------------

Kanal = 1

Berechnung_1a = 187 'alle Servos erst mal auf Mittelstellung
Berechnung_2a = 187
Berechnung_3a = 187
Berechnung_4a = 187


'-------------------------------------------------------------------------------------------------------------
'Ein- und Ausgang festlegen
'-------------------------------------------------------------------------------------------------------------

Ddra = &B00000111 'PA0 - PA2 werden Ausgänge, restlicher PortA bleibt Eingang
Ddrb = &B00000001 'PB0 wird Ausgang, restlicher PortB bleibt Eingang


'-------------------------------------------------------------------------------------------------
'Interrupt-Service-Routinen konfigurieren und freigeben
'-------------------------------------------------------------------------------------------------

'Info:
'Alle Porta Pinchangeinterrupts sind "PCINT0" zugeordnet.
'Alle Portb Pinchangeinterrupts sind "PCINT1" zugeordnet.


Pcmsk1.pcint9 = 1 'beim Flankenwechsel an PB1/PCINT9 (RC Eingang 1) Pinchangeinterrupt1 auslösen und in die Subroutine springen
Enable Pcint1 'Pinchangeinterrupt1 (1 weil auf PortB) zulassen
On Pcint1 Rc_eingang_1 Nosave 'Register werden manuel in der ISR gesichert


Pcmsk0.pcint7 = 1 'beim Flankenwechsel an PA7/PCINT6 (RC Eingang 2) Pinchangeinterrupt0 auslösen und in die Subroutine springen
Enable Pcint0 'Pinchangeinterrupt0 (0 weil auf PortA) zulassen
On Pcint0 Rc_eingang_2 Nosave 'Register werden manuel in der ISR gesichert


Config Int0 = Change 'beim Flankenwechsel an PB2/INT0 (RC Eingang 3) Int0 auslösen und in die Subroutine springen
Enable Int0
On Int0 Rc_eingang_3 Nosave 'Register werden manuel in der ISR gesichert

Enable Interrupts



'================================================= =====
'Hauptprogramm
'================================================= =====

Do


'RC Impulslänge berechnen, auf 8-Bit umrechnen und Grenzwerte festlegen

If Rc_signal_1_stop_flag = 1 Then 'Bearbeitung nur, wenn ISR Pulsende gefunden hat
Disable Pcint1 'Mögliche Korruption durch ISR vorbeugen
Impulslaenge_1 = Rc_signal_1_stop - Rc_signal_1_start
Enable Pcint1
Rc_signal_1_stop_flag = 0

Impulslaengenaenderung = Impulslaenge_1_alt - Impulslaenge_1 'SW-Hysterese
If Abs(impulslaengenaenderung) >= Jitterausgleich Then
Impulslaenge_1_alt = Impulslaenge_1

Berechnung_1 = Impulslaenge_1
Shift Berechnung_1 , Right , 3 'Division von Berechnung_1 durch 8
If Berechnung_1 > Rc_max Then Berechnung_1 = Rc_max
If Berechnung_1 < Rc_min Then Berechnung_1 = Rc_min
End If
End If


If Rc_signal_2_stop_flag = 1 Then 'Bearbeitung nur, wenn ISR Pulsende gefunden hat
Disable Pcint0 'Mögliche Korruption durch ISR vorbeugen
Impulslaenge_2 = Rc_signal_2_stop - Rc_signal_2_start
Enable Pcint0
Rc_signal_2_stop_flag = 0

Impulslaengenaenderung = Impulslaenge_2_alt - Impulslaenge_2 'SW-Hysterese
If Abs(impulslaengenaenderung) >= Jitterausgleich Then
Impulslaenge_2_alt = Impulslaenge_2

Berechnung_2 = Impulslaenge_2
Shift Berechnung_2 , Right , 3 'Division von Berechnung_1 durch 8
If Berechnung_2 > Rc_max Then Berechnung_2 = Rc_max
If Berechnung_2 < Rc_min Then Berechnung_2 = Rc_min
End If
End If


If Rc_signal_3_stop_flag = 1 Then 'Bearbeitung nur, wenn ISR Pulsende gefunden hat
Disable Int0 'Mögliche Korruption durch ISR vorbeugen
Impulslaenge_3 = Rc_signal_3_stop - Rc_signal_3_start
Enable Int0
Rc_signal_3_stop_flag = 0
Impulslaengenaenderung = Impulslaenge_3_alt - Impulslaenge_3 'SW-Hysterese
If Abs(impulslaengenaenderung) >= Jitterausgleich Then
Impulslaenge_3_alt = Impulslaenge_3

Berechnung_3 = Impulslaenge_3
Shift Berechnung_3 , Right , 3 'Division von Berechnung_1 durch 8
If Berechnung_3 > Rc_max Then Berechnung_3 = Rc_max
If Berechnung_3 < Rc_min Then Berechnung_3 = Rc_min
End If
End If





'--------------------------------------------------
'Start der größten IF Bedingung (Betriebsmodewahl)
'FAHRBETRIEB
'--------------------------------------------------

If Berechnung_3 < 187 Then 'falls der Betriebswahlkanal unter Mittelstellung ist, ist Fahrbetrieb
'(
'Hier wird durchgeschleift, NUR für Testzwecke!
Berechnung_3a = Berechnung_1 'Werte vom ersten RC Kanal gehen zu Fahrservo 1
Berechnung_4a = Berechnung_2 'Werte vom zweiten RC Kanal gehen zu Fahrservo 2
'Ende durchschleifen
')

'Hier wird gemischt (Deltamischer)
A1 = Berechnung_1 - 187 ' Mittelstellung nun bei 0, Auschläge von -67 bis +67
A2 = Berechnung_2 - 187

A1 = A1 / 2 ' Werte halbieren (Mischanteile)
'A2 = A2 / 2

B1 = A1 + A2
B2 = A1 - A2

B1 = 185 + B1 ' Unterschiedliche Ausgangswerte wegen Nullpunktjustierung!
B2 = 188 + B2

Berechnung_3a = B1
Berechnung_4a = B2

'Ende der Mischer Berechnung


If Berechnung_3a < Rc_min Then 'Werte begrenzen
Berechnung_3a = Rc_min
End If
If Berechnung_3a > Rc_max Then
Berechnung_3a = Rc_max
End If

If Berechnung_4a < Rc_min Then
Berechnung_4a = Rc_min
End If
If Berechnung_4a > Rc_max Then
Berechnung_4a = Rc_max
End If



'--------------------------------------------------
'BAGGERETRIEB
'--------------------------------------------------

Else 'falls Betriebswahlschalter über Mittelstellung ist, ist Baggerbetrieb

'Berechnung_3a = 187 ' Fahrservos in Mittelstellung
'Berechnung_4a = 187 ' Fahrservos in Mittelstellung


'Berechnung der Servogeschwindigkeit und Servostellung

If Tifr1.ocf1a = 1 And Berechnung_1 < 182 Then
Set Tifr1.ocf1a 'Interruptflag löschen
Servospeed = Berechnung_1 - Rc_min
Servospeed = Servospeed * 500 'eine Einheit in Servospeed macht 1µs Wartezeit
If Servospeed < 1000 Then Servospeed = 1000 'Sicherheitszeit 1000µs damit bei kleinen Werten kein kompletter timer rundlauf bis zum nächsten
'comparematch abgewartet werden muß. Länger sollte auch eine Runde in der Hauptschleife nicht dauern.
Ocr1a = Tcnt1 + Servospeed 'Setzen von OCR1A damit nach Servospeed ein Compare Match auftritt
Decr Berechnung_1a
End If

If Tifr1.ocf1a = 1 And Berechnung_1 > 193 Then
Set Tifr1.ocf1a
Servospeed = Rc_max - Berechnung_1
Servospeed = Servospeed * 500
If Servospeed < 1000 Then Servospeed = 1000 'Sicherheitszeit
Ocr1a = Tcnt1 + Servospeed
Incr Berechnung_1a
End If

If Tifr1.ocf1b = 1 And Berechnung_2 < 182 Then
Set Tifr1.ocf1b
Servospeed = Berechnung_2 - Rc_min
Servospeed = Servospeed * 500
If Servospeed < 1000 Then Servospeed = 1000 'Sicherheitszeit
Ocr1b = Tcnt1 + Servospeed
Decr Berechnung_2a
End If

If Tifr1.ocf1b = 1 And Berechnung_2 > 193 Then
Set Tifr1.ocf1b
Servospeed = Rc_max - Berechnung_2
Servospeed = Servospeed * 500
If Servospeed < 1000 Then Servospeed = 1000 'Sicherheitszeit
Ocr1b = Tcnt1 + Servospeed
Incr Berechnung_2a
End If


'Potis abfragen und Werte für Servolimits berechnen

Poti_1a = Getadc(3)
Poti_1a = Poti_1a / 25 '1024 / 25 = 41, ergibt ca 30% Wegbegrenzung
Limit_1a = 255 - Poti_1a

Poti_1b = Getadc(4)
Poti_1b = Poti_1b / 25
Limit_1b = 120 + Poti_1b

Poti_2a = Getadc(5)
Poti_2a = Poti_2a / 25
Limit_2a = 255 - Poti_2a

Poti_2b = Getadc(6)
Poti_2b = Poti_2b / 25
Limit_2b = 120 + Poti_2b


'RC Signal auf Limitwerte begrenzen (nur Baggerarmservos)

If Berechnung_1a > Limit_1a Then 'zu hohe Werte abfangen
Berechnung_1a = Limit_1a
End If
If Berechnung_1a < Limit_1b Then 'zu kleine Werte abfangen
Berechnung_1a = Limit_1b
End If


If Berechnung_2a > Limit_2a Then
Berechnung_2a = Limit_2a
End If
If Berechnung_2a < Limit_2b Then
Berechnung_2a = Limit_2b
End If


' Ende der größten IF Bedingung
End If

'Finale Berechnungen an Servoausgabe übergeben, Servoausgabe_x zum direkten Laden in TCNT0 vorbereiten (Cmd Load umgangen)

Servoausgabe_1 = 256 - Berechnung_1a 'Baggerarmservo 1,
Servoausgabe_2 = 256 - Berechnung_2a 'Baggerarmservo 2
Servoausgabe_3 = 256 - Berechnung_3a 'Fahrservo oder ESC 1
Servoausgabe_4 = 256 - Berechnung_4a 'Fahrservo oder ESC 2


Loop



'================================================= =====
'ISR
'================================================= =====


Rc_eingang_1:
$asm
push r17 'Register auf Stack sichern
sbis pinb , 1 'Skip next Instr if PINBx = 1
rjmp Puls_ende1 'Springe Puls_ende
in r17 , tcnt1l 'Timer1 low Byte holen
sts {Rc_signal_1_start} , r17 'Speichere Timer1 low Byte nach Rc_signal_x_start low Byte
in r17 , tcnt1h 'Timer1 high Byte holen
sts {Rc_signal_1_start} + 1 , r17 'Speichere Timer1 high Byte nach Rc_signal_x_start high Byte
rjmp ende1 'Springe zum Ende
Puls_ende1:
in r17 , tcnt1l 'Timer1 low Byte holen
sts {Rc_signal_1_stop} , r17 'Speichere Timer1 low Byte nach Rc_signal_x_stop low Byte
in r17 , tcnt1h 'Timer1 high Byte holen
sts {Rc_signal_1_stop} + 1 , r17 'Speichere Timer1 high Byte nach Rc_signal_x_stop high Byte
ldi r17 , 1
sts {Rc_signal_1_stop_flag} , r17 'Setze Flag zur Bearbeitung von Impulslaenge in Hauptschleife
Ende1:
pop r17 'Register vom Stack zurückholen
$end Asm
Return

Rc_eingang_2:
$asm
push r17 'Register auf Stack sichern
sbis pinA , 7 'Skip next Instr if PINBx = 1
rjmp Puls_ende2 'Springe Puls_ende
in r17 , tcnt1l 'Timer1 low Byte holen
sts {Rc_signal_2_start} , r17 'Speichere Timer1 low Byte nach Rc_signal_x_start low Byte
in r17 , tcnt1h 'Timer1 high Byte holen
sts {Rc_signal_2_start} + 1 , r17 'Speichere Timer1 high Byte nach Rc_signal_x_start high Byte
rjmp ende2 'Springe zum Ende
Puls_ende2:
in r17 , tcnt1l 'Timer1 low Byte holen
sts {Rc_signal_2_stop} , r17 'Speichere Timer1 low Byte nach Rc_signal_x_stop low Byte
in r17 , tcnt1h 'Timer1 high Byte holen
sts {Rc_signal_2_stop} + 1 , r17 'Speichere Timer1 high Byte nach Rc_signal_x_stop high Byte
ldi r17 , 1
sts {Rc_signal_2_stop_flag} , r17 'Setze Flag zur Bearbeitung von Impulslaenge in Hauptschleife
Ende2:
pop r17 'Register vom Stack zurückholen
$end Asm
Return

Rc_eingang_3:
$asm
push r17 'Register auf Stack sichern
sbis pinB , 2 'Skip next Instr if PINBx = 1
rjmp Puls_ende3 'Springe Puls_ende
in r17 , tcnt1l 'Timer1 low Byte holen
sts {Rc_signal_3_start} , r17 'Speichere Timer1 low Byte nach Rc_signal_x_start low Byte
in r17 , tcnt1h 'Timer1 high Byte holen
sts {Rc_signal_3_start} + 1 , r17 'Speichere Timer1 high Byte nach Rc_signal_x_start high Byte
rjmp ende3 'Springe zum Ende
Puls_ende3:
in r17 , tcnt1l 'Timer1 low Byte holen
sts {Rc_signal_3_stop} , r17 'Speichere Timer1 low Byte nach Rc_signal_x_stop low Byte
in r17 , tcnt1h 'Timer1 high Byte holen
sts {Rc_signal_3_stop} + 1 , r17 'Speichere Timer1 high Byte nach Rc_signal_x_stop high Byte
ldi r17 , 1
sts {Rc_signal_3_stop_flag} , r17 'Setze Flag zur Bearbeitung von Impulslaenge in Hauptschleife
Ende3:
pop r17 'Register vom Stack zurückholen
$end Asm
Return




Servoausgabe:

$asm
push r16 'Register auf Stack sichern
in r16,sreg 'Statusregister holen und halten
push r17 'Register auf Stack sichern
lds r17 , {kanal} 'hole Kanalnummer

cpi r17 , 1 'check Kanal und ...
brne LABEL_KANAL_2 '... "wenn nicht gleich" verzweige zum nächsten Kanal
sbic portB , 0 'Skip next instr. wenn PORTA.0 = 0 ist
rjmp label_1 'Springe zum LOW-setzen des Servosignals
sts {kanal} , r17 'Sichere Kanalnummer
lds r17 , {Servoausgabe_1} 'Hole aufbereiteten Pulslängenwert für Timer0
Out Tcnt0 , R17 'Setze Timer0 mit Pulslängenwert
sbi portB , 0 'Setze Servosignal HIGH
rjmp Ende_isr 'Springe zum Ende der ISR
Label_1:
cbi portB , 0 'Setze Servosignal nach LOW
inc r17 'Erhöhe Kanalnummer


Label_kanal_2: 'Bearbeitung von Kanal 2
cpi r17 , 2 'check Kanal und ...
brne LABEL_KANAL_3 '... "wenn nicht gleich" verzweige zum nächsten Kanal
sbic porta , 0 'Skip next instr. wenn PORTA.1 = 0 ist
rjmp label_2 'Springe zum LOW-setzen des Servosignals
sts {kanal} , r17 'Sichere Kanalnummer
lds r17 , {Servoausgabe_2} 'Hole aufbereiteten Pulslängenwert für Timer0
Out Tcnt0 , R17 'Setze Timer0 mit Pulslängenwert
sbi porta , 0 'Setze Servosignal HIGH
rjmp Ende_isr 'Springe zum Ende der ISR
Label_2:
cbi porta , 0 'Setze Servosignal nach LOW
inc r17 'Erhöhe Kanalnummer


Label_kanal_3: 'Bearbeitung von Kanal 3
cpi r17 , 3 'check Kanal und ...
brne LABEL_KANAL_4 '... "wenn nicht gleich" verzweige zum nächsten Kanal
sbic porta , 1 'Skip next instr. wenn PORTA.2 = 0 ist
rjmp label_3 'Springe zum LOW-setzen des Servosignals
sts {kanal} , r17 'Sichere Kanalnummer
lds r17 , {Servoausgabe_3} 'Hole aufbereiteten Pulslängenwert für Timer0
Out Tcnt0 , R17 'Setze Timer0 mit Pulslängenwert
sbi porta , 1 'Setze Servosignal HIGH
rjmp Ende_isr 'Springe zum Ende der ISR
Label_3:
cbi porta , 1 'Setze Servosignal nach LOW
inc r17 'Erhöhe Kanalnummer


Label_kanal_4: 'Bearbeitung von Kanal 4
cpi r17 , 4 'check Kanal und ...
brne LABEL_KANAL_5 '... "wenn nicht gleich" verzweige zum nächsten Kanal
sbic porta , 2 'Skip next instr. wenn PORTA.3 = 0 ist
rjmp label_4 'Springe zum LOW-setzen des Servosignals
sts {kanal} , r17 'Sichere Kanalnummer
lds r17 , {Servoausgabe_4} 'Hole aufbereiteten Pulslängenwert für Timer0
Out Tcnt0 , R17 'Setze Timer0 mit Pulslängenwert
sbi porta , 2 'Setze Servosignal HIGH
rjmp Ende_isr 'Springe zum Ende der ISR
Label_4:
cbi porta , 2 'Setze Servosignal nach LOW
inc r17 'Erhöhe Kanalnummer

'Pausenauffüllung

'Pausenanfang
inc r17
sts {kanal} , r17 'Sichere Kanalnummer
ldi r17 , &B00000101 'CS00 und CS02 für prescaler 1024 in Timer0 vorbereiten
Out Tccr0b , R17 'Timer0 läuft mit 128µs Auflösung
ldi r17 , 162 'Wert für ca 12ms bis zum nächsten OVF (162=256-94) bei prescaler 1024
Out Tcnt0 , R17
rjmp Ende_isr 'Springe zum Ende der ISR

'Pausenende
Label_kanal_5: 'Bearbeitung von Kanal 5 (Pausenende)
ldi r17 , &B00000011 'CS00 und CS01 für prescaler 64 in Timer0 vorbereiten
Out Tccr0b , R17 'Timer0 läuft wieder mit 8µs Auflösung
ldi r17 , 1 'Kanalnummer auf 1 setzen
sts {kanal} , r17 'Sichere Kanalnummer
Ende_isr:
pop r17 'Register vom Stack zurückholen
Out Sreg , R16 'Statusregister zurückspeichern
pop r16 'Register vom Stack zurückholen
$end Asm

Return

Searcher
03.01.2014, 20:59
Ich glaub das Ding geht, es geeeeeehhhhhhhhhhht \\:D/\\:D/\\:D/


Mist ...


.... läuft das Teil nun besser als erwartet.

... sieht wirklich so aus, als müßte man noch was deichseln ;):lol: :lol: :lol:


Mit herzlichen Glückwünschen
Searcher

R2D2 Bastler
04.01.2014, 14:46
So, hier nochmal der Code für ein einzelnes Servo auf dem kleineren Attiny45. Die Berechnung der Servogeschwindigkeit musste nochmals komplett überarbeitet werden, da beim Attiny45 nur 8-Bit Timer zur Verfügung stehen. Da sich der Kleine nicht mit mehreren RC Signalen rumschlagen muss, war es (fast) kein Problem, die entsprechende Berechnung umzugestalten.

mfg
Robert



'================================================= ==============================
'Single Baggerarmsteuerung V3
'
'RC Eingang 1 an Pin 7 (PB2, INT0)
'Poti 1a an Pin 2 (PB3, ADC3)
'Poti 1b an Pin 3 (PB4, ADC2)
'Servo 1 an Pin 6 (PB1) Baggerarmservo

'================================================= ==============================

$regfile = "attiny45.dat"
$crystal = 8000000 'FuseBit CKDIV8 deaktivieren

$hwstack = 40
$swstack = 40
$framesize = 50


'-------------------------------------------------------------------------------------------------
'Timer konfigurieren
'-------------------------------------------------------------------------------------------------

Tccr1 = &B00000111 'Timer1 für RC-Signal Einlesung wird gestartet (Prescale 64) (geht beim Attiny45 nicht mit "Config"!)

Config Timer0 = Timer , Prescale = 64 'Timer0 für Servoausgabe, Wert 125 entspricht 1ms, Wert 250 entspricht 2ms
On Timer0 Servoausgabe Nosave 'Register werden manuel in der ISR gesichert
Enable Timer0


'-------------------------------------------------------------------------------------------------
'Interrupt-Service-Routinen konfigurieren und freigeben
'-------------------------------------------------------------------------------------------------

Config Int0 = Change 'beim Flankenwechsel an PB2/INT0 (RC Eingang) Int0 auslösen und in die Subroutine springen
Enable Int0
On Int0 Rc_eingang_1 Nosave 'Register werden manuel in der ISR gesichert
Enable Interrupts


'-------------------------------------------------------------------------------------------------
'Poti-Eingänge konfigurieren
'-------------------------------------------------------------------------------------------------

Config Adc = Single , Prescaler = Auto
Start Adc



'-------------------------------------------------------------------------------------------------------------
'Konstanten definieren
'-------------------------------------------------------------------------------------------------------------
Const Jitterausgleich = 2 'Wert in µs um den sich gemessene Impulslaenge aendern darf/muss, bevor Servo gestellt wird

Const Rc_min = 122 'Minimaler RC-Wert für die Berechnungen (Werte zwischen 120 - 125 sinvoll)
Const Rc_max = 253 'Maximaler RC-Wert für die Berechnungen (Werte zwischen 250 - 255 sinvoll)


'-------------------------------------------------------------------------------------------------------------
'Variablen definieren
'-------------------------------------------------------------------------------------------------------------
Dim Rc_signal_1_start As Byte
Dim Rc_signal_1_stop As Byte
Dim Rc_signal_1_stop_flag As Byte
Dim Impulslaenge_1 As Byte
Dim Impulslaenge_1_alt As Byte
Dim Impulslaengenaenderung As Integer 'wird nur einmal gebraucht, da nur Hilfswariable



'Variablen für Servowegsbegrenzung
Dim Poti_1a As Word
Dim Poti_1b As Word

Dim Limit_1a As Byte
Dim Limit_1b As Byte

'Variablen für Berechnungen
Dim Berechnung_1 As Byte
Dim Berechnung_1a As Byte


Dim Servospeed As Byte
Dim Zeit_1 As Byte
Dim Zeit_2 As Word

'Variablen für Servoausgabe

Dim Kanal As Byte
Dim Servoausgabe_1 As Byte



'-------------------------------------------------------------------------------------------------
'Einigen Variablen Werte zuweisen
'-------------------------------------------------------------------------------------------------

Kanal = 1
Berechnung_1a = 187 'Servo erst mal auf Mittelstellung


'-------------------------------------------------------------------------------------------------------------
'Ein- und Ausgang festlegen
'-------------------------------------------------------------------------------------------------------------

Ddrb = &B00000010 'PB1 wird Ausgang, restlicher PortB bleibt Eingang


'================================================= =====
'Hauptprogramm
'================================================= =====

Do

'RC Impulslänge berechnen und Grenzwerte festlegen


If Rc_signal_1_stop_flag = 1 Then 'Bearbeitung nur, wenn ISR Pulsende gefunden hat
Disable Int0 'Mögliche Korruption durch ISR vorbeugen
Impulslaenge_1 = Rc_signal_1_stop - Rc_signal_1_start
Enable Int0
Rc_signal_1_stop_flag = 0
Impulslaengenaenderung = Impulslaenge_1_alt - Impulslaenge_1 'SW-Hysterese
If Abs(impulslaengenaenderung) >= Jitterausgleich Then
Impulslaenge_1_alt = Impulslaenge_1

Berechnung_1 = Impulslaenge_1

If Berechnung_1 > Rc_max Then Berechnung_1 = Rc_max
If Berechnung_1 < Rc_min Then Berechnung_1 = Rc_min
End If
End If





'Berechnung der Servogeschwindigkeit und Servostellung

If Berechnung_1 < 182 And Berechnung_1a > Rc_min Then

Servospeed = Berechnung_1 - Rc_min 'ergibt Werte zwischen 0 (schnell) und 61 (langsam)

Zeit_1 = 61 - Servospeed 'inventieren (0=langsam, 61=schnell)
Zeit_2 = Zeit_2 + Zeit_1

If Zeit_2 > 200 Then
Decr Berechnung_1a
Zeit_2 = 0
End If


End If

If Berechnung_1 > 193 And Berechnung_1a < Rc_max Then

Servospeed = Rc_max - Berechnung_1

Zeit_1 = 61 - Servospeed 'inventieren
Zeit_2 = Zeit_2 + Zeit_1

If Zeit_2 > 200 Then
Incr Berechnung_1a
Zeit_2 = 0
End If
End If


'Potis abfragen und Werte für Servolimits berechnen

Poti_1a = Getadc(3)
Poti_1a = Poti_1a / 25 '1024 / 25 = 41, ergibt ca 30% Wegbegrenzung
Limit_1a = 255 - Poti_1a

Poti_1b = Getadc(2)
Poti_1b = Poti_1b / 25
Limit_1b = 120 + Poti_1b


'RC Signal auf Limitwerte begrenzen

If Berechnung_1a > Limit_1a Then 'zu hohe Werte abfangen
Berechnung_1a = Limit_1a
End If
If Berechnung_1a < Limit_1b Then 'zu kleine Werte abfangen
Berechnung_1a = Limit_1b
End If


'Finale Berechnungen an Servoausgabe übergeben, Servoausgabe_x zum direkten Laden in TCNT0 vorbereiten (Cmd Load umgangen)

Servoausgabe_1 = 256 - Berechnung_1a 'Baggerarmservo

Loop




'================================================= =====
'ISR
'================================================= =====


Rc_eingang_1:
$asm
push r17 'Register auf Stack sichern
sbis pinb , 2 'Skip next Instr if PINBx = 1
rjmp Puls_ende1 'Springe Puls_ende
in r17 , tcnt1 'Timer1 Wert holen
sts {Rc_signal_1_start} , r17 'Speichere Timer1 nach Rc_signal_1_start

rjmp ende1 'Springe zum Ende
Puls_ende1:
in r17 , tcnt1 'Timer1 Wert holen
sts {Rc_signal_1_stop} , r17 'Speichere Timer1 nach Rc_signal_1_stop

ldi r17 , 1
sts {Rc_signal_1_stop_flag} , r17 'Setze Flag zur Bearbeitung von Impulslaenge in Hauptschleife
Ende1:
pop r17 'Register vom Stack zurückholen
$end Asm
Return


Servoausgabe:

$asm
push r16 'Register auf Stack sichern
in r16,sreg 'Statusregister holen und halten
push r17 'Register auf Stack sichern
lds r17 , {kanal} 'hole Kanalnummer

cpi r17 , 1 'check Kanal und ...
brne LABEL_KANAL_2 '... "wenn nicht gleich" verzweige zum nächsten Kanal
sbic portB , 1 'Skip next instr. wenn PORTA.0 = 0 ist
rjmp label_1 'Springe zum LOW-setzen des Servosignals
sts {kanal} , r17 'Sichere Kanalnummer
lds r17 , {Servoausgabe_1} 'Hole aufbereiteten Pulslängenwert für Timer0
Out Tcnt0 , R17 'Setze Timer0 mit Pulslängenwert
sbi portB , 1 'Setze Servosignal HIGH
rjmp Ende_isr 'Springe zum Ende der ISR
Label_1:
cbi portB , 1 'Setze Servosignal nach LOW
inc r17 'Erhöhe Kanalnummer

'Pausenauffüllung

'Pausenanfang

sts {kanal} , r17 'Sichere Kanalnummer
ldi r17 , &B00000101 'CS00 und CS02 für prescaler 1024 in Timer0 vorbereiten
Out Tccr0b , R17 'Timer0 läuft mit 128µs Auflösung
ldi r17 , 162 'Wert für ca 12ms bis zum nächsten OVF (162=256-94) bei prescaler 1024
Out Tcnt0 , R17
rjmp Ende_isr 'Springe zum Ende der ISR

'Pausenende
Label_kanal_2: 'Bearbeitung von Kanal 2 (Pausenende)
ldi r17 , &B00000011 'CS00 und CS01 für prescaler 64 in Timer0 vorbereiten
Out Tccr0b , R17 'Timer0 läuft wieder mit 8µs Auflösung
ldi r17 , 1 'Kanalnummer auf 1 setzen
sts {kanal} , r17 'Sichere Kanalnummer
Ende_isr:
pop r17 'Register vom Stack zurückholen
Out Sreg , R16 'Statusregister zurückspeichern
pop r16 'Register vom Stack zurückholen
$end Asm
Return

Tony DesWegeles
04.04.2015, 11:24
Hallo zusammen,

ich habe mit großer Aufmerksamkeit diesen Thread gelesen und wollte das Forum bitte mir bei meinem Vorhaben zu unterstützen diesen Code für einen Arduino Nano (ATME328p) umzuschreiben.

Ich habe schon mal begonnen, komme aber nicht wirklich weiter.
Im Anhang meine ersten Versuche den Code von R2D2 Bastler umzuschreiben.

Ich denke, das nicht soviele Änderungen notwendig wären?

Vielen Dank
VG
Tom

Unregistriert
04.04.2015, 16:02
na wirklich niemand Zeit sich den Code mal anzusehen?
komme wirklich nicht weiter...

Searcher
04.04.2015, 18:20
Ich habe mit großer Aufmerksamkeit diesen Thread gelesen und wollte das Forum bitte mir bei meinem Vorhaben zu unterstützen diesen Code für einen Arduino Nano (ATME328p) umzuschreiben.

Ich habe schon mal begonnen, komme aber nicht wirklich weiter.
Im Anhang meine ersten Versuche den Code von R2D2 Bastler umzuschreiben.


Läßt sich doch kompilieren. Wo sind denn die Probleme?

Gruß
Searcher

Unregistriert
04.04.2015, 18:53
Hallo Seacher,

danke für deine Antwort.
Ja Kompilieren lässt sich der Code, nur wenn ich diesen auf den Arduino spiele, funktioniert nur der Teil, wo die Servos auf die Mittelstellung fahren.
Die Steuerung reagiert nicht auf die Empfängereingangssignale.

Ich hab auf D4 ein Empfängersignal und auf D8 einen Servo. Die Potis habe ich nicht angeschlossen, kannes daran liegen?

Anbei nochmal der Code, hier habe ich nochmal die Eingangs und Ausgangsports angepasst.

VG
Tom

Tony DesWegeles
04.04.2015, 19:05
Nochmals der Code

Searcher
04.04.2015, 19:27
Hallo Tony,

Ich hab auf D4 ein Empfängersignal und auf D8 einen Servo. Die Potis habe ich nicht angeschlossen, kannes daran liegen?
ich kann nicht so genau überblicken welchen Einfluß die Potis haben. Ich habe damals praktisch nur die ASM Routinen gemacht und R2D2 hat das dann auf seinen Bagger angewendet. Wenn du keine Potis angeschlossen hast, würde ich statt des Getadc() Konstanten verwenden, die am Programmanfang zum Testen mit Werten zwischen 0 und 1023 belegt werden.

D4 bzw D8 sagt mir nichts. Das sind Arduino Bezeichnungen und ich vertraue Dir, das du die Servos schon richtig angeschlossen hast? Mittelstellung funktioniert ja.

Sehr wichtig sind jedoch die Timereinstellungen. Dein ATMega328p läuft mit 16MHz. Das Programm war aber für 8MHz ausgelegt. Auf den ersten Blick erscheint mir das nicht angepaßt, da die Timer mit den gleichen Prescalern wie für 8MHz laufen? Hab im Augenblick nicht die Ruhe, das im einzelnen jetzt durchzugehen. Da müßtest Du nochmal schauen.

Möchtest Du da auch einen Bagger o.ä. betreiben oder zu was brauchst Du das. Die Potis waren, glaub ich, nur zur Wegbegrenzung um keinen Knoten in den Baggerarm zu bekommen ... oder so :) Die Variablen sollten aber mit irgendwas initialisiert werden oder die Limitberechnung komplett rausgenommen werden.

Gruß
Searcher

Tony DesWegeles
04.04.2015, 20:41
Hallo Searcher,

danke für die Antwort. Ja ich würde gerne ebenfalls einen Bagger betreiben damit, da mir das ständige Halten der Steuerknüppel auf den Zeiger geht :-)
Okay das mit den Timern habe ich nicht bedacht, werde mich dahinter klemmen. Ich versuche mal deine gesamten Ratschläge durch.
D4 und D8 sind in der Tat die Belegung für den Arduino. Anbei ein Pin Mapping des Arduino.
http://christianto.tjahyadi.com/wp-content/uploads/2014/11/nano.jpg

Nach diesem Pin Mapping bin ich vorgegangen.

VG
Tom

- - - Aktualisiert - - -

Hallo Searcher,

also wirklich weiter bin ich nicht bekommen, also wenn du mal ne ruhige Minute hättest :-)
VG

Searcher
04.04.2015, 20:56
Hallo Tom,
das Programm auf 16MHz umzuschreiben ist nicht so einfach, weil der Timer0 nicht den passenden Prescaler bietet. Er sollte mit 8µs Auflösung laufen. Wird der µC mit 16MHz getaktet, läuft Timer0 mit 4µs und schafft dann als 8Bit Timer (256*4µs=1024µs) nicht den Servoimpulsbereich von 1000µs bis 2000µs. EDIT: ODER DOCH ??? Einfacher den µC mit 8MHz laufen zu lassen Der nächste Prescaler wäre 256. Dann würde der Timer bei 16MHz mit 16µs Auflösung laufen, was mir zu grob erscheint. Es wäre wermutlich eine leicht ruckelige Bewegung die Folge.

Man kann aber den µC fürs erste ohne HW Eingriff verlangsamen. Das sollte im Register CLkPR zu machen sein.
Gleich nach dem Header im Bascomprogramm nach Framesize und vor Timerkonfiguration diese beiden Zeilen einfügen:

Clkpr = &B1000_0000 'CLKPCE Bit hight zum clock devision bit change enable
Clkpr = &B1000_0001 'clock devision bit für division by 2

Dann sollte der ATMega328 nur noch mit 8MHz laufen und keine weiteren Eingriffe im Programm fürs Timing nötig sein. Falls möglich die Taktfrequenz mal überprüfen. Ich habe das selbst noch nicht in der Praxis durchgeführt. Laut Simulator sollte es aber hinhauen (Der zweite Registerzugriff muß innerhalb von 4 Taktzyklen nach Setzen des CLKPCE Bits abgeschlossen sein.)

Gruß
Searcher

Tony DesWegeles
04.04.2015, 22:04
Hallo Searcher,

also hab es so eingefügt wie du beschrieben hast. Das Servo zuckt jetzt nicht mehr so stark wie vorher.
Leider reagiert es kein bisschen auf irgendwelche Empfängersignale.

Ich habe das Servo auf einen Digital-Pin D8 (PB0, PCINT0) gelegt und das Empfänger Signal auf D4 (PD4, PCINT20); Stromversorgung des Empfängers (REX 5MPD) erhält dieser über das Board. Als Sender ist einer Graupner Sender MC-20 35Mhz im Einsatz.
Neben dem Arduino Nano habe ich noch ein Pin Board verbaut mit externe Stromversorgung (5V). Komisch ist das mit der externen Stromversorgung ein weiteres Servo am Empfänger nicht arbeitet, aber mit der USB Versorgung am Arduino schon.

Meine Vermutung ist, dass die Eingangssignal des Empfängers schon gar nicht passen?

Danke für deine Hilfe bisher.
VG

- - - Aktualisiert - - -

ach ja und die Servos fahren anscheinend nicht in die Mittelstellung, sonder in die Endstellung, besser gesagt würden gerne darüber hinaus fahren wollen.
Irgendwie strange die Sache.

Searcher
04.04.2015, 22:25
also hab es so eingefügt wie du beschrieben hast. Das Servo zuckt jetzt nicht mehr so stark wie vorher.
OK, scheint zu wirken.


Leider reagiert es kein bisschen auf irgendwelche Empfängersignale.
Ich habe das Servo auf einen Digital-Pin D8 (PB0, PCINT0) gelegt und das Empfänger Signal auf D4 (PD4, PCINT20);

Portpinanpassungen in Baggersteuerung_V23_ATmel_V3.bas scheinen OK


Stromversorgung des Empfängers (REX 5MPD) erhält dieser über das Board. Als Sender ist einer Graupner Sender MC-20 35Mhz im Einsatz.
Dazu kann ich nichts sagen. Das Programm erwartet an seinen Eingängen Impulse von ca 1ms bis ca 2ms. Müssen nicht nacheinander eintreffen, können auch parallel ankommen.


Neben dem Arduino Nano habe ich noch ein Pin Board verbaut mit externe Stromversorgung (5V). Komisch ist das mit der externen Stromversorgung ein weiteres Servo am Empfänger nicht arbeitet, aber mit der USB Versorgung am Arduino schon.

Na ja, Servos können viel Strom ziehen und damit übrige Elektronik, die an der gleichen Stromversorgung hängt durcheinander bringen. Wäre ein eigenes Thema. Zum Testen würd ich erstmal die Servos über ein eigenes Netzteil versorgen. GND Verbindung von der externen Stromversorgung zum Arduino aber nicht vergessen.


Meine Vermutung ist, dass die Eingangssignal des Empfängers schon gar nicht passen?
Da kann ich nu nix zu sagen. Kann man denn da die Servosignalleitung nicht direkt anschließen? Da mußt Du schon sicher sein, was da raus kommt.

Poste nochmal das aktuelle Programm und ich schau nochmal - aber heute wahrscheinlich nicht mehr. Du könntest erstmal alles auskommentieren bis auf Servosignal lesen und Servosignal ausgeben. Die ganzen Veränderungen, Limits usw. brauchst du erstmal zum Testen nicht. Es muß erst Lesen des/der Signal und Ausgeben der Signale funktionieren.

Gruß
Searcher

Tony DesWegeles
04.04.2015, 22:32
Hallo Searcher,

also anbei nochmal das Programm.
Ich werde deine Vorschläge nochmal testen. Die Empfängersignale kommen bestimmt zwischen 1ms und 2ms. Die Servos kommen ja damit klar.
Aber mit Bestimmtheit kann ich es nicht sagen.

Also schönen Abend noch.
VG

Searcher
05.04.2015, 08:43
Die Empfängersignale kommen bestimmt zwischen 1ms und 2ms. Die Servos kommen ja damit klar.
Dann ist es gut.


Aber mit Bestimmtheit kann ich es nicht sagen.
Oaahhh, genau wie ich.

Ich sehe gerade, daß du online bist. Das ich mir heute früh Deine letzte Version aus Post #53 herunter geladen habe, sagt nichts über darüber aus ob ich Dir früh auch eine Antwort geben kann. Es ist Ostern und ich muß schauen was sie Hasen machen. :lol:

Gruß
Searcher


Also schönen Abend noch.
VG[/QUOTE]

- - - Aktualisiert - - -



Portpinanpassungen in Baggersteuerung_V23_ATmel_V3.bas scheinen OK


Leider nicht :( Die Pinchangeinterrupts können so nicht richtig ablaufen.
Die Anweisung zB PCMSK1.PCINT20=1 sollte im Maskenregister PCMSK1 den PCINT20 freigeben. Es gibt den PCINT20 dort aber nicht beim ATMega328 (Datenblatt). Der ist im PCMSK2.

Danach versuchst Du PD4 mit PCINT21 freizugeben. Darf nicht sein, da PCINT21 im gleichen PCMSK2 Register liegt wie PCINT20 (PD4) . Die Pinchangeinterrupts werden für Gruppen von Portpins ausgelöst. PCINT0 für PB0 bis PB7 (im PCMSK0 den jeweiligen Pin freigeben und mit ENABLE PCINT0 den Interrupt freigeben) das Gleiche für PC8 bis PC14 im PCMSK1 und ENABLE PCINT1. Bischen verwirrend, da PCINT einmal für einen der 3 Pinchangeinterrupts und einmal für den Portpin verwendet wird.

Beim Vergeben der Eingänge mußt du also drauf achten, daß die zugehörigen PCINTS in verschiedenen PCMSK Registern liegen. PD2 (als INT0 kann erstmal bleiben so wie ich das im Augenblick sehe, da der INT Interrupt unabhängig von den PCINT Interrupt ist - wenn sie nicht gerade gleichzeitig verwendet werden sollen, was ja bei dir nicht der Fall ist)

So, das wars fürs Erste. Später im Programm werden noch Interruptflags manuell gelöscht. Dazu dann mehr, wenn Du eine neue Verteilung der Eingänge hast. Wie siehts bei der Stromversorgung aus?

Gruß
Searcher

Tony DesWegeles
05.04.2015, 15:09
Hallo Searcher,

also ich habe deine Änderungen angepasst, zumindest hoffe ich alle Änderungen richtig gemacht zu haben. Macht Sinn mit den unterschiedlichen Register. Wenn man sich den ursprünglichen Code ansieht, erkennt man das die Eingänge auch auf unterschiedlichen Registern hängen.
Das mit Stromversorgung habe ich auch gelöst.

Was meinst du mit den Manuellen Flags?

Searcher
05.04.2015, 18:50
Was meinst du mit den Manuellen Flags?

Da war ich einen Augenblick nicht ganz bei der Sache. Es gibt die Anweisung "Set Tifr1.ocf1b", die ein Interruptflag löscht. Ist aber hier nicht das Problem.

Im Augenblick finde ich keinen Fehler mehr bei den Portanpassungen im Programm :confused: Sieht für mich jetzt erstmal gut aus. Timing sollte mit dem Clockdevision by 2 auch klar gehen.

Im Header hast Du als Kommentar zB soetwas:
'RC Eingang 1 an Pin 3 (PC0, PCINT8 ) Stick 1 (Aileron)
'RC Eingang 2 an Pin 6 (PB4, PCINT4) Stick 2 (Elevator)
'RC Eingang 3 an Pin 5 (PD2, INT0) Betriebsmode

Mir ist die Nummerierung nicht klar. Nach Deinem Link mit der Zuordnungstabelle stimmen die Pinnummern im Header nicht mit den lila Nummern in der Arduinoskizze überein. Da könnte der HW-Anschluß vom Empfänger zum Arduinoeingang nicht mehr stimmen. Würd ich also nochmal überprüfen bzw Kommentae aktualisieren.

Eigentlich fällt mir nichts mehr ein. Wenn nichts geht, mit dem RC_MAX, RC_MIN, Potiwerten spielen, oder versuchen die ganzen Pulsbeeinflussungen im Programm rauszunehmen und schauen ob die Pulse dann durchgeschleift werden. Es gibt da auch noch eine Betriebswahl:
"If Berechnung_3 < 187 Then 'falls der Betriebswahlkanal unter Mittelstellung ist, ..."
hab ich auch noch nicht näher betrachtet.

Bin auf Weiteres von Dir gespannt ...

Gruß
Searcher

Tony DesWegeles
05.04.2015, 19:13
Hallo Searcher,

ja der Header hat nicht mehr gestimmt. Habe das angepasst. siehe nachfolgend:
'================================================= ==============================
'BAGGER Steuerung V23
'
'RC Eingang 1 an Pin A0 (PC0, PCINT8) Stick 1 (Aileron)
'RC Eingang 2 an Pin D12 (PB4, PCINT4) Stick 2 (Elevator)
'RC Eingang 3 an Pin D2 (PD2, INT0) Betriebsmode
'Poti 1a an Pin A5 (PC5, ADC5)
'Poti 1b an Pin A4 (PC4, ADC4)
'Poti 2a an Pin A3 (PC3, ADC3)
'Poti 2b an Pin A2 (PC2, ADC2)
'twe: Servo 1 an Pin D8 (PB0, PCINT0) Baggerarmservo
'twe: Servo 2 an Pin D9 (PB1, PCINT1) Baggerarmservo
'twe: Servo 3 an Pin D10 (PB2, PCINT2) Kettenantrieb
'twe: Servo 4 an Pin D11 (PB3, PCINT3) Kettenantrieb
'
'================================================= ==============================


Die Frage welche ich noch habe, oder wo ich mir unsicher bin ist bei den Ein- und Ausgängendefinition

'-------------------------------------------------------------------------------------------------------------
'Ein- und Ausgang festlegen
'-------------------------------------------------------------------------------------------------------------

Ddrb = &B00001111 'twe: PB0, PB1, PB2, PB3 werden Ausgänge, restlicher PortB bleibt Eingang
Ddrc = &B00000000 'twe: PortC bleibt Eingang
Ddrd = &B00000000 'twe: PortD bleibt Eingang


Stimmt das so? Musste das um die Ports D erweiteren.

Anbei nochmal der ganze Code.

Searcher
05.04.2015, 19:22
Die Frage welche ich noch habe, oder wo ich mir unsicher bin ist bei den Ein- und Ausgängendefinition

Ddrb = &B00001111 'twe: PB0, PB1, PB2, PB3 werden Ausgänge, restlicher PortB bleibt Eingang
Ddrc = &B00000000 'twe: PortC bleibt Eingang
Ddrd = &B00000000 'twe: PortD bleibt Eingang

Stimmt das so? Musste das um die Ports D erweiteren.


Ja, stimmt so. Eine 1 im entsprechenden Bit von entsprechendem DDR Register :) bedeutet Pin als Ausgang konfiguriert.
Der ATMega belegt nach Reset alle Register mit Defaultwerten. Beim DDRx sind das Nullen. Man müßte also Nullen nicht Schreiben, wenn man Eingänge haben möchte. "Ddrd = &B00000000" wäre unnötig, dient aber auf jeden Fall der Klarheit eines Programmes.

Gruß
Searcher

PS: Ich lad mir das letzte Programm nochmal und versuche daraus, wie von mir obenvorgeschlagen, ein Durchschleifprogramm zu machen. Wird heute und morgen aber sicher nichts mehr.

Unregistriert
05.04.2015, 19:31
Hallo Searcher,

danke für deinen Vorschlag. Ich werde jetzt alles nochmal komplett neu anschließen und mit dem print - Befehl versuchen zu ergründen, was vom Empfänger eigentlich ankommt.
Denn ich denke da ist der Fehler begraben.
An sich ist das Programm sehr nachvollziehbar, zumindest bis ich zu den asm Routinen komme. :-) Sind ja wie ich verstanden habevon Dir selbst geschrieben, oder?

VG
Tom

Searcher
05.04.2015, 19:50
danke für deinen Vorschlag. Ich werde jetzt alles nochmal komplett neu anschließen und mit dem print - Befehl versuchen zu ergründen, was vom Empfänger eigentlich ankommt.
Denn ich denke da ist der Fehler begraben.

Ich habe in meinen Anfangszeiten, als ich noch kein Oszilloskop besaß, mit einem Soundkartenoszilloskop solche Signale beobachten können. Hab leider keine Unterlagen mehr dazu, läßt sich aber im INET finden. Signal an Soundkarte-Soundin, Freeware Oszilloskop gestartet und mit viel Phantasie erahnt was vorgeht. Manche Audioaufnahmeprogramme können Waveformen auch sichtbar machen :confused:


An sich ist das Programm sehr nachvollziehbar, zumindest bis ich zu den asm Routinen komme. :-) Sind ja wie ich verstanden habevon Dir selbst geschrieben, oder?

Ja, die ASM Teile sind von mir.
Zum Messen des Impulses am Eingang wird bei einem (PCINT) in die entsprechende Interruptroutine gesprungen.
Dort wird mit "sbis pinC , 0" festgestellt ob der PCINT Flankenwechsel nach high oder low ging.
Es wird entsprechend verzweigt und der Timer1 ausgelesen und gesichert zur Nachbearbeitung im Hauptprogramm.
War der Pin low, ist der Impuls zu Ende und es wird noch ein Flag gesetzt um dem Hauptprogramm mitzuteilen, das jetz die Impulslänge berechnet werden kann.

Servoausgabe:
Die Servoimpulse für alle Servos werden nacheinander ausgegeben. Der Interrupt ...

Fotsetzung folgt später, Sorry.

Gruß
Searcher

Unregistriert
05.04.2015, 20:16
Hallo Searcher,

danke für die Infos über asm. Du kein Ding is heute eh schon spät und vorallem Sonntag :-)
Interessant ist, dass ich den RC_Signal 1 eigentlich sehr gute Werte zwischen 2201 und 3812 bekomme.
RC_Signal 2 kommt ziemlicher Quark an.

1647 Impulslaenge_2
1649 Impulslaenge_2
1649 Impulslaenge_2
1846 Impulslaenge_2
2924 Impulslaenge_2
6182 Impulslaenge_2
7473 Impulslaenge_2
8853 Impulslaenge_2
10579 Impulslaenge_2
38714 Impulslaenge_2
40492 Impulslaenge_2
16675 Impulslaenge_2
1035 Impulslaenge_2
1649 Impulslaenge_2
1649 Impulslaenge_2
2256 Impulslaenge_2
2972 Impulslaenge_2
3574 Impulslaenge_2
3987 Impulslaenge_2

irgendwie stimmt da etwas nicht.

VG

Searcher
05.04.2015, 20:33
Fortsetzung:

Servoausgabe:
Die Servoimpulse für alle Servos werden nacheinander ausgegeben und der Timer0 mit seinem Overflow Interrupt
zum Abmessen der Zeiten verwendet.

Die Servoimpulslänge für jeden Kanal wird vom Hauptprogramm in den Variablen "Servoausgabe_1" bis "Servoausgabe_4" als Vorladewert (Servoausgabe_1 = 256 - Berechnung_1a) abgelegt.

Tritt die ISR "Servoausgabe" zum erstenmal auf, wird Kanal 1 bearbeitet.
Die ISR wird durch den Timer0 Overflow Interrupt aufgerufen.
Der Vorladewert für den ersten Kanal wird geholt und der Timer0 damit geladen und der Portpin für Servo auf high gesetzt - Pulsbeginn. (Durch Abfrage des Zustandes des Portpins wird auf Puls beginnen oder Puls beendene entschieden)
Nach Erreichen des nächsten Overflowinterrupts, die Zeit wird ja durch den Vorladewert bestimmt, wird das Servosignal nach low gesetzt - Pulsende.
Dann wird die Kanalnummer noch erhöht und gleich der nächste Kanal bearbeitet mit Vorladewert holen und Pin für Pulsbegin auf high. Ablauf wie für Kanal 1.
So werden 4 Ausgabekanäle ausgegeben und dann eine Ausgabepause gemacht um die ca. 50Hz für Servopulsfrequenz zu erreichen.
Die "Pausenanfang" und "Pausenende" Instruktionen am Ende der ISR machen die ca. 20ms Folgen für die Servopulse. Wurden alle 4 Kanäle abgearbeitet/ausgegeben (ca 2ms * 4 = 8ms), wird eine Pause von 12ms auch durch temporäre Veränderung des Prescalers gemacht, damit weniger Interrupts auftreten und dadurch die Messung der Eingänge nicht so sehr gestört werden kann. Die 50Hz Ausgabe zu den einzelnen Servos ist etwas abhängig von den Pulslängen. Da die Genauigkeit von 50Hz aber nicht so kritisch sind, haben wir die damals vernachlässigt.

Gruß
Searcher

PS
irgendwie stimmt da etwas nicht. :)https://www.roboternetz.de/phpBB2/images/smiles/icon_smile.gifhttps://www.roboternetz.de/phpBB2/images/smiles/icon_smile.gif Sonst würde etwas nicht stimmen. https://www.roboternetz.de/phpBB2/images/smiles/icon_smile.gif
Viel Erfolg beim testen. Bis dann.

Tony DesWegeles
06.04.2015, 15:25
Hallo Searcher,

also nach langem hin und her, habe ich jetzt zumindest erreicht, dass der Servo verfährt und auf die Eingangssignale des Empfängers hört.
Ich habe die Codezeile: "Shift Berechnung_1 , Right , 3" und "Shift Berechung_1, Right, 4" umgeändert. Hierdurch wird das Eingangssignal des Empfängers von Neutral 2950 auf min 137 und max 236 umgerechnet; damit funktioniert es :-)
Das Servo reagiert jetzt eigentlich richtig, nur verfährt es in einem sehr kleinen Bereich, ziwschen den Werten "Signal Servoausgabe 2" (= MinPosition) und "Signalausgabe 60" (= MaxPosition Servo, sogar der harte Endanschlag des Servos). Den Prescaler des Timers0 auf 8 zu reduzieren, brachte nur einen Geschwindigkeitszuwachs aber keine Veränderung der Neutralposition und des Arbeitsbereichs des Servo.

Interessant ist auch, dass das Eingangssignal des Empfängers immer kurzzeitig auf 24000 springt, wodurch die Berechnungen immer um 1 springen und damit das Servo langsam in die Minposition (Servoausgabe_1= 2) verfährt und dort verharrt. Ist aber wohl nur ein Filterproblem, welches sich sicherlich einfach lösen lässt.

Ich sehen im Moment keine Lösung für den geringen Verfahrbereich. Kann das am Timing für den Servo (asm Schnittstelle) liegen? Prescaler und Pausenzeiten vielleicht?
Wenn ich an der Fernsteuerung den Arbeitsbereich erweitere, oder den Nullpunkt verschiebe, erreiche ich nichts. Da die Variable Servoausgabe_1 ja begrenzt ist.

Servoausgabe = 256 - Berechnung_1a

Vielleicht hast du noch eine Idee. Dacht mir vielleicht dass es schon an der Demo Version von Bascom liegt, diese gibt ja maximal 4KB große Dateien frei, die *.bin Datei hat aber nur 3,44KB, sollte also i.O. sein.

VG
Tom

Anbei nochmal der gesamte Code

Searcher
07.04.2015, 15:42
Ich habe die Codezeile: "Shift Berechnung_1 , Right , 3" und "Shift Berechung_1, Right, 4" umgeändert. Hierdurch wird das Eingangssignal des Empfängers von Neutral 2950 auf min 137 und max 236 umgerechnet; damit funktioniert es :-)

Hab ich gesehen und darf aber nicht sein :) Die Division hab ich wieder rückgängig gemacht. Siehe weiter unten - Es gab Problem mit dem Takt von 16MHz.


Interessant ist auch, dass das Eingangssignal des Empfängers immer kurzzeitig auf 24000 springt, wodurch die Berechnungen immer um 1 springen und damit das Servo langsam in die Minposition (Servoausgabe_1= 2) verfährt und dort verharrt. Ist aber wohl nur ein Filterproblem, welches sich sicherlich einfach lösen lässt.
Man müßte genau wissen, was der Empfänger wirklich schickt. Servosignale sollen nur zwischen 1000µs und 2000µs lang sein. Ich gehe immer von 500µs bis 2500µs aus.


Ich sehen im Moment keine Lösung für den geringen Verfahrbereich. Kann das am Timing für den Servo (asm Schnittstelle) liegen? Prescaler und Pausenzeiten vielleicht?
Wenn ich an der Fernsteuerung den Arbeitsbereich erweitere, oder den Nullpunkt verschiebe, erreiche ich nichts. Da die Variable Servoausgabe_1 ja begrenzt ist.
Servoausgabe = 256 - Berechnung_1a
Vielleicht hast du noch eine Idee.
Den Takt in Ordnung bringen und schauen ob das Durchschleifen paßt. Siehe unten.

Dacht mir vielleicht dass es schon an der Demo Version von Bascom liegt, diese gibt ja maximal 4KB große Dateien frei, die *.bin Datei hat aber nur 3,44KB, sollte also i.O. sein.
Ich nutze auch die Demoversion 2.0.7.5. Ist in Ordung und kann die Programmgröße noch händeln.

Hier fängt "unten" an: ;)
Ich habe das Programm zum Durchschleifen von 3 Eingängen auf 3 Ausgänge verändert und mit einem Mega88A mal getestet.
Das veränderte Programm "BaggerDurchschleif_V23_ATmel_V3_mega328.bas", lauffähig auf 16MHz ATMega328p ist im Anhang.

Soweit ich noch alles zusammenbekomme gibt es folgende Veränderungen:

1. Änderung bei der Clockdevision. Zweites CMD muß "Clkpr = &B0000_0001" sein, sonst wird das nix. (Bisher gab es dadurch bei 16MHz schon ein Problem weil es nicht gewirkt hat) $crystal ist noch auf 16000000. Dadurch, daß nach dem Heruntersetzen des Taktes der µC ja nur mit 8MHz läuft, wäre es vielleicht richtiger auch den $crystal Wert auf 8000000 zu setzten. Sollte aber im gegenwärtigen Programm keine Auswirkung haben, da keine WAIT Anweisungen oder so vorhanden sind. Es gibt allerdings deine PRINT Anweisung. Wenn der µC dann tatsächlich mit 8MHz läuft und $crystal noch auf 16000000 steht, geht die Ausgabe über UART dann wahrscheinlich nicht mehr.

2. Die RC_Min und RC_max Werte angepaßt um Ausgabepulsbereich zu erweitern.

3. Auskommentierung von allen Pulsveränderungen bis auf den Jitterausgleich. Das Durchschleifen getestet mit 3 Eingängen und 3 Ausgängen so gut es ich es mit einem Simulator durch einen Attiny44 auf die Schnelle hinbekommen habe. Läuft!

Die Zeilen mit den Auskommentierungszeichen sind an den hash Zeichen (##########..) erkennbar. Die Zeilen einfach wieder rausnehmen.

4. Anmerkung: Gemessen wird im Programm mit einer Auflösung von 1µs. Die Ausgabepulse werden mit einer Auflösung von 8µs ausgegeben. Divisionen durch 16 auf 8 rückgängig gemacht. (Jitterausgleich - funktioniert/wirkt laut R2D2, ich konnte das noch nicht testen - Jitter ist eventuell Ursache nur für Servozittern - zuletzt dran drehen, wenn sonst keine Ursache auszumachen ist)

5. An Zeilen mit "srch" im Kommentar hab ich auch noch gewerkelt.

6. Programm enthält noch auskommentierte Zeilen für meinen ATMega88A.

7. Die ASM Teile und Portanpassungen von Dir wurden nicht verändert.

So, und nun darfst Du Dich wieder auslassen.

Gruß
Searcher

Unregistriert
10.04.2015, 17:39
Hallo Searcher,

so endlich Wochenende und wieder mehr Zeit für das Projekt. :-)
Also ich habe deinen Code getestet und wirklich vielen Dank funktioniert sehr sehr gut.

Ich habe dann noch die andere Teile wieder aktiviert |) und die gleichen Phänomene waren wieder da.

If Tifr1.ocf1a = 1 And Berechnung_1 < 182 Then
Set Tifr1.ocf1a 'Interruptflag löschen
Servospeed = Berechnung_1 - Rc_min
Servospeed = Servospeed * 500 'eine Einheit in Servospeed macht 1µs Wartezeit
If Servospeed < 1000 Then Servospeed = 1000 'Sicherheitszeit 1000µs damit bei kleinen Werten kein kompletter timer rundlauf bis zum nächsten
'comparematch abgewartet werden muß. Länger sollte auch eine Runde in der Hauptschleife nicht dauern.
Ocr1a = Tcnt1 + Servospeed 'Setzen von OCR1A damit nach Servospeed ein Compare Match auftritt
Decr Berechnung_1a
End If

Das Servo fährt in die Mittelstellung, wenn ich nun den Hebel auslenke fährt das Servo in die richtige Richtung aber die Geschwindigkeit bleibt in der jeder Situation die Selbe.
Wenn ich die Fernsteuerung loslasse, fährt der Servo langsam in eine Endposition, da wieder das Eingangssignal kurzzeitig auf über 40000 geht.
Meine Vermutung ist jetzt, dass der Teil Tifr1.ocf1a dafür verantwortlich sein muss, da hier anscheinend der Interrupt gelöscht wird, was vermutlich das Phönomen auslöst.
Ganz habe ich diesen Codeteil auch nicht verstanden, den der Befehl "Decr" sagt nicht über die Geschwindigkeit aus und ist doch vom Takt der Berechnung abhängig.
Wenn jetzt von 187 rückwärts gezählt wird, dann doch genau in dem Takt wie die Berechnung läuft, oder täusche ich mich da jetzt gewaltig?

Vg
Tom

Searcher
10.04.2015, 19:41
Hallo Tom,
ehrlich gesagt bereitet es mir Mühe das Verhalten bei Dir theoretisch nachzuvollziehen.

1. Die folgenden 4 Zeilen mußten auch noch raus. Hast Du aber vermutlich gemacht.


Servoausgabe_1 = 256 - Berechnung_1 'srch Baggerarmservo 1,
Servoausgabe_2 = 256 - Berechnung_2 'srch Baggerarmservo 2
Servoausgabe_3 = 256 - Berechnung_3 'srch Fahrservo oder ESC 1
'Servoausgabe_4 = 256 - Berechnung_4 'srch Fahrservo oder ESC 2





Ganz habe ich diesen Codeteil auch nicht verstanden, den der Befehl "Decr" sagt nicht über die Geschwindigkeit aus und ist doch vom Takt der Berechnung abhängig.
Wenn jetzt von 187 rückwärts gezählt wird, dann doch genau in dem Takt wie die Berechnung läuft, oder täusche ich mich da jetzt gewaltig?


2. Ich versuche das mal zu erklären. Wie gesagt, ich habe keine Testumgebung mit Servos dazu. Getestet hat das R2D2 Bastler.

"If Berechnung_3 < 187 Then 'falls der Betriebswahlkanal unter Mittelstellung ist, ist Fahrbetrieb"

Welcher Betrieb ist bei Dir eingestellt? Im Baggerbetrieb ...



'--------------------------------------------------
'BAGGERETRIEB
'--------------------------------------------------
If Tifr1.ocf1a = 1 And Berechnung_1 < 182 Then
Set Tifr1.ocf1a 'Interruptflag löschen
Servospeed = Berechnung_1 - Rc_min
Servospeed = Servospeed * 500 'eine Einheit in Servospeed macht 1µs Wartezeit
If Servospeed < 1000 Then Servospeed = 1000 'Sicherheitszeit 1000µs damit bei kleinen Werten kein kompletter timer rundlauf bis zum nächsten
'comparematch abgewartet werden muß. Länger sollte auch eine Runde in der Hauptschleife nicht dauern.
Ocr1b = Tcnt1 + Servospeed 'Setzen von OCR1A damit nach Servospeed ein Compare Match auftritt
Decr Berechnung_1a
End If

Im Else Zweig (Baggerbetrieb) kommt erst das Interruptflaglöschen zum Tragen.

Der Timer1 läuft mit einer Auflösung von 1µs (1Mhz Takt)
In Berechnung_1 steht die Sollpulslänge, eingelesen vom Input, von dem 1. Servo in 8µs Einheiten.
Steht da zB 150 drin, bedeutet das, daß Servopulslänge für Servo1 soll 150*8µs = 1200µs werden
Servo1 soll also nach Position 1200 gebracht werden.
Servo1 steht aber auf Position, die in Berechnung_1a drinsteht.
Es wird Berechnung_1a decrementiert, da Berechnung_1 < 182 ist.
Würde Berechnung_1 zwischen 182 und 193 liegen (Fernsteuer vermutlich in neutral? ), würde nichts passieren.
Die Abfrage wird für Servo1 immer dann ausgeführt, wenn der Comparematch1A auftritt.
Die Abfrage wird für Servo2 immer dann ausgeführt, wenn der Comparematch1B auftritt.
Servospeed = Berechnung_1 - Rc_min (Servospeed = 150 - 122 = 28 )
Servospeed = Servospeed * 500 (Servospeed = 28 * 500 = 14000)
Ocr1b = Tcnt1 + Servospeed (Ocr1b = Tcnt1 + 14000) Hier werden 14000µs bis zum nächsten Auftreten des Comparematches eingstellt.
(Fehler? müßte Ocr1a = Tcnt1 + Servospeed sein, es sind also 14000µs (14ms), bis der Servo wieder ein 8µs Schritt durch decr Berechnung_1a macht)

Da keine Interruptserviceroutine für die Compare-Matches benutzt wird, die die Interruptflags automatisch löschen würde, müssen die Flags mit "Set Tifr1.ocf1a" bzw Set "Tifr1.ocf1b" "von Hand" gelöscht werden.

Die zusätzliche Verwendung des Timer1 als Zeitgeber durch Abfragen der Comparematchinterruptflags wurden gemacht um die Hauptschleife möglichst wenig aufzuhalten. Kann mich noch erinnern, daß ein Durchlauf sehr sehr weit unter den 1000µs war. Dein "Print" könnte da jetzt was ändern!

Der Timer1 wird hier also nur als Zeitgeber für langsames Verfahren der Servos benutzt, da diese sonst so schnell es ihnen möglich ist ihre Sollposition anfahren würden. Sieht beim Baggerarm dann wohl nicht so gut aus. Ich weiß leider auch nicht - sieht mir nicht so aus - daß da eine Rampe (langsames Anfahren und schneller werden bzw umgekehrt geht) - weiß auch nicht wie die Fernsteuerung geht ... ???

Im o.g. Teil müßtest Du mal OCR1B gegen OCR1A tauschen - scheint mir ein Fehler zu sein. k.A. ob das Absicht ist.

Gruß
Searcher

Tony DesWegeles
11.04.2015, 09:01
Hallo Searcher,

also gestern habe ich dann noch die Lösung gefunden und hab dann heute deine Vermutung gelesen.
Jup es war der "Print" Befehl der alles aufgehalten hat. :-)

Danke trotzdem für die Erklärung der Funktionen Tifr1.ocf1b. Ich war dann heute gleich so beflügelt vom Erfolg, dass ich das Ganze noch um einen weiteren Servo erweitert habe.
Ich habe einfach den letzten PIN-Change Interrupt ausgewählt PCINT2 und damit funktioniert es auch ganz gut.
Nur leider ist es so, dass der CompareMatch im Timer1 (hoffe liege da richtig) nur Compare Match A und B kann und C nicht möglich ist.
Daher meine Frage ob ich jetzt, damit alle drei Servos gleichzeitig verfahren werden können ich einen dritten Timer (Timer 2) aufmachen muss??

Anbei nochmal der Code erweitert um den dritten Servo. Ich habe den Compare Match einfach kopiert, was ich wohl nicht machen darf?
Den dritten Servo habe ich so gelegt, das PCMSK0, PCMSK1 und PCMSK2 auf unterschiedlichen Pins liegen und sich nicht stören sollten.
Der Servo 4 ist über den Befehl Berechnung_4 = 255 ausgehebelt, somit kommt der nicht in die Quere.

Wie gesagt es funktionieren alle drei sehr gut, 1 und 2 lassen sich gleichzeitig bewegen, also ruckelfrei, nur 3 fährt nur wenn 1 oder 2 stehen.

If Tifr1.ocf1a = 1 And Berechnung_3 < 182 Then
Set Tifr1.ocf1a
Servospeed = Berechnung_3 - Rc_min
Servospeed = Servospeed * 500
If Servospeed < 1000 Then Servospeed = 1000 'Sicherheitszeit
Ocr1a = Tcnt1 + Servospeed
Decr Berechnung_3a
End If

If Tifr1.ocf1a = 1 And Berechnung_3 > 193 Then
Set Tifr1.ocf1a
Servospeed = Rc_max - Berechnung_3
Servospeed = Servospeed * 500
If Servospeed < 1000 Then Servospeed = 1000 'Sicherheitszeit
Ocr1a = Tcnt1 + Servospeed
Incr Berechnung_3a
End If

Meine Überlegung war jetzt einen weiteren Timer 2 aufzumachen um hier die beiden weiteren Compare Matches A und B verwenden zu können?
Würde das so funktionieren, oder gibt es hier einen eleganteren Weg?

VG
Tom

Searcher
11.04.2015, 09:44
Ich war dann heute gleich so beflügelt vom Erfolg, dass ich das Ganze noch um einen weiteren Servo erweitert habe. Ich habe einfach den letzten PIN-Change Interrupt ausgewählt PCINT2 und damit funktioniert es auch ganz gut.
Hört sich ja prima an, gratuliere. (Ab jetzt dran denken, da kann was nicht stimmen :) :lol: )


Nur leider ist es so, dass der CompareMatch im Timer1 (hoffe liege da richtig) nur Compare Match A und B kann und C nicht möglich ist.
Was sagt das Datenblatt? Comparematch C gibt es hier nicht! Timer1 ist jetzt auch reichlich ausgenutzt und man muß anfangen aufzupassen, damit er nicht überlastet wird und unrund läuft :)



Daher meine Frage ob ich jetzt, damit alle drei Servos gleichzeitig verfahren werden können ich einen dritten Timer (Timer 2) aufmachen muss??
Ja, gute Idee. Vorher war das ja nicht möglich, da der ATTiny84, für den das Programm ja ursprünglich war, nur zwei Timer hat.


Anbei nochmal der Code erweitert um den dritten Servo. Ich habe den Compare Match einfach kopiert, was ich wohl nicht machen darf?
Na ja, die Comparematches sind den Servos zugeordnet und müssen für jedes Servo unabhängig sein. Die Abhängigkeit hast du ja bemerkt, indem Servo3 nur läuft wenn die anderen stehen. Dann wird das Interruptflag nicht gelöscht und steht noch für Servo3, das damit dann arbeiten kann.



Den dritten Servo habe ich so gelegt, das PCMSK0, PCMSK1 und PCMSK2 auf unterschiedlichen Pins liegen und sich nicht stören sollten.
Das sollte OK sein. Werd später nochmal in Dein Programm schauen.


Wie gesagt es funktionieren alle drei sehr gut, 1 und 2 lassen sich gleichzeitig bewegen, also ruckelfrei, nur 3 fährt nur wenn 1 oder 2 stehen.

If Tifr1.ocf1a = 1 And Berechnung_3 < 182 Then
Set Tifr1.ocf1a
Servospeed = Berechnung_3 - Rc_min
Servospeed = Servospeed * 500
If Servospeed < 1000 Then Servospeed = 1000 'Sicherheitszeit
Ocr1a = Tcnt1 + Servospeed
Decr Berechnung_3a
End If

If Tifr1.ocf1a = 1 And Berechnung_3 > 193 Then
Set Tifr1.ocf1a
Servospeed = Rc_max - Berechnung_3
Servospeed = Servospeed * 500
If Servospeed < 1000 Then Servospeed = 1000 'Sicherheitszeit
Ocr1a = Tcnt1 + Servospeed
Incr Berechnung_3a
End If

Meine Überlegung war jetzt einen weiteren Timer 2 aufzumachen um hier die beiden weiteren Compare Matches A und B verwenden zu können?
Würde das so funktionieren, oder gibt es hier einen eleganteren Weg?


Ja, ich denke, das es mit Timer2 funktionieren sollte. Weiß jetzt nicht, ob es was besseres geben würde. Eigentlich gibt es ja immer zig Möglichkeite. Timer2 läßt sich aber am einfachsten in das bestehende Programm einfügen. Ein CONFIG TIMER2 Kommando. Ausrechnen wie die Zeiten nach dem set tifr2.ocf2a sein müssen und analog zu den bestehenden Codezeilen anpassen.

Gruß
Searcher

Tony DesWegeles
11.04.2015, 12:45
Hallo Searcher,

ich hab jetzt den Timer 2 eingefügt:
onfig Timer1 = Timer , Prescale = 8 'Timer für Einlesen RC Signale
Start Timer1

Config Timer2 = Timer , Prescale = 8 'Timer für Einlesen RC Signale
Start Timer2

Config Timer0 = Timer , Prescale = 64 'Timer für Servoausgabe, Wert 125 entspricht 1ms, Wert 250 entspricht 2ms
Enable Timer0
On Timer0 Servoausgabe Nosave

und den unteren Teil habe ich folgendermaßen geändert

If Tifr2.ocf2a = 1 And Berechnung_3 < 182 Then
Set Tifr2.ocf2a
Servospeed = Berechnung_3 - Rc_min
Servospeed = Servospeed * 500
If Servospeed < 1000 Then Servospeed = 1000 'Sicherheitszeit
Ocr2a = Tcnt2 + Servospeed
Decr Berechnung_3a
End If

If Tifr2.ocf2a = 1 And Berechnung_3 > 193 Then
Set Tifr2.ocf2a
Servospeed = Rc_max - Berechnung_3
Servospeed = Servospeed * 500
If Servospeed < 1000 Then Servospeed = 1000 'Sicherheitszeit
Ocr2a = Tcnt2 + Servospeed
Incr Berechnung_3a
End If

Das Ergebnis dieser Änderung ist, dass das dritte Servo deutlich zu schnell fährt. Aber ich kann alle drei Servos gleichzeitig betätigen.
Vermutlich fehlt es jetzt nur am Timing. Ich habe in der Doku versucht zu verstehen, wie der Timer 2 funktioniert. Den Prescaler habe ich gleich gelassen wie bei Timer1.

Anbei noch mal der geänderte Code.

VG
Thomas

Searcher
11.04.2015, 14:52
Config Timer2 = Timer , Prescale = 8 'Timer für Einlesen RC Signale
Start Timer2

If Tifr2.ocf2a = 1 And Berechnung_3 < 182 Then
Set Tifr2.ocf2a
Servospeed = Berechnung_3 - Rc_min
Servospeed = Servospeed * 500
If Servospeed < 1000 Then Servospeed = 1000 'Sicherheitszeit
Ocr2a = Tcnt2 + Servospeed
Decr Berechnung_3a
End If

Das Ergebnis dieser Änderung ist, dass das dritte Servo deutlich zu schnell fährt. Aber ich kann alle drei Servos gleichzeitig betätigen.


Es geht schon mal was\\:D/. Aber ganz so einfach ist es doch nicht. Der Timer2 ist ein 8-Bit Timer und das OCR2A Register hat auch nur 8 Bit. Wenn man da 14000, wie im obigen Beispiel bei Timer1, aufaddiert, läuft der nicht nur einmal über, was er machen darf/soll, sondern gleich mehrfach und ist so nicht mehr einfach handhabbar.

Man muß also drauf achten, daß der Wert Servospeed für Servo3 einen Wert von 255 nicht übersteigt bzw die Zeit nicht kürzer als die Sicherheitszeit von 1000µs = 1ms wird.

Wenn man den Timer2 mit Prescaler 1024 betreibt, braucht er für einen kompletten Durchlauf:
8000000Hz/1024 = 7812,5Hz 1/7812,5Hz = 0,000128s 8Bit Timer = 256 Werte -> 0,000128s * 256 = 0,032768s
Die längste Zeit, die zwischen zwei 8µs Bewegungen (Decr Berechnung_3a) von Servo3 liegt, können ungefähr 0,032768s sein.
Die kürzeste Zeit soll eben die Sicherheitszeit sein, die dann 1000µs / 128µs = 8 Einheiten lang ist:
If Servospeed < 8 Then Servospeed = 8 'Sicherheitszeit

Berechnung_3 liegt ja irgendwo zwischen 182 und RC_Min (120)
Servospeed = Berechnung_3 - Rc_min (zwischen 0 und 62)
Damit man jetzt in etwa auf gleiche Verzögerung wie bei Timer1 kommt, wird mit 4 multipliziert.
Servospeed = Servospeed * 4 (zwischen 0 und 248 (0 * 4 bzw 62 * 4))

Könnte dann so aussehen ...


Config Timer2 = Timer , Prescale = 1024

If Tifr2.ocf2a = 1 And Berechnung_3 < 182 Then
Set Tifr2.ocf2a
Servospeed = Berechnung_3 - Rc_min
Servospeed = Servospeed * 4
If Servospeed < 8 Then Servospeed = 8 'Sicherheitszeit
Ocr2a = Tcnt2 + Servospeed
Decr Berechnung_3a
End If

'Für INCR Berechnung_3a analog ...


Die Steuerung bzw Langsamfahrt für Servo3 wird aber viel rauher sein ??? Bin gespannt ...

Start Timer ist nicht notwendig. Spart Code und Timer werden durch Angabe von Prescaler im CONFIG TIMER Kommando gestartet.

Keine Gewähr, ich könnte mich sicherlich irgendwo verrechnet haben :( Die Richtung sollte aber stimmen. Die Verzögerung der Servofahrt hat R2D2 Bastler, soweit ich mich erinnern kann, ausprobiert.

Viel Erfolg beim Endspurt
Gruß
Searcher

Unregistriert
12.04.2015, 11:11
Hallo Searcher,

also ich habe den Code so ausprobiert und wie Du schon vermutet hast reagiert der 3.Servo etwas rauer. Ich habe auch festgestellt, dass es aber sehr abhängig vom Servotyp ist.
Im Moment verwende ich nur analoge Servos, ich werde morgen noch einen digitalen Servo ausprobieren.

Was mir aufgefallen ist, dass die Servogeschwindigkeit sehr linear ist; Ganz habe ich es auch noch nicht verstanden, wie ich hier eine eher exponentiellere Kennlinie fahren könnte.
Damit meine ich bei kleineren Ausschlägen ein schnelleres Ansprechverhalten.
Dann hätte ich noch eine Verständnisfrage, ganz ist mir noch nicht klar warum die Rechenzeit 8µs beträgt. Wodurch wird dies bestimmt, durch den Timer 1?
Das Problem mit der externen Stromversorgung lag übrigens an einem zu schwachen Netzteil, welches zwar mit 5Volt bezeichnet ist, 8Volt anzeigt unbelastet, aber durch den Spannugnswandler auf dem IO Board nur irgendwas mit 4.2V liefert. Ein 12V Netzteil brachte die Lösung :-)

Aber ansonsten funktioniert es sehr sehr gut. Habe noch ein wenig mit den Werten hier und da gespielt, aber meistens habe ich es verschlimmbessert :-) Wie immer:-)
Ich mach mich morgen noch daran, den Deltabetrieb, d.h. Umschaltung auf Kettenantrieb einzuarbeiten, damit das auch wieder funktioniert.
Mal sehen wie ich das mit den Potis gedeichselt bekomme, denke das werde ich rausmachen, da ein Code schneller gebaut ist, als mit den Potis rumspielen.
Dann fehlt noch das Einschalten von LEDs, dann gehts an den Umbau des Baggers. Der Umbau sollte eine schneller Sache sein, da hab ich eher Heimspiel :-)

VG
Thomas

Searcher
12.04.2015, 12:31
Dann hätte ich noch eine Verständnisfrage, ganz ist mir noch nicht klar warum die Rechenzeit 8µs beträgt. Wodurch wird dies bestimmt, durch den Timer 1?
Meinst Du diese 8µs?

Die längste Zeit, die zwischen zwei 8µs Bewegungen (Decr Berechnung_3a) von Servo3 liegt, ...

Die 8µs kommen vom Timer0. Der läuft ja durch seinen Prescaler von 64 mit 125000Hz. 1/125000Hz = 8µs.
Alle 8µs schaltet der Timer also einen Schritt weiter - erhöht sein Register TCNT0 um eins weiter bis 255 wo es dann beim nächsten Schritt auf 0 gesetzt wird (überläuft) und ab da wieder den Berg rauf schreitet. Weil er damit die Zeiten für die Pulsbreite der Servos macht, kann die Pulsbreite nur in Schritten mit 8µs verändert werden. Puls kann also ein Vielfaches von 8 lang sein (8µs, 16µs, 32µs ....) aber nicht 5µs, 17µs etc. Wenn also Berechnung_3a um eins verändert wird, wird die Pulslänge von Servo3 um 8µs, eben die kleinst mögliche für Timer0 abmeßbare Zeiteinheit, verändert. *keuch* *lufthol*

Ich sollte nicht soviel schreiben. Je mehr man schreibt, desto mehr Mist kann dabei rauskommen :)


Also allgemein bekommt ein Servo eine Sollposition durch die Pulsbreite seine Servosignals mitgeteilt. Das Servo versucht nun mithilfe seiner eingebauten Elektronik und dessen Eigenschaften die Sollposition so schnell wie möglich zu erreichen. (Da gibt es schon Unterschiede zwischen den verschiedenen Typen)

Um den Lauf des Servos von "außen", also durch das Programm zu verlangsamen wird nicht die Endsollposition übermittelt, sondern eine Pulsweite, die nur wenig von der vorherigen abweicht. Das Servo fährt also nicht zur Endposition siondern ein kleines Stück dorthin (In unserem Fall ein Stück, daß eine 8µs Pulsweitenveränderung ausmacht). Nach einer Weile wird die Pulsweite wieder ein Stück in Richtung Endsollweite verändert - das Servo macht wieder einen kleinen Schritt. Die Geschwindigkeit des Servos hängt also von diesen "Weilen" ab, die zwischen den Pulsweitenveränderungen in Richtung Sollpulsweite liegt.

Die "Weile" also die Zeit, die zwischen den Veränderungen vergeht, wird hier mit Timer1 bzw Timer2 bestimmt. Jedesmal, wenn die Zeit abgelaufen ist (Das Interruptflag steht) wird zB Berechnung_3a verändert.

Man könnte sich auch andere Methoden einfallen lassen. Ich meine, es gab mal zu Beginn des threads, eine bzw mehrere Variablen, die immer mit jedem Durchlauf der Hauptschleife hochgezählt wurden und bei einem bestimmten Wert ein Servo ein Stück weitergeschaltet hatten. Damit könnte man Servo3 wieder weniger "rauh" bekommen. Oder sich andere Methoden einfallen lassen ....

Exponetielle Geschwindigkeitsverläufe oder Rampen - Gehirnschmalz zum Erzeugen der Zeiten zwischen den Servoimpulsveränderungen stecken. Ich kann aber heute und demnächst vermutlich nicht :lol: :lol: :lol:


Ich mach mich morgen noch daran, den Deltabetrieb, d.h. Umschaltung auf Kettenantrieb einzuarbeiten, damit das auch wieder funktioniert.
Mal sehen wie ich das mit den Potis gedeichselt bekomme, denke das werde ich rausmachen, da ein Code schneller gebaut ist, als mit den Potis rumspielen.
Dann fehlt noch das Einschalten von LEDs, dann gehts an den Umbau des Baggers. Der Umbau sollte eine schneller Sache sein, da hab ich eher Heimspiel :-)

Genau, erstmal die Basis schaffen, ich bin beim Daumen drücken.

Gruß
Searcher

pocketpcuser
02.08.2019, 07:40
Hallo Fachleute

Ich nutze den Code von #64 auf einem Arduino nano, das Programm funktioniert einwandfrei!!
Ich möchte nun wenn der Baggerarm Servo gegen einen Taster fährt das Servo Stoppen und erst bei einem Richtungswechsel des Fernsteuer Signales das Servo wieder freigeben.
(evtl. könnte man mit einem Transistor den Plusspannung des Servos unterbrechen oder gibt es noch bessere Möglichkeiten??)

Für eine Antwort möchte ich mich jetzt schon Bedanken !

R2D2 Bastler
09.01.2022, 15:26
Ich entschuldige mich dafür, dass ich seit Jahren nicht mehr hier war. :(
Auch wenn es für eine Antwort auf die Frage von pocketpcuser schon einige Jahre zu spät ist, antworte ich trotzdem (vielleicht sucht ja mal jemand anderes nach genau dieser Lösung).

Einem Servo die Betriebsspannung zu nehmen ist keine gute Idee. Das Servo verliert seine Haltekraft. Zudem reagieren manche Servos sehr nachtragend, wenn die Versorgungsspannung wegfällt, aber die Impulse des Empfängers weiter anliegen.

Im Code wäre es recht einfach gewesen, einen Endschalter für ein bestimmes Servo zu integrieren. In den Zeilen 299 bis 334 werden unter anderem die Servostellungen berechnet. Dies passiert mit den Zeilen:

Decr Berechnung_1a
Incr Berechnung_1a
Decr Berechnung_2a
Incr Berechnung_2a

In diesen Zeilen wird die jeweilige Stellung von Servo 1 oder Servo 2 nach rechts oder links verschoben.
Wenn nun z.B. die Linksbewegung des Servos 1 mit einem Endschalter begrenzt werden soll, liese sich das mit lediglich 3 Programmzeilen bewerkstelligen.

Vor der Hauptschleife den Endschalter einrichten mittels:

Endschalter Alias Pinx.y ' Pin festlegen, an dem der Endschalter angeschlossen wird
Portx.y = 1 ' PullUp an dem Pin einschalten


Im jeweiligen Berechnungsblock (abhängig von Servo und Richtung) dann z.B. folgende Änderung durchführen:

' Decr Berechnung_1a <--- diese Zeile ersetzen durch...
If Endschalter = 1 then Decr Berechnung_1a ' Nur ausführen, wenn Endschalter nicht betätigt ist


Nun würde das Servo nur solange in die entsprechende Richtung bewegt werden können, solange der Endschalter (oder besser gesagt der EndTASTER) nicht betätigt ist.
Sobald der Baggerarm den Taster betätigt (am Endschalter-Pin liegt dann Masse an), würde die Variable "Berechnung_1a" nicht mehr weiter decreased -> Das Servo bleibt stehen.

pocketpcuser
10.01.2022, 08:35
Danke für Die INFO.
Auf diese Lösung bin ich dann auch gekommen.