www.mikrocontroller.net

Forum: PC-Programmierung Linux C-Programm - Zeitmessung per Interrupt


Important announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
Autor: Guido Scheidat (flintstone)
Datum:

Diesen Beitrag bewerten:
lesenswert
nicht lesenswert
Hallo,

gerade bin ich dabei, ein Programm in C zu schreiben, welches 
Temperaturdaten vom Arduino empfängt und in einer Datenbank ablegt.
Das Programm macht inzwischen, was ich möchte.

Das Problem ist, dass ich eine Schleife geschrieben habe, die eine 
bestimmte Zeit misst und nicht optimal ist.
while((diff=difftime(stop=time(NULL),start)) != 15);
Dummerweise hat diese Art der Programmierung den Nachteil, dass die CPU 
immer in Beschäftigung ist.
Wie kann ich unter Linux einen Timer programmieren, der am Besten 
interruptgesteuert ist, so wie ich es auch beim AVR machen würde?
Hier ist mein kleines Programm:
Bitte erschlagt mich nicht, falls ich grundlegende Dinge nicht beachtet 
haben sollte. Es ist mein erstes Programm unter Linux. Für konstruktive 
Hinweise bin ich natürlich dankbar.
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <sys/types.h>
#include <fcntl.h>
#include <mysql.h>

// globale Variablen deklarieren
int port;
char puffer[100];
char puffer2[150];
MYSQL_RES *res;
MYSQL_ROW row;
int bytes;
char buffer[1];
int i = 0;
int x;
int y;

// Instanz von mysql
MYSQL *mysql;

/* Bricht bei Fehler (mysql_error != 0) das Programm ab. */
void check_error(void)
{
  if (mysql_errno(mysql) != 0)
  {
    fprintf(stderr, "Fehler: %s\n", mysql_error(mysql));
    exit(EXIT_FAILURE);
  }
}

/* Baut eine Verbindung zum Datenbankserver auf.
 * Passen Sie ggf. Usernamen und Passwort und, sofern
 * andere Parameter benötigt werden, diese an Ihre
 * Bedürfnisse selbst an.
 */
void verbinden(void) 
{
  mysql=mysql_init(mysql);
  check_error();
  /* mysql_real_connect (
   *        my,    // Zeiger auf MYSQL-Handler 
   *        NULL,    // Host-Name
   *        NULL,    // User-Name
   *        NULL,    // Passwort für user_name
   *        NULL,    // Name der Datenbank
   *        port_num,  // Port
   *        socket_name,  // Socket
   *        0 )    // keine Flags 
   */
  mysql_real_connect(mysql, "localhost", "root", "", "arduino", 0, NULL, 0);
  check_error();
}

/* Serververbindung wieder schließen und den Speicher für die
 * Struktur MYSQL wieder freigeben */
void verbindung_schliessen(void)
{
  mysql_close(mysql);
}



int arduino(void)
{

  // jetzt soll ein 'A' an die serielle Schnittstelle ausgegeben werden
  buffer[0] = (char)'A';
  //printf(buffer);
  //printf("\n");
  write(port, buffer, sizeof(buffer));
  //printf("Zeichen wurde gesendet\n");

  // Zeichen von der seriellen Schnittstelle einlesen
  // es wird bis Zeilenende oder max. 80 Zeichen gelesen
  y=0;
  for (x=0; x<80; x++)
  {
    // Jetzt können Daten gelesen werden
    bytes = read(port, buffer, sizeof(buffer));
    printf(buffer);
    if (buffer[0] >= ' ')
    {
      puffer[y]=buffer[0];
      y++;
    }

      if (buffer[0] == '\n')
      {
      break;
      }
  }
  puffer[y] = (char)'\0';

  printf("\n");


  //SQL-Abfrage
   sprintf( puffer2, "INSERT INTO `temperatur` (wert) VALUES('%s')", puffer);
  printf(puffer2);
   mysql_query(mysql, puffer2);
  check_error();
}



int main(int argc, char *argv[])
{
  time_t start, stop;
  double diff;

  // serielle Schnittstelle für die Kommunikation zum Arduiono öffnen
  // beim Arduino wird dadurch ein RESET durchgeführt, Grund: DTR geht kurz auf LOW
  port = open("/dev/ttyACM1", O_RDWR);
  // prüfen, ob die Schnittstelle erfolgreich geöffnet wurde
  if (port == -1)
  {  // das ist der Fehlerfall
    printf("Schnittstelle konnte nicht geöffnet werden.\n");
    return(1);
  }
  // Erste Zeile nach dem Reset einlesen (dient nur zur Kontrolle für den Test)
  for (x=0; x<30; x++)
  {
    // Jetzt können Daten gelesen werden
    bytes = read(port, buffer, sizeof(buffer));
    printf(buffer);
    if (buffer[0] == '\n')
    {
      break;
    }
  }

  // Verbindung zur Datenbank herstellen
  verbinden();

  int A=1;

  while(A=1)
  {
    // Zeitschleife (alle 15 Sekunden werden Daten abgerufen)
    printf("Einen Augenblick bitte ...\n");
    start=time(NULL);
    while((diff=difftime(stop=time(NULL),start)) != 15);
    printf("%.1f Sekunden vorbei!!\n",diff);

    arduino();
  }

  // Verbindung zur Datenbank beenden
  verbindung_schliessen();

  // serielle Schnittstelle beenden
  close(port);
}

Gruß
Guido

Autor: stuff (Gast)
Datum:

Diesen Beitrag bewerten:
lesenswert
nicht lesenswert
sleep() alarm() /dev/rtc

Autor: stuff (Gast)
Datum:

