-
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:
Code:
$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:
Code:
$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
-
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.
-
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:
Code:
$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:
Code:
$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
-
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.
-
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:
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
-
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.
-
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:
Code:
$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:
Code:
$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?
-
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.
-
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
-
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:
Code:
$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:
Code:
$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.