Forum: Mikrocontroller und Digitale Elektronik epoll() für "Interrupts" bei Embedded Linux GPIOs bei fallender Flanke?


von epolli (Gast)


Lesenswert?

Hallo Forum,

da Ihr mir bei fscanf() schon so gut geholfen habt. Noch eine Frage:

Ich habe mir folgende Funktion geschrieben um einen GPIO Pin in meinem 
Embedded Linux zu überwachen. Das Programm soll später an dieser Stelle 
solange hängen bis die Flanke erkannt wurde. Die Flanke ist bereits bei 
dem genutzten GPIO auf falling gesetzt. Dieser ist auch ein Input und 
das active_low ist auf 0 gesetzt. Wenn ich den Wert des Pins auslese 
bekomme ich 0 zurück. Somit sollte keine fallende Flanke auftreten 
können (ist ja schon 0)..

Mein Problem ist nun: Wenn ich das Programm ausführe, läuft es so direkt 
durch ohne irgendwo zu warten. Die Funktion gibt mir auch schön 
ordentlich 0 zurück (Wert des Pins). Achja EPOLL_TIMEOUT ist auf -1 also 
unendlich gesetzt.

Folgende Funktion habe ich mir geschrieben um einen "Interrupt" zu 
bekommen (ich weiß das mit dem free usw. hatten wir ja schon in 
fscanf()..):
1
int_fast8_t
2
get_interrupt_gpio (char const * const gpionum) //Achtung blocking!! Also extra thread oder process!
3
{
4
  int_fast8_t ret = 0;
5
  int val = 0;
6
  size_t size = (sizeof(char)
7
      * (strlen ("/sys/class/gpio/gpio") + strlen ("/value") + strlen (gpionum)
8
    + 1));
9
  char * Target2 = (char *) malloc (size);
10
  while (Target2 == NULL)
11
    {
12
      printf ("No space left in RAM!");
13
      Target2 = (char *) malloc (size);
14
    }
15
  ret = snprintf (Target2, size, "%s%s%s", "/sys/class/gpio/gpio", gpionum,
16
      "/value");
17
  if (ret != (int_fast8_t) (size - 1))
18
    {
19
      printf ("Internal Problem write_value_gpio_out @ snprintf!");
20
      free (Target2);
21
      Target2 = NULL;
22
      return (-3);
23
    }
24
  int epollins = epoll_create (1);
25
  if (epollins < 0)
26
    {
27
      printf ("Error @ epoll_create! %i", errno);
28
      free (Target2);
29
      Target2 = NULL;
30
      return (-6);
31
    }
32
  int pin = open (Target2, O_RDWR | O_NONBLOCK);
33
  if (pin > 0)
34
    {
35
      struct epoll_event ev;
36
      struct epoll_event events;
37
      ev.events = EPOLLET;
38
      ev.data.fd = pin;
39
      ret = epoll_ctl (epollins, EPOLL_CTL_ADD, pin, &ev);
40
      if (ret)
41
  {
42
    printf ("Error @ epoll_ctl! %i", errno);
43
    close (pin);
44
    free (Target2);
45
    Target2 = NULL;
46
    return (-2);
47
  }
48
      ret = epoll_wait (epollins, &events, 1, EPOLL_TIMEOUT);
49
      if (ret > 0)
50
  {
51
    ret = (int_fast8_t) lseek (pin, 0, SEEK_SET);
52
    if (ret < 0)
53
      {
54
        printf ("Error @ lseek!");
55
        close (pin);
56
        free (Target2);
57
        Target2 = NULL;
58
        return (-4);
59
      }
60
    ret = (int_fast8_t) read (pin, &val, 1);
61
    if (!ret)
62
      {
63
        printf ("Cant read correct value!");
64
        close (pin);
65
        free (Target2);
66
        Target2 = NULL;
67
        return (-5);
68
      }
69
    close (pin);
70
    free (Target2);
71
    Target2 = NULL;
72
    return ((int_fast8_t) val);
73
  }
74
      else if (ret == 0)
75
  {
76
    printf ("Timeout!");
77
    close (pin);
78
    free (Target2);
79
    Target2 = NULL;
80
    return (2);
81
  }
82
      else
83
  {
84
    printf ("Error @ epoll_wait! %i", errno);
85
    close (pin);
86
    free (Target2);
87
    Target2 = NULL;
88
    return (-3);
89
  }
90
91
    }
92
  else
93
    {
94
      printf ("No PIN for Interrupt!");
95
      free (Target2);
96
      Target2 = NULL;
97
      return (-1);
98
    }
99
100
}

