lundi 17 septembre 2018

Chapitre 45 :Clignotement de la led activité du Raspberry avec le timer du BCM2835


Bon ! il faut bien un jour se lancer dans la compréhension et l’exploitation des interruptions. Cela n’a pas été facile mais le projet que je vais vous présenter fonctionne correctement sur mon raspberry(mais j’ai encore de nombreuses interrogations non résolues à ce jour). Nous allons nous servir du timer ARM du BCM2835 (attention il y a plusieurs timers et nous utiliserons celui de la page 196 de la doc du BCM2835) pour générer une interruption à intervalle régulier (environ 3s) pour allumer et éteindre la led activité suivant le même principe que le chapitre précédent.
La théorie est simple !! : le timer génère toutes les 3 secondes une interruption qui sera gérée par un contrôleur d’interruption puis prise en compte par le processeur qui changera de mode de fonctionnement et appellera la bonne routine de traitement de l’interruption.
Donc nous aurons un module qui contiendra les routines du timer, un autre celles du contrôleur d’interruption (IC) un autre la gestion des adresses à appeler suivant le type et le mode (appelé vecteur des interruptions) et bien sûr les modules boot.s et kernel.s comme dans les exemples précédents. Tout le projet est ici.
Dans le module timer, nous trouvons une fonction d’initialisation du timer qui fixe la fréquence, et qui autorise l’envoi d’une interruption. J’ai eu du mal à calculer correctement la fréquence en effet il faut diviser la fréquence de base de l’horloge soit 250 000 000 hertz par le facteur de division +1 (ici 7F  soit 127 + 1 = 128) ce qui donne une fréquence de 1 953 125hz qu’il faut diviser par 256 (indiqué par le code 10 se trouvant sur les bits 2et 3 du registre de contrôle du timer) ce qui donne 7629 hz. Le compteur à une valeur de 22887 ce qui donne un temps de 22887/7629 = 3 secondes
Nous trouvons aussi une fonction de test de l’interruption et de sa remise à zéro.
Dans le module du Contrôleur d’Interruption (IC page 109 doc BCM2835) nous trouvons des fonctions simples d’initialisation, d’activation, de recherche de l’identification de l’interruption et de la fin de l’interruption.
Voyons la partie la plus compliquée : la mise en place du vecteur d’interruption module interruptions.s
Le processeur ARM peut fonctionner dans plusieurs modes : le mode user que nous avons utilisé par défaut dans tous les premiers chapitres de ce blog, le mode superviseur que nous utilisons depuis que nous développons sans OS, puis les modes abort, indéfini, irq et fiq. Pour ces derniers modes, le processeur y passera en fonction du type d’interruptions reçues : instruction non autorisé, problème mémoire, interruptions générés par les périphériques (irq=normale, Fiq= rapide ). Notre timer génère une interruption de type irq.
Pour chaque mode, nous devons prévoir la procédure à appeler lors d’une interruption. Pour cela le processeur se branche sur les adresses en début de la mémoire. Dans notre module, nous commençons donc par recopier une table des adresses des procédures pour tous les modes. Bien sûr pour les modes autre que irq, nous mettons le minimum d’instructions à exécuter (quoiqu’il vaudrait mieux envoyer un message pour indiquer ce qui se passe !!!).
Pour écrire ce module, je me suis servi d’un exemple trouvé dans un livre et cela commence par une instruction que je n’avais jamais vue : mcr p15,0,r0,c12,c0,0 !!!!  Dans la documentation ARM, on trouve d’autres instructions de ce type !! qui servent à piloter des fonctions très particulières du processeur. J’ai mis cette instruction en commentaire car elle est peut être utile pour d’autres processeurs. Ensuite nous trouvons la recopie à l’adresse zéro de la mémoire de la table des adresses d’interruptions puis pour chaque mode la procédure à exécuter. Vous remarquerez qu’il y a une procédure reset qui appellera notre première routine _start.
Vous remarquerez qu’à la place de push et pop, nous utilisons les instructions stmfd et ldmfd qui font la même chose. Remarquez que les adresses de retour au programme appelant sont différentes suivant le mode.
Enfin, nous ne complétons que la fonction irq_handler en appelant les fonctions du contrôleur d’interruptions ou du timer pour gérer l’interruption.
Ce module sera appelé dans le module boot_s car le vecteur d’interruptions doit être chargé au plus tôt.
Ensuite il nous reste à modifier le module kernel.s pour modifier l’allumage et l’extinction de la led ; Pour cela on utilise l’instruction mcr p15,0,r0,c7,c0,4 qui met le processeur en attente d’une interruption. Hélas, je me suis aperçu que cette instruction ne fonctionne qu’en mode superviseur. Si on veut utiliser le mode user, il faudra modifier la partie allumage extinction pour être appelée de la fonction irq_handler ou de la fonction test_interruption_timer. Je vais essayer cela dans le prochain post.
La mise au point de tout cela a été laborieuse car en cas de non fonctionnement, il est pas facile de savoir quelle partie est en cause : est ce le timer qui ne génère pas l’interruption ou le contrôleur qui ne la pas autorisé ou le handler qui fonctionne mal ?  Donc prudence lors des modifications. De plus lors de mes premiers tests, j’ai réussi à planter mon portable sous windows10 (soit 10 minutes au moins pour redémarrer !! ) malgré la liaison série entre les deux. Je pense que la procédure mini_uart d’envoi de caractère ne vérifiant pas la validité des caractères, j’ai du envoyer des cochonneries qui ont fait planter window10 !!!
Mes explications sur le sujet des interruptions étant simplistes, je vous renvoie à toute la documentation sur internet traitant des interruptions des processeurs ARM

Aucun commentaire:

Enregistrer un commentaire