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


von Guido S. (flintstone)


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.
1
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.
1
#include <stdio.h>
2
#include <stdlib.h>
3
#include <time.h>
4
#include <sys/types.h>
5
#include <fcntl.h>
6
#include <mysql.h>
7
8
// globale Variablen deklarieren
9
int port;
10
char puffer[100];
11
char puffer2[150];
12
MYSQL_RES *res;
13
MYSQL_ROW row;
14
int bytes;
15
char buffer[1];
16
int i = 0;
17
int x;
18
int y;
19
20
// Instanz von mysql
21
MYSQL *mysql;
22
23
/* Bricht bei Fehler (mysql_error != 0) das Programm ab. */
24
void check_error(void)
25
{
26
  if (mysql_errno(mysql) != 0)
27
  {
28
    fprintf(stderr, "Fehler: %s\n", mysql_error(mysql));
29
    exit(EXIT_FAILURE);
30
  }
31
}
32
33
/* Baut eine Verbindung zum Datenbankserver auf.
34
 * Passen Sie ggf. Usernamen und Passwort und, sofern
35
 * andere Parameter benötigt werden, diese an Ihre
36
 * Bedürfnisse selbst an.
37
 */
38
void verbinden(void) 
39
{
40
  mysql=mysql_init(mysql);
41
  check_error();
42
  /* mysql_real_connect (
43
   *        my,    // Zeiger auf MYSQL-Handler 
44
   *        NULL,    // Host-Name
45
   *        NULL,    // User-Name
46
   *        NULL,    // Passwort für user_name
47
   *        NULL,    // Name der Datenbank
48
   *        port_num,  // Port
49
   *        socket_name,  // Socket
50
   *        0 )    // keine Flags 
51
   */
52
  mysql_real_connect(mysql, "localhost", "root", "", "arduino", 0, NULL, 0);
53
  check_error();
54
}
55
56
/* Serververbindung wieder schließen und den Speicher für die
57
 * Struktur MYSQL wieder freigeben */
58
void verbindung_schliessen(void)
59
{
60
  mysql_close(mysql);
61
}
62
63
64
65
int arduino(void)
66
{
67
68
  // jetzt soll ein 'A' an die serielle Schnittstelle ausgegeben werden
69
  buffer[0] = (char)'A';
70
  //printf(buffer);
71
  //printf("\n");
72
  write(port, buffer, sizeof(buffer));
73
  //printf("Zeichen wurde gesendet\n");
74
75
  // Zeichen von der seriellen Schnittstelle einlesen
76
  // es wird bis Zeilenende oder max. 80 Zeichen gelesen
77
  y=0;
78
  for (x=0; x<80; x++)
79
  {
80
    // Jetzt können Daten gelesen werden
81
    bytes = read(port, buffer, sizeof(buffer));
82
    printf(buffer);
83
    if (buffer[0] >= ' ')
84
    {
85
      puffer[y]=buffer[0];
86
      y++;
87
    }
88
89
      if (buffer[0] == '\n')
90
      {
91
      break;
92
      }
93
  }
94
  puffer[y] = (char)'\0';
95
96
  printf("\n");
97
98
99
  //SQL-Abfrage
100
   sprintf( puffer2, "INSERT INTO `temperatur` (wert) VALUES('%s')", puffer);
101
  printf(puffer2);
102
   mysql_query(mysql, puffer2);
103
  check_error();
104
}
105
106
107
108
int main(int argc, char *argv[])
109
{
110
  time_t start, stop;
111
  double diff;
112
113
  // serielle Schnittstelle für die Kommunikation zum Arduiono öffnen
114
  // beim Arduino wird dadurch ein RESET durchgeführt, Grund: DTR geht kurz auf LOW
115
  port = open("/dev/ttyACM1", O_RDWR);
116
  // prüfen, ob die Schnittstelle erfolgreich geöffnet wurde
117
  if (port == -1)
118
  {  // das ist der Fehlerfall
119
    printf("Schnittstelle konnte nicht geöffnet werden.\n");
120
    return(1);
121
  }
122
  // Erste Zeile nach dem Reset einlesen (dient nur zur Kontrolle für den Test)
123
  for (x=0; x<30; x++)
124
  {
125
    // Jetzt können Daten gelesen werden
126
    bytes = read(port, buffer, sizeof(buffer));
127
    printf(buffer);
128
    if (buffer[0] == '\n')
129
    {
130
      break;
131
    }
132
  }
133
134
  // Verbindung zur Datenbank herstellen
135
  verbinden();
136
137
  int A=1;
138
139
  while(A=1)
140
  {
141
    // Zeitschleife (alle 15 Sekunden werden Daten abgerufen)
142
    printf("Einen Augenblick bitte ...\n");
143
    start=time(NULL);
144
    while((diff=difftime(stop=time(NULL),start)) != 15);
145
    printf("%.1f Sekunden vorbei!!\n",diff);
146
147
    arduino();
148
  }
149
150
  // Verbindung zur Datenbank beenden
151
  verbindung_schliessen();
152
153
  // serielle Schnittstelle beenden
154
  close(port);
155
}

Gruß
Guido

von stuff (Gast)


Lesenswert?

sleep() alarm() /dev/rtc

von stuff (Gast)


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)

von Rolf M. (rmagnus)


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.

von Guido S. (flintstone)


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!

von Klaus W. (mfgkw)


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

von Rolf Magnus (Gast)


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.

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.