{"id":2248,"date":"2012-05-28T09:00:15","date_gmt":"2012-05-28T08:00:15","guid":{"rendered":"http:\/\/www.blaess.fr\/christophe\/?p=2248"},"modified":"2012-05-28T09:00:15","modified_gmt":"2012-05-28T08:00:15","slug":"gpio-pandaboard-et-temps-reel-4-gestion-des-interruptions","status":"publish","type":"post","link":"https:\/\/www.blaess.fr\/christophe\/2012\/05\/28\/gpio-pandaboard-et-temps-reel-4-gestion-des-interruptions\/","title":{"rendered":"GPIO, Pandaboard et temps r\u00e9el &#8211; 4 &#8211; Gestion des interruptions"},"content":{"rendered":"<p><a href=\"http:\/\/www.blaess.fr\/christophe\/2012\/05\/28\/gpio-pandaboard-et-temps-reel-4-gestion-des-interruptions\/\"><img loading=\"lazy\" decoding=\"async\" class=\"alignright size-medium wp-image-2445\" title=\"GPIO, Pandaboard et temps r\u00e9el\" src=\"http:\/\/www.blaess.fr\/christophe\/wp-content\/uploads\/2012\/05\/img-03.jpg\" alt=\"GPIO, Pandaboard et temps r\u00e9el\" width=\"150\" height=\"201\" \/><\/a><\/p>\n<p style=\"text-align: justify;\">Nous avons vu dans les articles pr\u00e9c\u00e9dents comment \u00e9crire sur une broche de sortie du connecteur d&rsquo;extension de la Pandaboard <a title=\"GPIO, Pandaboard et temps r\u00e9el \u2013 1 \u2013 Sorties depuis l\u2019espace utilisateur\" href=\"http:\/\/www.blaess.fr\/christophe\/2012\/05\/09\/gpio-pandaboard-temps-reel-1\/\">depuis l&rsquo;espace utilisateur<\/a>, puis <a title=\"GPIO, Pandaboard et temps r\u00e9el \u2013 2 \u2013 sorties depuis l\u2019espace kernel\" href=\"http:\/\/www.blaess.fr\/christophe\/2012\/05\/14\/gpio-pandaboard-et-temps-reel-2-sorties-depuis-lespace-kernel\/\">depuis le kernel<\/a>. Nous avons \u00e9galement r\u00e9ussi \u00e0 <a title=\"GPIO, Pandaboard et temps r\u00e9el \u2013 3 \u2013 Lecture sur des entr\u00e9es\" href=\"http:\/\/www.blaess.fr\/christophe\/2012\/05\/21\/gpio-pandaboard-et-temps-reel-3-lecture-sur-des-entrees\/\">lire l&rsquo;\u00e9tat de broches GPIO d&rsquo;entr\u00e9e<\/a>. Cette fois, nous allons am\u00e9liorer cette lecture en g\u00e9rant l&rsquo;occurence d&rsquo;\u00e9v\u00e9nements par l&rsquo;interm\u00e9diaire d&rsquo;interruptions.<\/p>\n<p>\n<!--more-->\n<\/p>\n<h1>Principe<\/h1>\n<p style=\"text-align: justify;\">Lorsqu&rsquo;on veut conna\u00eetre \u00e0 un moment donn\u00e9 l&rsquo;\u00e9tat d&rsquo;un capteur par exemple, la lecture simple d&rsquo;une broche d&rsquo;entr\u00e9e suffit. Si l&rsquo;on veut \u00eatre capable de r\u00e9pondre \u00e0 des sollicitations ext\u00e9rieures, par exemple activer l&rsquo;ouverture d&rsquo;un portillon lorsqu&rsquo;un bouton a \u00e9t\u00e9 press\u00e9, il suffit de lire en boucle l&rsquo;\u00e9tat du bouton et de r\u00e9agir d\u00e8s qu&rsquo;il est au niveau attendu. C&rsquo;est ce que l&rsquo;on nomme <em>polling<\/em>, une technique utilis\u00e9e depuis longtemps dans <a title=\"Automates industriels sur Wikip\u00e9dia\" href=\"http:\/\/fr.wikipedia.org\/wiki\/Automate_programmable_industriel\" target=\"_blank\">les automates industriels<\/a> et dans la programmation de nombreux micro-contr\u00f4leurs.<\/p>\n<p style=\"text-align: justify;\">Les choses se compliquent lorsque l&rsquo;on d\u00e9sire suivre avec finesse l&rsquo;\u00e9tat de nombreux capteurs, tout en r\u00e9alisant d&rsquo;autres t\u00e2ches en m\u00eame temps (calcul, interface utilisateur, etc.). Pour simplifier le traitement, on utilise plut\u00f4t un m\u00e9canisme d&rsquo;interruption&nbsp;: le processeur est dot\u00e9 d&rsquo;un contr\u00f4leur d&rsquo;interruption qui surveille l&rsquo;\u00e9tat des lignes d&rsquo;entr\u00e9e et peut \u00e0 tout moment signaler un changement d&rsquo;\u00e9tat au microprocesseur. Celui-ci <em>interrompt<\/em> son travail en cours, et ex\u00e9cute des op\u00e9rations enregistr\u00e9es au pr\u00e9alable.<\/p>\n<h1 style=\"text-align: justify;\">Impl\u00e9mentation sous Linux<\/h1>\n<p style=\"text-align: justify;\">Sous Linux, le contr\u00f4leur d&rsquo;interruption (<a title=\"APIC sur Wikipedia (Anglais)\" href=\"http:\/\/en.wikipedia.org\/wiki\/Advanced_Programmable_Interrupt_Controller\" target=\"_blank\">APIC<\/a>) est pilot\u00e9 par le noyau, et lorsqu&rsquo;une demande d&rsquo;interruption se pr\u00e9sente (par exemple l&rsquo;arriv\u00e9e d&rsquo;un caract\u00e8re sur un port s\u00e9rie), le code pr\u00e9programm\u00e9 est ex\u00e9cut\u00e9 dans le kernel. Ce code, ce gestionnaire (<em>handler<\/em>) bas-niveau, assure l&rsquo;acquittement de l&rsquo;interruption vis-\u00e0-vis de l&rsquo;APIC. Puis il invoque les \u00e9ventuelles routines de service install\u00e9es par les drivers. Ces routines devront effectuer un travail en r\u00e9ponse \u00e0 l&rsquo;\u00e9v\u00e9nement repr\u00e9sent\u00e9 par l&rsquo;interruption (par exemple r\u00e9veiller une t\u00e2che en attente de donn\u00e9es sur une liaison s\u00e9rie).<\/p>\n<p style=\"text-align: justify;\">Les ports GPIO configur\u00e9s en entr\u00e9e peuvent d\u00e9clencher des interruptions de diff\u00e9rentes mani\u00e8res. Le type de d\u00e9clenchement le plus courant est celui sur front montant, c&rsquo;est-\u00e0-dire lorsqu&rsquo;une transition de 0 \u00e0 1 (logique) se produit sur la broche d&rsquo;entr\u00e9e.<\/p>\n<p style=\"text-align: justify;\">Nous allons traiter les interruptions en \u00e9crivant un petit module du kernel Linux qui installera un gestionnaire pour le num\u00e9ro d&rsquo;interruption qui nous int\u00e9resse&nbsp;; ce num\u00e9ro\u00a0 est obtenu gr\u00e2ce au sous-syt\u00e8me GPIO du noyau (et d\u00e9pend de l&rsquo;architecture).<\/p>\n<p style=\"text-align: justify;\">\u00a0Nous ferons ensuite la m\u00eame exp\u00e9rience avec un module \u00e9crit pour Xenomai en utilisant l&rsquo;API RTDM (dont nous avons parl\u00e9 dans le pr\u00e9c\u00e9dent article), et nous comparerons les r\u00e9sultats.<\/p>\n<h1 style=\"text-align: justify;\">Gestion des interruptions dans le kernel Linux<\/h1>\n<p style=\"text-align: justify;\">Notre premier exemple consistera en un module pour noyau 2.6.38.8 standard, dont le fichier de configuration pour Pandaboard est <a title=\"http:\/\/www.blaess.fr\/christophe\/files\/article-2012-05-29\/config-linux-2.6.38.8\" href=\"http:\/\/www.blaess.fr\/christophe\/files\/article-2012-05-29\/config-linux-2.6.38.8\" target=\"_blank\">disponible ici<\/a>.<\/p>\n<p style=\"text-align: justify;\">Ce module installe un gestionnaire pour l&rsquo;interruption associ\u00e9e au GPIO d&rsquo;entr\u00e9e. \u00c0 chaque fois qu&rsquo;un front \u00e9lectrique montant se pr\u00e9sente sur cette broche, le handler est appel\u00e9 et bascule une broche GPIO de sortie.<\/p>\n<p style=\"text-align: justify;\">J&rsquo;ai choisi d&rsquo;utiliser en sortie la broche 14, dont le GPIO est indiqu\u00e9 dans le <a title=\"http:\/\/www.blaess.fr\/christophe\/files\/article-2012-05-10\/Panda_Board_Spec_REVEA1_04.pdf\" href=\"http:\/\/www.blaess.fr\/christophe\/files\/article-2012-05-10\/Panda_Board_Spec_REVEA1_04.pdf\">manuel de r\u00e9f\u00e9rence de la Pandaboard<\/a> comme valant 139.<\/p>\n<p style=\"text-align: justify;\"><a href=\"http:\/\/www.blaess.fr\/christophe\/wp-content\/uploads\/2012\/05\/img-25.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-2263\" title=\"Connecteur d'extension de la Pandaboard\" src=\"http:\/\/www.blaess.fr\/christophe\/wp-content\/uploads\/2012\/05\/img-25.jpg\" alt=\"Connecteur d'extension de la Pandaboard\" width=\"600\" height=\"301\" \/><\/a><\/p>\n<p style=\"text-align: justify;\">La broche 10 reste en entr\u00e9e comme dans <a title=\"GPIO, Pandaboard et temps r\u00e9el \u2013 3 \u2013 Lecture sur des entr\u00e9es\" href=\"http:\/\/www.blaess.fr\/christophe\/2012\/05\/21\/gpio-pandaboard-et-temps-reel-3-lecture-sur-des-entrees\/\">l&rsquo;article pr\u00e9c\u00e9dent<\/a>.<\/p>\n<p style=\"text-align: justify;\"><a href=\"http:\/\/www.blaess.fr\/christophe\/wp-content\/uploads\/2012\/05\/img-26.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-2266\" title=\"Documentation du connecteur d'extension A de la Pandaboard\" src=\"http:\/\/www.blaess.fr\/christophe\/wp-content\/uploads\/2012\/05\/img-26.jpg\" alt=\"Documentation du connecteur d'extension A de la Pandaboard\" width=\"600\" height=\"446\" \/><\/a><\/p>\n<p style=\"text-align: justify;\">Voici le code source de notre module.<\/p>\n<pre><a title=\"http:\/\/www.blaess.fr\/christophe\/files\/article-2012-05-29\/irq-gpio-linux.c\" href=\"http:\/\/www.blaess.fr\/christophe\/files\/article-2012-05-29\/irq-gpio-linux.c\" target=\"_blank\"><strong>irq-gpio-linux.c:<\/strong><\/a>\n#include &lt;linux\/fs.h&gt;\n#include &lt;linux\/gpio.h&gt;\n#include &lt;linux\/interrupt.h&gt;\n#include &lt;linux\/irq.h&gt;\n#include &lt;linux\/module.h&gt;\n\n#define <strong>GPIO_IN 138<\/strong>\n#define <strong>GPIO_OUT 139<\/strong>\n\nirqreturn_t <strong>handler_irq_gpio<\/strong>(int irq, void * ident)\n{\n  static int value = 0;\n  <strong>gpio_set_value<\/strong>(GPIO_OUT, value);\n  value = 1 - value;\n  return IRQ_HANDLED;\n}\n\nstatic int numero_interruption = 0;\n\nint __init exemple_init (void)\n{\n  int err;\n\n  numero_interruption = <strong>gpio_to_irq<\/strong>(GPIO_IN);\n\n  if ((err = <strong>gpio_request<\/strong>(GPIO_OUT, THIS_MODULE-&gt;name)) != 0)\n    return err;\n  if ((err = <strong>gpio_direction_output<\/strong>(GPIO_OUT, 0)) != 0) {\n    gpio_free(GPIO_OUT);\n    return err;\n  }\n\n  if ((err = <strong>gpio_request<\/strong>(GPIO_IN, THIS_MODULE-&gt;name)) != 0) {\n    gpio_free(GPIO_OUT);\n    return err;\n  }\n  if ((err = <strong>gpio_direction_input<\/strong>(GPIO_IN)) != 0) {\n    gpio_free(GPIO_IN);\n    gpio_free(GPIO_OUT);\n    return err;\n  }\n\n  if ((err = <strong>request_irq<\/strong>(numero_interruption, handler_irq_gpio, 0, THIS_MODULE-&gt;name, THIS_MODULE-&gt;name)) != 0) {\n    gpio_free(GPIO_IN);\n    gpio_free(GPIO_OUT);\n    return err;\n  }\n  <strong>set_irq_type<\/strong>(numero_interruption, IRQF_TRIGGER_RISING);\n\n  return 0;\n}\n\nvoid __exit exemple_exit (void)\n{\n  <strong>free_irq<\/strong>(numero_interruption, THIS_MODULE-&gt;name);\n  <strong>gpio_free<\/strong>(GPIO_IN);\n  <strong>gpio_free<\/strong>(GPIO_OUT);\n}\n\nmodule_init(exemple_init);\nmodule_exit(exemple_exit);\nMODULE_LICENSE(\"GPL\");<\/pre>\n<p style=\"text-align: justify;\">Nous allons envoyer un signal p\u00e9riodique sur l&rsquo;entr\u00e9e GPIO 138, gr\u00e2ce \u00e0 un g\u00e9n\u00e9rateur basse fr\u00e9quence (GBF).<\/p>\n<p>&nbsp;<\/p>\n<p><a href=\"http:\/\/www.blaess.fr\/christophe\/wp-content\/uploads\/2012\/05\/img-27.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-2265\" title=\"G\u00e9n\u00e9rateur Basse Fr\u00e9quence\" src=\"http:\/\/www.blaess.fr\/christophe\/wp-content\/uploads\/2012\/05\/img-27.jpg\" alt=\"G\u00e9n\u00e9rateur Basse Fr\u00e9quence\" width=\"600\" height=\"195\" \/><\/a><\/p>\n<p style=\"text-align: justify;\">Pour \u00e9viter les probl\u00e8mes de surtension en entr\u00e9e, nous v\u00e9rifions d&rsquo;abord \u00e0 l&rsquo;oscilloscope que l&rsquo;amplitude du signal ne d\u00e9passe pas 1.8V.<\/p>\n<p style=\"text-align: justify;\"><a href=\"http:\/\/www.blaess.fr\/christophe\/wp-content\/uploads\/2012\/05\/img-28.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-2267\" title=\"Signal sur broche d'entr\u00e9e 10 de la Pandaboard\" src=\"http:\/\/www.blaess.fr\/christophe\/wp-content\/uploads\/2012\/05\/img-28.jpg\" alt=\"Signal sur broche d'entr\u00e9e 10 de la Pandaboard\" width=\"600\" height=\"447\" \/><\/a>Visiblement le signal plafonne \u00e0 1.5V, et les fronts montants se pr\u00e9sentent approximativement toutes les deux millisecondes.<\/p>\n<p style=\"text-align: justify;\">Compilons notre module avec un <a title=\"http:\/\/www.blaess.fr\/christophe\/files\/article-2012-05-29\/Makefile-linux\" href=\"http:\/\/www.blaess.fr\/christophe\/files\/article-2012-05-29\/Makefile-linux\" target=\"_blank\">Makefile<\/a> sp\u00e9cifique, en pr\u00e9cisant l&#8217;emplacement des sources du noyau install\u00e9 sur la Pandaboard.<\/p>\n<pre>$ <strong>make -f Makefile-linux ARCH=arm CROSS_COMPILE=~\/cross-panda\/usr\/bin\/arm-linux- KERNEL_DIR=~\/Projets\/Panda\/linux-2.6.38.8-vanilla\/<\/strong>\ncp -f Makefile-linux Makefile\nmake -C \/home\/cpb\/Projets\/Panda\/linux-2.6.38.8-vanilla\/ SUBDIRS=\/home\/cpb\/Articles\/Blog\/article-2012-05-25  modules\nmake[1]: entrant dans le r\u00e9pertoire \u00ab \/home\/cpb\/Projets\/Panda\/linux-2.6.38.8-vanilla \u00bb\n  Building modules, stage 2.\n  MODPOST 1 modules\n  CC      \/home\/cpb\/Articles\/Blog\/article-2012-05-25\/irq-gpio-linux.mod.o\n  LD [M]  \/home\/cpb\/Articles\/Blog\/article-2012-05-25\/irq-gpio-linux.ko\nmake[1]: quittant le r\u00e9pertoire \u00ab \/home\/cpb\/Projets\/Panda\/linux-2.6.38.8-vanilla \u00bb\n$<\/pre>\n<p style=\"text-align: justify;\">V\u00e9rifions, sur la Pandaboard les interruptions d\u00e9j\u00e0 g\u00e9r\u00e9es par des drivers, puis ins\u00e9rons notre module et examinons \u00e0 nouveau la liste<\/p>\n<pre>[Panda]# <strong>cat \/proc\/interrupts <\/strong>\n            CPU0       CPU1\n  39:          2          0         GIC  TWL6030-PIH\n  44:        430          0         GIC  DMA\n  69:         31          0         GIC  gp timer\n  88:        257          0         GIC  omap_i2c\n  89:          0          0         GIC  omap_i2c\n  93:          0          0         GIC  omap_i2c\n  94:          0          0         GIC  omap_i2c\n 102:          0          0         GIC  serial idle\n 104:          0          0         GIC  serial idle\n 105:          0          0         GIC  serial idle\n 106:          0          0         GIC  serial idle\n 109:        470          0         GIC  ehci_hcd:usb1\n 115:       1121          0         GIC  mmc0\n 124:          0          0         GIC  musb-hdrc\n 125:          0          0         GIC  musb-hdrc\n 372:          1          0     twl6030  twl6030_usb\n 378:          0          0     twl6030  twl6030_usb\n 379:          0          0     twl6030  rtc0\nIPI0:          0          0  Timer broadcast interrupts\nIPI1:        654        727  Rescheduling interrupts\nIPI2:          0          0  Function call interrupts\nIPI3:          2          6  Single function call interrupts\nIPI4:          0          0  CPU stop interrupts\n LOC:      42035      35007  Local timer interrupts\n Err:          0\n[Panda]# <strong>\/sbin\/insmod \/root\/irq-gpio-linux.ko <\/strong>\n[Panda]# <strong>cat \/proc\/interrupts<\/strong>\n            CPU0       CPU1\n  39:          2          0         GIC  TWL6030-PIH\n  44:        467          0         GIC  DMA\n  69:         31          0         GIC  gp timer\n  88:        257          0         GIC  omap_i2c\n  89:          0          0         GIC  omap_i2c\n  93:          0          0         GIC  omap_i2c\n  94:          0          0         GIC  omap_i2c\n 102:          0          0         GIC  serial idle\n 104:          0          0         GIC  serial idle\n 105:          0          0         GIC  serial idle\n 106:          0          0         GIC  serial idle\n 109:        594          0         GIC  ehci_hcd:usb1\n 115:       1173          0         GIC  mmc0\n 124:          0          0         GIC  musb-hdrc\n 125:          0          0         GIC  musb-hdrc\n 298:          0          0         GPIO irq_gpio_linux\n 372:          1          0     twl6030  twl6030_usb\n 378:          0          0     twl6030  twl6030_usb\n 379:          0          0     twl6030  rtc0\nIPI0:          0          0  Timer broadcast interrupts\nIPI1:        680        751  Rescheduling interrupts\nIPI2:          0          0  Function call interrupts\nIPI3:          2          6  Single function call interrupts\nIPI4:          0          0  CPU stop interrupts\n LOC:      43797      35903  Local timer interrupts\n Err:          0\n[Panda]#<\/pre>\n<p style=\"text-align: justify;\">Nous voyons qu&rsquo;un handler a \u00e9t\u00e9 enregistr\u00e9 pour l&rsquo;interruption 298. Connectons le signal issu du g\u00e9n\u00e9rateur sur la broche 10.<\/p>\n<pre>[Panda]# <strong>cat \/proc\/interrupts<\/strong>\n            CPU0       CPU1\n  39:          2          0         GIC  TWL6030-PIH\n  44:        500          0         GIC  DMA\n  69:         31          0         GIC  gp timer\n  88:        257          0         GIC  omap_i2c\n  89:          0          0         GIC  omap_i2c\n  93:          0          0         GIC  omap_i2c\n  94:          0          0         GIC  omap_i2c\n 102:          0          0         GIC  serial idle\n 104:          0          0         GIC  serial idle\n 105:          0          0         GIC  serial idle\n 106:          0          0         GIC  serial idle\n 109:        603          0         GIC  ehci_hcd:usb1\n 115:       1288          0         GIC  mmc0\n 124:          0          0         GIC  musb-hdrc\n 125:          0          0         GIC  musb-hdrc\n 298:      13885          0        GPIO  irq_gpio_linux\n 372:          1          0     twl6030  twl6030_usb\n 378:          0          0     twl6030  twl6030_usb\n 379:          0          0     twl6030  rtc0\nIPI0:          0          0  Timer broadcast interrupts\nIPI1:        750        823  Rescheduling interrupts\nIPI2:          0          0  Function call interrupts\nIPI3:          3          6  Single function call interrupts\nIPI4:          0          0  CPU stop interrupts\n LOC:      48396      39003  Local timer interrupts\n Err:          0\n[Panda]#<\/pre>\n<p style=\"text-align: justify;\">En quelques secondes, notre carte a trait\u00e9 plus de treize mille interruptions. Utilisons maintenant l&rsquo;oscilloscope afin de visualiser en haut l&rsquo;entr\u00e9e du signal sur la broche 10 et en bas, la sortie de la broche 14.<\/p>\n<p style=\"text-align: justify;\"><a href=\"http:\/\/www.blaess.fr\/christophe\/wp-content\/uploads\/2012\/05\/img-29.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-2269\" title=\"R\u00e9ponse aux interruptions - Kernel Linux\" src=\"http:\/\/www.blaess.fr\/christophe\/wp-content\/uploads\/2012\/05\/img-29.jpg\" alt=\"R\u00e9ponse aux interruptions - Kernel Linux\" width=\"600\" height=\"446\" \/><\/a>Nous voyons bien qu&rsquo;\u00e0 chaque front montant du signal envoy\u00e9 sur la broche 10 (en haut), le handler d&rsquo;interruption commute la valeur de la broche 14 (en bas).<\/p>\n<p style=\"text-align: justify; padding-left: 60px;\">Les plus attentifs ont s\u00fbrement remarqu\u00e9 que la synchronisation (onglet SCE en bas) se fait \u00e0 pr\u00e9sent sur un front montant du signal de sortie et non plus sur le signal d&rsquo;entr\u00e9e. Ceci \u00e9vite les fluctuations d&rsquo;affichage. En outre, le point de synchronisation se trouve au d\u00e9but de la douzi\u00e8me case comme l&rsquo;indique l&rsquo;onglet TG-PT (<em>Trigger Point<\/em>) en bas de l&rsquo;\u00e9cran. Les points de synchronisation sont num\u00e9rot\u00e9s \u00e0 partir de z\u00e9ro. Le petit symbole \u00ab\u00a0T\u00a0\u00bb en haut \u00e0 droite de l&rsquo;\u00e9cran indique l&#8217;emplacement du point de synchronisation.<\/p>\n<p style=\"text-align: justify;\">Notre syst\u00e8me r\u00e9pond donc bien aux interruptions. Le signal est stable. Mais une question se pose souvent dans les syst\u00e8mes industriels devant r\u00e9pondre \u00e0 des sollicitations ext\u00e9rieures&nbsp;: \u00ab\u00a0quelle est la latence des interruptions&nbsp;?\u00a0\u00bb ou, autrement dit, \u00ab\u00a0combien de temps s&rsquo;\u00e9coule apr\u00e8s l&rsquo;arriv\u00e9e du front montant en entr\u00e9e et avant que le handler ait pu r\u00e9pondre&nbsp;?\u00a0\u00bb Pour le savoir, nous allons \u00ab\u00a0zoomer\u00a0\u00bb fortement sur l&rsquo;image pr\u00e9c\u00e9dente.<\/p>\n<p><a href=\"http:\/\/www.blaess.fr\/christophe\/wp-content\/uploads\/2012\/05\/img-30.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-2271\" title=\"Latence des interruptions GPIO de la Pandaboard\" src=\"http:\/\/www.blaess.fr\/christophe\/wp-content\/uploads\/2012\/05\/img-30.jpg\" alt=\"Latence des interruptions GPIO de la Pandaboard\" width=\"600\" height=\"446\" \/><\/a><\/p>\n<p style=\"text-align: justify;\">La latence est ici d&rsquo;environ 5 microsecondes. Il faut bien noter que cette valeur fluctue beaucoup. La synchronisation de l&rsquo;oscilloscope se faisant sur le signal du bas, c&rsquo;est celui du haut (l&rsquo;entr\u00e9e) que nous voyons bouger en permanence. Parfois, il saute vers la gauche en dehors de l&rsquo;\u00e9cran, indiquant que le noyau n&rsquo;a pas pu traiter l&rsquo;interruption avant &#8211; au moins &#8211; 22 microsecondes, la largeur de l&rsquo;\u00e9cran jusqu&rsquo;au signal de r\u00e9ponse.<\/p>\n<h1>\u00a0Gestion des interruptions par Xenomai<\/h1>\n<p style=\"text-align: justify;\">Pour am\u00e9liorer les performances temps-r\u00e9el, il y a deux solutions principales aujourd&rsquo;hui&nbsp;: appliquer le patch Linux-rt sur notre noyau standard, et utiliser notre m\u00eame module (recompil\u00e9), ou utiliser Xenomai en r\u00e9-\u00e9crivant le module suivant son API sp\u00e9cifique RTDM. J&rsquo;ai choisi la seconde solution, car il s&rsquo;agit la plupart du temps de celle qui offre les meilleures performances (toutefois pour une application r\u00e9elle, ceci devrait \u00eatre v\u00e9rifi\u00e9 avec des tests prolong\u00e9s).<\/p>\n<p style=\"text-align: justify;\">Voici donc un petit module employant l&rsquo;API RTDM de Xenomai pour r\u00e9aliser exactement la m\u00eame t\u00e2che que pr\u00e9c\u00e9demment.<\/p>\n<pre><a title=\"http:\/\/www.blaess.fr\/christophe\/files\/article-2012-05-29\/irq-gpio-rtdm.c\" href=\"http:\/\/www.blaess.fr\/christophe\/files\/article-2012-05-29\/irq-gpio-rtdm.c\" target=\"_blank\"><strong>irq-gpio-rtdm.c:<\/strong><\/a>\n#include &lt;linux\/fs.h&gt;\n#include &lt;linux\/gpio.h&gt;\n#include &lt;linux\/interrupt.h&gt;\n#include &lt;linux\/module.h&gt;\n\n#include &lt;rtdm\/rtdm_driver.h&gt;\n\n#define GPIO_IN  138\n#define GPIO_OUT 139\n\nstatic <strong>rtdm_irq_t<\/strong> irq_rtdm;\n\nstatic int handler_interruption(rtdm_irq_t * irq)\n{\n  static int value = 0;\n  <strong>gpio_set_value<\/strong>(GPIO_OUT, value);\n  value = 1 - value;\n  return RTDM_IRQ_HANDLED;\n}\n\nstatic int __init exemple_init (void)\n{\n  int err;\n\n  int numero_interruption = <strong>gpio_to_irq<\/strong>(GPIO_IN);\n\n  if ((err = <strong>gpio_request<\/strong>(GPIO_IN, THIS_MODULE-&gt;name)) != 0) {\n    return err;\n  }\n  if ((err = <strong>gpio_direction_input<\/strong>(GPIO_IN)) != 0) {\n    gpio_free(GPIO_IN);\n    return err;\n  }\n  if ((err = <strong>gpio_request(<\/strong>GPIO_OUT, THIS_MODULE-&gt;name)) != 0) {\n    gpio_free(GPIO_IN);\n    return err;\n  }\n  if ((err = <strong>gpio_direction_output<\/strong>(GPIO_OUT, 1)) != 0) {\n    gpio_free(GPIO_OUT);\n    gpio_free(GPIO_IN);\n    return err;\n  }\n\n  if ((err = <strong>rtdm_irq_request<\/strong>(&amp; irq_rtdm,\n                   numero_interruption, handler_interruption,\n                   0,\n                   THIS_MODULE-&gt;name, NULL)) != 0) {\n    gpio_free(GPIO_OUT);\n    gpio_free(GPIO_IN);\n    return err;\n  }\n  <strong>set_irq_type<\/strong>(numero_interruption, IRQF_TRIGGER_RISING);\n  \/\/ Voir le commentaire de Gilles Chanteperdrix en fin d'article\n  rtdm_irq_enable<\/strong>(&amp; irq_rtdm);\n \u00a0return 0;\n}\n\nstatic void __exit exemple_exit (void)\n{\n  <strong>rtdm_irq_disable<\/strong>(&amp; irq_rtdm);\n  <strong>rtdm_irq_free<\/strong>(&amp; irq_rtdm);\n  <strong>gpio_free<\/strong>(GPIO_OUT);\n  <strong>gpio_free<\/strong>(GPIO_IN);\n}\n\nmodule_init(exemple_init);\nmodule_exit(exemple_exit);\nMODULE_LICENSE(\"GPL\");<\/pre>\n<p style=\"text-align: justify;\">Lors de la compilation, avec un <a title=\"http:\/\/www.blaess.fr\/christophe\/files\/article-2012-05-29\/Makefile-xenomai\" href=\"http:\/\/www.blaess.fr\/christophe\/files\/article-2012-05-29\/Makefile-xenomai\" target=\"_blank\">Makefile<\/a> sp\u00e9cifique \u00e9galement, on pr\u00e9cise l&#8217;emplacement du script <code>xeno-config<\/code> install\u00e9 avec Xenomai.<\/p>\n<pre>$ <strong>make -f Makefile-xenomai ARCH=arm CROSS_COMPILE=~\/cross-panda\/usr\/bin\/arm-linux- KERNEL_DIR=~\/Projets\/Panda\/linux-2.6.38.8-xenomai XENOCONFIG=~\/Projets\/Panda\/xenomai\/bin\/xeno-config<\/strong>\ncp -f Makefile-xenomai Makefile\nmake -C \/home\/cpb\/Projets\/Panda\/linux-2.6.38.8-xenomai SUBDIRS=\/home\/cpb\/Articles\/Blog\/article-2012-05-25  modules\nmake[1]: entrant dans le r\u00e9pertoire \u00ab \/home\/cpb\/Projets\/Panda\/linux-2.6.38.8-xenomai \u00bb\n  CC [M]  \/home\/cpb\/Articles\/Blog\/article-2012-05-25\/irq-gpio-rtdm.o\n  Building modules, stage 2.\n  MODPOST 1 modules\n  CC      \/home\/cpb\/Articles\/Blog\/article-2012-05-25\/irq-gpio-rtdm.mod.o\n  LD [M]  \/home\/cpb\/Articles\/Blog\/article-2012-05-25\/irq-gpio-rtdm.ko\nmake[1]: quittant le r\u00e9pertoire \u00ab \/home\/cpb\/Projets\/Panda\/linux-2.6.38.8-xenomai \u00bb\n$<\/pre>\n<p style=\"text-align: justify;\">Comme pr\u00e9c\u00e9demment, v\u00e9rifions les interruptions g\u00e9r\u00e9es avant et apr\u00e8s insertion de notre module. Naturellement, il faut d&rsquo;abord booter sur un noyau comprenant Xenomai (compil\u00e9 par exemple avec <a title=\"http:\/\/www.blaess.fr\/christophe\/files\/article-2012-05-29\/config-linux-2.6.38.8-xenomai\" href=\"http:\/\/www.blaess.fr\/christophe\/files\/article-2012-05-29\/config-linux-2.6.38.8-xenomai\" target=\"_blank\">ce fichier de configuration<\/a>).<\/p>\n<pre>[Panda]# <strong>cat \/proc\/xenomai\/irq <\/strong>\nIRQ         CPU0        CPU1\n 29:      568565      568700         [timer]\n416:           0           0         [critical sync]\n417:           0           0         [IPI0]\n420:           2           0         [IPI3]\n421:           0           0         [virtual]\n424:           0           0         [virtual]\n[Panda]# <strong>\/sbin\/insmod \/root\/irq-gpio-rtdm.ko<\/strong>\n[Panda]# <strong>cat \/proc\/xenomai\/irq<\/strong>\nIRQ         CPU0        CPU1\n 29:      674274      674435         [timer]\n298:           0           0         irq_gpio_rtdm\n416:           0           0         [critical sync]\n417:           0           0         [IPI0]\n420:           2           0         [IPI3]\n421:           0           0         [virtual]\n424:           0           0         [virtual]\n[Panda]#<\/pre>\n<p style=\"text-align: justify;\">La ligne d&rsquo;interruption est bien apparue dasn la liste de celles g\u00e9r\u00e9es par Xenomai. Branchons le g\u00e9n\u00e9rateur sur l&rsquo;entr\u00e9e.<\/p>\n<pre>[Panda]# <strong>cat \/proc\/xenomai\/irq <\/strong>\nIRQ         CPU0        CPU1\n 29:      772983      773168         [timer]\n298:        9712           0         irq_gpio_rtdm\n416:           0           0         [critical sync]\n417:           0           0         [IPI0]\n420:           2           0         [IPI3]\n421:           0           0         [virtual]\n424:           0           0         [virtual]\n[Panda]#<\/pre>\n<p style=\"text-align: justify;\">Nous pouvons observer \u00e0 l&rsquo;oscilloscope l&rsquo;entr\u00e9e et la sortie, comme avec le noyau Linux.<\/p>\n<p><a href=\"http:\/\/www.blaess.fr\/christophe\/wp-content\/uploads\/2012\/05\/img-31.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-2282\" title=\"Latence des interruptions sous Xenomai\" src=\"http:\/\/www.blaess.fr\/christophe\/wp-content\/uploads\/2012\/05\/img-31.jpg\" alt=\"Latence des interruptions sous Xenomai\" width=\"600\" height=\"457\" \/><\/a><\/p>\n<p style=\"text-align: justify;\">Le signal est beaucoup plus stable qu&rsquo;avec le noyau standard. Et nous pouvons voir sur la capture ci-dessus que le temps de r\u00e9action est sensiblement meilleur (le calibre est maintenant r\u00e9gl\u00e9 sur une microseconde par division), puisqu&rsquo;il est pass\u00e9 de 5 microsecondes \u00e0 un peu plus de 2,5 microsecondes. Cet oscilloscope ne dispose pas de fonction de m\u00e9morisation prolong\u00e9e (une sorte de r\u00e9manence infinie) qui nous permettrait de visualiser les latences maximales. Toutefois, il est probable que celles-ci ne d\u00e9passeraient pas dix \u00e0 quinze microsecondes avec Xenomai.<\/p>\n<h1 style=\"text-align: justify;\">Conclusion<\/h1>\n<p style=\"text-align: justify;\">Cette exp\u00e9rience permet de mettre en relief une autre am\u00e9lioration apport\u00e9e par Xenomai (apr\u00e8s celle des timers vus dans le second article)&nbsp;: la qualit\u00e9 du traitement des interruptions. La dur\u00e9e de prise en charge d&rsquo;un \u00e9v\u00e9nement externe est \u00e0 la fois plus stable et plus courte qu&rsquo;avec le kernel Linux standard. C&rsquo;est un param\u00e8tre important pour la mise en \u0153uvre d&rsquo;une application temps r\u00e9el.<\/p>\n<p style=\"text-align: justify;\">Remarques et commentaires bienvenus&nbsp;!<\/p>","protected":false},"excerpt":{"rendered":"<p>Nous avons vu dans les articles pr&eacute;c&eacute;dents comment &eacute;crire sur une broche de sortie du connecteur d&rsquo;extension de la Pandaboard depuis l&rsquo;espace utilisateur, puis depuis le kernel. Nous avons &eacute;galement r&eacute;ussi &agrave; lire l&rsquo;&eacute;tat de broches GPIO d&rsquo;entr&eacute;e. Cette fois, nous allons am&eacute;liorer cette lecture en g&eacute;rant l&rsquo;occurence d&rsquo;&eacute;v&eacute;nements par l&rsquo;interm&eacute;diaire d&rsquo;interruptions.<\/p>","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[5,8,10,14],"tags":[],"class_list":["post-2248","post","type-post","status-publish","format-standard","hentry","category-embarque","category-linux-2","category-microprocesseur","category-temps-reel"],"_links":{"self":[{"href":"https:\/\/www.blaess.fr\/christophe\/wp-json\/wp\/v2\/posts\/2248","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=2248"}],"version-history":[{"count":0,"href":"https:\/\/www.blaess.fr\/christophe\/wp-json\/wp\/v2\/posts\/2248\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.blaess.fr\/christophe\/wp-json\/wp\/v2\/media?parent=2248"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.blaess.fr\/christophe\/wp-json\/wp\/v2\/categories?post=2248"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.blaess.fr\/christophe\/wp-json\/wp\/v2\/tags?post=2248"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}