Inhaltsverzeichnis   zurückvor

Das erste Programm zur Steuerung

Allgemeines

Da ich seit kurzem als Betriebssystem Linux verwende, werde ich auch alle Programme für Linux schreiben. Die Programme werde ich wie Linux selbst und die verwendeten Tools unter die GNU General Public License (GPL) stellen.
Als erstes werde ich ein Programm in C schreiben welches auf der Konsole (unter Windows wäre das die Kommandozeile) ausgeführt werden kann.

Aufbau und Ablauf

Denn Aufbau des Programms stelle ich mir wie folgt vor: Die drei Teile auslesen der Tastatur, verzögern und ausgeben der Zeichen betrachte ich einzeln und werde sie nach ihrer Lösung zu einem Programm kombinieren. Die drei Teile bilden dann ein Unterprogramm (in C Funktion genannt).

Auslesen der Tastatur

Die Funktion muss die Tastatur auslesen ohne auf einen Tastendruck zu warten, falls eine Taste gedrückt wurde muss eine Variable mit deren Wert gesetzt werden.
Ich musste feststellen, dass diese Aufgabe unter Linux nicht ohne weiteres zu lösen ist. Nach mehreren Wochen vergeblicher Suche fand ich eine Lösung im Buch "Linux Programmierung". Im nachhinein denke ich, dass mein Programmablauf nicht optimal ist und ein Threading-Programm (zwei Programmteile laufen gleichzeitig ab) besser gewesen wäre, ein Teil würde die Tastatur überwachen und der andere den Motor steuern.

Listing Tastatur auslesen

// abgeschrieben von thomas@fam-kuster.ch aus dem Buch "Linux Programmierung"
// von Richard Stones und Neil Matthew
// 1. Auflage 2000, MITP Verlag GmbH, Bonn (http://mitp.de)
// S. 203ff
// Copyleft: GNU-Public License Version 2 oder höher (http://www.gnu.org)

#include <stdio.h>
#include <termios.h>
#include <ter <curses.h>
#include <unistd.h>

static struct termios initial_settings, new_settings;
static int peek_character = -1;

void init_keyboard();
void close_keyboard();
int kbhit();
int readch();

int main()
{
  int ch = 0;

  init_keyboard();
  printf("\nzum beenden 'q' drücken\n\n");
  while(ch != 'q')
    {
      printf("durchlauf Schlaufe\n");
      sleep(1);                                    //warten nur zur Demonstartion
      if(kbhit())
      	{
	  ch = readch();
	  printf("du hast die Taste %c gedrückt\n",ch);
	}
    }
  close_keyboard();
  exit(0);
}

void init_keyboard()
  {
    tcgetattr(0,&initial_settings);
    new_settings = initial_settings;
    new_settings.c_lflag &= ~ICANON;
    new_settings.c_lflag &= ~ECHO;
    new_settings.c_lflag &= ~ISIG;

    new_settings.c_cc[VMIN] = 1;
    new_settings.c_cc[VTIME] = 0;
    tcsetattr(0, TCSANOW, &new_settings);
  }

void close_keyboard()
  {
    tcsetattr(0, TCSANOW, &initial_settings);
  }

int kbhit()
  {
    char ch;
    int nread;
    
    if(peek_character != -1)
      return 1;
    new_settings.c_cc[VMIN]=0;
    tcsetattr(0, TCSANOW, &new_settings);
    nread = read(0,&ch,1);
    new_settings.c_cc[VMIN]=1;
    tcsetattr(0, TCSANOW, &new_settings);
    
    if(nread == 1)
      {
        peek_character = ch;
	return 1;
      }
    return 0;
  }

int readch()
  {
    char ch;
    if(peek_character != -1)
      {
	ch = peek_character;
	peek_character = -1;
	return ch;
      }
    read(0,&ch,1);
    return ch;
  }

Verzögerung (warten)

