PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : PWM und SOFTUART gleichzeitig



Che Guevara
11.05.2011, 17:12
Hallo,

für mein momentanes Projekt benötige ich einen AVR, der über 1 Pin Daten empfängt (SOFTUART), diese dann zerlegt und anschließend mittels PWM ausgibt. Beispiel:

Der ATMEGA8 sendet "120*90*100*200"
Jetzt soll mein zweiter AVR (ein ATTINY45) diesen String empfangen und in 4 Teile zerlegen:
1. "120"
2. "90"
3. "100"
4. "200"

Diese 4 Strings soll er nun auf 4 verschiedene PWM-Ausgänge geben. Der PWM-Teil läuft soweit, jedoch nur, solange ich meinen Split-Befehl auskommentiere.
Hier mal mein Programm:


$regfile = "attiny45.dat"
$crystal = 8000000
$framesize = 60
$hwstack = 60
$swstack = 60


Open "COMB.0:19200,8,N,2,INVERTED" For Input As #1

Config Pinb.0 = Input



Config Servos = 3 , Servo1 = Portb.3 , Servo2 = Portb.2 , Servo3 = Portb.4 , Servo4 = Portb.1 , Reload = 4 , Intervall = 10
Config Portb.1 = Output
Config Portb.2 = Output
Config Portb.3 = Output
Config Portb.4 = Output


Dim _input As String * 15
Dim _data(4) As String * 5
Dim _lenght As Byte
Dim _bl(4) As Byte
Dim _blold(4) As Byte
Dim I As Byte


Const Min_servo = 11
Const Max_servo = 70


'STARTUP###########
_input = ""
_lenght = 0
For I = 1 To 4
_bl(i) = Min_servo
_blold(i) = _bl(i)
Servo(i) = _bl(i)
Next I
I = 0
'##################


Enable Interrupts



Do


Input #1 , _input Noecho
_lenght = Split(_input , _data(1) , "*")
If _lenght > 0 Then
For I = 1 To 4
_bl(i) = Val(_data(i))
Servo(i) = _bl(i)
Next I
_input = ""
_lenght = 0
End If


Loop

End
Sobald der Split-Befehl im Programm steht, wird die PWM-ISR teilweise blockiert und meine Signale stimmen nicht mehr.
Was kann ich nun dagegen tun? Den Split-Befehl rausnehmen und das Ganze irgendwie anders lösen?
Meine Hardware sieht so aus:
Ein Atmega8 lest ein WM+ und einen 4CH-Empfänger (über Summensignal) ein und errechnet daraus 4 versch. Werte für 3 BLM und 1 Servo.
Diese 4 Werte muss ich nun an den ATTINY45 (als "PWM-Controller") senden (habe nur 1 Pin). Dieser soll dann seinen 4 PWM-Ausgängen diese 4 Werte zuweisen. Die PWM-Frequenz liegt bei 50Hz.

Über ein paar konstruktive Ideen würde ich mich sehr freuen.
Gruß
Chris

for_ro
11.05.2011, 17:54
Sobald der Split-Befehl im Programm steht, wird die PWM-ISR teilweise blockiert und meine Signale stimmen nicht mehr.
Wo ist denn die PWM-ISR? Ich sehe nur einen Servo Befehl in der Hauptschleife. Wann der dran kommt, hängt natürlich von der Bearbeitungszeit aller anderen Befehle der Hauptschleife ab.

Warum überträgst du die Werte als Strings? Wenn es keine größeren Werte werden können, als in ein Byte passen, dann würde ich mit Printbin das Byte mit Wert 200 übertragen und nicht den String "200". Denn dies wären dann mindestens 3 Byte.
Und dann könntest du auch ohne weiteres auf die Funktionen Split() und Val() verzichten, die ziemlich viel Zeit benötigen.

Che Guevara
11.05.2011, 18:14
Das ging ja schnell :)
Mit der PWM-ISR ist der Servo-Befehl gemeint.
Wenn ich die Werte (4 mal 1Byte) mit Printbin übertrage, woher weiß ich dann, welcher empfangene Wert für welchen Servo / BL Motor zuständig ist?

Gruß
Chris

Besserwessi
11.05.2011, 18:27
Zum Zuordnen der Motoren kann eine Wert (z.B. 0 oder 255 ) nutzen der der PWM Wert nicht vor kommt. Der Wert markiert dann den Anfang der Daten. Mann muss dann halt 5 Bytes statt 4 übertragen. Die UART kann dann langsamer laufen.

Che Guevara
11.05.2011, 20:07
Ich habs gerade ausprobiert mit Printbin und Inputbin, allerdings habe ich jetzt ein neueres, größeres Problem. Hier mein Code auf dem ATTINY45:


$regfile = "attiny45.dat"
$crystal = 8000000
$framesize = 30
$hwstack = 30
$swstack = 30



Open "COMB.0:38400,8,N,1" For Input As #1
Config Pinb.0 = Input


Config Servos = 4 , Servo1 = Portb.3 , Servo2 = Portb.2 , Servo3 = Portb.4 , Servo4 = Portb.1 , Reload = 4 , Intervall = 10
Config Portb.1 = Output
Config Portb.2 = Output
Config Portb.3 = Output
Config Portb.4 = Output



Dim _bl(4) As Byte
Dim _blold(4) As Byte
Dim I As Byte


Const Min_servo = 78 '900µs
Const Max_servo = 170 '2000µs


For I = 1 To 4
_bl(i) = Min_servo
_blold(i) = _bl(i)
Servo(i) = _bl(i)
Next I


Enable Interrupts


Do


Servo(1) = 78
Wait 1
Servo(1) = 100
Wait 1

'(
Inputbin #1 , _bl(1) , _bl(2) , _bl(3) , _bl(4)

For I = 1 To 4
If _bl(i) <> 0 And _bl(i) <> 255 Then
If _bl(i) >= Min_servo And _bl(i) <= Max_servo Then
Servo(i) = _bl(i)
End If
End If
Next I
')

Loop

End