Es könnte meiner Vermutung nach an meinen Angegebenen events liegen. 
Aber so richtig weiß ich es nicht.

von epolli (Gast)


Lesenswert?

So ich glaube ich habe es...

man muss vor dem epoll_wait() das File lesen. Dann hängt er auch an der 
Stelle bis die Flanke kommt...

Das was ich vorher gefunden hatte funktioniert auch...:
https://gist.github.com/jadonk/2587524
aber ich denke der Code geht erst einmal durch die Schleife durch und 
hängt dann am epoll_wait()..


Dank:
http://wiki.gnublin.org/index.php/GPIO
bin ich drauf gekommen...

So funktioniert es:
1
int_fast8_t
2
get_interrupt_gpio (char const * const gpionum) //Achtung blocking!! Also extra thread oder process!
3
{
4
  int_fast8_t ret = 0;
5
  int val = 0;
6
  size_t size = (sizeof(char)
7
      * (strlen ("/sys/class/gpio/gpio") + strlen ("/value") + strlen (gpionum)
8
    + 1));
9
  char * Target2 = (char *) malloc (size);
10
  while (Target2 == NULL)
11
    {
12
      printf ("No space left in RAM!");
13
      Target2 = (char *) malloc (size);
14
    }
15
  ret = snprintf (Target2, size, "%s%s%s", "/sys/class/gpio/gpio", gpionum,
16
      "/value");
17
  if (ret != (int_fast8_t) (size - 1))
18
    {
19
      printf ("Internal Problem write_value_gpio_out @ snprintf!");
20
      free (Target2);
21
      Target2 = NULL;
22
      return (-3);
23
    }
24
  int epollins = epoll_create1 (0);
25
  if (epollins < 0)
26
    {
27
      printf ("Error @ epoll_create! %i", errno);
28
      free (Target2);
29
      Target2 = NULL;
30
      return (-6);
31
    }
32
  int pin = open (Target2, O_RDWR | O_NONBLOCK);
33
  if (pin > 0)
34
    {
35
      struct epoll_event ev;
36
      struct epoll_event events;
37
      ev.events = EPOLLET;
38
      ev.data.fd = pin;
39
      ret = epoll_ctl (epollins, EPOLL_CTL_ADD, pin, &ev);
40
      if (ret)
41
  {
42
    printf ("Error @ epoll_ctl! %i", errno);
43
    close (pin);
44
    free (Target2);
45
    Target2 = NULL;
46
    return (-2);
47
  }
48
      ret = (int_fast8_t) read (pin, NULL, 1);
49
      ret = epoll_wait (epollins, &events, 1, EPOLL_TIMEOUT);
50
      if (ret > 0)
51
  {
52
    ret = (int_fast8_t) lseek (pin, 0, SEEK_SET);
53
    if (ret < 0)
54
      {
55
        printf ("Error @ lseek!");
56
        close (pin);
57
        free (Target2);
58
        Target2 = NULL;
59
        return (-4);
60
      }
61
    ret = (int_fast8_t) read (pin, &val, 1);
62
    if (!ret)
63
      {
64
        printf ("Cant read correct value!");
65
        close (pin);
66
        free (Target2);
67
        Target2 = NULL;
68
        return (-5);
69
      }
70
    close (pin);
71
    free (Target2);
72
    Target2 = NULL;
73
    return ((int_fast8_t) val);
74
  }
75
      else if (ret == 0)
76
  {
77
    printf ("Timeout!");
78
    close (pin);
79
    free (Target2);
80
    Target2 = NULL;
81
    return (2);
82
  }
83
      else
84
  {
85
    printf ("Error @ epoll_wait! %i", errno);
86
    close (pin);
87
    free (Target2);
88
    Target2 = NULL;
89
    return (-3);
90
  }
91
92
    }
93
  else
94
    {
95
      printf ("No PIN for Interrupt!");
96
      free (Target2);
97
      Target2 = NULL;
98
      return (-1);
99
    }
100
101
}

von Karl H. (kbuchegg)


Lesenswert?

furchtbarer Code


was anderes.
Gibts denn keine Abschätzung darüber, wie lang gpionum maximal werden 
kann? Da wird ja wohl kaum ein String mit der Textlänge der Bibel 
daherkommen.

Nimm deine Abschätzung, gib noch 10 Zeichen dazu und allokier ein 
statisches Array mit dieser Länge. Am Anfang der Funktion kann man noch 
prüfen ob die allokierte Array-Länge ausreichen wird und gut ists.

