Forum: Mikrocontroller und Digitale Elektronik RS232 Steuerungs-Ablauf


von avrler (Gast)


Lesenswert?

Hallo miteinander,

ich bin mal wieder an einem kleinen Porjekt bei dem ich 2
Schrittmotoren über die serielle Schnittstelle steuern möchte. Also
Mega8 + tmc222 und max232. Hardwaretechnisch ist das ganze kein Problem
nur stellt sich mir bei jedem meiner Projekte die Frage wie macht man
das wenn man Daten vom PC zum µC schickt.
Bei dem jetzigen Projekt ist es so das der PC die Positionsdaten quasi
x-Achse: 298472 y-Achse: 88292 an den Controller schicken soll er diese
Position anfahren solll und evt. dann sagen soll das die Positionierung
abgeschlossen ist. Dann sollen evt. noch 1 oder 2 Relais gesteuert
werden also müsste nur sowas wie Relais1=1, Relais2=0,... übertragen
werden.
Wie setzt man das ganze nun softwaremäßig um ?
EInen interrupt auf das Recive der seriellen und dann jedesmal
Select Case Input
  Case Relais1=1: Portb.1 = 1;
  Case y-achse:   i2cout ......
...

Irgendwie muss man doch da ein einheiltiches Format treffen?
z.b "#wertxachse;wertyachse;statusrelais1;statusrelais2;...
nur irendwann ist doch dann ende oder ? da wird der  zu übertragende
string so ellen lang!

habt ihr ein paar ideen dazu wie macht ihr es wenn der computer viele
sachen auf dem controller einstellen soll bzw. übertragen soll (als
befehl quasi)
quellcodes , ideen, anregungen ....

gruß avrler

von Thorsten (Gast)


Lesenswert?

denk dir n protokoll aus, in das du die befehle verpackst.

z.b.

startbyte (immer gleich)
anzahl bytes die folgen
---
nutzlast
--
checksumme

und lass den controller die befehle interpretieren, die in der nutzlast
drinstehen.
musst natürlich die länge der nutzdaten an die resourcen im controller
anpassen.

gruß

von avrler (Gast)


Lesenswert?

ja genau um dieses interpretieren der gesendeten befehle geht es mir -
wie setzt man das elegant um.

avrler

von Thorsten (Gast)


Lesenswert?

http://www.atmel.com/dyn/products/app_notes.asp?family_id=607
lad dir da mal die AVR068 appnote runter.
da is z.b. son serielles protokoll beschrieben.
im 1. byte der nutzlast steht der befehl, im rest die daten die der
befehl vearbeitet.

um das leserlich zu machen kannst du dir da ne headerdatei runterladen
wo die befehle und antworten vom stk500 board definiert sind.

kannst darauf ja z.b. aufbauen.

gruß

von Ralf (Gast)


Lesenswert?

Mal ne kleine Querfrage zum Protokoll aus der Appnote, reicht für eine
einigermassen sichere Übertrag die Exklusiv-Veroderung der Daten aus,
oder lieber einen "kompletten" CRC?

Ralf

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Aus Forum "Codesammlung" verschoben.

von avrler (Gast)


Lesenswert?

danke das beispiel aus der apnote ist schonmal sehr gut !! hat jemand
noch ein direktes beispiel wie er sowetwas löst ? wie ist die
praktische umsetzung im gcc !? ich muss den empfangenen string dann ja
irgendwie am besten mit der suche nach einem trennzeichen in die
einzelnen befehle kappen und die dann ausführen mit den jeweils
übergebenen werten ( in meinem fall z.b schrittanzahl)

dankbar für alles was kommt g

avrler

von Karl H. (kbuchegg)


Lesenswert?

zuallererst trennst du mal grundsätzlich das empfangen der
Zeichen von der Auswertung. Beides hat nichts miteinander
zu tun.
In der Empfangs-ISR speicherst du die empfangenen Zeichen
einfach in einem Buffer. Wenn du ein vernünftiges Protokoll
definierst, dann mach das so, dass du den Anfang deines
kompletten Kommandos eindeutig erkenne kannst. Stell dir
einfach vor, jemand liest aus einem Buch vor. Er tut dies
indem er die Wörter buchstabiert und auch Satzzeichen ausspricht.
Deine Aufgabe ist es jetzt aus dem was er vorliest ganze Sätze
zu bilden. Dein Problem: Wenn du in den Raum kommst, liest der
schon eine Weile. Du brauchst alse eine Methode um mit absoluter
Sicherheit den nächsten Satzanfang zu erkennen. Denk dir dazu was
aus! Nimm mein (blödes) Beispiel und überleg mal, wie du da das
machst. Das ist überhaupt eine gute Strategie! Wenn du vor einem
Problem stehst transformiere es in deine Welt und überlege
und beobachte dich selbst wie du dieses Problem löst. Wir Menschen
machen viele Dinge und sind uns gar nicht bewusst wie wir sie
machen. Programmieren heist auch seine Umwelt, einschliesslich
sich selbst, zu beobachten und lernen wie dort Probleme
gelöst werden.