Diesen Beitrag bewerten:
lesenswert
nicht lesenswert
Hier noch mehr: (SEE ALSO beachten)

USLEEP(3) 
Linux Programmer's Manual 
USLEEP(3)

NAME
       usleep - suspend execution for microsecond intervals

SYNOPSIS
       #include <unistd.h>

       int usleep(useconds_t usec);

   Feature Test Macro Requirements for glibc (see 
feature_test_macros(7)):

       usleep(): _BSD_SOURCE || _XOPEN_SOURCE >= 500

DESCRIPTION
       The  usleep() function suspends execution of the calling process 
for (at least) usec microseconds.  The sleep may be lengthened slightly 
by any system activity or by the time spent pro‐
       cessing the call or by the granularity of system timers.

RETURN VALUE
       0 on success, -1 on error.

ERRORS
       EINTR  Interrupted by a signal; see signal(7).

       EINVAL usec is not smaller than 1000000.  (On systems where that 
is considered an error.)

CONFORMING TO
       4.3BSD, POSIX.1-2001.  POSIX.1-2001 declares this function 
obsolete; use nanosleep(2) instead.  POSIX.1-2008 removes the 
specification of usleep().

       On the original BSD implementation, and in glibc before version 
2.2.2, the return type of this function is void.  The POSIX version 
returns int, and this  is  also  the  prototype  used
       since glibc 2.2.2.

       Only the EINVAL error return is documented by SUSv2 and 
POSIX.1-2001.

NOTES
       The type useconds_t is an unsigned integer type capable of 
holding integers in the range [0,1000000].  Programs will be more 
portable if they never mention this type explicitly.  Use

           #include <unistd.h>
           ...
               unsigned int usecs;
           ...
               usleep(usecs);

       The  interaction  of  this  function  with  the  SIGALRM signal, 
and with other timer functions such as alarm(2), sleep(3), nanosleep(2), 
setitimer(2), timer_create(2), timer_delete(2),
       timer_getoverrun(2), timer_gettime(2), timer_settime(2), 
ualarm(3) is unspecified.

SEE ALSO
       alarm(2), getitimer(2), nanosleep(2), select(2), setitimer(2), 
sleep(3), ualarm(3), time(7)

COLOPHON
       This page is part of release 3.23 of the Linux man-pages project. 
A description of the project, and information about reporting bugs, can 
be  found  at  http://www.kernel.org/doc/man-
       pages/.

                                                                                           2007-07-26 
USLEEP(3)

Autor: Rolf Magnus (rmagnus)
Datum:

Diesen Beitrag bewerten:
lesenswert
nicht lesenswert
Guido Scheidat schrieb:
> Wie kann ich unter Linux einen Timer programmieren, der am Besten
> interruptgesteuert ist, so wie ich es auch beim AVR machen würde?

man setitimer

Das ist exakt das Pendat zun einem Interrupt-Timer. Statt eines 
Interrupts bekommst du ein Signal, aber die Handhabung ist sehr ähnlich.
Du hast dann aber immer noch eine Hauptschleife, die die CPU-Last auf 
100% hält, wenn du da nicht was dagegen tust. Das kannst du z.B. machen, 
indem du in der Haupstschleife mit select() ohne Dateideskriptoren und 
einer sehr langen Wartezeit wartetst. Das wird durch das Signal 
automatisch unterbrochen. Der Rest ist dann wie am µC: Im Signal-Handler 
ein Flag setzen (Als Typ empfielt sich sig_atomic_t) und in der 
Hauptschleife testen.

Autor: Guido Scheidat (flintstone)
Datum:

Diesen Beitrag bewerten:
lesenswert
nicht lesenswert
Vielen, vielen Dank!

Ich habe jetzt USLEEP eingesetzt. Die Genauigkeit reicht für diesen 
Zweck aus. Es funktioniert ganz Klasse, die Idle-Time der CPU liegt bei 
99-100%. So hatte ich es mir vorgestellt.

Danke!

Autor: Klaus Wachtler (mfgkw)
Datum:

Diesen Beitrag bewerten:
lesenswert
nicht lesenswert
Wenn man eh select() nimmt, kann man dem doch auch gleich die Wartezeit 
geben?

Rolf Magnus schrieb:
> indem du in der Haupstschleife mit select() ohne Dateideskriptoren und
> einer sehr langen Wartezeit wartetst

Autor: Rolf Magnus (Gast)
Datum:

Diesen Beitrag bewerten:
lesenswert
nicht lesenswert
Klaus Wachtler schrieb:
> Wenn man eh select() nimmt, kann man dem doch auch gleich die Wartezeit
> geben?

Kommt halt drauf an, was das Ziel ist. Wenn man ein Timing will, das 
nicht davonläuft, kommt man um einen periodischen Timer nicht rum. Denn 
zu der Wartezeit des select() kommt ja immer noch die (womöglich 
variable) Laufzeit für den Rest der Schleife dazu.
Wenn das egal ist, kann man sich den Timer natürlich sparen.
Ich hatte mich hier auch speziell auf diese Frage bezogen:

Guido Scheidat schrieb:
> Wie kann ich unter Linux einen Timer programmieren, der am Besten
> interruptgesteuert ist, so wie ich es auch beim AVR machen würde?

Die von mir genannte Variante ist der Art, wie man beim AVR mit 
Timer-Interrupts arbeitet, am ähnlichsten, ohne dabei aber 100% CPU-Last 
zu produzieren.

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




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 erkennst du die Nutzungsbedingungen an.

webmaster@mikrocontroller.netImpressumNutzungsbedingungenWerbung auf Mikrocontroller.net