Man kann auch alles übertreiben. Nach dem was ich beim kurzen schmöckern 
in deinen Links gesehen habe, ist diese 'Zahl' maximal 3 stellig. Ich 
hab zb gpionum34 gesehen und geb noch eine Ziffer in Reserve mit dazu. 
D.h. für das temporäre "Verschwenden" von 13 (3 + 10) Bytes zwirbelst du 
deinen Pi in eine Menge Sonderarbeit für die dynamische Allokierung 
rein. Und ich glaube nicht, dass der Pi jemals eine Pinanzahl bekommen 
wird, die sich mit einer 12-stelligen Zahl nicht mehr beschreiben lässt. 
Zumindest wirst du das ziemlich sicher nicht mehr erleben.

von epolli (Gast)


Lesenswert?

Ist ein Beaglebone ;-)

Ne stimmt schon, ist maximal 3-stellig.


Hab mir nur gedacht ich schreibe es so universal wie möglich. (Bis auf 
das read() gibts ja nur unter Linux... aber da jetzt noch close(fd); 
dann fopen(...) usw. nur für fscanf() hatte ich keine Lust.


Hab mich damals immer geärgert das mein Prof getch() genutzt hat und auf 
dem Mac ging es nicht...

Dafür variable Array Größen mit int array[a]; wobei a auf der Console 
eingegeben werden konnte....

Was heißt denn furchtbarer Code? Wie macht man es denn besser?
Ich meine ich kann schon programmieren bzw. ich bekomm es zumindest hin, 
dass es das macht was es soll...
Aber womit lernt man denn "guten" Code zu schreiben?

Ich habe gemerkt, dass ich etwas inkonsistent bin mit meiner Art ifs zu 
verwenden..
Also immer mal andersrum abgefragt oder mal if else obwohl if genügt 
hätte usw. Ich schreibe erstmal alles runter und dann, wenn es geht, vll 
noch bissel Lesbarkeit verbessern.

Den Code gab es schon als ich das mit fscanf geschrieben habe. Daher 
sind hier auch so viele free() drin. Aber ich glaub ich bin zu faul 
alles zu überarbeiten. Habe mittlerweile viel Code für das Projekt.

von Karl H. (kbuchegg)


Lesenswert?

epolli schrieb:

> Was heißt denn furchtbarer Code?

Na ja. SIeh dir das Teil an.
Extrem in die Länge gezogen. Denn Programmfluss kann man nur nach 
Intensivstudium erkennen, weil da immer wieder der ganze Sermon mit 
Freigeben und return dazwischen liegt.

> Aber womit lernt man denn "guten" Code zu schreiben?

Wenn man die Grundidee dieser eigentlich recht einfachen Funktion in 
unter 5 Sekunden erfassen und im Code nachvollziehen kann.

> hätte usw. Ich schreibe erstmal alles runter und dann, wenn es geht, vll
> noch bissel Lesbarkeit verbessern.

Das ist eine gute Idee.
Es lohnt sich immer, sich auch mal zurückzulehnen und zu überlegen, wie 
man Code anders strukturieren könnte und da einfach mal ein paar Dinge 
auszuprobieren.

> sind hier auch so viele free() drin. Aber ich glaub ich bin zu faul
> alles zu überarbeiten. Habe mittlerweile viel Code für das Projekt.

Das Problem ist nicht, dass du jetzt deinen Code verstehst. Das Problem 
wird in ein paar Wochen/Monaten auf dich zukommen, wenn du das nächste 
mal an diesen Code rann musst und dann erst mal nur noch Bahnhof 
verstehst.

von epolli (Gast)


Lesenswert?

Hm jetzt hab ich ein komisches Problem.

Wenn ich meine beiden Funktionen (get_value_gpio (die fscanf Funktion) 
und get_interrupt_gpio (Funktion von hier) in einer while(1) schleife 
laufen lasse (Einfach nur zum Testen..) dann geht es solange ich von 
Hand durchsteppe (auch wenn die ganze Schleife immer auf Knopfdruck 
wiederholt wird, beliebig oft aber halt "handbetrieb"..)

Wenn ich nun das Programm einfach so starte kommt Plötzlich immer der 
Fehler das mein gpio_get_value() -1 zurückgibt also das File (der Pin) 
nicht zum öffnen geöffnet werden kann. Und bei  der Interrupt funktion 
kommt der Fehler das epoll_create1(0) fehlgeschlagen ist mit dem Errno 
24 also zu viele Files offen.

Aber ich mach die doch überall wieder zu.
Das einzige ist dieses epoll_create1() da weiß ich nciht wie die 
gelöscht werden.
Bzw. habe gelesen das wenn ich den fd close würden die wieder gelöscht 
werden. Aber irgendwie...

Anscheinend nicht. Habe auch schon alles nachgeschaut ob ich es irgendwo 
nicht zu mache. Aber es werden alle fd zugemacht.

von epolli (Gast)


Lesenswert?

Ist da irgendetwas nebenläufig im Kernel und der kommt mim tatsächlichen 
zumachen nicht hinterher?

von epolli (Gast)


Lesenswert?

AHHH ...

ich sollte erst den Pin öffnen.. Wenn der offen ist weiß ich das 
epoll_create zugewiesen werden kann...

von epolli (Gast)


Lesenswert?

Hm war es nicht.

Er kann wenn es schnell hintereinander ausgeführt wird irgendwann den 
Pin nicht mehr öffnen. Also meine /sys/class/gpio/gpio27/value.

Nur wo liegt der Fehler?
Ist es wirklich weil der Kernel mit dem löschen der fd nicht 
hinterherkommt??

Oder übersehe ich da irgendwo etwas das ich den Pin nicht mehr 
schließe...?

von epolli (Gast)


Lesenswert?

So,

der Fehler muss in der Interrupt Funktion liegen.
Ich habe es mal etwas umgebaut um das ganze besser lesbar zu machen.
Hoffentlich könnt Ihr mir nun helfen.
Ich schließe den Pin wie Ihr seht immer. Aber nach einigen Aufrufen kann 
plötzlich epoll_create1() nicht mehr ausgeführt werden. Es gibt mir 
errno 24 zurück.

Irgendwie müssen doch die epoll_create1() wieder geschlossen werden. 
Aber wie?
1
int_fast8_t get_interrupt_gpio(char const * const gpionum) //Achtung blocking!! Also extra thread oder process!
2
{
3
  int_fast8_t ret = 0;
4
  int val = 0;
5
  size_t size = (sizeof(char)
6
      * (strlen("/sys/class/gpio/gpio") + strlen("/value")
7
          + strlen(gpionum) + 1));
8
  char * Target2 = (char *) malloc(size);
9
  while (Target2 == NULL) {
10
    printf("No space left in RAM!");
11
    Target2 = (char *) malloc(size);
12
  }
13
  ret = snprintf(Target2, size, "%s%s%s", "/sys/class/gpio/gpio", gpionum,
14
      "/value");
15
  if (ret != (int_fast8_t) (size - 1)) {
16
    printf("Error write_value_gpio_out @ snprintf!");
17
    free(Target2);
18
    Target2 = NULL;
19
    return (-3);
20
  }
21
  int pin = open(Target2, O_RDWR | O_NONBLOCK);
22
  if (pin > 0) {
23
    int epollins = epoll_create1(0);
24
    if (epollins >= 0) {
25
      struct epoll_event ev;
26
      struct epoll_event events;
27
      ev.events = EPOLLET;
28
      ev.data.fd = pin;
29
      ret = epoll_ctl(epollins, EPOLL_CTL_ADD, pin, &ev);
30
      if (!ret) {
31
        ret = (int_fast8_t) read(pin, NULL, 1);
32
        ret = epoll_wait(epollins, &events, 1, EPOLL_TIMEOUT);
33
        if (ret > 0) {
34
          ret = (int_fast8_t) lseek(pin, 0, SEEK_SET);
35
          if (ret >= 0) {
36
            ret = (int_fast8_t) read(pin, &val, 1);
37
            if (ret) {
38
              close(pin);
39
              free(Target2);
40
              Target2 = NULL;
41
              if (val == 49)
42
                return (1);
43
              else
44
                return (0);
45
            }
46
            printf("Cant read correct value!");
47
            close(pin);
48
            free(Target2);
49
            Target2 = NULL;
50
            return (-5);
51
          }
52
53
          printf("Error @ lseek!");
54
          close(pin);
55
          free(Target2);
56
          Target2 = NULL;
57
          return (-4);
58
        }
59
60
        else if (ret == 0) {
61
          printf("Timeout!");
62
          close(pin);
63
          free(Target2);
64
          Target2 = NULL;
65
          return (2);
66
        }
67
        printf("Error @ epoll_wait! %i", errno);
68
        close(pin);
69
        free(Target2);
70
        Target2 = NULL;
71
        return (-3);
72
      }
73
      printf("Error @ epoll_ctl! %i", errno);
74
      close(pin);
75
      free(Target2);
76
      Target2 = NULL;
77
      return (-2);
78
    }
79
80
    printf("Error @ epoll_create! %i", errno);
81
    close(pin);
82
    free(Target2);
83
    Target2 = NULL;
84
    return (-6);
85
  }
86
87
  printf("No PIN for Interrupt!");
88
  free(Target2);
89
  Target2 = NULL;
90
  return (-1);
91
}

von epolli (Gast)


Lesenswert?

Okay hab es kapiert.. man sollte richtig lesen. Epoll create gibt nen fd 
zurück der natürlich auch mit close geschlossen werden muss...

von epolli (Gast)


Lesenswert?

Was ist eigentlich schneller?
Oben der Code mit dem epoll()? Oder sowas hier über die gpio-key Treiber 
von Linux?
Wobei der GPIO dann im DTO als Key definiert wird und die Debounce-Time 
auf 0 gesetzt wird usw.
Ich brauche eigentlich nur etwas, was meine state Machine blockiert bis 
der GPIO auf 0 gezogen wurde. Der gpio wird von einem Mikrocontroller 
nur sehr kurz auf 0 gezogen und dann wieder auf 1. Der Pin gibt nur an, 
das die Daten bereit zum abholen über SPI sind.
1
int_fast8_t button_pressed_fast(char const * const Target2,
2
    unsigned int whichkey, int press_release) //Knopf zum Mount/Umount
3
{
4
  int_fast8_t ret = 0;
5
  int but = open(Target2, O_RDWR);
6
  if (but > 0) {
7
    struct input_event event;
8
    unsigned int scan_code = 0;
9
    int rbytes = read(but, &event, sizeof(struct input_event));
10
    close(but);
11
    if (rbytes) {
12
      if (event.type == EV_KEY) {
13
        if (event.value == press_release) {
14
          scan_code = event.code;
15
          if (scan_code == whichkey) {
16
            return (1);
17
          }
18
          printf("Not the correct Key!");
19
          return (-5);
20
        }
21
        printf("No Key Pressed/Released!");
22
        return (-4);
23
      }
24
      printf("No EV_KEY!");
25
      return (-3);
26
    }
27
    printf("Cant read correct value!");
28
    return (-2);
29
  }
30
  printf("No Button!");
31
  return (-1);
32
}

von epolli (Gast)


Lesenswert?

Bzw. als der hier... mal one malloc()..
1
int_fast8_t get_interrupt_gpio_fast(char const * const Target2, int timeout,
2
    uint32_t trigger_event) //Achtung blocking!! Also extra thread oder process! //TODO Falls der Interrupt schneller bereit sein muss Pfad mit GPIO direkt übergeben also ohne allokieren usw..
3
{
4
  int_fast8_t ret = 0;
5
  int val = 0;
6
  int pin = open(Target2, O_RDWR | O_NONBLOCK);
7
  if (pin > 0) {
8
    int epollins = epoll_create1(0);
9
    if (epollins >= 0) {
10
      struct epoll_event ev;
11
      struct epoll_event events;
12
      ev.events = trigger_event;
13
      ev.data.fd = pin;
14
      ret = epoll_ctl(epollins, EPOLL_CTL_ADD, pin, &ev);
15
      if (!ret) {
16
        ret = (int_fast8_t) read(pin, NULL, 1);
17
        ret = epoll_wait(epollins, &events, 1, timeout);
18
        if (ret > 0) {
19
          ret = (int_fast8_t) lseek(pin, 0, SEEK_SET);
20
          if (ret >= 0) {
21
            ret = (int_fast8_t) read(pin, &val, 1);
22
            if (ret) {
23
              close(pin);
24
              close(epollins);
25
              if (val == 49)
26
                return (1);
27
              else
28
                return (0);
29
            }
30
            printf("Cant read correct value!");
31
            close(epollins);
32
            close(pin);
33
            return (-5);
34
          }
35
          printf("Error @ lseek!");
36
          close(epollins);
37
          close(pin);
38
          return (-4);
39
        } else if (ret == 0) {
40
          printf("Timeout!");
41
          close(epollins);
42
          close(pin);
43
          return (2);
44
        }
45
        printf("Error @ epoll_wait! %i", errno);
46
        close(pin);
47
        close(epollins);
48
        return (-3);
49
      }
50
      printf("Error @ epoll_ctl! %i", errno);
51
      close(epollins);
52
      close(pin);
53
      return (-2);
54
    }
55
    printf("Error @ epoll_create! %i", errno);
56
    close(pin);
57
    return (-6);
58
  }
59
  printf("No PIN for Interrupt!");
60
  return (-1);
61
}

von epolli (Gast)


Lesenswert?

Quelle zum oberen (von mir halt noch angepasst..):
http://rico-studio.com/category/c-2/

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.