GPIO, Pandaboard et temps réel – 5 – Le multiplexage des GPIO

Publié par cpb
Juin 04 2012

Pandaboard, GPIO et temps réel

Suite à un petit commentaire de Yoann Sculo concernant l’avant dernier article, j’ai eu envie de détailler un peu le fonctionnement du multiplexage des GPIO sur un processeur OMAP (comme celui de la Pandaboard), et les paramètres auxquels nous pouvons avoir accès par l’intermédiaire du système de fichiers debugfs.

Multiplexage des GPIO de l’OMAP

Pour obtenir des informations détaillées sur le fonctionnement de l’OMAP 4430 (celui utilisé dans la Pandaboard), il ne faut pas hésiter à se reporter au TRM (Technical Reference Manual) diffusé par Texas Instruments quitte à prendre un peu de temps pour se familiariser avec l’organisation de ses 5554 pages !

Les processeurs de la famille OMAP proposent un nombre beaucoup plus important de lignes logiques de GPIO que le nombre de broches physiquement disponibles sur leurs boîtiers. Aussi un multiplexage est-il nécessaire pour sélectionner quelle fonctionnalité est affectée à chaque broche à un instant donné.

Broche 10 du connecteur d'extension A

Prenons l’exemple de la broche 10 du connecteur J3 que nous avons déjà employée à plusieurs reprises. En examinant la documentation de la Pandaboard, nous avons vu que trois fonctions lui sont attribuées : MCSPI1_CS (Chip Select du bus SPI1), UART1_RX (ligne de réception du port série numéro 1) et GPIO_138. C’est cette dernière fonction que nous avons employée dans les articles précédents.

Le choix entre ces trois fonctions est fait tout d’abord par le bootloader (U-boot), comme nous l’avons vu dans l’avant-dernier article. Ensuite, le noyau Linux lui-même affecte les broches qui le concernent. Toutefois, il nous est possible d’intervenir sur le paramétrage depuis l’espace utilisateur.

Chaque broche peut théoriquement avoir jusqu’à huit fonctions. À un moment donné, une broche est configurée suivant un mode, et le processeur OMAP propose donc les modes 0 à 7. Par exemple, sur la Pandaboard la broche 10 du connecteur J3 peut être configurée avec les modes suivants :

  • Mode 0 : MCSPI1_CS
  • Mode 1 : UART1_RX
  • Mode 2 : Non utilisé
  • Mode 3 : GPIO_138
  • Mode 4 à 6 : Non utilisés
  • Mode 7 : Safe mode : la broche est isolée, les contacts éventuels avec d’autres broches ne provoqueront pas de court-circuit.

Consultation avec debugfs

Le système de fichiers debugfs est, comme son nom l’indique, destiné à aider à la mise au point du noyau. Il s’agit d’un système de fichiers virtuel, comme ceux que l’on monte sur /proc ou /sys, mais les règles d’utilisation sont beaucoup plus souples qu’avec ces derniers (/proc est réservé aux processus et aux paramètres de fonctionnement du noyau, /sys contient la représentation du système vu du kernel en fournissant une unique valeur par fichier).

Bien que l’on puisse choisir un autre répertoire, on convient généralement de monter (dans un script de démarrage ou dans le fichier /etc/fstab) le système debugfs sur le répertoire /sys/kernel/debug.

En voici un exemple sur la Pandaboard :

[Panda]# mount none /sys/kernel/debug/ -t debugfs
[Panda]# cd /sys/kernel/debug/
[Panda]# ls
bdi        gpio  ieee80211  mmc0      regulator       tracing    voltage
bluetooth  hid   memblock   omap_mux  sched_features  usb
[Panda]#

Nous pouvons examiner dans le fichier gpio la configuration actuelle des GPIO employés par le système.

[Panda]# cat gpio
GPIOs 0-31, gpio:
 gpio-1   (hub_power           ) out hi
 gpio-7   (pandaboard::status1 ) out lo
 gpio-8   (pandaboard::status2 ) out lo

GPIOs 32-63, gpio:
 gpio-62  (hub_nreset          ) out hi

GPIOs 64-95, gpio:

GPIOs 96-127, gpio:

GPIOs 128-159, gpio:

GPIOs 160-191, gpio:
[Panda]#

Nous voyons qu’il y a six contrôleurs, chacun gérant 32 GPIO, ce qui est confirmé par le Technical Reference Manual de l’OMAP. Ceci correspond aux entrées gpiochip que nous trouvons dans le répertoire /sys/class/gpio.

[Panda]# ls /sys/class/gpio/
export       gpio62       gpiochip128  gpiochip32   gpiochip96
gpio1        gpiochip0    gpiochip160  gpiochip64   unexport
[Panda]#

Le GPIO 1 correspond à l’alimentation du bloc Ethernet + USB de la Pandaboard, ainsi une commande.

[Panda]# echo 0 > /sys/class/gpio/gpio1/value

éteint-elle ce hub (et coupe par la même occasion ma connexion sur la Pandaboard puisque j’y accède par ssh, ce qui m’oblige à réinitialiser la carte !).

Les GPIO 7 et 8 sont utilisés pour les LEDs d’état de la Pandaboard (voir cet article), et le 62 permet de réinitialiser le contrôleur USB/Ethernet.

Examinons le contenu du répertoire omap_mux, qui concerne le multiplexage des sorties de l’OMAP.

[Panda]# cd omap_mux/
[Panda]# ls
abe_clks         csi22_dy1       gpmc_a19       gpmc_ncs7      kpd_row2     sim_pwrctrl          usbb1_ulpitll_clk
abe_dmic_clk1    dpm_emu0        gpmc_a20       gpmc_noe       kpd_row3     sim_reset            usbb1_ulpitll_dat0
abe_dmic_din1    dpm_emu1        gpmc_a21       gpmc_nwe       kpd_row4     sr_scl               usbb1_ulpitll_dat1
abe_dmic_din2    dpm_emu10       gpmc_a22       gpmc_nwp       kpd_row5     sr_sda               usbb1_ulpitll_dat2
abe_dmic_din3    dpm_emu11       gpmc_a23       gpmc_wait0     mcspi1_clk   sys_32k              usbb1_ulpitll_dat3
abe_mcbsp1_clkx  dpm_emu12       gpmc_a24       gpmc_wait1     mcspi1_cs0   sys_boot0            usbb1_ulpitll_dat4
abe_mcbsp1_dr    dpm_emu13       gpmc_a25       gpmc_wait2     mcspi1_cs1   sys_boot1            usbb1_ulpitll_dat5
abe_mcbsp1_dx    dpm_emu14       gpmc_ad0       hdmi_cec       mcspi1_cs2   sys_boot2            usbb1_ulpitll_dat6
abe_mcbsp1_fsx   dpm_emu15       gpmc_ad1       hdmi_ddc_scl   mcspi1_cs3   sys_boot3            usbb1_ulpitll_dat7
abe_mcbsp2_clkx  dpm_emu16       gpmc_ad10      hdmi_ddc_sda   mcspi1_simo  sys_boot4            usbb1_ulpitll_dir
abe_mcbsp2_dr    dpm_emu17       gpmc_ad11      hdmi_hpd       mcspi1_somi  sys_boot5            usbb1_ulpitll_nxt
abe_mcbsp2_dx    dpm_emu18       gpmc_ad12      hdq_sio        mcspi4_clk   sys_boot6            usbb1_ulpitll_stp
abe_mcbsp2_fsx   dpm_emu19       gpmc_ad13      i2c1_scl       mcspi4_cs0   sys_boot7            usbb2_hsic_data
abe_pdm_dl_data  dpm_emu2        gpmc_ad14      i2c1_sda       mcspi4_simo  sys_nirq1            usbb2_hsic_strobe
abe_pdm_frame    dpm_emu3        gpmc_ad15      i2c2_scl       mcspi4_somi  sys_nirq2            usbb2_ulpitll_clk
abe_pdm_lb_clk   dpm_emu4        gpmc_ad2       i2c2_sda       sdmmc1_clk   sys_nrespwron        usbb2_ulpitll_dat0
abe_pdm_ul_data  dpm_emu5        gpmc_ad3       i2c3_scl       sdmmc1_cmd   sys_nreswarm         usbb2_ulpitll_dat1
board            dpm_emu6        gpmc_ad4       i2c3_sda       sdmmc1_dat0  sys_pwr_req          usbb2_ulpitll_dat2
cam_globalreset  dpm_emu7        gpmc_ad5       i2c4_scl       sdmmc1_dat1  sys_pwron_reset_out  usbb2_ulpitll_dat3
cam_shutter      dpm_emu8        gpmc_ad6       i2c4_sda       sdmmc1_dat2  uart2_cts            usbb2_ulpitll_dat4
cam_strobe       dpm_emu9        gpmc_ad7       jtag_ntrst     sdmmc1_dat3  uart2_rts            usbb2_ulpitll_dat5
csi21_dx0        fref_clk0_out   gpmc_ad8       jtag_rtck      sdmmc1_dat4  uart2_rx             usbb2_ulpitll_dat6
csi21_dx1        fref_clk1_out   gpmc_ad9       jtag_tck       sdmmc1_dat5  uart2_tx             usbb2_ulpitll_dat7
csi21_dx2        fref_clk2_out   gpmc_clk       jtag_tdi       sdmmc1_dat6  uart3_cts_rctx       usbb2_ulpitll_dir
csi21_dx3        fref_clk3_out   gpmc_nadv_ale  jtag_tdo       sdmmc1_dat7  uart3_rts_sd         usbb2_ulpitll_nxt
csi21_dx4        fref_clk3_req   gpmc_nbe0_cle  jtag_tms_tmsc  sdmmc5_clk   uart3_rx_irrx        usbb2_ulpitll_stp
csi21_dy0        fref_clk4_out   gpmc_nbe1      kpd_col0       sdmmc5_cmd   uart3_tx_irtx        usbc1_icusb_dm
csi21_dy1        fref_clk4_req   gpmc_ncs0      kpd_col1       sdmmc5_dat0  uart4_rx             usbc1_icusb_dp
csi21_dy2        fref_clk_ioreq  gpmc_ncs1      kpd_col2       sdmmc5_dat1  uart4_tx
csi21_dy3        fref_slicer_in  gpmc_ncs2      kpd_col3       sdmmc5_dat2  usba0_otg_ce
csi21_dy4        fref_xtal_in    gpmc_ncs3      kpd_col4       sdmmc5_dat3  usba0_otg_dm
csi22_dx0        gpmc_a16        gpmc_ncs4      kpd_col5       sim_cd       usba0_otg_dp
csi22_dx1        gpmc_a17        gpmc_ncs5      kpd_row0       sim_clk      usbb1_hsic_data
csi22_dy0        gpmc_a18        gpmc_ncs6      kpd_row1       sim_io       usbb1_hsic_strobe
[Panda]#