Mit dem Oszi überprüfe ich den Ausgang des Servo(1)-Signals. Die Werte (78 und 100) stimmen zwar, jedoch ändert sich das Signal nicht jede Sekunde (wait 1), sondern nur ca. alle 5 bis 6 Sekunden. Clockdiv/8 ist deaktiviert, Clock output ist deaktiviert,
100010:Int. RC Osc. 8 MHz; Start-up time PWRDWN/RESET: 6 CK/14 CK + 64 ms; [CKSEL=0010 SUT=10]; default value,
und bod ist deaktiviert.
Warum braucht mein AVR so lange? Liegts am TINY? Hab in der BASCOM-Hilfe was von $tiny gelesen, allerdings gehen damit 90% meiner Instructions nicht mehr :(

Gruß
Chris

for_ro
11.05.2011, 20:15
Wenn ich die Werte (4 mal 1Byte) mit Printbin übertrage, woher weiß ich dann, welcher empfangene Wert für welchen Servo / BL Motor zuständig ist?

Woher weißt du es bei deiner Version?
Du kannst natürlich auch ein komplizierteres Protokoll nehmen anstelle von 1.Wert ist 1. Servo, 2. Wert ist 2.Servo usw. Aber wofür, wenn du immer 4 Werte sendest. Anders sieht es aus, wenn du z.B. nur die Werte sendest, die sich ändern. Aber das macht bei 4 Byte keinen Sinn, weil dann der Overhead zum Auswerten dann viel zu groß wird.
Womit du eher ein Problem bekommen könntest ist die Synchronisierung, falls dir mal aus irgendeinem Grund ein Wert verlorengeht.

for_ro
11.05.2011, 20:21
Liegts am TINY?

Das glaube ich eher nicht.

Hab in der BASCOM-Hilfe was von $tiny gelesen, ...
Das ist nur für die Tinys, die kein SRAM haben. Deiner dagegen ist doch ziemlich gut bestückt.

Che Guevara
11.05.2011, 20:22
Ok, aber warum ist dann das Timing so grottenschlecht?

EDIT: Habs gerade probiert, wenn ich die Servo-Funktion auskommentiere und nur einen Pin im Sekundentakt toggln lasse, dann funzts wunderbar. Mit der Servo-Funktion dauerts jedoch ca. 6sek bis die neuen Werte übernommen werden?!

for_ro
11.05.2011, 20:25
Config Servos = 4 , Servo1 = Portb.3 , Servo2 = Portb.2 , Servo3 = Portb.4 , Servo4 = Portb.1 , Reload = 4 , Intervall = 10

Hast du mal die Hilfe zu Config Servo unter Alternative Servocode gelesen?

Che Guevara
11.05.2011, 20:46
Ja, habs grad ausprobiert, bei mir kommen zwar die korrekten High-impluse raus, die Pausezeit (normal 20ms) dauert bei mir jedoch 200ms. Habs mit reload 100 und intervall = 100 gemacht, genau wie im Hilfe-Dokument.

EDIT: Mir ist gerade aufgefallen, dass ich "Interval" mit 2 statt einem l geschrieben habe. Das erzeugt zwar keinen Fehler, jedoch stimmt dann das Timing nicht mehr! Jetzt, richtig geschrieben, funktioniert es perfekt!

Che Guevara
11.05.2011, 21:14
Jetzt habe ich 2 Probleme:
1. Der Wertebereich zur Servoansteuerung (bei mir 8 bis 19) ist zu klein. Eigtl. soll das eine Tricopter-Steuerung werden, da brauch ich auch PID-Regler usw.. Ich denke, dass damit zu ungenau geregelt wird?!
2. Der neue Wert, der per UART reinkommt, wird erst nach einer geschätzten Minute auf die Servo-Ausgänge gegeben.

Hier mal die Codes:
ATMEGA8:


$regfile = "m8def.dat"
$crystal = 16000000
$framesize = 100
$hwstack = 100
$swstack = 100


Open "COMD.4:38400,8,N,1" For Output As #1


Dim _bl(4) As Byte
Dim Channel As Byte
Dim Kanal(4) As Byte
Dim I As Byte

For I = 1 To 4
_bl(4) = 80
Next I


Config Timer2 = Timer , Prescale = 256
On Timer2 Detectrxpause
Enable Timer2

Config Int1 = Rising
On Int1 Getreceiver
Enable Int1


Config Pind.3 = Input
Portd.3 = 0


Enable Interrupts



Do


For I = 1 To 4
_bl(i) = 8
Next I
Printbin #1 , _bl(1) , _bl(2) , _bl(3) , _bl(4)
Wait 3

For I = 1 To 4
_bl(i) = 10
Next I
Printbin #1 , _bl(1) , _bl(2) , _bl(3) , _bl(4)
Wait 3


Loop

End


Getreceiver:
If Channel > 0 And Channel < 5 Then
Kanal(channel) = Timer2
End If
Timer2 = 6
Incr Channel
Return

Detectrxpause:
Channel = 0
Return

ATTINY45:


$regfile = "attiny45.dat"
$crystal = 8000000
$framesize = 40
$hwstack = 40
$swstack = 40



Open "COMB.0:38400,8,N,1" For Input As #1
Config Pinb.0 = Input


Config Servos = 4 , Servo1 = Portb.3 , Servo2 = Portb.2 , Servo3 = Portb.4 , Servo4 = Portb.1 , Reload = 100 , Interval = 100
Config Portb.1 = Output
Config Portb.2 = Output
Config Portb.3 = Output
Config Portb.4 = Output



Dim _bl(4) As Byte
Dim _blold(4) As Byte
Dim I As Byte


Const Min_servo = 8 '900µs
Const Max_servo = 19 '2000µs


For I = 1 To 4
_bl(i) = Min_servo
_blold(i) = _bl(i)
Servo(i) = _bl(i)
Next I


Enable Interrupts
Wait 2


Do


Inputbin #1 , _bl(1) , 4

For I = 1 To 4
If _bl(i) <> 0 And _bl(i) <> 255 Then
If _bl(i) >= Min_servo And _bl(i) <= Max_servo Then
Servo(i) = _bl(i)
End If
End If
Next I


Loop

End


Wo liegt mein Fehler?

Gruß
Chris

Besserwessi
12.05.2011, 16:35
Das Problem für die beschränkte Auflösung dürfte der Servo Befehl sein. Da wird man wohl einen altenativen Code für die Servofunktion brauchen, und ggf. das auch von Hand im Interrupt programmieren müssen. Als Ansatzpunkt könnte der Code dienen:
http://www.rn-wissen.de/index.php/Servoansteuerung.

Man wird eventuell die UART langsamer (z.B. 4800 Baud) laufen lassen müssen. So schnell wird es ohnehin nicht benötigt für die paar Bytes. Ein Problem ist, das sowohl die Software UART, als auch der Servo Befehl Interrupts nutzen, und die können sich gegenseitig stören. Es würde etwas einfacher mit einem Tiny2313 statt Tiny45: der hat eine Hardware UART und einen 16 Bit Timer.

Der Tiny45 hat auch knapp Pins - da ist man dann auf den internen Takt angewiesen, und der ist von der Genauigkeit grenzwertig für die UART - da sollte man dann schon die Wortlänge auf 5 oder 6 Bits reduzieren, also von jedem Byte nur ersten 5 oder 6 auch benutzen. Bei den zuletzt übertragenen Bits steigt die Fehlergefahr schon.

Che Guevara
14.05.2011, 18:41
Hallo,

hab mir jetzt ein neues Board gemacht, mit einem ATMEGA8 (@16MHz) und einem ATTINY2313 (@16MHz). Den Servo-Befehl hab ich mir jetzt selbst geschrieben, funktioniert sehr gut. Allerdings habe ich jetzt ein neues Problem, welches vorher schon angesprochen wurde (glaube ich); und zwar kann es passieren, dass die Synchronisation verloren geht, d.h. der Wert vom 1. Motor (ATMEGA8) wird beim ATTINY2313 plötzlich zum Wert für den 2. / 3. / 4. Motor... Jetzt brauche ich eine Art Check, ob die Daten auch richtig zugeordnet werden, z.b. mittels einem Anfangs- und einem End-Byte?! Wäre das sinnvoll, oder denkt ihr, es gäbe eine einfachere / bessere / schnellere Lösung?
Hier mal die Codes:
ATTINY2313:


$regfile = "attiny2313.dat"
$crystal = 16000000
$framesize = 30
$hwstack = 30
$swstack = 30
$baud = 115000


Config Timer0 = Timer , Prescale = 1 '100kHz
Timer0 = 96
On Timer0 Servo_irq
Enable Timer0


Config Portd.2 = Output
Config Portd.3 = Output
Config Portd.4 = Output
Config Portd.5 = Output


Dim Servo(4) As Byte
Dim _ist As Byte
Dim _bl(4) As Byte
Dim I As Byte

Const Min_servo = 65
Const Max_servo = 160

For I = 1 To 4
_bl(i) = Min_servo
Servo(i) = _bl(i)
Next I


Enable Interrupts


Do


Inputbin _bl(1) , 4
For I = 1 To 4
If _bl(i) < Min_servo Then _bl(i) = Min_servo
If _bl(i) > Max_servo Then _bl(i) = Max_servo
Servo(i) = _bl(i)
Next I


Loop

End


Servo_irq:
Timer0 = 96
Incr _ist
If _ist >= 200 Then _ist = 0
If Servo(1) > _ist Then
Portd.2 = 1
Else
Portd.2 = 0
End If
If Servo(2) > _ist Then
Portd.3 = 1
Else
Portd.3 = 0
End If
If Servo(3) > _ist Then
Portd.4 = 1
Else
Portd.4 = 0
End If
If Servo(4) > _ist Then
Portd.5 = 1
Else
Portd.5 = 0
End If
Return

ATMEGA8:


$regfile = "m8def.dat"
$crystal = 16000000
$framesize = 80
$hwstack = 80
$swstack = 80
$baud = 115000


Config Serialout = Buffered , Size = 20


Config Timer2 = Timer , Prescale = 256
On Timer2 Detectrxpause
Enable Timer2

Config Int1 = Rising
On Int1 Getreceiver
Enable Int1


Config Pind.3 = Input
Portd.3 = 0

Const _maxchannel = 4

Dim Bufferbyte As Byte
Dim Kanal(_maxchannel) As Byte
Dim Channel As Byte
Dim _bl(4) As Byte
Dim I As Byte


For I = 1 To 4
_bl(i) = 0
Next I


Enable Interrupts

Wait 1


Do


For I = 1 To 4
_bl(i) = 200 - Kanal(i)
Next I

Printbin _bl(1) ; 4


Loop

End


Getreceiver:
If Channel > 0 And Channel < 5 Then
Kanal(channel) = Timer2
End If
Timer2 = 6
Incr Channel
Return

Detectrxpause:
Channel = 0
Return

Beim ATMEGA8 hab ich schon die Summensignal-Auswertung mit drinen! Das WM+ (welches noch an den ATMEGA8 kommt), wird erst später hinzugefügt, wenn das jetzt mal funktioniert.
Zur Hardware:
RxD vom ATTINY2313 ist mit TxD vom ATMEGA8 verbunden, DITO für die anderen beiden.
Ansonsten gibts IMHO keine wichtigen Infos über die Hardware (bzw. der Rest ist Standart), falls doch, einfach fragen ;)

