====== Accéder aux entrées/sorties depuis l'espace utilisateur via SysFs ======
===== Principe de base =====
((Voir wiki Xilinx [[http://www.wiki.xilinx.com/Linux+GPIO+Driver#SysFs%20Interface-Using%20GPIO%20with%20SysFs|Linux GPIO Driver]] et forum Xilinx [[http://forums.xilinx.com/xlnx/attachments/xlnx/EDK/37000/1/petalinux_entry_guide.pdf|petalinux entry guide]]))Par défaut, le système de fichiers est configuré pour accéder aux GPIO via le procédé SysFs. Il s'agit d'un procédé de fichiers avec une arborescence spécifique accessible depuis// /sys/.// On peut retenir ces ces points importants :
- Les contrôleurs de GPIO sont visibles depuis// /sys/class/gpio.// Chaque contrôleur contrôle quelques GPIO.
root@Petalinux_LEDs:~# ls /sys/class/gpio
export gpiochip890 gpiochip898 gpiochip906 unexport
Ici nous pouvons voir trois contrôleurs //gpiochip890//, //gpiochip898// et //gpiochip906//. Des informations sont disponibles pour chacun d'eux.
root@Petalinux_LEDs:~# ls /sys/class/gpio/gpiochip890
base label ngpio power subsystem uevent
root@Petalinux_LEDs:~# cat /sys/class/gpio/gpiochip890/base
890
root@Petalinux_LEDs:~# cat /sys/class/gpio/gpiochip890/label
/amba_pl/gpio@41200000
root@Petalinux_LEDs:~# cat /sys/class/gpio/gpiochip890/ngpio
8
root@Petalinux_LEDs:~# ls /sys/class/gpio/gpiochip898
base label ngpio power subsystem uevent
root@Petalinux_LEDs:~# cat /sys/class/gpio/gpiochip898/base
898
root@Petalinux_LEDs:~# cat /sys/class/gpio/gpiochip898/label
/amba_pl/gpio@41200000
root@Petalinux_LEDs:~# cat /sys/class/gpio/gpiochip898/ngpio
8
root@Petalinux_LEDs:~# ls /sys/class/gpio/gpiochip906/
base device label ngpio power subsystem uevent
root@Petalinux_LEDs:~# cat /sys/class/gpio/gpiochip906/base
906
root@Petalinux_LEDs:~# cat /sys/class/gpio/gpiochip906/label
zynq_gpio
root@Petalinux_LEDs:~# cat /sys/class/gpio/gpiochip906/ngpio
118
Le premier contrôleur est réservé pour le port des interrupteurs, le second pour le port des DELs que nous avons configurés lors de la création du projet Vivado (il y en a 8 interrupteurs et 8 DELs au total) et le troisième correspond à l'ensemble des GPIOs qui sont accessibles par le Zynq. Les numéro indiqués dans le fichier //base// correspondent aux numéros de la première GPIO accessible sur le port correspondant. Les GPIO suivantes sont accessibles aux numéros suivants, jusqu'à //base// + //ngpio//.
- Les GPIOs à utiliser doivent être configurées. Il faut les déclarer dans le fichier //export//. Cela crée un répertoire du nom de la GPIO.
root@Petalinux_LEDs:~# echo 898 > /sys/class/gpio/export
root@Petalinux_LEDs:~# ls /sys/class/gpio/
export gpio898 gpiochip898 gpiochip906 unexport
root@Petalinux_LEDs:~# ls /sys/class/gpio/gpio898
active_low direction power subsystem uevent value
Une fois créée, il est nécessaire d'indiquer sa direction (entrée ou sortie), dans le fichier //direction//.
root@Petalinux_LEDs:~# echo out > /sys/class/gpio/gpio898/direction
Idem pour la les autres DELs et interrupteurs :
root@Petalinux_LEDs:~# echo 905 > /sys/class/gpio/export
root@Petalinux_LEDs:~# echo out > /sys/class/gpio/gpio905/direction
root@Petalinux_LEDs:~# echo 890 > /sys/class/gpio/export
root@Petalinux_LEDs:~# echo 897 > /sys/class/gpio/export
root@Petalinux_LEDs:~# echo in > /sys/class/gpio/gpio890/direction
root@Petalinux_LEDs:~# echo in > /sys/class/gpio/gpio897/direction
- Pour affecter un état à la sortie, on utilise le fichier //value//
root@Petalinux_LEDs:~# echo 1 > /sys/class/gpio/gpio898/value
root@Petalinux_LEDs:~# echo 1 > /sys/class/gpio/gpio905/value
Les DELs //LD0// et //LD7// s'allument.
root@Petalinux_LEDs:~# echo 0 > /sys/class/gpio/gpio898/value
root@Petalinux_LEDs:~# echo 0 > /sys/class/gpio/gpio905/value
Les DELs //LD0// et //LD7// s'éteignent.
- Pour contrôler l'état d'une entrée, on utilise aussi le fichier //value//
root@Petalinux_LEDs:~# cat /sys/class/gpio/gpio890/value
0
root@Petalinux_LEDs:~# cat /sys/class/gpio/gpio897/value
0
Lorsque les interrupteurs //SW0// et //SW7// sont ouverts.
root@Petalinux_LEDs:~# cat /sys/class/gpio/gpio890/value
1
root@Petalinux_LEDs:~# cat /sys/class/gpio/gpio897/value
1
Lorsque les interrupteurs //SW0// et //SW7// sont fermés.
- Pour libérer les ports d'entrées sorties pour d'autres applications, il est nécessaire de les désinscrire par l'intermédiaire du fichier //unexport//
root@Petalinux_LEDs:~# echo 890 > /sys/class/gpio/unexport
root@Petalinux_LEDs:~# echo 897 > /sys/class/gpio/unexport
root@Petalinux_LEDs:~# echo 898 > /sys/class/gpio/unexport
root@Petalinux_LEDs:~# echo 905 > /sys/class/gpio/unexport
===== Programme d'exemple =====
Ce programme //C// donne un aperçu sur la façon de manipuler ce système de fichiers pour accéder aux entrées / sorties
/* allume_LED_sysfs.c
*
* Example of sysfs use for Zedboard
* Eric Meyer
* From "blink.c" for Raspaberry-Pi by Guillermo A. & Amaral B.
* http://elinux.org/RPi_GPIO_Code_Samples#sysfs
*
* This software will make LEDs to blink, from LD0 to LD7
* enabled by the switches from SW0 to SW7
*/
#include
#include
#include
#include
#include
#include
#define IN 0
#define OUT 1
#define LOW 0
#define HIGH 1
#define SWbase 890
#define LDbase 898
#define LD0 LDbase + 0
#define LD1 LDbase + 1
#define LD2 LDbase + 2
#define LD3 LDbase + 3
#define LD4 LDbase + 4
#define LD5 LDbase + 5
#define LD6 LDbase + 6
#define LD7 LDbase + 7
#define SW0 SWbase + 0
#define SW1 SWbase + 1
#define SW2 SWbase + 2
#define SW3 SWbase + 3
#define SW4 SWbase + 4
#define SW5 SWbase + 5
#define SW6 SWbase + 6
#define SW7 SWbase + 6
#define LD_dir "out"
#define SW_dir "in"
/* Function to register io number */
static int GPIOExport(int io)
{
#define BUFFER_MAX 5
char buffer[BUFFER_MAX];
ssize_t bytes_written;
int fd;
fd = open("/sys/class/gpio/export", O_WRONLY);
if (-1 == fd) {
fprintf(stderr, "allume_LED_sysfs : Failed to open export for writing!\n");
return(-1);
}
bytes_written = snprintf(buffer, BUFFER_MAX, "%d", io);
write(fd, buffer, bytes_written);
close(fd);
return(0);
}
/* Function to release io number */
static int GPIOUnexport(int io)
{
#define BUFFER_MAX 5
char buffer[BUFFER_MAX];
ssize_t bytes_written;
int fd;
fd = open("/sys/class/gpio/unexport", O_WRONLY);
if (-1 == fd) {
fprintf(stderr, "allume_LED_sysfs : Failed to open unexport for writing!\n");
return(-1);
}
bytes_written = snprintf(buffer, BUFFER_MAX, "%d", io);
write(fd, buffer, bytes_written);
close(fd);
return(0);
}
/* Function to define in or out for the io number */
static int GPIODirection(int io, char *dir)
{
#define DIRECTION_MAX 35
char path[DIRECTION_MAX];
ssize_t bytes_written;
int fd;
snprintf(path, DIRECTION_MAX, "/sys/class/gpio/gpio%d/direction", io);
fd = open(path, O_WRONLY);
if (-1 == fd) {
fprintf(stderr, "allume_LED_sysfs : Failed to open gpio direction for writing!\n");
return(-1);
}
bytes_written = snprintf(path, DIRECTION_MAX, "%s", dir);
if (-1 == write(fd, path, bytes_written)) {
fprintf(stderr, "allume_LED_sysfs : Failed to set direction!\n");
return(-1);
}
close(fd);
return(0);
}
static int GPIORead(int io)
{
#define VALUE_MAX 30
char path[VALUE_MAX];
char value_str[3];
int fd;
snprintf(path, VALUE_MAX, "/sys/class/gpio/gpio%d/value", io);
fd = open(path, O_RDONLY);
if (-1 == fd) {
fprintf(stderr, "allume_LED_sysfs : Failed to open gpio value for reading!\n");
return(-1);
}
if (-1 == read(fd, value_str, 3)) {
fprintf(stderr, "allume_LED_sysfs : Failed to read value!\n");
return(-1);
}
close(fd);
return(atoi(value_str));
}
static int GPIOWrite(int io, int value)
{
static const char s_values_str[] = "01";
char path[VALUE_MAX];
char value_str[3];
ssize_t bytes_written;
int fd;
snprintf(path, VALUE_MAX, "/sys/class/gpio/gpio%d/value", io);
fd = open(path, O_WRONLY);
if (-1 == fd) {
fprintf(stderr, "allume_LED_sysfs : Failed to open gpio value for writing!\n");
return(-1);
}
bytes_written = snprintf(value_str, 3, "%d", value);
if (1 != write(fd, value_str, bytes_written)) {
fprintf(stderr, "allume_LED_sysfs : Failed to write value!\n");
return(-1);
}
close(fd);
return(0);
}
int main(int argc, char *argv[])
{
int repeat = 60; //number of seconds to run = repeat / 2
int i;
int LD[8];
printf("Hello switches and LEDs !\n");
/*
* Enable GPIO pins
*/
for (i=0 ; i<8 ; i++) {
if ((-1 == GPIOExport((int)LDbase + i)) || (-1 == GPIOExport((int)SWbase + i)) )
return(1);
}
/*
* Set GPIO directions
*/
for (i=0 ; i<8 ; i++) {
if (-1 == GPIODirection((int)LDbase + i, LD_dir) || -1 == GPIODirection((int)SWbase + i, SW_dir))
return(2);
}
/*
* Set LEDs to off
*/
for (i=0 ; i<8 ; i++) {
LD[i]=0;
if (-1 == GPIOWrite((int)LDbase+i, LD[i]))
return(3);
}
do {
/*
* Read switches values and write to LEDs values
*/
for (i=0 ; i<8 ; i++) {
if (GPIORead(SWbase+i)==1){
LD[i]=!LD[i];
}
else {
LD[i]=0;
}
if (-1 == GPIOWrite((int)LDbase + i, LD[i]))
return(3);
}
usleep(500 * 1000);
}
while (repeat--);
/*
* Disable GPIO pins
*/
for (i=0 ; i<8 ; i++) {
if ((-1 == GPIOUnexport((int)LDbase + i)) || (-1 == GPIOUnexport((int)SWbase + i)) )
return(4);
}
return(0);
}
==== Compiler, charger et exécuter le programme ====
Pour compiler le programme sur le PC de développement, et après avoir "sourcé" la chaine de développement //PetaLinux//, on peut utiliser le compilateur //arm-xilinx-linux-gnueabi-gcc// :
$ arm-xilinx-linux-gnueabi-gcc -o allume_LED_sysfs allume_LED_sysfs.c
On peut utiliser un transfert de fichiers par //tftp// si le serveur a été configuré et que la //ZedBoard// a été correctement installée sur le réseau. Pour cela, on va placer le fichier compilé dans le répertoire// /tfptpboot/ //, puis changer ses droits.
$ cp allume_LED_sysfs /tftpboot/
$ chmod 777 /tftpboot/allume_LED_sysfs
Sur la console série reliée à la //Zedboard//, on télécharge le fichier, le rend exécutable et l'exécute :
root@Petalinux_LEDs:/tftpboot# tftp -l allume_LED_sysfs -r allume_LED_sysfs -g XXX.XXX.XXX.XXX
root@Petalinux_LEDs:/tftpboot# chmod +x allume_LED_sysfs
root@Petalinux_LEDs:/tftpboot# ./allume_LED_sysfs
Hello switches and LEDs !
Goodbye switches and LEDs !
;-) En manipulant les interrupteurs, les DELs clignotent !