Par convention, les ports du processeur OMAP sont nommés en utilisant leur fonction première, celle du Mode 0. Ainsi la broche 10 de connecteur J3 est-elle nommée mcspi1_cs1.

[Panda]# cat mcspi1_cs1 
name: mcspi1_cs1.gpio_138 (0x4a10013a/0x13a = 0x010b), b af23, t NA
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE3
signals: mcspi1_cs1 | uart1_rx | NA | gpio_138 | NA | NA | NA | safe_mode
[Panda]#

Les signaux possibles pour cette broche sont décrits sur la ligne “signals:” et correspondent à la liste observée plus haut. La ligne “mode:” indique la configuration actuelle. Ici, le mode sélectionné est Mode_3 (gpio_138) configuré en entrée avec une résistance de rappel à la masse (pulldown).

Paramétrage depuis l’espace utilisateur

Nous pouvons modifier les paramètres présentés par debugfs. Pour cela il faut écrire dans le fichier une valeur sur deux octets qui se compose ainsi.

  • Bits 0, 1 et 2 : numéro de mode
  • Bit 3 : activation de la résistance de rappel en entrée
  • Bit 4 : résistance de rappel vers le haut (pull-up).
  • Bit 5-7 : inutilisés
  • Bit 8 : broche en entrée
  • Bit 9-15 : configuration de l’état de la broche lors de la mise en sommeil (non détaillée ici)