Vielen Dank
Gruß
Chris

Besserwessi
14.05.2011, 19:39
Beim jetzigen Code werden nur rund 100 Werte genutz. Da wäre also im Prinzip noch 1 Bit frei um die Werte für einen Motor (z.B. Servor 1) zu markieren. Statt direkt dem Servo-Wert zu übertagen einfach eine Konstante anziehen, so das der Wert übertragene Wert kleiner als 128 bleibt. Bei einem Servo dann zusätzlich das Bit 7 setzen,so dass man da werte von 128 - 255 hat und den Wert gut erkennen kann.
Ein extra Start und stopp Wert würde auch gehen - man hat ja wenigstens 20 ms für jeden Datensatz.
Ein hohe Baudrate wird funktionieren, aber es gibt damit eine steigende Gefahr das man ein Wert verloren geht - bei den wenigen Daten sollten auch 19200 Baud oder so reichen.

Che Guevara
14.05.2011, 19:52
Also die 115kBaud funktionieren sehr gut (bei 16MHz 0% Fehler), aber ich denke, werd mal runter gehen, wenn du das sagst.
Aber dann tritt wieder ein Problem auf:
Der Inputbin-Befehl wartet solange, bis alle Arrays (also hier 4) gefüllt sind. D.h. wenn ich einmal die Werte falsch zugewiesen habe, dann zieht sich diese Falschzuweisung durch bis zum bitteren Ende :(
Deswegen müsste ich, wenn ich einen Sync Fehler regisitriet habe, "neu von Vorne beginnen". Aber wie??
Kann ich z.b. NUR beim 1. Wert das 7. Bit setzen und dann immer Abfragen, ob dieses gesetzt ist. Wenn nicht, dann muss ich neu Syncronisieren, indem ich eine Schleife einbaue, die auf das 7. gesetzte Bit wartet?! Hier mal ein Code:


Inputbin _bl(1) ; 4
If _bl(1).7 = 0 Then 'Fehler
While _bl(1).7 = 0
Inputbin _bl(1) ; 1
Wend
Inputbin _bl(2) ; 3
End If
Reset _bl(1).7

Werde das jetzt gleich mal ausprobieren, melde mich dann wieder. Aber wäre trotzdem nett, wenn du mal drüber schauen könntest und mir sagen könntest, ob das eine gute Lösung ist oder ob ichs anders machen soll?!

Gruß
Chris

Besserwessi
14.05.2011, 21:11
Der Code oben scheint noch nicht ganz Vollständig, aber es könnte passen. Am sichersten wäre es wohl mit extra Marker für den Start und Stop, z.B. als 2 Extra Bytes. Dann also erst auf den Start Marker warten, dann 5 Werte einlesen und Überprüfen ob der letzte Wert der Stop Marker ist. Wenn alles OK ist, kann man die Daten in die Variablen für den Servo kopieren.
Wenn man unbedingt will, könnte man bei Stop Marker sogar noch eine kurze Prüfsumme (z.B. 4 Bits) mit unterbringen. Das ist dann aber wohl schon übertrieben.

Che Guevara
14.05.2011, 21:35
Meinst du den letzten Code von mir? Was vermisst du den daran?
Ok, werds mal mit 6 Bytes insgesamt probieren.

Gruß
Chris

EDIT:
Habs jetzt mal so probiert mit Start- und Stop-Byte, jedoch tut sich nichts... PWM wird zwar erzeugt, aber sie verändert sich nicht, d.h. die Werte werden nicht korrekt eingelesen?!
ATTINY2313:


$regfile = "attiny2313.dat"
$crystal = 16000000
$framesize = 20
$hwstack = 20
$swstack = 20
$baud = 19200



Config Timer0 = Timer , Prescale = 1 '100kHz
Timer0 = 96
On Timer0 Servo_irq
Enable Timer0


Config Portd.2 = Output
Config Portd.3 = Output
Config Portd.4 = Output
Config Portd.5 = Output

Const Start_byte = 0
Const Stop_byte = 127

Dim _start As Byte
Dim _stop As Byte

Dim Servo(4) As Byte
Dim _ist As Byte
Dim _bl(4) As Byte
Dim I As Byte

Const Min_servo = 65
Const Max_servo = 160
Const Diff_servo = Max_servo - Min_servo

For I = 1 To 4
_bl(i) = Min_servo
Servo(i) = _bl(i)
Next I


Enable Interrupts


Do


Inputbin _start
While _start <> Start_byte 'warte auf start
Inputbin _start
Wend
Inputbin _bl(1) ; _bl(2) ; _bl(3) ; _bl(4) ; _stop

If _stop = Stop_byte Then
For I = 1 To 4
If _bl(i) < Min_servo Then _bl(i) = Min_servo
If _bl(i) > Max_servo Then _bl(i) = Max_servo
Servo(i) = _bl(i)
Next I
End If


Loop

End


Servo_irq:
Timer0 = 96
Incr _ist
If _ist >= 200 Then _ist = 0
If Servo(1) > _ist Then
Portd.2 = 1
Else
Portd.2 = 0
End If
If Servo(2) > _ist Then
Portd.3 = 1
Else
Portd.3 = 0
End If
If Servo(3) > _ist Then
Portd.4 = 1
Else
Portd.4 = 0
End If
If Servo(4) > _ist Then
Portd.5 = 1
Else
Portd.5 = 0
End If
Return

ATMEGA8:


$regfile = "m8def.dat"
$crystal = 16000000
$framesize = 80
$hwstack = 80
$swstack = 80
$baud = 19200


Config Serialout = Buffered , Size = 20


Config Timer2 = Timer , Prescale = 256
On Timer2 Detectrxpause
Enable Timer2

Config Int1 = Rising
On Int1 Getreceiver
Enable Int1


Config Pind.3 = Input
Portd.3 = 0

Const Start_byte = 0
Const Stop_byte = 127

Const _maxchannel = 4

Dim Bufferbyte As Byte
Dim Kanal(_maxchannel) As Byte
Dim Channel As Byte
Dim _bl(_maxchannel) As Byte
Dim I As Byte

Dim Max_kanal(_maxchannel) As Byte
Dim Min_kanal(_maxchannel) As Byte
Dim Diff_kanal(_maxchannel) As Byte


For I = 1 To _maxchannel
_bl(i) = 0
Max_kanal(i) = 100
Min_kanal(i) = 100
Diff_kanal(i) = Max_kanal(i) - Min_kanal(i)
Next I


Enable Interrupts


Do

'(
For I = 1 To _maxchannel
If Kanal(i) > Max_kanal(i) Then
Max_kanal(i) = Kanal(i)
Diff_kanal(i) = Max_kanal(i) - Min_kanal(i)
End If
If Kanal(i) < Min_kanal(i) Then
Min_kanal(i) = Kanal(i)
Diff_kanal(i) = Max_kanal(i) - Min_kanal(i)
End If
Next I
')

For I = 1 To _maxchannel
_bl(i) = Kanal(i) '- Min_kanal(i)
Next I


Printbin Start_byte ; _bl(1) ; _bl(2) ; _bl(3) ; _bl(4) ; Stop_byte

Waitms 100


Loop

End


Getreceiver:
If Channel > 0 And Channel < 5 Then
Kanal(channel) = Timer2
End If
Timer2 = 6
Incr Channel
Return

Detectrxpause:
Channel = 0
Return

Wo könnte mein Problem sein?

Besserwessi
15.05.2011, 13:52
Bei der Datenübertragung kann ist keinen Fehler finden. Ich würde da noch mal einen testlaufen lassen mit ggf. 1) einer LED die Blinkt, wenn Fehlerhafte Daten erkannt werden und / oder am Sender ein festes Muster senden, damit man weiss ob es an der Sender oder Empfängerhälfte hakt.