Die nächste Funktion sollte so lange warten wie der Wert der ihr übergeben Variable. Den Befehl sleep kannte ich mit ihm lässt sich jedoch nur vielfache von Sekunden warten. Trotz längerer Suche im Internet fand ich keinen einfachen Befehl um Bruchteile einer Sekunde zu warten. Ein "posten" des Problems im "Linux Developer Forum" brachte die Lösung (Danke Shellschrubber(tm)).

Listing Verzögerung (warten)

// thomas@fam-kuster.ch
// Copyleft: GNU-Public License Version 2 oder höher (http://www.gnu.org)

#include <stdio.h>
#include <unistd.h>

int main()
  {
    printf("warten 1000000ms\n");
    usleep(1000000); 
  }

Zeichenausgabe an die parallele Schnittstelle

Diese Funktion sollte die ihr übergebene Variable an die parallele Schnittstelle senden. Ich verwende dazu den Befehl write, mit open wurde zuerst die parallele Schnittstelle zum Schreiben geöffnet (unter Linux/Unix /dev/lp0). Zum Testen des Programms verwendete ich anstelle /dev/lp0 /dev/tty2 (die zweite Konsole) dadurch konnte die Ausgabe auf dem Bildschirm eins zu eins verfolgt werden.
Die jetzige Lösung muss jedoch noch verbessert werden, das Gerät (/dev) wird nur geöffnet jedoch nicht geschlossen, daher funktioniert ein mehrmaliges Aufrufen dieser Funktion nicht immer (bei tty2 ist es möglich, bei lp0 jedoch nicht).

Listing schreiben auf die parallele Schnittstelle

// thomas@fam-kuster.ch
// Copyleft: GNU-Public License Version 2 oder höher (http://www.gnu.org)

#include <stdio.h>
#include <fcntl.h>

int main()
{
  /*Interface alles ausschalten*/
  zuruecksetzen();
}

/* gibt den übergebenen Wert ausgabe auf lp0 aus (länge muss als zweiter parameter angegeben werden -> laenge) */
int drucken(ausgabe, laenge)
{
  int interface;
  printf("ausgabe = %s und laenge = %d\n", ausgabe, laenge);


  interface = open("/dev/tty2", O_WRONLY|O_APPEND); // eigentlich /dev/lp0, bei /dev/tty2 einlogen auf tty2!

  if ((write(interface, ausgabe, laenge)) != laenge)
  write(2, "Fehler beim Schreiben auf /dev/lp0\n", 35);

}

int zuruecksetzen()
{
  drucken("0-1-2-3-4-5-6-7-", 16);
}

Ganzes Programm

Das folgende Programm funktioniert, muss jedoch noch verbessert werden: Die ersten beiden Punkte möchte ich mit einer Funktion lösen welche im Unendlichen gegen 0 geht und die Beschleunigung bei gleichmässiger Veränderung des x-Wertes konstant bleibt.
Wie ich die Bildschrimausgabe und die Hilfefunktion löse weiss ich noch nicht, vielleicht schreibe ich auch das ganze Programm um zu einem Threading-Programm oder erstelle ein X-Programm (X ist die grafische Oberfläche unter Linux/Unix) das wäre noch schöner.

Listing des ganzen Programms

Der neuste Quellcode ist unter Download auffindbar.
// thomas@fam-kuster.ch
// Copyleft: GNU-Public License Version 2 oder höher (http://www.gnu.org)

#include <stdio.h>
#include <termios.h>
#include <term.h>
#include <curses.h>
#include <unistd.h>
//Start eigene
#include <fcntl.h>

static struct termios initial_settings, new_settings;
static int peek_character = -1;

void init_keyboard();
void close_keyboard();

//Start eigene
int ausgabe_an();
int verzoegerung();
int zuruecksetzen();
//Ende eigene

int kbhit();
int readch();

//Start eigene
int warten = 10, max_v = 500000; //max_v = maximale Verzögerung zwischen dem Umschalten
int interface;

