{"id":5284,"date":"2018-10-29T07:00:06","date_gmt":"2018-10-29T06:00:06","guid":{"rendered":"https:\/\/www.blaess.fr\/christophe\/?p=5284"},"modified":"2018-10-28T11:46:11","modified_gmt":"2018-10-28T10:46:11","slug":"pilotage-de-gpio-avec-lapi-libgpiod-partie-3","status":"publish","type":"post","link":"https:\/\/www.blaess.fr\/christophe\/2018\/10\/29\/pilotage-de-gpio-avec-lapi-libgpiod-partie-3\/","title":{"rendered":"Pilotage de GPIO avec l&rsquo;API Libgpiod (Partie 3)"},"content":{"rendered":"\n<p>Apr\u00e8s l&rsquo;\u00e9tude des outils disponible en ligne de commande pour piloter des lignes GPIO (<a href=\"https:\/\/www.blaess.fr\/christophe\/2018\/10\/15\/pilotage-de-gpio-avec-lapi-libgpiod-partie-1\/\">partie 1<\/a>), puis celles de l&rsquo;acc\u00e8s depuis un programme avec l&rsquo;appel syst\u00e8me <code>ioctl()<\/code> ou la version <em>context-less<\/em> de la biblioth\u00e8que Libgpiod (<a href=\"https:\/\/www.blaess.fr\/christophe\/2018\/10\/22\/pilotage-de-gpio-avec-lapi-libgpiod-partie-2\/\">partie 2<\/a>), nous poursuivons en examinant les fonctions bas niveaux de cette biblioth\u00e8que. Nous aborderons \u00e9galement le th\u00e8me des performances d&rsquo;acc\u00e8s aux GPIO selon la m\u00e9thode choisie.<br\/><\/p>\n\n\n\n<!--more-->\n\n\n\n<h2 class=\"wp-block-heading\">PROGRAMMATION AVEC LA BIBLIOTH\u00c8QUE LIBGPIOD<\/h2>\n\n\n\n<p>Les fonctions <em>context-less<\/em>, nous l&rsquo;avons vu, permettent un acc\u00e8s simple et rapide aux lignes GPIO pour la lecture. L&rsquo;\u00e9criture aussi reste simple tant que nous souhaitons piloter une seule ligne pour sortir une impulsion ou un signal p\u00e9riodique. Si on souhaite g\u00e9rer plusieurs lignes GPIO avec des valeurs d\u00e9termin\u00e9es dynamiquement (par exemple pour commander des relais en fonction de mesures obtenues par certains capteurs ou de demandes d&rsquo;un op\u00e9rateur), cette API <em>context-less<\/em> devient complexe d&rsquo;usage.<\/p>\n\n\n\n<p>De plus, pour certaines applications il peut \u00eatre n\u00e9cessaires de conna\u00eetre des param\u00e8tres pr\u00e9cis des lignes GPIO, par exemple le type de configuration de sortie (collecteur ouvert, drain ouvert, etc.). Pour cela nous pr\u00e9f\u00e9rerons utiliser l&rsquo;API bas-niveau de Libgpiod.<\/p>\n\n\n\n<p>Tous les exemples d\u00e9crits ci-dessous sont disponibles dans mon d\u00e9p\u00f4t Framagit&nbsp;: <a href=\"https:\/\/framagit.org\/cpb\/example-programs-using-libgpiod\" target=\"_blank\">https:\/\/framagit.org\/cpb\/example-programs-using-libgpiod<\/a>.<br\/><\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Structures de donn\u00e9es<\/h3>\n\n\n\n<p>Nous manipulerons plusieurs types de donn\u00e9es opaques, d\u00e9finis et g\u00e9r\u00e9s de mani\u00e8re interne dans la biblioth\u00e8que mais auxquels nous ne ferons r\u00e9f\u00e9rence que par des pointeurs&nbsp;:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li><code>struct gpiod_chip<\/code> : un contr\u00f4leur de GPIO. Il nous permettra d&rsquo;avoir acc\u00e8s \u00e0 ses diff\u00e9rentes lignes.<\/li><li><code>struct gpiod_line<\/code> : une ligne GPIO, identifi\u00e9 par un offset au sein de son contr\u00f4leur.<\/li><li><code>struct gpiod_line_bulk<\/code> : un groupe de lignes GPIO auxquelles on applique des op\u00e9rations.<\/li><li><code>struct gpiod_chip_iter<\/code> : un it\u00e9rateur permettant de parcourir les diff\u00e9rents contr\u00f4leurs disponibles.<\/li><li><code>struct gpiod_line_iter<\/code> : un it\u00e9rateur pour parcourir les lignes d&rsquo;un contr\u00f4leur.<\/li><\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Liste des contr\u00f4leurs et lignes disponibles<\/h3>\n\n\n\n<p>L&rsquo;acc\u00e8s \u00e0 un contr\u00f4leur GPIO se fait en ouvrant le descripteur correspondant. On utilise l&rsquo;une des fonctions suivantes&nbsp;:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">struct gpiod_chip *<strong>gpiod_chip_open<\/strong>(const char *<em>path<\/em>);<br\/>  \/\/ ex:  chip = gpiod_chip_open(\"\/dev\/gpiochip2\");<br\/><br\/>struct gpiod_chip *<strong>gpiod_chip_open_by_name<\/strong>(const char *<em>name<\/em>);<br\/>  \/\/ ex: chip = gpiod_chip_open_by_name(\"gpiochip2\");<br\/><br\/>struct gpiod_chip *<strong>gpiod_chip_open_by_number<\/strong>(unsigned int <em>number<\/em>);<br\/>  \/\/ ex: chip = gpio_chip_open_by_number(2);<br\/><br\/>struct gpiod_chip *<strong>gpiod_chip_open_by_label<\/strong>(const char *<em>label<\/em>);<br\/>  \/\/ ex: chip = gpio_chip_open_by_label(\"pinctrl-bcm2835\");<br\/>  \/\/   See device tree or kernel driver.<\/pre>\n\n\n\n<p>Ou la fonction g\u00e9n\u00e9rique suivante, qui recherche la plus ad\u00e9quate des pr\u00e9c\u00e9dentes&nbsp;:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">struct gpiod_chip *<strong>gpiod_chip_open_lookup<\/strong>(const char *<em>string<\/em>);<\/pre>\n\n\n\n<p>On peut \u00e9galement utiliser un it\u00e9rateur pour parcourir les contr\u00f4leurs disponibles avec les fonctions&nbsp;:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">struct gpiod_chip_iter *<strong>gpiod_chip_iter_new<\/strong>(void);<br\/>struct gpiod_chip *<strong>gpiod_chip_iter_next<\/strong>(struct gpiod_chip_iter *<em>iter<\/em>);<br\/>void <strong>gpiod_chip_iter_free<\/strong>(struct gpiod_chip_iter *<em>iter<\/em>);<br\/>void <strong>gpiod_chip_iter_free_noclose<\/strong>(struct gpiod_chip_iter *<em>iter<\/em>);<\/pre>\n\n\n\n<p>Si aucun argument n&rsquo;est fourni sur sa ligne de commande, le programme <code><a href=\"https:\/\/framagit.org\/cpb\/example-programs-using-libgpiod\/blob\/master\/list-gpio-lines.c\" target=\"_blank\">list-gpio-lines.c<\/a><\/code> parcourt tous les contr\u00f4leurs disponibles ainsi&nbsp;:<\/p>\n\n\n\n<p> <\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">struct <strong>gpiod__chip_iter<\/strong> *iter;<br\/>struct gpio_chip *chip;<br\/>[...]<br\/>iter = <strong>gpiod_chip_iter_new<\/strong>();<br\/>if (iter == NULL)<br\/>  [...]<br\/>while ((chip = <strong>gpiod_chip_iter_next<\/strong>(iter)) != NULL)<br\/>    list_gpio_lines(chip);<br\/><strong>gpiod_chip_iter_free<\/strong>(iter);<br\/><\/pre>\n\n\n\n<p>Si, au contraire, on lui fournit des arguments sur sa ligne de commande il tente d&rsquo;ouvrir le(s) contr\u00f4leur(s) ainsi&nbsp;:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">for (i = 1; i &lt; argc; i ++) {<br\/>    chip = <strong>gpiod_chip_open_lookup<\/strong>(argv[i]);<br\/>    if (chip == NULL)<br\/>        perror(argv[i]);<br\/>    else<br\/>        list_gpio_lines(chip);<br\/>}<br\/><\/pre>\n\n\n\n<p>Une fois un contr\u00f4leur ouvert, on peut conna\u00eetre son nom, son label et son nombre de lignes avec les routines&nbsp;:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">const char * <strong>gpiod_chip_name<\/strong>(struct gpiod_chip *<em>chip<\/em>);<br\/>const char * <strong>gpiod_chip_label<\/strong>(struct gpiod_chip *<em>chip<\/em>);<br\/>unsigned int <strong>gpiod_chip_num_lines<\/strong>(struct gpiod_chip *<em>chip<\/em>);<br\/><\/pre>\n\n\n\n<p>Pour acc\u00e9der aux lignes GPIO d&rsquo;un contr\u00f4leur, nous avons plusieurs possibilit\u00e9s. Tout d&rsquo;abord on peut r\u00e9clamer une ligne donn\u00e9e par son num\u00e9ro (son <em>offset<\/em>)&nbsp;:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">struct gpiod_line *<strong>gpio_chip_get_line<\/strong>(struct gpiod_chip *<em>chip<\/em>,<br\/>                                      unsigned int <em>offset<\/em>);<\/pre>\n\n\n\n<p>On peut aussi r\u00e9clamer un ensemble (<em>bulk<\/em>) de lignes en passant un tableau d&rsquo;<em>offsets<\/em> \u00e0 la fonction&nbsp;:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">int <strong>gpiod_chip_get_lines<\/strong>(struct gpiod_chip *<em>chip<\/em>,<br\/>                         unsigned int *<em>offset<\/em>,<br\/>                         int <em>num_offset<\/em>,<br\/>                         struct gpiod_line_bulk *<em>bulk<\/em>);<\/pre>\n\n\n\n<p>La structure <code>gpiod_line_bulk<\/code> qui est renseign\u00e9e par cette fonction est de la forme&nbsp;:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">struct <strong>gpiod_line_bulk<\/strong> {<br\/>    struct gpiod_line *<strong>lines<\/strong>[GPIOD_LINE_BULK_MAX_LINES];<br\/>    unsigned int <strong>num_lines<\/strong>;<br\/>};<\/pre>\n\n\n\n<p>Le champ <code>num_lines<\/code> indique le nombre de lignes inscrites dans le tableau <code>lines<\/code>. Il est toutefois possible d&rsquo;it\u00e9rer sur un ensemble <em>bulk<\/em> directement avec les macros&nbsp;:<br\/><\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">struct gpiod_line_bulk <em>bulk<\/em>;<br\/>struct gpiod_line *<em>line<\/em>;<br\/>struct gpiod_line **<em>save<\/em>;<br\/>[...]<br\/><strong>gpiod_line_bulk_foreach_line<\/strong>(&amp;<em>bulk<\/em>, <em>line<\/em>, <em>save<\/em>) {<br\/>    [...]<br\/>}<br\/><\/pre>\n\n\n\n<p>ou <\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">struct gpiod_line_bulk <em>bulk<\/em>;<br\/>struct gpiod_line *<em>line<\/em>;<br\/>int offset;<br\/>[...]<br\/><strong>gpiod_line_bulk_foreach_line_off<\/strong>(&amp;<em>bulk<\/em>, <em>line<\/em>, <em>offset<\/em>) {<br\/>    [...]<br\/>}<\/pre>\n\n\n\n<p>On peut \u00e9galement demander un tableau contenant toutes les lignes GPIO du contr\u00f4leur avec&nbsp;:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">int <strong>gpiod_chip_get_all_lines<\/strong>(struct gpiod_chip *<em>chip<\/em>,<br\/>                             struct gpiod_line_bulk *<em>bulk<\/em>);<\/pre>\n\n\n\n<p>Et enfin, il est possible de rechercher une ou plusieurs lignes par leur noms&nbsp;:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">struct gpiod_line *<strong>gpiod_find_line<\/strong>(struct gpiod_chip *<em>chip<\/em>,<br\/>                                   const char *<em>name<\/em>);<br\/>int <strong>gpiod_find_lines<\/strong>(struct gpiod_chip *<em>chip<\/em>,<br\/>                     const char **<em>names<\/em>,<br\/>                     struct gpiod_line_bulk *<em>bulk<\/em>);<\/pre>\n\n\n\n<p>La liste de nom fournie \u00e0 <code>gpiod_find_lines()<\/code> doit se terminer par un pointeur <code>NULL<\/code>.<\/p>\n\n\n\n<p>Les informations sur une ligne peuvent \u00eatre consult\u00e9es avec les fonctions suivantes&nbsp;:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">unsigned int <strong>gpiod_line_offset\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/strong>(struct gpiod_line *<em>line<\/em>);<br\/>const char  *<strong>gpiod_line_name\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/strong>(struct gpiod_line *<em>line<\/em>);<br\/>const char  *<strong>gpiod_line_consumer\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/strong>(struct gpiod_line *<em>line<\/em>);<br\/>int          <strong>gpiod_line_direction\u00a0\u00a0\u00a0\u00a0\u00a0<\/strong>(struct gpiod_line *<em>line<\/em>);<br\/>  \/\/ GPIOD_LINE_DIRECTION_INPUT, GPIOD_LINE_DIRECTION_OUTPUT<br\/>int <strong>\u00a0        gpiod_line_active_state\u00a0\u00a0<\/strong>(struct gpiod_line *<em>line<\/em>);<br\/>  \/\/ GPIOD_LINE_ACTIVE_STATE_HIGH, GPIOD_LINE_ACTIVE_STATE_LOW<br\/>bool         <strong>gpiod_line_is_used\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 <\/strong>(struct gpiod_line *<em>line<\/em>);<br\/>bool         <strong>gpiod_line_is_open_drain\u00a0<\/strong>(struct gpiod_line *<em>line<\/em>);<br\/>bool         <strong>gpiod_line_is_open_source<\/strong>(struct gpiod_line *<em>line<\/em>);<br\/><\/pre>\n\n\n\n<p>Le programme <a href=\"https:\/\/framagit.org\/cpb\/example-programs-using-libgpiod\/blob\/master\/list-gpio-lines.c\" target=\"_blank\"><code>list-gpio-lines.c<\/code><\/a> parcourt les lignes de chaque contr\u00f4leur trouv\u00e9 et affiche ces informations ainsi&nbsp;:<br\/><\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">struct gpiod_line_bulk <em>bulk<\/em>;\nstruct gpiod_line *<em>line<\/em>;\nint <em>offset<\/em>;\nconst char *<em>string<\/em>;\n\nfprintf(stdout, \"%s - %s - %d lines\\n\",\n    <strong>gpiod_chip_name<\/strong>(<em>chip<\/em>),\n    <strong>gpiod_chip_label<\/strong>(<em>chip<\/em>),\n    <strong>gpiod_chip_num_lines<\/strong>(<em>chip<\/em>));\n\nif (<strong>gpiod_chip_get_all_lines<\/strong>(<em>chip<\/em>, &amp;<em>bulk<\/em>) != 0) {\n    perror(\"gpiod_chip_get_all_lines()\");\n    return -1;\n}\n\n<strong>gpiod_line_bulk_foreach_line_off<\/strong>(&amp;<em>bulk<\/em>, <em>line<\/em>, <em>offset<\/em>) {\n    fprintf(stdout, \"  %2d: \", <em>offset<\/em>);\n\n    string = <strong>gpiod_line_name<\/strong>(<em>line<\/em>);\n    if (string == NULL)\n        fprintf(stdout, \"- \");\n    else\n        fprintf(stdout, \"%s \", <em>string<\/em>);\n\n    string = <strong>gpiod_line_consumer<\/strong>(<em>line<\/em>);\n    if (string == NULL)\n        fprintf(stdout, \"() \");\n    else\n        fprintf(stdout, \"(%s) \", <em>string<\/em>);;\n\n    if (<strong>gpiod_line_direction<\/strong>(<em>line<\/em>) == GPIOD_LINE_DIRECTION_OUTPUT)\n        fprintf(stdout, \"out \");\n    else\n        fprintf(stdout, \"in  \");\n\n    if (<strong>gpiod_line_active_state<\/strong>(<em>line<\/em>) == GPIOD_LINE_ACTIVE_STATE_LOW)\n        fprintf(stdout, \"active-low \");\n\n    if (<strong>gpiod_line_is_open_drain<\/strong>(<em>line<\/em>))\n        fprintf(stdout, \"open-drain \");<br\/>\n    if (<strong>gpiod_line_is_open_source<\/strong>(<em>line<\/em>))\n        fprintf(stdout, \"open-source \");\n\n    if (<strong>gpiod_line_is_used<\/strong>(<em>line<\/em>))\n        fprintf(stdout, \"* \");\n\n    fprintf(stdout, \"\\n\");\n}<\/pre>\n\n\n\n<p>Voici un exemple d&rsquo;ex\u00e9cution de ce programme sur un Raspberry Pi 3&nbsp;:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">$ <strong>.\/list-gpio-lines<\/strong> \ngpiochip0 - pinctrl-bcm2835 - 54 lines\n   0: - () in  \n   1: - () in  \n   2: - () in  \n   3: - () in  \n   4: - () in  \n   5: - () in  \n   6: - () in  \n   7: - () in  \n   8: - () in  \n   9: - () in  \n  10: - () in<br\/>[...]\n  50: - () in  \n  51: - () in  \n  52: - () in  \n  53: - () in  \ngpiochip1 - brcmexp-gpio - 8 lines\n   0: - () out \n   1: - () out \n   2: - () out \n   3: - () out \n   4: - () in  \n   5: - () out \n   6: - () out \n   7: - (led1) in  * \ngpiochip2 - brcmvirt-gpio - 2 lines\n   0: - (led0) out * \n   1: - () in  \n$<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Acc\u00e8s en lecture et \u00e9criture<\/h3>\n\n\n\n<p>Avant d&rsquo;acc\u00e9der \u00e0 une ligne GPIO, il est d&rsquo;usage d&rsquo;en r\u00e9server l&rsquo;acc\u00e8s. C&rsquo;est le r\u00f4le des fonctions suivantes, qui fonctionne pour une ligne ou pour tout un ensemble. Elles renvoient 0 si elles r\u00e9ussissent et -1 en cas d&rsquo;\u00e9chec.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">int <strong>gpiod_line_request_input<\/strong>(struct gpiod_line *<em>line<\/em>,\n                             const char *<em>consumer<\/em>);\n<br\/>int <strong>gpiod_line_request_bulk_input<\/strong>(struct gpiod_line_bulk *<em>bulk<\/em>,<br\/>                                  const char *<em>consumer<\/em>)<br\/>\nint <strong>gpiod_line_request_output<\/strong>(struct gpiod_line *<em>line<\/em>,\n                              const char *<em>consumer<\/em>,<br\/>                              int <em>initial_value<\/em>);<br\/><br\/>int <strong>gpiod_line_request_bulk_output<\/strong>(struct gpiod_line_bulk *<em>bulk<\/em>,<br\/>                                   const char *<em>consumer<\/em>,<br\/>                                   const int *<em>initial_values<\/em>);<br\/><\/pre>\n\n\n\n<p>Pour lib\u00e9rer l&rsquo;acc\u00e8s \u00e0 une ou plusieurs lignes, on emploiera&nbsp;:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">void <strong>gpiod_line_release<\/strong>(struct gpiod_line *<em>line<\/em>);<br\/>void <strong>gpiod_line_release_bulk<\/strong>(struct gpiod_line_bulk *<em>bulk<\/em>);<br\/><\/pre>\n\n\n\n<p>Les op\u00e9rations de lecture et d&rsquo;\u00e9criture sont simples. Lorsqu&rsquo;elles agissent sur un ensemble, il convient que le tableau pass\u00e9 en second argument soit assez grand pour contenir les valeurs lues ou \u00e0 \u00e9crire.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">int <strong>gpiod_line_get_value<\/strong>(struct gpiod_line *<em>line<\/em>);<br\/>int <strong>gpiod_line_set_value<\/strong>(struct gpiod_line *<em>line<\/em>,<br\/>                         int <em>value<\/em>);<br\/><br\/>int <strong>gpiod_line_get_value_bulk(<\/strong>struct gpiod_line_bulk *<em>bulk<\/em>,\n                              int *<em>values<\/em>);<br\/>int <strong>gpiod_line_set_value_bulk<\/strong>(struct gpiod_line_bulk *<em>bulk<\/em>,\n                              const int *<em>values<\/em>)<br\/><br\/><\/pre>\n\n\n\n<p>Le programme <a href=\"https:\/\/framagit.org\/cpb\/example-programs-using-libgpiod\/blob\/master\/invert-gpio.c\" target=\"_blank\"><code>invert-gpio.c<\/code><\/a> prend en argument les param\u00e8tres contr\u00f4leur et offset d&rsquo;une ligne GPIO d&rsquo;entr\u00e9e et d&rsquo;une ligne de sortie. Ensuite il boucle toutes les millisecondes en \u00e9crivant sur la ligne de sortie la valeur inverse de celle lue sur la ligne d&rsquo;entr\u00e9e. Hors traitement d&rsquo;erreur (pr\u00e9sent dans le vrai fichier), sa structure est en substance la suivante&nbsp;:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">sscanf(argv[2], \"%d\", &amp;input_offset);\nsscanf(argv[4], \"%d\", &amp;output_offset);\n\ninput_chip  = <strong>gpiod_chip_open_lookup<\/strong>(argv[1]);\noutput_chip = <strong>gpiod_chip_open_lookup<\/strong>(argv[3]);\n\ninput_line  = <strong>gpiod_chip_get_line<\/strong>(input_chip, input_offset);\noutput_line = <strong>gpiod_chip_get_line<\/strong>(output_chip, output_offset);\n\n<strong>gpiod_line_request_input<\/strong>(input_line, argv[0]);\n<strong>gpiod_line_request_output<\/strong>(output_line, argv[0], 0);<br\/>\nfor (;;) {\n    <strong>gpiod_line_set_value<\/strong>(output_line,\n                         1 - <strong>gpiod_line_get_value<\/strong>(input_line));\n        usleep(1000);\n}<br\/>\n<strong>gpiod_line_release<\/strong>(input_line);\n<strong>gpiod_line_release<\/strong>(output_line);<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">D\u00e9tection d&rsquo;\u00e9v\u00e9nements<\/h3>\n\n\n\n<p>Le programme ci-dessus se comporte de mani\u00e8re d\u00e9plorable, en consommant du temps CPU inutilement (toutes les it\u00e9rations o\u00f9 l&rsquo;entr\u00e9e n&rsquo;a pas chang\u00e9 de valeur) et en risquant de manquer des \u00e9v\u00e9nments brefs o\u00f9 l&rsquo;entr\u00e9e change deux fois d&rsquo;\u00e9tat pendant la milliseconde de sommeil.<\/p>\n\n\n\n<p>Il serait largement pr\u00e9f\u00e9rable d&rsquo;attendre passivement que le noyau nous indique qu&rsquo;une transition est survenue sur la ligne d&rsquo;entr\u00e9e pour faire l&rsquo;\u00e9criture correspondant en sortie. Pour cela la biblioth\u00e8que Libgpiod met \u00e0 notre disposition des fonctions d&rsquo;attente. Tout d&rsquo;abord on indique pour quel type d&rsquo;\u00e9v\u00e9nment on souhaite se mettre en attente<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">int <strong>gpiod_line_request_rising_edge_events<\/strong>(<br\/>                          struct gpiod_line *<em>line<\/em>,\n                          const char *<em>consumer<\/em>);\nint <strong>gpiod_line_request_bulk_rising_edge_events<\/strong>(<br\/>                          struct gpiod_line_bulk *<em>bulk<\/em>,\n                          const char *<em>consumer<\/em>);\n\nint <strong>gpiod_line_request_falling_edge_events<\/strong>(<br\/>                          struct gpiod_line *<em>line<\/em>,\n                          const char *<em>consumer<\/em>);\nint <strong>gpiod_line_request_bulk_falling_edge_events<\/strong>(<br\/>                          struct gpiod_line_bulk *<em>bulk<\/em>,\n                          const char *<em>consumer<\/em>);\n\nint <strong>gpiod_line_request_both_edges_events<\/strong>(<br\/>                          struct gpiod_line *<em>line<\/em>,\n                          const char *<em>consumer<\/em>);\nint <strong>gpiod_line_request_bulk_both_edges_events<\/strong>(<br\/>                          struct gpiod_line_bulk *<em>bulk<\/em>,\n                          const char *<em>consumer<\/em>);<\/pre>\n\n\n\n<p>On peut ensuite se mettre en attente de l&rsquo;occurrence d&rsquo;un \u00e9v\u00e9nement souhait\u00e9&nbsp;:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">int <strong>gpiod_line_event_wait<\/strong>(struct gpiod_line *<em>line<\/em>,\n                          const struct timespec *<em>timeout<\/em>);\n\nint <strong>gpiod_line_event_read<\/strong>(struct gpiod_line *<em>line<\/em>,\n                          struct gpiod_line_event *<em>event<\/em>);\n\nint <strong>gpiod_line_event_wait_bulk<\/strong>(<br\/>                          struct gpiod_line_bulk *<em>bulk<\/em>,\n                          const struct timespec *<em>timeout<\/em>,\n                          struct gpiod_line_bulk *<em>event_bulk<\/em>);<\/pre>\n\n\n\n<p>La premi\u00e8re fonction attend que l&rsquo;\u00e9v\u00e9nement se produise avec une dur\u00e9e maximale si on le souhaite. Si on attend n&rsquo;importe quel type de front, il convient de savoir si l&rsquo;\u00e9v\u00e9nement survenu est une mont\u00e9e ou une descente du signal. Pour cela on invoque la seconde fonction, qui renseigne une structure contenant le type d&rsquo;\u00e9v\u00e9nement et son horodatage.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">struct <strong>gpiod_line_event<\/strong> {\n    struct timespec <strong>ts<\/strong>;\n    int <strong>event_type<\/strong>;\n    \/\/ GPIOD_LINE_EVENT_RISING_EDGE, GPIOD_LINE_EVENT_FALLING_EDGE\n};<\/pre>\n\n\n\n<p>Le programme <a href=\"https:\/\/framagit.org\/cpb\/example-programs-using-libgpiod\/blob\/master\/wait-gpio-event.c\" target=\"_blank\"><code>wait-gpio-event.c<\/code><\/a> reprend le m\u00eame comportement que le pr\u00e9c\u00e9dent mais ne se r\u00e9veille que lorsqu&rsquo;un changement d&rsquo;\u00e9tat sur la ligne GPIO d&rsquo;entr\u00e9e est d\u00e9tect\u00e9. Les modifications principales sont&nbsp;:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">[...]\n<strong>gpiod_line_request_both_edges_events<\/strong>(input_line, argv[0]);\n<strong>gpiod_line_request_output<\/strong>(output_line, argv[0], 0);\n\n<strong>gpiod_line_set_value<\/strong>(output_line, \n                     1 - gpiod_line_get_value(input_line));\n\nfor (;;) {\n    <strong>gpiod_line_event_wait<\/strong>(input_line, NULL);\n    if (<strong>gpiod_line_event_read<\/strong>(input_line, &amp;event) == 0) {\n        if (event.event_type == GPIOD_LINE_EVENT_RISING_EDGE)\n            <strong>gpiod_line_set_value<\/strong>(output_line, 0);\n        else\n            <strong>gpiod_line_set_value<\/strong>(output_line, 1);\n    }\n}<\/pre>\n\n\n\n<p>La fonction <code>gpiod_line_event_wait()<\/code> utilise en interne l&rsquo;appel syst\u00e8me <code>ppoll()<\/code> pour r\u00e9aliser l&rsquo;attente passive. Si nous souhaitons r\u00e9aliser nous m\u00eame l&rsquo;attente, en ajoutant par exemple des descripteurs de sockets r\u00e9seau, de p\u00e9riph\u00e9riques caract\u00e8res (s\u00e9rie, i2c&#8230;), de console, etc. on utilisera les fonctions suivantes pour obtenir le descripteur et lire l&rsquo;\u00e9v\u00e9nement survenu.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">int <strong>gpiod_line_event_get_fd<\/strong>(struct gpiod_line *<em>line<\/em>);<br\/><br\/>int <strong>gpiod_line_event_read_fd<\/strong>(int <em>fd<\/em>,<br\/>                             struct gpiod_line_event *<em>event<\/em>);<br\/><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">PERFORMANCES<\/h2>\n\n\n\n<p>Nous avons vu dans cet article et les pr\u00e9c\u00e9dents plusieurs m\u00e9thodes d&rsquo;acc\u00e8s aux lignes GPIO. Certaines sont plus simples que d&rsquo;autres, mais une question se pose&nbsp;: qu&rsquo;en est-il des performances&nbsp;? De la vitesse de commutation d&rsquo;une ligne par exemple&nbsp;?<\/p>\n\n\n\n<p>Une m\u00e9thode simple consiste \u00e0 faire un petit programme qui alterne une sortie le plus rapidement possible et \u00e0 observer le r\u00e9sultat \u00e0 l&rsquo;oscilloscope.<\/p>\n\n\n\n<p>Les exemples ci-dessous ont \u00e9t\u00e9 ex\u00e9cut\u00e9s sur un Raspberry Pi 3, apr\u00e8s l&rsquo;avoir bascul\u00e9 en mode <em>performance<\/em> plut\u00f4t que <em>ondemand<\/em> :<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">$ <strong>sudo -i<\/strong><br\/># <strong>echo performance > \/sys\/devices\/system\/cpu\/cpu0\/cpufreq\/scaling_governor<\/strong><br\/># <strong>exit<\/strong><br\/><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Commande <code>gpioset<\/code> depuis un script shell<\/h3>\n\n\n\n<p>Nous nous en doutons imm\u00e9diatement, l&rsquo;appel d&rsquo;une commande depuis le shell ne sera pas la m\u00e9thode la plus rapide. V\u00e9rifions quand m\u00eame le fonctionnement. Pour cela, j&rsquo;appelle la ligne de commande suivante&nbsp;:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">$ <strong>while true; do gpioset \/dev\/gpiochip0 24=0; gpioset \/dev\/gpiochip0 24=1; done<\/strong><\/pre>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter\"><a href=\"https:\/\/www.blaess.fr\/christophe\/wp-content\/uploads\/2018\/10\/oscillo-01-1.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1920\" height=\"988\" src=\"https:\/\/www.blaess.fr\/christophe\/wp-content\/uploads\/2018\/10\/oscillo-01-1.png\" alt=\"Cycle complet d'appels gpioset\" class=\"wp-image-5333\" srcset=\"https:\/\/www.blaess.fr\/christophe\/wp-content\/uploads\/2018\/10\/oscillo-01-1.png 1920w, https:\/\/www.blaess.fr\/christophe\/wp-content\/uploads\/2018\/10\/oscillo-01-1-300x154.png 300w, https:\/\/www.blaess.fr\/christophe\/wp-content\/uploads\/2018\/10\/oscillo-01-1-768x395.png 768w, https:\/\/www.blaess.fr\/christophe\/wp-content\/uploads\/2018\/10\/oscillo-01-1-1024x527.png 1024w\" sizes=\"auto, (max-width: 1920px) 100vw, 1920px\" \/><\/a><figcaption>Cycle complet d&rsquo;appels \u00e0 <code>gpioset<\/code><\/figcaption><\/figure><\/div>\n\n\n\n<p><\/p>\n\n\n\n<p>Nous voyons deux pics espac\u00e9s de 7,13 millisecondes. Cet \u00e9norme \u00e9cart est moins d\u00fb aux performances du shell (une boucle simple \u00e0 incr\u00e9menter une variable durant environ 0,5 milliseconde) qu&rsquo;au temps d&rsquo;appel de la commande <code>gpioset<\/code> et \u00e0 la configuration du contr\u00f4leur et de la ligne GPIO.\u00a0 Il faut donc <strong>3,5 millisecondes<\/strong> pour ex\u00e9cuter une commande <code>gpioset<\/code>. Zoomons sur un pic&nbsp;:<br\/><\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter\"><a href=\"https:\/\/www.blaess.fr\/christophe\/wp-content\/uploads\/2018\/10\/oscillo-02.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1920\" height=\"988\" src=\"https:\/\/www.blaess.fr\/christophe\/wp-content\/uploads\/2018\/10\/oscillo-02.png\" alt=\"Sortie GPIO \u00e0 l'\u00e9tat haut avec gpioset\" class=\"wp-image-5334\" srcset=\"https:\/\/www.blaess.fr\/christophe\/wp-content\/uploads\/2018\/10\/oscillo-02.png 1920w, https:\/\/www.blaess.fr\/christophe\/wp-content\/uploads\/2018\/10\/oscillo-02-300x154.png 300w, https:\/\/www.blaess.fr\/christophe\/wp-content\/uploads\/2018\/10\/oscillo-02-768x395.png 768w, https:\/\/www.blaess.fr\/christophe\/wp-content\/uploads\/2018\/10\/oscillo-02-1024x527.png 1024w\" sizes=\"auto, (max-width: 1920px) 100vw, 1920px\" \/><\/a><figcaption>Sortie GPIO \u00e0 l&rsquo;\u00e9tat haut avec <code>gpioset<\/code><\/figcaption><\/figure><\/div>\n\n\n\n<p>En observant de plus pr\u00e8s l&rsquo;un des pics, on voit qu&rsquo;une fois la sortie activ\u00e9e, elle reste \u00e0 l&rsquo;\u00e9tat haut pendant 15 microsecondes environ, puis qu&rsquo;elle est rel\u00e2ch\u00e9e par la commande, retournant \u00e0 l&rsquo;\u00e9tat bas par une courbe de d\u00e9charge d&rsquo;une trentaine de microsecondes.<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter\"><a href=\"https:\/\/www.blaess.fr\/christophe\/wp-content\/uploads\/2018\/10\/oscillo-03.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1920\" height=\"988\" src=\"https:\/\/www.blaess.fr\/christophe\/wp-content\/uploads\/2018\/10\/oscillo-03.png\" alt=\"D\u00e9croissance du signal apr\u00e8s lib\u00e9ration de la ligne GPIO\" class=\"wp-image-5335\" srcset=\"https:\/\/www.blaess.fr\/christophe\/wp-content\/uploads\/2018\/10\/oscillo-03.png 1920w, https:\/\/www.blaess.fr\/christophe\/wp-content\/uploads\/2018\/10\/oscillo-03-300x154.png 300w, https:\/\/www.blaess.fr\/christophe\/wp-content\/uploads\/2018\/10\/oscillo-03-768x395.png 768w, https:\/\/www.blaess.fr\/christophe\/wp-content\/uploads\/2018\/10\/oscillo-03-1024x527.png 1024w\" sizes=\"auto, (max-width: 1920px) 100vw, 1920px\" \/><\/a><figcaption>D\u00e9croissance du signal apr\u00e8s lib\u00e9ration de la ligne GPIO<\/figcaption><\/figure><\/div>\n\n\n\n<h3 class=\"wp-block-heading\">API context-less de Libgpiod<\/h3>\n\n\n\n<p>Nous pouvons reprendre le programme <a href=\"https:\/\/framagit.org\/cpb\/example-programs-using-libgpiod\/blob\/master\/toggle-gpio.c\" target=\"_blank\"><code>toggle-gpio.c<\/code><\/a> de l&rsquo;article pr\u00e9c\u00e9dent pour le modifier rapidement et faire une boucle la plus rapide possible autour de la fonction <code>gpiod_ctxless_set_values()<\/code>.<\/p>\n\n\n\n<p>C&rsquo;est ce que r\u00e9alise le programme <a href=\"https:\/\/framagit.org\/cpb\/example-programs-using-libgpiod\/blob\/master\/ctxless-toggle-gpio.c\" target=\"_blank\"><code>ctxless-toggle-gpio.c<\/code><\/a> :<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">[...]\nfor (;;) {\n    <strong>gpiod_ctxless_set_value<\/strong>(argv[1], offset, <strong>0<\/strong>, 0, argv[0], NULL, NULL);\n    <strong>gpiod_ctxless_set_value<\/strong>(argv[1], offset, <strong>1<\/strong>, 0, argv[0], NULL, NULL);\n}<\/pre>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter\"><a href=\"https:\/\/www.blaess.fr\/christophe\/wp-content\/uploads\/2018\/10\/oscillo-04.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1920\" height=\"988\" src=\"https:\/\/www.blaess.fr\/christophe\/wp-content\/uploads\/2018\/10\/oscillo-04.png\" alt=\"Cycle d'appels \u00e0 gpiod_ctxless_set_value()\" class=\"wp-image-5337\" srcset=\"https:\/\/www.blaess.fr\/christophe\/wp-content\/uploads\/2018\/10\/oscillo-04.png 1920w, https:\/\/www.blaess.fr\/christophe\/wp-content\/uploads\/2018\/10\/oscillo-04-300x154.png 300w, https:\/\/www.blaess.fr\/christophe\/wp-content\/uploads\/2018\/10\/oscillo-04-768x395.png 768w, https:\/\/www.blaess.fr\/christophe\/wp-content\/uploads\/2018\/10\/oscillo-04-1024x527.png 1024w\" sizes=\"auto, (max-width: 1920px) 100vw, 1920px\" \/><\/a><figcaption>Cycle d&rsquo;appels \u00e0 gpiod_ctxless_set_value()<\/figcaption><\/figure><\/div>\n\n\n\n<p>Le r\u00e9sultat est d\u00e9j\u00e0 nettement meilleur que le pr\u00e9c\u00e9dent, les pics sont espac\u00e9s de 64 microsecondes.<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter\"><a href=\"https:\/\/www.blaess.fr\/christophe\/wp-content\/uploads\/2018\/10\/oscillo-05.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1920\" height=\"988\" src=\"https:\/\/www.blaess.fr\/christophe\/wp-content\/uploads\/2018\/10\/oscillo-05.png\" alt=\"Sortie GPIO \u00e0 l'\u00e9tat haut avec gpiod_ctxless_set_value()\" class=\"wp-image-5338\" srcset=\"https:\/\/www.blaess.fr\/christophe\/wp-content\/uploads\/2018\/10\/oscillo-05.png 1920w, https:\/\/www.blaess.fr\/christophe\/wp-content\/uploads\/2018\/10\/oscillo-05-300x154.png 300w, https:\/\/www.blaess.fr\/christophe\/wp-content\/uploads\/2018\/10\/oscillo-05-768x395.png 768w, https:\/\/www.blaess.fr\/christophe\/wp-content\/uploads\/2018\/10\/oscillo-05-1024x527.png 1024w\" sizes=\"auto, (max-width: 1920px) 100vw, 1920px\" \/><\/a><figcaption>Sortie GPIO \u00e0 l&rsquo;\u00e9tat haut avec <code>gpiod_ctxless_set_value()<\/code><\/figcaption><\/figure><\/div>\n\n\n\n<p>La broche reste \u00e0 l&rsquo;\u00e9tat haut sept microsecondes avant de revenir \u00e0 l&rsquo;\u00e9tat bas en une vingtaine de microsecondes. On peut conclure qu&rsquo;un changement d&rsquo;\u00e9tat avec l&rsquo;API <em>contex-less<\/em> de Libgpiod dure environ <strong>32 microsecondes<\/strong>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">API bas-niveau de Libgpiod<\/h3>\n\n\n\n<p>L&rsquo;\u00e9tape suivante va consister \u00e0 utiliser l&rsquo;API bas-niveau de Libgpiod, celle que nous avons \u00e9tudi\u00e9 dans cet article. Le programme <code><a href=\"https:\/\/framagit.org\/cpb\/example-programs-using-libgpiod\/blob\/master\/low-level-toogle-gpio.c\" target=\"_blank\">low-level-toggle-gpio.c<\/a><\/code> r\u00e9alise ce travail&nbsp;:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">[...]<br\/>for (;;) { \n    <strong>gpiod_line_set_value<\/strong>(output_line, <strong>0<\/strong>);\n    <strong>gpiod_line_set_value<\/strong>(output_line, <strong>1<\/strong>);\n}<\/pre>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter\"><a href=\"https:\/\/www.blaess.fr\/christophe\/wp-content\/uploads\/2018\/10\/oscillo-06.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1920\" height=\"988\" src=\"https:\/\/www.blaess.fr\/christophe\/wp-content\/uploads\/2018\/10\/oscillo-06.png\" alt=\"Cycles autour de gpiod_line_set_value()\" class=\"wp-image-5339\" srcset=\"https:\/\/www.blaess.fr\/christophe\/wp-content\/uploads\/2018\/10\/oscillo-06.png 1920w, https:\/\/www.blaess.fr\/christophe\/wp-content\/uploads\/2018\/10\/oscillo-06-300x154.png 300w, https:\/\/www.blaess.fr\/christophe\/wp-content\/uploads\/2018\/10\/oscillo-06-768x395.png 768w, https:\/\/www.blaess.fr\/christophe\/wp-content\/uploads\/2018\/10\/oscillo-06-1024x527.png 1024w\" sizes=\"auto, (max-width: 1920px) 100vw, 1920px\" \/><\/a><figcaption>Cycles autour de <code>gpiod_line_set_value()<\/code><\/figcaption><\/figure><\/div>\n\n\n\n<p>Le r\u00e9sultat est sans appel, le temps d&rsquo;un cycle complet est de 2,19 microsecondes, et la ligne ne revenant pas en haute imp\u00e9dance, il n&rsquo;y a plus de courbe de d\u00e9croissance comme dans les exemple pr\u00e9c\u00e9dentes.<\/p>\n\n\n\n<p>Cette fonction permet donc de configurer la ligne GPIO en <strong>1,09 microseconde<\/strong>. Elle pr\u00e9sente quand m\u00eame un tr\u00e8s l\u00e9ger <em>overhead<\/em> par rapport \u00e0 l&rsquo;appel syst\u00e8me <code>ioctl()<\/code> direct que nous avons \u00e9voqu\u00e9 dans l&rsquo;article pr\u00e9c\u00e9dent.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Appel syst\u00e8me ioctl()<\/h3>\n\n\n\n<p>Logiquement l&rsquo;invocation directe de l&rsquo;appel syst\u00e8me sous-jacent \u00e0 toutes ces fonctions devraient \u00eatre plus performante. Il nous suffit de modifier l\u00e9g\u00e8rement le programme de l&rsquo;article pr\u00e9c\u00e9dent pour obtenir un programme <a href=\"https:\/\/framagit.org\/cpb\/ioctl-access-to-gpio\/blob\/master\/ioctl-fast-toggle-gpio.c\" target=\"_blank\"><code>ioctl-fast-toggle-gpio.c<\/code><\/a> :<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">[...]\noutput_values.values[0] = 0;\nfor (;;) {\n    output_values.values[0] = <strong>0<\/strong>;\n    <strong>ioctl<\/strong>(output_request.fd, <strong>GPIOHANDLE_SET_LINE_VALUES_IOCTL<\/strong>, &amp;output_values);\n    output_values.values[0] = <strong>1<\/strong>;\n    <strong>ioctl<\/strong>(output_request.fd, <strong>GPIOHANDLE_SET_LINE_VALUES_IOCTL<\/strong>, &amp;output_values);\n}<\/pre>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter\"><a href=\"https:\/\/www.blaess.fr\/christophe\/wp-content\/uploads\/2018\/10\/oscillo-07.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1920\" height=\"988\" src=\"https:\/\/www.blaess.fr\/christophe\/wp-content\/uploads\/2018\/10\/oscillo-07.png\" alt=\"Cycles autour de l'appel ioctl()\" class=\"wp-image-5340\" srcset=\"https:\/\/www.blaess.fr\/christophe\/wp-content\/uploads\/2018\/10\/oscillo-07.png 1920w, https:\/\/www.blaess.fr\/christophe\/wp-content\/uploads\/2018\/10\/oscillo-07-300x154.png 300w, https:\/\/www.blaess.fr\/christophe\/wp-content\/uploads\/2018\/10\/oscillo-07-768x395.png 768w, https:\/\/www.blaess.fr\/christophe\/wp-content\/uploads\/2018\/10\/oscillo-07-1024x527.png 1024w\" sizes=\"auto, (max-width: 1920px) 100vw, 1920px\" \/><\/a><figcaption>Cycles autour de l&rsquo;appel <code>ioctl()<\/code><\/figcaption><\/figure><\/div>\n\n\n\n<p>En effet, une l\u00e9g\u00e8re am\u00e9lioration est perceptible, le cycle complet se d\u00e9roulant en 1,76 microsecondes, donc la configuration d&rsquo;une sortie durant <strong>0,88 microseconde<\/strong>. Ceci est tr\u00e8s correct pour une action depuis\u00a0 l&rsquo;espace utilisateur sur un processeur comme celui du Raspberry Pi 3.<br\/><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">CONCLUSION<\/h2>\n\n\n\n<p>Nous avons pu voir que l&rsquo;API bas-niveau propos\u00e9e par la biblioth\u00e8que Libgpiod est riche et assez simple \u00e0 utiliser. Elle remplace avantageusement les acc\u00e8s <em>via<\/em> sysfs et offre la portabilit\u00e9 qui manque aux solutions comme WiringPi sp\u00e9cifiques \u00e0 certaines plates-formes. La surcharge par rapport \u00e0 l&rsquo;invocation directe de l&rsquo;appel-syst\u00e8me <code>ioctl()<\/code> est minime, et la complexit\u00e9 sensiblement moindre.<br\/><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">R\u00c9F\u00c9RENCES<\/h2>\n\n\n\n<ul class=\"wp-block-list\"><li>[GOLASZEWSKI 2017] Bartosz Golaszewsi\u00a0: Doxygen documentation of <a href=\"https:\/\/git.kernel.org\/pub\/scm\/libs\/libgpiod\/libgpiod.git\/tree\/include\/gpiod.h\" target=\"_blank\" rel=\"noreferrer noopener\">gpiod.h<\/a>.<\/li><li>[GOLASZEWSKI 2018] Bartosz Golaszewski\u00a0: \u201c<a href=\"https:\/\/www.youtube.com\/watch?v=XVFht4MkIl4\" target=\"_blank\" rel=\"noreferrer noopener\">New GPIO Interface for User Space (video)<\/a>\u201d <em>Kernel Recipes<\/em> 2018.<\/li><li>[WALLEIJ 2016] Linus Walleij\u00a0: \u201c<a href=\"https:\/\/elinux.org\/images\/9\/9b\/GPIO_for_Engineers_and_Makers.pdf\" target=\"_blank\" rel=\"noreferrer noopener\">GPIO for Engineers and Makers<\/a>\u201c, Las Vegas 2016.<\/li><\/ul>","protected":false},"excerpt":{"rendered":"<p>Apr&egrave;s l&rsquo;&eacute;tude des outils disponible en ligne de commande pour piloter des lignes GPIO (partie 1), puis celles de l&rsquo;acc&egrave;s depuis un programme avec l&rsquo;appel syst&egrave;me ioctl() ou la version context-less de la biblioth&egrave;que Libgpiod (partie 2), nous poursuivons en examinant les fonctions bas niveaux de cette biblioth&egrave;que. Nous aborderons &eacute;galement le th&egrave;me des performances [&hellip;]<\/p>","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[5,19,8,10,11],"tags":[],"class_list":["post-5284","post","type-post","status-publish","format-standard","hentry","category-embarque","category-kernel","category-linux-2","category-microprocesseur","category-raspberry-pi"],"_links":{"self":[{"href":"https:\/\/www.blaess.fr\/christophe\/wp-json\/wp\/v2\/posts\/5284","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.blaess.fr\/christophe\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.blaess.fr\/christophe\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.blaess.fr\/christophe\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.blaess.fr\/christophe\/wp-json\/wp\/v2\/comments?post=5284"}],"version-history":[{"count":49,"href":"https:\/\/www.blaess.fr\/christophe\/wp-json\/wp\/v2\/posts\/5284\/revisions"}],"predecessor-version":[{"id":5358,"href":"https:\/\/www.blaess.fr\/christophe\/wp-json\/wp\/v2\/posts\/5284\/revisions\/5358"}],"wp:attachment":[{"href":"https:\/\/www.blaess.fr\/christophe\/wp-json\/wp\/v2\/media?parent=5284"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.blaess.fr\/christophe\/wp-json\/wp\/v2\/categories?post=5284"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.blaess.fr\/christophe\/wp-json\/wp\/v2\/tags?post=5284"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}