Der Servo Code sieht mir komisch bzw. Fehlerträchtig aus: bei 100 kHz Frequenz für den Interrupt bleibt schon nicht viel Rechenzeit über. Da wäre es ggf. besser die Servopulse nacheinander zu machen - so bekommt man dann auch eine ausreichend lange Periodendauer: normal sind da 20 ms vorgesehen, in dem Programm werden das nur 2 ms. Wenn man die Pules nacheinander macht, kann man auch die Zeit direkt vom Timer bestimmen lassen - man braucht dann also nur einen Interruptaufruf pro Servo und könnte ggf. auch noch etwas mehr an Auflösung bekommen. Beim Code im RN Wissen wird das auch so gemacht, so weit ich mich erinnere.

Richard
15.05.2011, 13:57
Das Problem kann schon Input selber sein, suche mal in der Bascom Hilfe nach GET. Dort werden auch Link's zu alternativen I/O Möglichkeiten geboten.

Gruß Richard

Che Guevara
15.05.2011, 17:42
Ok, das mit dem festen Senden von Daten und dem Blinken werd ich mal ausprobieren, damit ich weiß, was funktioniert und was nicht! Alternativ dazu möchte ich auch noch den PC über einen MAX232 anschließen, um die Input-Daten vom ATTINY ausgeben zu lassen. Mal sehen, was dabei rauskommt..
@Richard:
Kannst du mir sagen, wo der Unterschied zwischen Inputbin und Get liegt? Ich habe keinen gefunden, evtl. bin ich momentan etwas zu abgelenkt, ums zu sehen.. Wäre nett von dir.

Gruß
Chris

EDIT:
Habs jetzt endlich erfolgreich geschafft, das meiste so hinzubiegen, wie ich es will :D
Hier mal die Codes:
ATMEGA8:


$regfile = "m8def.dat"
$crystal = 16000000
$framesize = 80
$hwstack = 80
$swstack = 80
$baud = 115000



Config Timer2 = Timer , Prescale = 256
On Timer2 Detectrxpause
Enable Timer2

Config Int1 = Rising
On Int1 Getreceiver
Enable Int1


Config Pind.3 = Input
Portd.3 = 0


Const Start_byte = 127
Const _maxchannel = 4

Dim Bufferbyte As Byte
Dim Kanal(_maxchannel) As Byte
Dim Channel As Byte
Dim _bl(_maxchannel) As Word
Dim I As Byte
Dim _crc As Word


Enable Interrupts


Do


For I = 1 To 4
_bl(i) = 63200
Next I

_crc = Crc16(_bl(1) , 4)


Printbin Start_byte ; _bl(1) ; _bl(2) ; _bl(3) ; _bl(4) ; _crc

Waitms 100


Loop

End


Getreceiver:
If Channel > 0 And Channel < 5 Then
Kanal(channel) = Timer2
End If
Timer2 = 6
Incr Channel
Return

Detectrxpause:
Channel = 0
Return

ATTINY2313:


$regfile = "attiny2313.dat"
$crystal = 16000000
$framesize = 30
$hwstack = 32
$swstack = 30
$baud = 115000


Config Timer1 = Timer , Prescale = 8
Timer1 = 62535
On Timer1 Servoirq
Enable Timer1


Config Portd.2 = Output
Config Portd.3 = Output
Config Portd.4 = Output
Config Portd.5 = Output


Dim Kanal As Byte
Dim Servo(4) As Word
Dim _bl(4) As Word
Dim I As Word
Dim _crc As Word
Dim _crcold As Word
Dim _start As Byte

'min: 61535, mitte 62535, max 63535 = 2000 schritte

Const Start_byte = 127
Const Min_servo = 63800
Const Max_servo = 61535
Const Diff_servo = Max_servo - Min_servo

For I = 1 To 4
_bl(i) = Min_servo
Servo(i) = _bl(i)
Next I


Enable Interrupts
Wait 3


Do


If Ischarwaiting() > 0 Then
Inputbin _start
If _start = Start_byte Then
Inputbin _bl(1) , _bl(2) , _bl(3) , _bl(4) , _crc
If _crc = Crc16(_bl(1) , 4) And _crc <> _crcold Then
For I = 1 To 4
Servo(i) = _bl(i)
Next I
_crcold = _crc
End If
Else
For I = 1 To 4
Servo(i) = Min_servo
Next I
End If
End If


Loop