int main()
{
  int ch = 0;

  /* Start eigene Variablen */
  //int interface;
  int s = 0, i = 0;
  int s_alt;
  char ausgabe[4], kanal[4] = "0123";

  /* Ende eigene Variablen */

  /* Interface Anschluss zum schreiben öffnen */
  interface = open("/dev/lp0", O_WRONLY|O_APPEND);  //zum testen Konsole nehmen zum Beispiel tty2


  zuruecksetzen(); //alles ausschalten

  init_keyboard();
  printf("\nzum beenden 'q' drücken\n\n");
  while(ch != 'q')
    {
      // printf("durchlauf Schlaufe\n");

      ch = 0; //zurücksetzen Tastenvariable

      if(kbhit())
      	{
	  ch = readch();
	}

        /* Start Einbau eigener Quelltext (gedrückte Taste ist in ch gespeichert)*/

      //printf("durchlauf und taste %c\n", ch);

	if(ch == 'l')
	  warten += 10000;

	if(ch == 'r')
	  warten -= 10000;

    i++;
    if(warten >= 0)
      {
	s_alt = s;
	s++;
        if(s >= 4)
	  s = 0;

	// printf("warten ist grösser oder gleich0\ns = %d\ns_alt = %d\n", s, s_alt);
      }

    if(warten < 0)
      {
	s_alt = s;
	s--;
        if(s <= -1)
	  s = 3;

	// printf("warten ist kleiner 0\ns = %d\ns_alt = %d\n", s, s_alt);
      }

    ausgabe[0] = kanal[s];
    ausgabe[1] = '+';
    ausgabe[2] = kanal[s_alt];
    ausgabe[3] = '-';
    ausgabe[4] = '\0'; 

    ausgabe_an(ausgabe, 4); //ausgeben an Interface

    // printf("warte etwas\n");
    verzoegern(max_v - abs(warten));

        /* Ende Einbau eigener Quelltext */

    }
  close_keyboard();
  exit(0);
}

void init_keyboard()
  {
    tcgetattr(0,&initial_settings);
    new_settings = initial_settings;
    new_settings.c_lflag &= ~ICANON;
    new_settings.c_lflag &= ~ECHO;
    new_settings.c_lflag &= ~ISIG;

    new_settings.c_cc[VMIN] = 1;
    new_settings.c_cc[VTIME] = 0;
    tcsetattr(0, TCSANOW, &new_settings);
  }

void close_keyboard()
  {
    tcsetattr(0, TCSANOW, &initial_settings);
  }

int kbhit()
  {
    char ch;
    int nread;
    
    if(peek_character != -1)
      return 1;
    new_settings.c_cc[VMIN]=0;
    tcsetattr(0, TCSANOW, &new_settings);
    nread = read(0,&ch,1);
    new_settings.c_cc[VMIN]=1;
    tcsetattr(0, TCSANOW, &new_settings);
    
    if(nread == 1)
      {
        peek_character = ch;
	return 1;
      }
    return 0;
  }

int readch()
  {
    char ch;
    if(peek_character != -1)
      {
	ch = peek_character;
	peek_character = -1;
	return ch;
      }
    read(0,&ch,1);
    return ch;
  }

//Start eigene

/* gibt den übergebenen Wert ausgabe auf lp0 aus (länge muss als zweiter parameter angegeben werden -> laenge) */

int ausgabe_an(ausgabe, laenge)
{
  //printf("laenge = %d\n", was_laenge);
 
  //int interface;
  //interface = open("/dev/tty2", O_WRONLY|O_APPEND);
  if ((write(interface, ausgabe, laenge)) != laenge)
  write(2, "Fehler beim Schreiben auf /dev/lp0\n", 35);

  // printf("sizeof = %d\n", sizeof(plus) );
  //exit(0);
}

int verzoegern(wert)
{
  usleep(wert);
  printf("Die Wartezeit beträgt %d Mikrosekunden.\n", wert);
}

int zuruecksetzen()
{
  printf("zurücksetzen");
  ausgabe_an("0-1-2-3-4-5-6-7-", 16);
}
Inhaltsverzeichnis   zum top   zurückvor