Configuration du multiplexeur Omap

Modifions ces paramètres, en conservant le mode 3 (celui du GPIO).

Basculement en sortie

Il faut désactiver les bits Input_enable, Pull_enable et calculer le mode désiré.

Multiplexeur en sortieNous allons donc écrire la valeur 0x0003.

[Panda]# echo 0x0003 > mcspi1_cs1
[Panda]# cat mcspi1_cs1
name: mcspi1_cs1.gpio_138 (0x4a10013a/0x13a = 0x0003), b af23, t NA
mode: OMAP_PIN_OUTPUT | OMAP_MUX_MODE3
signals: mcspi1_cs1 | uart1_rx | NA | gpio_138 | NA | NA | NA | safe_mode
[Panda]#

La ligne “mode:” a bien été modifiée pour afficher OMAP_PIN_OUTPUT.

Basculement en entrée simple

Pour ramener notre broche en entrée, nous activons le bit Input_enable.

Multiplexeur en entrée simpleLa valeur calculée est 0x0103

[Panda]# echo 0x0103 > mcspi1_cs1
[Panda]# cat mcspi1_cs1 
name: mcspi1_cs1.gpio_138 (0x4a10013a/0x13a = 0x0103), b af23, t NA
mode: OMAP_PIN_INPUT | OMAP_MUX_MODE3
signals: mcspi1_cs1 | uart1_rx | NA | gpio_138 | NA | NA | NA | safe_mode
[Panda]#

Aucun pull-up ou pull-down n’étant activé, la valeur lue en entrée peut fluctuer si elle n’est pas connectée. Par exemple les lignes suivantes

[Panda]# echo 138 > /sys/class/gpio/export
[Panda]# while true; do cat /sys/class/gpio/gpio138/value ; done

affichent une alternance de valeurs 0 et 1 qui évoluent si on effleure du doigt la broche 10 (ce que je ne conseille pas, des tensions élevées engendrées par l’électricité statique pouvant endommager le processeur).

Basculement en entrée avec rappel à la masse (pull-down)

Pour activer un pull-down, il suffit d’activer le bit Pull_enable en laissant le bit Pull_up désactivé. Il s’agit de la configuration fixée par défaut par le bootloader U-boot.

Multiplexeur en entrée pull-down

La valeur à écrire est 0x010B

[Panda]# echo 0x010B > mcspi1_cs1 
[Panda]# cat mcspi1_cs1 
name: mcspi1_cs1.gpio_138 (0x4a10013a/0x13a = 0x010b), b af23, t NA
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE3
signals: mcspi1_cs1 | uart1_rx | NA | gpio_138 | NA | NA | NA | safe_mode
[Panda]#

Nous pouvons vérifier l’état de l’entrée. Cette fois la valeur est figée, même si l’entrée est laissée non connectée et si on l’effleure du doigt.

[Panda]# cat /sys/class/gpio/gpio138/value 
0
[Panda]#

Basculement en entrée avec tirage à la tension d’alimentation (pull-up)

Enfin, nous allons modifier la résistance de rappel en activant un pull-up vers la tension d’alimentation grâce au bit Pull_up.

Multiplexeur en entrée pull-upLa valeur obtenue est 0x011B.

[Panda]# echo 0x011B > mcspi1_cs1 
[Panda]# cat mcspi1_cs1
name: mcspi1_cs1.gpio_138 (0x4a10013a/0x13a = 0x011b), b af23, t NA
mode: OMAP_PIN_INPUT_PULLUP | OMAP_MUX_MODE3
signals: mcspi1_cs1 | uart1_rx | NA | gpio_138 | NA | NA | NA | safe_mode
[Panda]#

À présent l’entrée (toujours non connectée) ne présente plus la même valeur.

[Panda]# cat /sys/class/gpio/gpio138/value 
1
[Panda]#

Changement de mode sur une broche