Servoirq:
If Kanal = 0 Then
If Portd.2 = 0 Then 'wenn port low
Timer1 = Servo(1) 'dann timer auf entsprechende verzögerung
Portd.2 = 1 'und port anschalten
Else 'das hier passiert erst bei dem darauf folgenden interrupt
Portd.2 = 0 'dann port wieder ausschalten
Incr Kanal 'und den nächsten kanal bearbeiten
End If
End If
If Kanal = 1 Then
If Portd.3 = 0 Then
Timer1 = Servo(2)
Portd.3 = 1
Else
Portd.3 = 0
Incr Kanal
End If
End If
If Kanal = 2 Then
If Portd.4 = 0 Then
Timer1 = Servo(3)
Portd.4 = 1
Else
Portd.4 = 0
Incr Kanal
End If
End If
If Kanal = 3 Then
If Portd.5 = 0 Then
Timer1 = Servo(4)
Portd.5 = 1
Else
Portd.5 = 0
Incr Kanal
End If
End If

If Kanal = 4 Then
Timer1 = 65530 '40000 | eine pause von ca. 12ms bis zum nächsten interrupt. Bei guten Servos oder Brushlessreglern kann man hier bis auf 65530 gehen ==> ansteuerfrequenz von ~ 200Hz
Kanal = 0
End If
Return
End

Der Unterschied liegt wirklich im Servo-Code! Danke für den Tip... Jetzt gehts auf die nächste Hürde zu, das Summensignal auf den Servo-Bereich umzuskalieren. Danach muss noch die WM+ eingebunden werden.

Che Guevara
16.05.2011, 16:51
Hallo,

nachdem ich gestern ein paar Erfolge hatte, liefs heute leider nicht so gut... Ich habe das Gehäuse zusammengebaut, die Motoren und die Steuerung usw... befestigt und wollts mal ausprobieren. Jedoch habe ich ein paar Probleme:
Bei meinem ersten Code (hier gehts jetzt nur noch um den ATMEGA8) funktionierte alles so mittelmäßig:


$regfile = "m8def.dat"
$crystal = 16000000
$framesize = 80
$hwstack = 80
$swstack = 80
$baud = 115200


Declare Sub Wmp_init()
Declare Sub Send_zero()
Declare Sub Read_data()

Declare Sub Set_offset()



$lib "I2C_TWI.LBX" 'Hardware I2C
Config Scl = Portc.5 'Ports for I2C-Bus
Config Sda = Portc.4
Config Twi = 400000
I2cinit


Config Timer1 = Timer , Prescale = 1
On Timer1 Pausedetect
Enable Timer1

Config Int1 = Falling
On Int1 Measure
Enable Int1


Config Pind.3 = Input
Portd.3 = 0


Const Start_byte = 127
Const _maxchannel = 4

Dim Bufferbyte As Byte
Dim Kanal(_maxchannel) As Word
Dim Channel As Byte
Dim _bl(_maxchannel) As Word
Dim I As Byte
Dim _crc As Word
Dim _sbl(_maxchannel) As Integer


Dim Buffer(6) As Byte

Dim Yaw As Word
Dim Yaw0 As Byte At Yaw + 1 Overlay
Dim Yaw1 As Byte At Yaw Overlay

Dim Roll As Word
Dim Roll0 As Byte At Roll + 1 Overlay
Dim Roll1 As Byte At Roll Overlay

Dim Pitch As Word
Dim Pitch0 As Byte At Pitch + 1 Overlay
Dim Pitch1 As Byte At Pitch Overlay

Dim _yawoffset As Long
Dim _rolloffset As Long
Dim _pitchoffset As Long

Dim _yawnow As Integer
Dim _rollnow As Integer
Dim _pitchnow As Integer


Call Wmp_init()
Waitms 500
Call Set_offset()

Enable Interrupts


'Empfänger:
'29000 < kanal(x) < 64000
'mitte = (64000 + 29000) / 2 = 93000 / 2 = 46500
'Ausschlag: 17500

'Servo-Funktion:
'min: 63800, mitte: 62667.5 , max: 61535 --> 2265 Schritte



Do


Call Read_data()

For I = 1 To 4
_sbl(i) = Kanal(i) - 17500
_sbl(i) = _sbl(i) / 15.4525386
Next I

_sbl(2) = _sbl(2) - 600
_sbl(3) = _sbl(3) - 500

_bl(1) = 62667.5 + _sbl(1)
_bl(2) = _bl(1) - _sbl(3)
_bl(3) = _bl(1) + _sbl(3)
_bl(1) = _bl(1) - _sbl(2)
'_bl(4) = 62535 + _sbl(4)

'##############################
_yawnow = Yaw - _yawoffset
_yawnow = _yawnow / 2
_rollnow = Roll - _rolloffset
_rollnow = _rollnow / 2
_pitchnow = Pitch - _pitchoffset
_pitchnow = _pitchnow / 2
'##############################


_bl(1) = _bl(1) - _pitchnow

_bl(2) = _bl(2) - _rollnow
_bl(3) = _bl(3) + _rollnow

_bl(4) = 62535 + _yawnow
_bl(4) = _bl(4) + _sbl(4)

_crc = Crc16(_bl(1) , 4)


Printbin Start_byte ; _bl(1) ; _bl(2) ; _bl(3) ; _bl(4) ; _crc

'Waitms 100


Loop


Measure:
If Channel > 0 And Channel < 5 Then
Kanal(channel) = Timer1
End If
Timer1 = 1536
Incr Channel
Return

Pausedetect:
Channel = 0
Return


Sub Wmp_init()
I2cstart
I2cwbyte &HA6 ' sends memory address
I2cwbyte &HFE ' WM+ activation
I2cwbyte &H04 . ' Now Adress changes to &HA4
I2cstop
End Sub

Sub Send_zero()
I2cstart
I2cwbyte &HA4 ' sends memory address
I2cwbyte &H00 ' sends zero before receiving
I2cstop
Waitms 1
End Sub

Sub Read_data()
Gosub Send_zero ' sends zero before receiving
I2creceive &HA4 , Buffer(1) , 0 , 6 ' receive 6 bytes
Yaw1 = Buffer(1)
Roll1 = Buffer(2) ' Low Bytes
Pitch1 = Buffer(3)
Shift Buffer(4) , Right , 2 : Yaw0 = Buffer(4)
Shift Buffer(5) , Right , 2 : Roll0 = Buffer(5) ' High Bytes
Shift Buffer(6) , Right , 2 : Pitch0 = Buffer(6)
End Sub

Sub Set_offset()
_yawoffset = 0
_rolloffset = 0
_pitchoffset = 0
For I = 1 To 50
Call Read_data
_yawoffset = _yawoffset + Yaw
_rolloffset = _rolloffset + Roll
_pitchoffset = _pitchoffset + Pitch
Next I
_yawoffset = _yawoffset / 50
_rolloffset = _rolloffset / 50
_pitchoffset = _pitchoffset / 50
End Sub

End

Wenn ich den Tricopter i-wie gedreht habe, haben die Motoren und der Servo darauf reagiert, jedoch fing dann das Ganze zu schwingen an. Dann dachte ich mir, bevor ich da jetzt optimiere, baue ich lieber gleich einen PID-Regler ein. Gesagt, getan....
Hier mal der Code:


$regfile = "m8def.dat"
$crystal = 16000000
$framesize = 80
$hwstack = 80
$swstack = 80
$baud = 115200


Declare Sub Wmp_init()
Declare Sub Send_zero()
Declare Sub Read_data()
Declare Sub Set_offset()



$lib "I2C_TWI.LBX" 'Hardware I2C
Config Scl = Portc.5 'Ports for I2C-Bus
Config Sda = Portc.4
Config Twi = 400000
I2cinit


