M2 SETI B4 / MS SE SE758


TP Interruptions

Introduction

Objectifs

L’objectif de ce TP est de compléter le pilote de périphérique ADXL345 que vous avez commencé à écrire lors des TP précédents pour lui ajouter le support des interruptions.

Pré-requis

Environnement de TP

Nous allons reprendre le répertoire créé lors du TP précédent.

$ export TPROOT=xxx  # À adapter
$ cd $TPROOT
$ cd pilote_i2c

Pensez à adapter les commandes ci-dessus en fonction du nom donné au répertoire lors du premier TP.

Pour rappel, le lien vers la documentation de l’accéléromètre :

Compléments sur l’ADXL345

FIFO

L’accéléromètre possède une FIFO permettant de stocker 32 échantillons (triplet de valeurs X, Y et Z issu d’une acquisition). Dans le mode utilisé jusqu’à maintenant, cette FIFO était désactivée (mode bypass) et le dernier échantillon mesuré était accessible via les registres DATA_X0, DATA_X1, DATA_Y0, DATA_Y1, DATA_Z0 et DATA_Z1.

Nous allons maintenant configurer l’accéléromètre pour utiliser le mode Stream. Dans ce mode, les échantillons acquis sont stockés dans la FIFO. Si cette dernière est pleine, l’échantillon le plus ancien est supprimé avant le stockage du nouvel échantillon (ainsi, si elle est pleine, elle contient les 32 échantillons les plus récents).

Le nombre courant d’échantillons dans la FIFO est consultable dans le champ Entries du registre FIFO_STATUS.

Une fois la FIFO activée, le comportement des registres DATA_xy change légèrement. Ces registres contiennent alors la valeur des différentes composantes de l’échantillon le plus ancien contenu dans la FIFO. Dès qu’un de ces registres est lu, l’échantillon en question est retiré de la FIFO. La seule façon de récupérer plusieurs composantes d’un même échantillon (par exemple X0 et X1) est de faire une lecture unique de plusieurs octets (multi-byte read). Si vous faites la lecture de DATA_X0, puis la lecture de DATA_X1 (en deux transactions I2C), les deux valeurs que vous récupérez correspondent à deux échantillons différents.

Interruptions

De plus, l’accéléromètre possède une sortie d’interruption (en pratique il en possède même deux mais la deuxième n’est pas simulée). En interne, il possède 8 sources différentes d’interruption (Data Ready, Single Tap, Double Tap, Activity, Inactivity, Free Fall, Watermark et Overrun). L’état de ces différentes sources d’interruption est consultable dans le registre INT_SOURCE (en lecture seule). Un autre registre, INT_ENABLE permet d’activer ou de désactiver chacune de ces sources d’interruption.

À chaque instant, un ET logique est réalisé entre le contenu de ces deux registres, et si le résultat est différent de zéro, la sortie d’interruption est mise à l’état haut.

Parmi toutes ces sources d’interruption, nous allons utiliser l’interruption Watermark. Son fonctionnement est simple : lorsque la FIFO contient au moins n éléments (où n est la valeur contenue dans le champ Samples du registre FIFO_CTL et est compris dans l’intervalle 0-31), l’interruption Watermark sera levée. Lorsque le nombre d’élément dans la FIFO repasse en dessous de n, cette interruption est remise à zéro.

La ligne d’interruption simulée est connectée à l’entrée numéro 50 du contrôleur d’interruption principal. Cette information est déjà décrite dans l’arbre des périphérique fourni :

&v2m_i2c_dvi {
      adxl345: adxl345@53 {
          compatible = "qemu,adxl345";
          reg = <0x53>;
          interrupt-parent = <&gic>;
          interrupts = <0 50 4>;
      };
};

Les deux lignes interrupt... permettent au noyau de repérer exactement la bonne ligne d’interruption et de calculer pour vous son numéro (dont vous aurez besoin pour enregistrer un gestionnaire d’interruption). Ce numéro sera accessible via le champ irq de la structure struct i2c_client.

Changement de la récupération des données dans votre pilote

À des fins pédagogiques (pour pouvoir manipuler les interruptions ainsi que la mise en veille de processus), nous allons modifier la façon dont votre pilote va récupérer les données depuis l’accéléromètre.

Jusqu’à maintenant, la récupération des données depuis l’accéléromètre est effectuée dans la fonction de rappel adxl345_read, donc lorsqu’une application en fait explicitement la demande par l’intermédiaire de l’appel système read sur le fichier représentant notre périphérique.

Nous allons maintenant changer ce fonctionnement pour le suivant :

kfifo

Votre pilote va maintenir une petite FIFO. Vous pourriez en réimplémenter une vous-même, mais le noyau vous en fournit une : kfifo. L’API est documentée : https://www.kernel.org/doc/htmldocs/kernel-api/kfifo.html, mais vous trouverez ci-dessous un bref aperçu des commandes les plus utiles.

#include <linux/kfifo.h>

struct fifo_element { 
  // Un element de la FIFO, dans votre cas un échantillon de l'accéléromètre
}

struct adxl345_device {
  // Création d'une FIFO, nommée samples_fifo
  // contenant au maximum 16 (doit être une puissance de 2)
  // éléments de type struct fifo_element
  DECLARE_KFIFO(samples_fifo, struct fifo_element, 16);

  // ...
}

// Initialise la FIFO avant son utilisation :
INIT_KFIFO(adev->samples_fifo);

// Ajoute un élément à la FIFO
// Renvoie 0 si la FIFO était pleine
struct fifo_element el = { ... };
res = kfifo_put(&adev->samples_fifo, el);

// Récupère un élément depuis la FIFO
// Renvoie 0 si la FIFO était vide
struct fifo_element el;
res = kfifo_get(&adev->samples_fifo, &el);

// Test si la FIFO est vide
kfifo_is_empty(&adev->samples_fifo);

Travail à faire

  1. Créez une structure pour représenter un échantillon et créez une FIFO dans la structure adxl345_device.

  2. Modifiez votre fonction probe :

  1. Écrivez la fonction bottom half (adxl345_int par exemple) :
  1. Modifiez votre fonction adxl345_read :

Et testez le tout !

Remarque : pour vérifier que vous récupérez bien toutes les données depuis l’accéléromètre, et dans l’ordre, vous pouvez vous baser sur l’algorithme de génération des échantillons disponible dans la page TP : Pilote accéléromètre, première partie (modèle de périphérique).


© Copyright 2020 Guillaume Duc. Le contenu de cette page est mis à disposition selon les termes de la Licence Creative Commons Attribution - Partage dans les Mêmes Conditions 4.0 International (à l'exception des exemples de code tirés du noyau Linux et qui sont distribués sous leurs licences d'origine).

Licence
Creative Commons