Nous pouvons choisir de modifier le mode de fonctionnement d’une broche du port d’extension A. Il n’est pas facile de savoir comment les broches sont configurées par défaut, car la documentation de la Pandaboard est un peu  floue sur ce sujet. Voici donc un petit script shell qui nous affiche pour chaque broche (identifiée par son numéro et son nom officiel) la configuration actuelle ainsi que la liste des configurations possibles.

/root/liste-j3.sh:
#/bin/sh

cd /sys/kernel/debug/omap_mux
num=3
for name in gpmc_ad7 mcspi1_cs3 gpmc_ad6 uart4_tx gpmc_ad5 uart4_rx gpmc_ad4 mcspi1_cs1 gpmc_ad3 mcspi1_simo gpmc_ad2 mcspi1_cs2 gpmc_ad1 mcspi1_cs0 gpmc_ad0 mcspi1_somi gpmc_nwe mcspi1_clk gpmc_noe gpmc_ad15 i2c4_sda i2c4_scl
do
  mode=$(grep "^mode:" "${name}" | sed 's/.*OMAP_MUX_MODE//')
  list=$(grep "^signals:" "${name}" | sed 's/^signals://; s/|//g')
  item=0
  for i in ${list}
  do
    if [ ${item} -eq ${mode} ]
    then
      selected_function=$i
      break;
    fi
    item=$((item + 1))
  done
  echo "${num} ${name} ${mode} ${selected_function} (${list})"
  num=$((num + 1))
done

A l’exécution, nous obtenons une liste un peu compliquée à lire qui contient pour chaque broche : son numéro, son nom, le numéro du mode sélectionné, la fonction sélectionnée et la liste des fonctions disponibles. Ci-dessous j’ai aligné manuellement les colonnes pour améliorer la lisibilité.

[Panda]# /root/liste-j3.sh 
3  gpmc_ad7    1 sdmmc2_dat7 ( gpmc_ad7    sdmmc2_dat7 sdmmc2_clk_fdbk NA       NA  NA           NA  NA        )
4  mcspi1_cs3  3 gpio_140    ( mcspi1_cs3  uart1_rts   slimbus2_data   gpio_140 NA  NA           NA  safe_mode )
5  gpmc_ad6    1 sdmmc2_dat6 ( gpmc_ad6    sdmmc2_dat6 sdmmc2_dir_cmd  NA       NA  NA           NA  NA        )
6  uart4_tx    0 uart4_tx    ( uart4_tx    sdmmc4_dat1 kpd_col8        gpio_156 NA  NA           NA  safe_mode )
7  gpmc_ad5    1 sdmmc2_dat5 ( gpmc_ad5    sdmmc2_dat5 sdmmc2_dir_dat1 NA       NA  NA           NA  NA        )
8  uart4_rx    0 uart4_rx    ( uart4_rx    sdmmc4_dat2 kpd_row8        gpio_155 NA  NA           NA  safe_mode )
9  gpmc_ad4    1 sdmmc2_dat4 ( gpmc_ad4    sdmmc2_dat4 sdmmc2_dir_dat0 NA       NA  NA           NA  NA        )
10 mcspi1_cs1  3 gpio_138    ( mcspi1_cs1  uart1_rx    NA              gpio_138 NA  NA           NA  safe_mode )
11 gpmc_ad3    1 sdmmc2_dat3 ( gpmc_ad3    sdmmc2_dat3 NA              NA       NA  NA           NA  NA        )
12 mcspi1_simo 0 mcspi1_simo ( mcspi1_simo NA          NA              gpio_136 NA  NA           NA  safe_mode )
13 gpmc_ad2    1 sdmmc2_dat2 ( gpmc_ad2    sdmmc2_dat2 NA              NA       NA  NA           NA  NA        )
14 mcspi1_cs2  3 gpio_139    ( mcspi1_cs2  uart1_cts   slimbus2_clock  gpio_139 NA  NA           NA  safe_mode )
15 gpmc_ad1    1 sdmmc2_dat1 ( gpmc_ad1    sdmmc2_dat1 NA              NA       NA  NA           NA  NA        )
16 mcspi1_cs0  0 mcspi1_cs0  ( mcspi1_cs0  NA          NA              gpio_137 NA  NA           NA  safe_mode )
17 gpmc_ad0    1 sdmmc2_dat0 ( gpmc_ad0    sdmmc2_dat0 NA              NA       NA  NA           NA  NA        )
18 mcspi1_somi 0 mcspi1_somi ( mcspi1_somi NA          NA              gpio_135 NA  NA           NA  safe_mode )
19 gpmc_nwe    1 sdmmc2_cmd  ( gpmc_nwe    sdmmc2_cmd  NA              NA       NA  NA           NA  NA        )
20 mcspi1_clk  0 mcspi1_clk  ( mcspi1_clk  NA          NA              gpio_134 NA  NA           NA  safe_mode )
21 gpmc_noe    1 sdmmc2_clk  ( gpmc_noe    sdmmc2_clk  NA              NA       NA  NA           NA  NA        )
22 gpmc_ad15   3 gpio_39     ( gpmc_ad15   kpd_col3    c2c_data8       gpio_39  NA  sdmmc1_dat7  NA  NA        )
23 i2c4_sda    0 i2c4_sda    ( i2c4_sda    NA          NA              gpio_133 NA  NA           NA  safe_mode )
24 i2c4_scl    0 i2c4_scl    ( i2c4_scl    NA  NA  gpio_132  NA  NA  NA  safe_mode)
[Panda]#