Config Timer1 = Timer , Prescale = 1
On Timer1 Pausedetect
Enable Timer1

Config Int1 = Falling
On Int1 Measure
Enable Int1


Config Pind.3 = Input
Portd.3 = 0


Const Start_byte = 127
Const _maxchannel = 4

Dim Bufferbyte As Byte
Dim Kanal(_maxchannel) As Word
Dim Channel As Byte
Dim _bl(_maxchannel) As Word
Dim I As Byte
Dim _crc As Word
Dim _sbl(_maxchannel) As Integer


Dim Buffer(6) As Byte

Dim Yaw As Word
Dim Yaw0 As Byte At Yaw + 1 Overlay
Dim Yaw1 As Byte At Yaw Overlay

Dim Roll As Word
Dim Roll0 As Byte At Roll + 1 Overlay
Dim Roll1 As Byte At Roll Overlay

Dim Pitch As Word
Dim Pitch0 As Byte At Pitch + 1 Overlay
Dim Pitch1 As Byte At Pitch Overlay

Dim _yawoffset As Long
Dim _rolloffset As Long
Dim _pitchoffset As Long

Dim _yawnow As Integer
Dim _rollnow As Integer
Dim _pitchnow As Integer


Dim Signal(3) As Integer
Dim Pid(3) As Integer

Dim Kp_err(3) As Integer
Dim Ki_err(3) As Integer
Dim Kd_err(3) As Integer
Dim Ki_sum(3) As Integer
Dim Kd_old(3) As Integer

Dim Kp(3) As Single
Dim Ki(3) As Single
Dim Kd(3) As Single

Kp(1) = 0.10
Kp(2) = 1
Kp(3) = 1

Ki(1) = 0
Ki(2) = 0
Ki(3) = 0

Kd(1) = 0
Kd(2) = 0
Kd(3) = 0


Call Wmp_init()
Waitms 500
Call Set_offset()

Enable Interrupts


'Empfänger:
'29000 < kanal(x) < 64000
'mitte = (64000 + 29000) / 2 = 93000 / 2 = 46500
'Ausschlag: 17500

'Servo-Funktion:
'min: 63800, mitte: 62667.5 , max: 61535 --> 2265 Schritte


Const _bl1offset = 0
Const _bl2offset = 0
Const _bl3offset = 0



Do


For I = 1 To 4
_sbl(i) = Kanal(i) - 17500
_sbl(i) = _sbl(i) / 15.4525386
Next I

_sbl(2) = _sbl(2) - 600
_sbl(3) = _sbl(3) - 500

_bl(1) = 62667.5 + _sbl(1)
_bl(2) = _bl(1) - _sbl(3)
_bl(3) = _bl(1) + _sbl(3)
_bl(1) = _bl(1) - _sbl(2)
_bl(4) = 62535 + _sbl(4)

_bl(1) = _bl(1) + _bl1offset
_bl(2) = _bl(2) + _bl2offset
_bl(3) = _bl(3) + _bl3offset


Call Read_data()

_yawnow = Yaw - _yawoffset
_rollnow = Roll - _rolloffset
_pitchnow = Pitch - _pitchoffset

Signal(1) = _yawnow
Signal(2) = _rollnow
Signal(3) = _pitchnow


For I = 1 To 3
'Proportional
Kp_err(i) = Signal(i) * Kp(i)
'Kp_err(i) = Kp_err(i) / 10
'Integral
Ki_err(i) = Signal(i) * Ki(i)
'Ki_err(i) = Ki_err(i) / 10
Ki_sum(i) = Ki_sum(i) + Ki_err(i)
'Differential
Kd_err(i) = Signal(i) * Kd(i)
'Kd_err(i) = Kd_err(i) / 10
Kd_err(i) = Kd_old(i) - Kd_err(i)
Kd_old(i) = Kd_err(i)
'Aufsummieren
Pid(i) = Kp_err(i) + Ki_sum(i)
Pid(i) = Pid(i) + Kd_err(i)
Next I

'Pid(1) --> Yaw-PID
'Pid(2) --> Roll-PID
'Pid(3) --> Pitch-PID


_bl(1) = _bl(1) - Pid(3)
_bl(2) = _bl(2) - Pid(2)
_bl(3) = _bl(3) + Pid(2)
_bl(4) = _bl(4) + Pid(1)


_crc = Crc16(_bl(1) , 4)
Printbin Start_byte ; _bl(1) ; _bl(2) ; _bl(3) ; _bl(4) ; _crc


Loop


Measure:
If Channel > 0 And Channel < 5 Then
Kanal(channel) = Timer1
End If
Timer1 = 1536
Incr Channel
Return

Pausedetect:
Channel = 0
Return


Sub Wmp_init()
I2cstart
I2cwbyte &HA6 ' sends memory address
I2cwbyte &HFE ' WM+ activation
I2cwbyte &H04 . ' Now Adress changes to &HA4
I2cstop
End Sub

Sub Send_zero()
I2cstart
I2cwbyte &HA4 ' sends memory address
I2cwbyte &H00 ' sends zero before receiving
I2cstop
Waitms 1
End Sub

Sub Read_data()
Gosub Send_zero ' sends zero before receiving
I2creceive &HA4 , Buffer(1) , 0 , 6 ' receive 6 bytes
Yaw1 = Buffer(1)
Roll1 = Buffer(2) ' Low Bytes
Pitch1 = Buffer(3)
Shift Buffer(4) , Right , 2 : Yaw0 = Buffer(4)
Shift Buffer(5) , Right , 2 : Roll0 = Buffer(5) ' High Bytes
Shift Buffer(6) , Right , 2 : Pitch0 = Buffer(6)
End Sub

Sub Set_offset()
_yawoffset = 0
_rolloffset = 0
_pitchoffset = 0
For I = 1 To 50
Call Read_data
_yawoffset = _yawoffset + Yaw
_rolloffset = _rolloffset + Roll
_pitchoffset = _pitchoffset + Pitch
Next I
_yawoffset = _yawoffset / 50
_rolloffset = _rolloffset / 50
_pitchoffset = _pitchoffset / 50
End Sub

End

ABER:
Egal welche Werte ich für Kp einsetze (hab von 0.1 bis 1000 alles probiert), die Motoren / der Servo reagieren auf keinerlei Bewegung, selbst wenn ich das Teil innerhalb von ca. 0.5sek um 180° drehe. Aber warum? Der Offset vom WM+ liegt bei ca. 8000, das wird auch richtig berechnet.. Nur mit dem PID-Regler klappts nicht?! Wer kann miir weiterhelfen?
Dann habe ich noch eine Frage:
Im Programm stehen die Maximal und Minimal Werte des Empfängers und der Servos. Eigentlich sollte man die Knüppelposition doch so umrechnen können:


For I = 1 To 4
_sbl(i) = Kanal(i) - 46500 '-17500 < _sbl(i) < +17500
_sbl(i) = _sbl(i) / 15.4525386 '-1132 < _sbl(i) < +1132
Next I
_bl(1) = 62667 - _sbl(1)

Jedoch laufen dann die Motoren nicht so wie sie sollen. Normalerweise sollte doch Motor 1 (_bl(1)) nicht laufen, wenn der 1. Stick unten ist und die volle Drehzahl erreichen, wenn der Stick oben ist.. Das ist bei mir aber nicht so. Habe ich einen Denkfehler?

Vielen Dank
Gruß
Chris

