Hallo,
ich würde gerne von euch wissen, ob die nachfolgende Überlegung sinnvoll
ist, oder ob ihr einen anderen Ansatz wählen würdet. Ich möchte mit
einem Atmega8 vier Servos ansteuern, dafür möchte ich 250 Positionen
festlegen die per USB übertragen werden sollen.
Dazu habe ich mir folgendes Protokoll ausgedacht: Es wird immer als
erstes Zeichen ASCII/255 gesendet, dann ASCI/251-254 zur Auswahl des
Servos und letztendlich der Wert als ASCII/0-250.
Erste Frage: Ist das sinnvoll, oder geht es einfacher?
Zweite Frage: Wird die unten angegebene Interruptroutine dem oben
beschriebenen Protokoll gerecht? Wenn ich es richtig verstanden habe
wird ja bei Eintritt in die Interruptroutine diese nicht wieder von
einem Interrupt unterbrochen. Und jedesmal beim auslesen des UDR wird
das RXC wieder gelöscht. Sollte im UDR nicht der Schlüsselwert gelesen
werden beim Eintritt in den Interrupt, wird dieser verlassen und beim
nächsten Zeichen wieder aufgerufen.
Danke fürs drüberschauen, bevor ich das so weiterverfolge.
Gruß,
Tobias
Achso, jetzt habe ich doch tatsächlich die brakes bei der Caseanweisung
vergessen, sorry. Und noch was ist mir eingefallen, sollte man das UDR
vor der UDR==255-Abfrage vielleicht lieber auch in einen Puffer
schreiben und erst dann die if-Abfrage durchführen, oder bleibt sich das
gleich?
>In einer ISR wartet man sicher nicht. Mach eine Zustandsmaschine>aussenrum.
Also ungefähr so (evtl noch in case 1 und 2 die Abfrage des
Wertebereichs, aber vom Prinzip her)?
Hier ein kleiner Auszug wie ich es gelöst habe, es funktioniert, wie
elegant es ist weiss ich nicht ;-)
String Start/Stopbits benutze ich nicht, nur ein rudimentäres System
gegen zu lange und zu Kurze Kommandos.
Ein Kommando hat immer 4 Bytes bei mir:
Sender ID
Kommando Typ
Wert 1
Wert 2
Der RX-Interrupt setzt das Flag das das Kommando pollt, inklusive
Timeout:
1
// UART command receive interrupt
2
ISR(USART_RXC_vect){
3
4
// save first byte as sender ID
5
cmd_bf[0]=UDR;
6
7
// set uart active flag
8
uart_active=1;
9
10
// disable interrupt
11
UCSRB&=~(1<<RXCIE);
12
}
13
14
// get command string from uart
15
voiduart_getcmd(void){
16
17
if(uart_active==1){
18
19
// save 3 incoming bytes in buffer, cmd_bf[0] reserved and fetched in interrupt
20
uint8_ti=1;
21
while(i<4){
22
cmd_bf[i]=uart_getc();
23
i++;
24
}
25
26
// when complete string received set new command flag
Ich wollte mich nur kurz für die Überlegungen bedanken, meine oben
gepostete Variante habe ich noch etwas angepasst:
1
ISR(USART_RXC_vect)// Interruptroutine bei Zeichenempfang
2
{
3
buffer=UDR;// Zwischenspeichern
4
switch(bytenummer)// Abfrage der Bytenummer
5
{
6
case0:// Steuerbyte (muss 255 sein)
7
if(buffer==255)bytenummer=1;
8
break;
9
10
case1:// Auswahlbyte (25x, x=Servonummer)
11
if((buffer>=251)&&(buffer<=254))
12
{
13
auswahl=buffer-251;
14
bytenummer=2;
15
}
16
else
17
{
18
bytenummer=0;
19
}
20
break;
21
22
case2:// Datenbyte (0 bis 250)
23
if(buffer<=250)
24
{
25
servo[auswahl]=buffer;break;
26
}
27
bytenummer=0;
28
break;
29
}
30
}
1.Ich nutze jetzt ein Array zum Ablegen der Werte, spart die zweite
case-Abfrage
2. Der Wertebereich wird noch geprüft und evtl. wieder auf Byte 0
resettet
Ich bin sehr glücklich mit meiner ersten Ansteuerung über USB, läuft
besser als erwartet.
Genau das ist sehr suboptimal.
Da werden die Servos aus dem Takt kommen wenn du zuviel/zuoft Daten über
den UART empfängst.
In den Interrupt gehört nur was unbedingt notwendig ist, nicht mehr.
Ich hab auch erst gedacht "das macht schon nix" und dann Probleme
bekommen, seitdem wird der Code auf ein minimum begrenzt.
Wie in meinem Beispiel, nur ein Flag setzen und vielleicht ein Paket
abholen, mehr nich.
Mfg,
Peter
>Genau das ist sehr suboptimal.>Da werden die Servos aus dem Takt kommen wenn du zuviel/zuoft Daten über>den UART empfängst.
Werden sie deshalb nicht, weil vor dem Ansteuern eines Servos ein cli()
und danach ein sei() steht, die Ansteuerung eines jeden Servos stimmt
also weiterhin, wenn ich Glück habe geht noch nicht einmal ein Byte
verloren.
Auch die maximal zu sendenden Daten halten sich in Grenzen, denn deren
Menge bestimmt sich eigtl. einzig und allein von der
Ausführgeschwindigkeit der Software die die Daten generiert und das ist
die begrenzende Größe in meinem System (evtl. noch die
Stellgeschwindigkeit der Servos selbst), im Moment sende ich Daten alle
5ms und es klappt hervorragend, mehr braucht es nicht und dafür reicht
es nach meinen bisherigen Tests, aber falls es Probleme gibt, weiß ich
wo ich ansetzen muss und ich werde mich gleich auf die ISR stürzen,
danke für den Hinweis.
Ich war jetzt davon ausgegangen das du soft-PWM benutzt, da würde es
nichts bringen vorher die interrupts zu sperren.
Bei Hardware-PWM hast du allerdings recht.
Ich steuere 8 Servos mit Soft-PWM per UART da gibt es probleme wenn
zulang im UART Interrupt getrödelt wird.
Trotzdem bleib ich dabei, state-machine im Interrupt ist böse. ;-P
Peter F. wrote:
> Ich steuere 8 Servos mit Soft-PWM per UART da gibt es probleme wenn> zulang im UART Interrupt getrödelt wird.> Trotzdem bleib ich dabei, state-machine im Interrupt ist böse. ;-P
Das ist Quatsch mit Soße.
Statemachines sind keine Zeitfresser, wie Delays, Division, float,
printf.
Größere Statemachines werden als Sprungtabelle ausgeführt, so daß selten
>20 Zyklen benötigt werden.
Der Interrupthandler ist vollkommen o.k., dürfte <100 Zyklen sein.
Kürzer gehts nur unwesentlich.
Peter