Prenons la ligne correspondant à la broche numéro 6 par exemple. Son nom est “uart4_tx” et il s’agit également de sa fonction par défaut car son mode initial est zéro. Nous voyons dans la liste des fonctions possibles – entre parenthèses – qu’elle peut être commutée sur le GPIO 156 (mode 3). Nous allons essayer.

Petite parenthèse : une légère erreur dans le script ci-dessus avait initialement décalé les numéros de broches et j’ai passé un long moment à essayer de chercher la fonction uart4_tx et le gpio_156 sur la broche 7 au lieu de 6. Cette fois l’erreur était inoffensive mais dans d’autres circonstances un tel décalage peut être fatal au processeur (cela m’est arrivé récemment…) Soyez donc très prudents dans les manipulations des GPIO de ce type de carte, les broches sont directement reliées aux pattes du processeur sans plus de protection.

Pour commencer, relions une sonde d’oscilloscope à la broche 6, et l’autre à la masse.

Broche 6 du connecteur J3

Le signal est immobile, fixé sur 1,8V. Nous allons demander au kernel d’envoyer des données sur la ligne série ttyO3, ce qui se traduira par une activité sur la ligne d’émission (TX) de l’UART numéro 4.

[Panda]# cat /bin/ls > /dev/ttyO3

Alors que le noyau émet le contenu du fichier exécutable /bin/ls sur sa sortie série, nous observons sur la broche 6 une activité incessante qui est typique d’un transfert sur liaison série.

UART4_TX sur broche 6

Après avoir arreté l’émission (avec Contrôle-C) nous basculons la broche 6 sur le GPIO 156 en modifiant le mode.

[Panda]# echo 0x0003 > uart4_tx
[Panda]#

Plus aucun signal n’est visible sur l’oscilloscope. Activons la sortie GPIO 156.

[Panda]# echo 156 > /sys/class/gpio/export
[Panda]# echo out > /sys/class/gpio/gpio156/direction

Puis basculons régulèrement l’état du GPIO 156 pour vérifier si nous le voyons à l’oscilloscope.

[Panda]# while true; do echo 0 > /sys/class/gpio/gpio156/value ; usleep 100; echo 1 > /sys/class/gpio/gpio156/value; usleep 100; done

Le signal carré est alors clairement visible.

GPIO_156 sur brroche 6

Nous avons réussi à basculer notre broche 6 du connecteur J3 de la fonction TX de l’UART4 à la fonction GPIO 156.

 Conclusion

Nous voyons que le pseudo système de fichiers debugfs peut s’avérer très utile pour consulter ou modifier les fonctions attribuées aux broches de sortie du processeur OMAP. Il ne faut toutefois pas oublier que son interface est avant tout destinée aux développeurs du kernel, aussi n’y a-t-il aucune garantie de perénité des paramètres configurables au gré des futures évolutions du noyau.

URL de trackback pour cette page