Besserwessi
16.05.2011, 17:36
Beim PID Code kommt mir die Berechnung des Ableitungsteils komisch vor: als alter Wert für die Differenzbildung wird bereits die Differenz gespeichert. Ich hätte da erwartet das man sich den Wert ohne die Differenzbildung merkt. So wie ich es kenne wird auch einfach die alte Regelabweichung gemerkt, also noch vor der Multiplikation mit Kd - das ist aber vielleicht auch nur Geschmackssache.

So ein PID Regler ist nicht ganz einfach einzustellen. Der erste Test wäre erstmal nur als Proportionalregler, also Ki = Kd = 0.

Beim Code für den Servo Controller (Tiny2313) kommt mir die ISR komisch vor: die Kanalnummer wird in den IF Abfragen erhöht. Wenn man mit Kanalnr 0 anfängt paßt dann erst die NR 0 und dann auch noch die 1 usw. Durch die Kanalnr. ist eigentlich auch der Zustand der Ports festgelegt und muss nicht extra Abgefragt werden. Also immer einen IO-Pin setzen und einen IO-Pin Löschen.

Che Guevara
16.05.2011, 17:50
Bei meinem jetzigen / obigen Code habe ich Ki und Kd auf 0 gesetzt, lediglich für Kp habe ich versch. Werte eingesetzt...
Der Code für die Servos funktioniert einwandfrei, er läuft jetzt seit ca. 1 std im Dauerbetrieb ohne auch nur einen einzigen Fehler ;)

Gruß
Chris

EDIT:
@Besserwessi: Versteh mich bitte nicht falsch, aber ich möchte am ATTINY2313 ungerne noch etwas verändern, solange es so funktioniert.. Das Teil hat mich schon genug Nerven gekostet, sodass ich wirklich froh bin, dass alles läuft! Für Anregungen, Tips, whatever bin ich aber trotzdem dankbar ;)

Che Guevara
16.05.2011, 21:27
So, nach ein paar zeitraubenden Tests habe ich jetzt herausgefunden, dass meine oben genannten Empfänger-werte nicht stimmten ...
Leider weichen die Werte von Kanal zu Kanal geringfügig ab, sodass ich für jeden Kanal mit anderen Werten rechnen muss.
Hier mal mein momentaner Code:


$regfile = "m8def.dat"
$crystal = 16000000
$framesize = 80
$hwstack = 80
$swstack = 80
$baud = 38400 '115200


Declare Sub Wmp_init()
Declare Sub Send_zero()
Declare Sub Read_data()
Declare Sub Set_offset()



$lib "I2C_TWI.LBX" 'Hardware I2C
Config Scl = Portc.5 'Ports for I2C-Bus
Config Sda = Portc.4
Config Twi = 400000
I2cinit


Config Timer1 = Timer , Prescale = 1
On Timer1 Pausedetect
Enable Timer1

Config Int1 = Falling
On Int1 Measure
Enable Int1


Config Pind.3 = Input
Portd.3 = 0


Const Start_byte = 127
Const _maxchannel = 4

Dim Bufferbyte As Byte
Dim Kanal(_maxchannel) As Word
Dim Channel As Byte
Dim _bl(_maxchannel) As Word
Dim I As Byte
Dim _crc As Word
Dim _sbl(_maxchannel) As Integer


Dim Buffer(6) As Byte

Dim Yaw As Word
Dim Yaw0 As Byte At Yaw + 1 Overlay
Dim Yaw1 As Byte At Yaw Overlay

Dim Roll As Word
Dim Roll0 As Byte At Roll + 1 Overlay
Dim Roll1 As Byte At Roll Overlay

Dim Pitch As Word
Dim Pitch0 As Byte At Pitch + 1 Overlay
Dim Pitch1 As Byte At Pitch Overlay

Dim _yawoffset As Long
Dim _rolloffset As Long
Dim _pitchoffset As Long

Dim _yawnow As Integer
Dim _rollnow As Integer
Dim _pitchnow As Integer


Dim Signal(3) As Integer
Dim Pid(3) As Integer

Dim Kp_err(3) As Integer
Dim Ki_err(3) As Integer
Dim Kd_err(3) As Integer
Dim Ki_sum(3) As Integer
Dim Kd_old(3) As Integer

Dim Kp(3) As Single
Dim Ki(3) As Single
Dim Kd(3) As Single

Kp(1) = 0.50
Kp(2) = 0.50
Kp(3) = 0.50

Ki(1) = 0
Ki(2) = 0
Ki(3) = 0

Kd(1) = 0
Kd(2) = 0
Kd(3) = 0


Call Wmp_init()
Waitms 500
Call Set_offset()


'Servo-Funktion:
'min: 63800, mitte: 62667.5 , max: 61535 --> 2265 Schritte


Dim _empfmiddle(4) As Word
Dim _empfmin(4) As Word
Dim _empfmax(4) As Word
Dim _empfdiv(4) As Word

_empfmiddle(1) = 26500
_empfmiddle(2) = 23800
_empfmiddle(3) = 25300
_empfmiddle(4) = 22250

_empfmin(1) = 14300
_empfmin(2) = 14650
_empfmin(3) = 17100
_empfmin(4) = 14750

_empfmax(1) = 32300
_empfmax(2) = 32600
_empfmax(3) = 32600
_empfmax(4) = 30500

For I = 1 To 4
_empfdiv(i) = _empfmiddle(i) - _empfmin(i)
_empfdiv(i) = _empfdiv(i) / 2265
_empfdiv(i) = _empfdiv(i) * 2
Next I



Const _bl1offset = 0
Const _bl2offset = 0
Const _bl3offset = 0
Const _bl4offset = 700


Enable Interrupts


Do


For I = 1 To 4
_sbl(i) = Kanal(i) - _empfmiddle(i)
_sbl(i) = _sbl(i) / _empfdiv(i)
Next I

_bl(1) = 62667 - _sbl(1)
_bl(2) = _bl(1) - _sbl(3)
_bl(3) = _bl(1) + _sbl(3)
_bl(1) = _bl(1) + _sbl(2)
_bl(4) = 62667 + _sbl(4)

_bl(1) = _bl(1) + _bl1offset
_bl(2) = _bl(2) + _bl2offset
_bl(3) = _bl(3) + _bl3offset
_bl(4) = _bl(4) + _bl4offset


Call Read_data()

_yawnow = Yaw - _yawoffset
_rollnow = Roll - _rolloffset
_pitchnow = Pitch - _pitchoffset

Signal(1) = _yawnow
Signal(2) = _rollnow
Signal(3) = _pitchnow


For I = 1 To 3
'Proportional
Kp_err(i) = Signal(i) * Kp(i)
'Kp_err(i) = Kp_err(i) / 10
'Integral
Ki_err(i) = Signal(i) * Ki(i)
'Ki_err(i) = Ki_err(i) / 10
Ki_sum(i) = Ki_sum(i) + Ki_err(i)
'Differential
Kd_err(i) = Signal(i) * Kd(i)
'Kd_err(i) = Kd_err(i) / 10
Kd_err(i) = Kd_old(i) - Kd_err(i)
Kd_old(i) = Kd_err(i)
'Aufsummieren
Pid(i) = Kp_err(i) + Ki_sum(i)
Pid(i) = Pid(i) + Kd_err(i)
Next I

'Pid(1) --> Yaw-PID
'Pid(2) --> Roll-PID
'Pid(3) --> Pitch-PID

