mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik RS232 Steuerungs-Ablauf


Autor: avrler (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Thorsten (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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ß

Autor: avrler (Gast)
Datum:

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

avrler

Autor: Thorsten (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
http://www.atmel.com/dyn/products/app_notes.asp?fa...
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ß

Autor: Ralf (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Rufus Τ. Firefly (rufus) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Aus Forum "Codesammlung" verschoben.

Autor: avrler (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: avrler (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Thorsten (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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ß

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

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

Autor: avrler (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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 !!!!

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.