Soweit, so gut: Wenn die ISR also signalisiert, zb über eine
globale Variable, liegt in einem Character Buffer ein komplett
übertragener Befehl  vor. Na ja, und den wertest du aus.
zb. in main()

volatile unsigned char CommandReveived;
char Command[30];  // oder wieviele du halt brauchst

ISR( ... )
{
  // empfängt ein Kommando Zeichen für Zeichen
  // und stellt das Kommando in 'Command' zur Verfügung
  // Ist ein Kommando vollständig wird 'CommandReceived'
  // auf TRUE gestellt
}

int main()
{

  ...
  while( 1 ) {
    if( CommandReceived ) {
      ...
      switch(Command[0]) {    // 1. Zeichen ist der Befehl

        case 'M':     // Kommando 'MoveTo'
                      // die restlichen empfangenen Zeichen
                      // geben die Position an
          HandleMove();
          break;

        case 'U':     // Kommando 'Up'
                      // Keine weitere Info notwendig
                      // Stift, Fräser abheben
          HandleUp();
          break;

        case 'D':     // Kommando 'Down'
                      // Stift, Fräser absenken
          HandleDown();
          break;

        case 'W':     // Kommando Kühlwasser
                      // das nachste Byte imm Buffer sagt aus
                      // ob 'Wasser ein' oder'Wasser aus'
          HandleWater();
          break;

        ...

                      // die UART wieder scharf stellen
        CommandReceiver = FALSE;
    }
  }
}

So ungefähr. Obiges ist nicht optimal, aber es ist mal ein
Anfang. Das Problem liegt darin, dass während die Verarbeitung
eines Kommandos läuft kein weiteres empfangen werden kann.
Ist lösbar. Aber eines nach dem anderen.

von avrler (Gast)


Lesenswert?

ok danke das ist wirklich sehr hilfreich gewesen !!! du verfolgst einen
sehr guten ansatz der wie ich denke zu jedem problem eine "mögliche"
lösung bietet g!

jedoch sehe ich auch daraus das es keine alternative zu einer case oder
if auswertung gibt. ?
ich habe gehört solche sachen werden mit hilfe von lookuptables gemacht
?! jedoch kann ich mir darunter gerade absolut nichts vorstellen !

danke für eure hilfe !!!

schönen abend

von Karl H. (kbuchegg)


Lesenswert?

> jedoch sehe ich auch daraus das es keine alternative zu einer case
> oder if auswertung gibt. ?

Oh. Da gibt es noch jede Menge anderer Möglichkeiten.
Die von dir angesprochene Lookup-Tabelle ist zb eine.
Ich gehe allerdings davon aus, dass du nur wenige Kommandos
auf der Schnittstelle hast. Da ist eine switch-case Leiste
mehr als ausreichend und erfüllt seinen Job hervorragend.

Eine Lookup Tabelle könnte zb so aussehen:
Gesucht ist duch die Umsetzung eines Kommandos (in Form eines
Characters) in einen Funktionsaufruf.
Da gibt es jetzt mehrere Möglichkeiten, je nachdem über wieviele
zu erkennenden Kommandos wir reden. Sind es relativ viele, so dass
das Alphabet für das Kommandobyte gut ausgenutzt wird, so macht
man sich ganz einfach ein Array, wobei dann das Kommandobyte als
Index in dieses Array benutzt wird (Handlexxx sind jeweils Funktionen
die dafür zuständig sind ein Kommando abzuarbeiten).

    typedef void (*Fnct)(void);

    Fnct Functions[] =
      { HandleA,
        HandleB,
        HandleC,
        HandleD,
        NULL,
        HandleE,
        ... etc
        HandleZ
      };

     void HandleA()
     {
        // Bearbeite das Kommando 'A'
     }

     void HandleB()
     {
       // Bearbeite das Kommando 'B'
     }

     ...

Damit ist die Bühne aufgesetzt, jetzt muss nur noch in main(),
abhängig vom Kommandobyte die richtige Funktion aufrufen:

     ...
     if( Functions[ Command[0] - 'A' ] )
       Functions[ Command[0] - 'A' ]();

(Command[0], also das erste Byte, wird als index in die Tabelle
von oben mit den Funktionspointern genommen und die Funktion ueber
den Funktionspointer aufgerufen.

Möglichkeit 2:
Wenn es nur wenige Kommandos sind, dann ist obiges Array etwas
überkandidelt. Die meisten Einträgre sind NULL. Dann ist es ev.
besser sich eine Struktur zu basteln

  struct Entry {
    char Command;
    Fnct Function;
  }

Damit kann ich dann gezielt ein Array bauen:

  struct Entry Functions[] = {
     { 'M', HandleMove },
     { 'U', HandleUp },
     { 'D', HandleDown },
     { 'W', HandleWater }
  }

Nette kleine Tabelle :-)
Jetzt geht man mit dem ersten Zeichen des Kommandos wieder her
und durchsucht die Tabelle nach dem entsprechenden Eintrag. Hat
man ihn gefunden, hat man dann auch den Funktionspointer und kann
ueber diesen die Funktion aufrufen. Bei derartig wenig Kommandos
kann man ganz einfach linear suchen:

    for( i = 0; i < sizeof(Functions)/sizeof(*Functions); ++i )
      if( Command[0] == Functions[i].Command )
        Functions[i].Function();

Wenn es mehr Kommandos sind, könnte man auch eine binäre suchen
machen, oder das Ganze über eine Hashtable realisiern

Eine komplett andere Möglichkeit wäre es einen schönen Parser
aufzusetzen. Nimmt man dann wenn man nicht nur Kommandos mit
einem Buchstaben hat, sondern Schlüsselwörter.

Oder ...

Es gibt noch viele Möglichkeiten.
Aber für wenige Kommandos dürfte eine switch-case Leiste
das Einfachste aber nicht notwendigerweise das Schlechteste sein.

von Thorsten (Gast)


Lesenswert?

tja, was will man mehr. c erklärungen in perfektion.

nebenbei: ich empfange das protokoll aus atmels appnote per
uart-polling. ist zwar eher unelegant aber viel was anderes hat der
controller sonst nicht zu tun.

in der auswerteroutine nutze ich dann eine switch-case konstruktion auf
das 1. byte in der nutzlast und bearbeite dann den rest der nutzdaten je
nach befehl unterschiedlich.

zum thema xor über alle bytes oder richtiges crc, wird wohl die
umgebung ausschlaggebend sein, in der das ganze zum einsatz kommen
soll.

für den basteltisch oder ähnliches sollte xor wohl ausreichen.

gruß

von Karl H. (kbuchegg)


Lesenswert?

Für den Basteltisch würde ich sogar ganz darauf verzichten
und einfach auf der UART-Ebene die Parity einschalten.

von avrler (Gast)


Lesenswert?

Ok ich steige durch g! ! ! Deine Erklärungen kbuchegg sind wirklich
->perfect<- !
jetzt wo ich das blut geleckt habe - hat jemand noch ein beispiel für
einen parser ? damit sollte ja dann so wirklich alles abgedekt sein
egal ob es 10 oder 100 befehle sind die ich verarbeiten will. wenn man
so einen parser genauso einfach aufbauen könnte wie eine case auswahl
... ein traum g
bei einem parser wäre es dann wahrscheinlich so das ich den beginn des
command string erstmal erkennen muss so wie in der appnote 0x1b oder
was es da war. dann fange ich an den string stück für stück
auszuwerten. die reihenfolge ist ja immer die gleiche erst der befehl
(die funktion) dann der dazugehörige wert. wenn ich meinen string 20
zeichen lang mache könnte ich dann wenn ich pro funktionsbeschreibung 2
zeichen und pro wert 3 zeichen verwende jeweils max. 4 befehle pro
string übertragen. der praser sucht dann die erste
funktion(sbeschreibung) schreibt den in eine variable sucht dann weiter
nach dem dazugehörigen wert und schreibt diesen wieder in eine variable.
dann suche ich die funktion (über den inhalt in der varibalen in der ich
die abkürzung gespeichert habe), springe in die funktion, führe sie aus
mit dem funktionswert den ich in der anderen variable gespeichert habe
- oder ?

ich glaube das wäre genau das richtige - denke ich ....

eure hilfe - einfach super !!!!

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.