_yawnow = _yawnow / 2


_bl(1) = _bl(1) - Pid(3)
_bl(2) = _bl(2) - Pid(2)
_bl(3) = _bl(3) + Pid(2)
_bl(4) = _bl(4) '- _yawnow


_crc = Crc16(_bl(1) , 4)
Printbin Start_byte ; _bl(1) ; _bl(2) ; _bl(3) ; _bl(4) ; _crc


Loop


Measure:
If Channel > 0 And Channel < 5 Then
Kanal(channel) = Timer1
End If
Timer1 = 1536
Incr Channel
Return

Pausedetect:
Channel = 0
Return


Sub Wmp_init()
I2cstart
I2cwbyte &HA6 ' sends memory address
I2cwbyte &HFE ' WM+ activation
I2cwbyte &H04 . ' Now Adress changes to &HA4
I2cstop
End Sub

Sub Send_zero()
I2cstart
I2cwbyte &HA4 ' sends memory address
I2cwbyte &H00 ' sends zero before receiving
I2cstop
Waitms 1
End Sub

Sub Read_data()
Gosub Send_zero ' sends zero before receiving
I2creceive &HA4 , Buffer(1) , 0 , 6 ' receive 6 bytes
Yaw1 = Buffer(1)
Roll1 = Buffer(2) ' Low Bytes
Pitch1 = Buffer(3)
Shift Buffer(4) , Right , 2 : Yaw0 = Buffer(4)
Shift Buffer(5) , Right , 2 : Roll0 = Buffer(5) ' High Bytes
Shift Buffer(6) , Right , 2 : Pitch0 = Buffer(6)
End Sub

Sub Set_offset()
_yawoffset = 0
_rolloffset = 0
_pitchoffset = 0
For I = 1 To 50
Call Read_data
_yawoffset = _yawoffset + Yaw
_rolloffset = _rolloffset + Roll
_pitchoffset = _pitchoffset + Pitch
Next I
_yawoffset = _yawoffset / 50
_rolloffset = _rolloffset / 50
_pitchoffset = _pitchoffset / 50
End Sub

End
Jetzt funktioniert wenigstens schon mal der Fernbedinungsteil :) Der PID-Regler macht aber trotzdem noch nichts... Außerdem habe ich gerade ein für mich unerklärliches Phänomen entdeckt:
Wenn ich


_bl(4) = _bl(4) - _yawnow
schreibe, dreht sich der Servo (_bl(4)) in beide möglichen Richtungen, abhängig von der Drehung des WM+ (i-wie logisch ^^)
Schreibe ich allerdings


_bl(4) = _bl(4) + _yawnow
(man achte auf das "+" statt "-")
so dreht er sich nur in eine Richtung (der Servo), die andere geht nur mit der Fernbedinung, jedoch nicht durch drehen des WM+?!
Kann sich das jemand erklären? Oder ist es evtl. ein Bug?
Übrigens habe ich jetzt die Baudrate auf beiden Chips auf 38.4k gesenkt, sollte auch genügen und der Fehler beträgt hier nur 0.2%.
Aber dazu habe ich auch noch eine Frage:
Ist es nicht egal, wie groß der Fehler in Prozent ist, wenn beide Chips die gleiche Baudraten-Einstellung und den gleichen Quarz haben? Dann hebt sich doch der Fehler des einen durch den Fehler des anderen auf, oder sehe ich das Falsch?

Gruß
Chris

Besserwessi
16.05.2011, 22:28
Der komische Effekt mit _bl(4) +- _yawnow könnte durch die Datentypen verursacht werden. _bl(4) ist Word und _yawnow ist Integer. Da könnte es Probleme mit Überläufen kommen, wenn das Ergebnis nicht mehr in eine Interger variable paßt. Normal wird Word und Integer bei der Addition / Subtraction ja gleich bearbeitet, nur ist dann nicht klar wie man das Ergebnis Interpretieren soll - ist das dann ein Word oder Integer.

Ein Fehler in der Baudrate sollte sich auch beide µCs gleich auswirken. Es kann aber kleine Unterschiede geben, weil die Teiler für die Baudrate ggf. etwas anders sind. Tiny2313 und Mega8 sollte hier aber gleich sein.

Che Guevara
17.05.2011, 17:05
Hm, also dass der Fehler in den versch. Typen liegt, habe ich mir schon fast gedacht... Aber ich dachte, da ja _bl(4) ein Word ist, ist doch klar, als was das Ergebnis interpretiert werden muss, als WORD!? ... Das komische daran ist ja, dass es mit - funktioniert, nur mit + nicht...
Aber ok, ist wohl so! Wie kann ich das Problem nun umgehen? Beide als Integer oder als Word deklarieren geht nicht, da dann die Wertebereiche nicht mehr passen würden? Habt ihr eine Idee? Oder muss ich erst alles i-wie in andere Wertebereiche umrechnen, dann subtrahieren/addieren und dann wieder "in die alten Wertebereiche zurückrechnen"? Das wäre doch schon sehr umständlich!
Evtl. hat da jemand einen heißen Tip für mich? :)

Gruß
Chris

Besserwessi
17.05.2011, 17:23
Um zu sehen was der Compiler aus dem Code macht, müsste man sich mal den ASm Code ansehen. Wohl nicht mit dem Ganze Programm, sonder eine kleines Testprogramm mit nur 2-3 Zeilen. Das sich da - und + unteschiedlich verhalten ist schon komisch, könnte aber auch an den Werten liegen die dabei rauskommen.

Es sollte gehen, wenn man das Ergebnis erstmal in eine Variable vom Typ long steckt und dann in erst in Word.

Che Guevara
17.05.2011, 18:11
Ich muss ehrlich gestehen, dass ich nicht weiß, wo sich der asm Code befindet? Hab damit noch nie gearbeitet...
Hm ok, ein long ist zwar 4 Bytes groß, aber ok, wenns nicht anders geht mach ichs vorerst mal so! Danke für die Hilfe

Gruß
Chris

EDIT:
Gerade ist mir aufgefallen, dass mein _yawnow woll garnicht richtig berechnet wird. Bei einem Test habe ich


_yawnow = _yawnow * -1
geschrieben, und wollte damit eigentlich die Drehrichtung umkehren... Doch ich musste feststellen, dass _yawnow bei keiner Bewegung der WM+ garnicht 0 ist?! Ohne diese Invertierung steht der Servo ohne Bewegung leicht nach links geneigt, mit dieser Invertierung jedoch nach rechts?! Das bedeutet doch, dass _yawnow nicht Null (0) ist...
Werds dann gleich mal ausprobieren, _yawnow mit anderen Datentypen zu berechnen!
Mittels der Umwandlung in eine Long-Variable hab ichs auch nicht hinbekommen, hab mehrere (alle, die mir eingefallen sind) Möglichkeiten ausprobiert, jedoch ohne Erfolg!

EDIT2:
Gerade ist mir eingefallen, dass ich evtl. Framesize zu klein gemacht habe... Hab jetzt mal provisorisch sw-, hw-stack und framesize raufgesetzt, jetzt klappts auch mit


_bl(4) = _bl(4) + _yawnow

Bin vorher auch schon ein bisschen geflogen, jedoch muss ich morgen etwas Pappelholz kaufen, damit das Teil leichter wird (momentan noch teilweise aus Alu)...
Zum Thema PID, werds dann morgen mal probieren, obs jetzt mit dem größeren Framesize klappt, aber erstmal Physik-Klausur ....