![]() |
M2 SETI B4 / MS SE SE758 |
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.
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 :
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.
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
.
À 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 :
Votre pilote va maintenir une FIFO (différente de celle de l’accéléromètre, de taille arbitraire, par exemple 64 éléments) contenant les derniers échantillons récupérés depuis l’accéléromètre.
L’accéléromètre fonctionnera en mode Stream (donc FIFO matérielle activée), avec l’interruption Watermark activée et configurée à 20 (c’est-à-dire lorsqu’au moins 20 échantillons sont présents dans la FIFO, l’interruption est levée).
Un gestionnaire d’interruption dans votre pilote sera configuré pour réagir à cette interruption. Lorsqu’elle se déclenche, il doit vider la FIFO matérielle de l’accéléromètre et stocker les données dans la FIFO de votre pilote. Il doit également réveiller des processus éventuellement en attente de ces données (voir ci-dessous).
La fonction de rappel adxl345_read
, lorsqu’elle est
appelée, vérifie si des données sont disponibles dans la FIFO de votre
pilote (pas celle de l’accéléromètre). Si oui, elle renvoie les données
à l’application depuis cette FIFO, dans le cas contraire, elle met
le processus en sommeil jusqu’à ce que des données soient
disponibles. La fonction adxl345_read
ne communique donc
plus directement avec votre accéléromètre et peut bloquer le
processus.
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
(samples_fifo, struct fifo_element, 16);
DECLARE_KFIFO
// ...
}
// Initialise la FIFO avant son utilisation :
(adev->samples_fifo);
INIT_KFIFO
// Ajoute un élément à la FIFO
// Renvoie 0 si la FIFO était pleine
struct fifo_element el = { ... };
= kfifo_put(&adev->samples_fifo, el);
res
// Récupère un élément depuis la FIFO
// Renvoie 0 si la FIFO était vide
struct fifo_element el;
= kfifo_get(&adev->samples_fifo, &el);
res
// Test si la FIFO est vide
(&adev->samples_fifo); kfifo_is_empty
Créez une structure pour représenter un échantillon et créez une
FIFO dans la structure adxl345_device
.
Modifiez votre fonction probe
:
Configurez correctement l’accéléromètre (registres
INT_ENABLE
et FIFO_CTL
)
Enregistrez une fonction en tant que bottom half (vous n’aurez pas besoin d’écrire un top half) avec le mécanisme de Threaded IRQ
Initialisez votre FIFO
Initialisez la file d’attente
adxl345_int
par exemple) :Récupérez le nombre d’échantillons disponibles dans la FIFO de
l’accéléromètre (registre FIFO_STATUS
)
Récupérez tous les échantillons depuis la FIFO de l’accéléromètre et stockez les dans votre FIFO interne
Réveillez les éventuels processus en attente de données
adxl345_read
:Vérifiez si des données sont disponibles dans la FIFO de votre pilote
Si non, mettez le processus en attente
Renvoyez les données depuis la FIFO interne
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).