«

»

Oct 04

Ada et les GPIOs

Salut à tous,

Aujourd’hui un petit tutorial pour mieux comprendre l’utilisation des GPIOs sur stm32 avec Ada ! C’est parti !

Avant de commencer, expliquons ce que sont les GPIOs. GPIO (General Purpose Input/Output = Entrée/Sortie à usage général) sont des ports pouvant jouer soit le rôle d’entrée sur le microcontrolleur, soit le rôle de sortie. On accède en général aux données du port GPIO via des registres, qui sont des emplacements mémoire internes au processeur. C’est une des mémoires les plus rapides mais aussi une des plus chères à produire.

Sur notre stm32f4-discovery, les ports GPIOs se trouvent sur les côtés et sont faits de telle sorte qu’ils soient facile d’accés.

Configuration

Sachant tout cela, nous allons devoir nous munir de deux packages : Stm32.GPIO et Stm32.RCC(Reset and Clock Control).

Lorsque l’on veut utiliser une entrée/sortie, il faut initialiser l’horloge de ce périphérique, sans quoi rien ne peut fonctionner. Pour cela, nous avons besoin de la procedure suivante, se trouvant dans Stm32.RCC :
procedure RCC_PeriphClockCmd (Periph : Stm_Periph; State : FunctionalState);
Elle prend deux paramètres : le périphérique cible et si l’on veut activer ou désactiver l’horloge. Pour le périphérique, nous avons le choix entre :

TIM2, TIM3, TIM4, TIM5, TIM6, TIM7, TIM12, TIM13, TIM14, WWDG, SPI2, SPI3, USART2, USART3, UART4, UART5, I2C1, I2C2, I2C3, CAN1, CAN2, PWR, DAC, TIM1, TIM8, USART1, USART6, ADC, ADC1, ADC2, ADC3, SDIO, SPI1, SYSCFG, TIM9, TIM10, TIM11, GPIOA, GPIOB, GPIOC, GPIOD, GPIOE, GPIOF, GPIOG, GPIOH, GPIOI, CRC, FLITF, SRAM1, SRAM2, BKPSRAM, CCMDATARAMEN, DMA1, DMA2, ETH_MAC, ETH_MAC_Tx, ETH_MAC_Rx, ETH_MAC_PTP, OTG_HS, OTG_HS_ULPI, DCMI, CRYP, HASH, RNG, OTG_FS

Nous voyons bien qu’en réalité, cette fonction permet d’initialiser l’horloge de tous les périphériques. Concernant l’état de l’horloge, nous pouvons choisir entre Disable et Enable selon que l’on souhaite désactiver ou activer l’horloge.

Toutefois, nous utiliserons rarement cette fonction directement car elle est inclue dans d’autres fonctions déjà fournies. Par la suite, nous ne l’utiliserons donc plus mais sachez qu’elle est presque toujours utilisée implicitement.

 

Rentrons maintenant dans le vif du sujet et configurons notre GPIO. Toutes les fonctions qui suivrons seront présentes dans le package Stm32.GPIO. Pour configurer, nous allons devoir définir les paramètres qui vont le caractériser. Nous utiliserons pour cela le type GPIO_Params qui prend divers arguments :

  • Pins : il s’agit d’un tableau de Boolean indexés par les numéros des pins qui va de 0 à 15. Le tableau s’appelle Mask. Les pins que l’on souhaite configurer ont comme valeur True, les autres False.
  • Mode : il s’agit du mode de fonctionnement de la pin, qui est déterminé par la future utilisation. Nous avons quatre choix. Mode_In permet de dire qu’il s’agit d’une pin jouant le rôle d’entrée. Mode_Out, au contraire, permet de jouer le rôle de sortie. Alternate permet de passer en mode « alternate function » et peut être utiliser par des périphériques tels que l’UART, le STI,… Nous y reviendrons dans un autre cours. Enfin, Analog permet de transformer la sortie en entrée Analogique qui contrairement au numérique permet l’entrée de valeurs continues.
  • Speed : permet de déterminer la vitesse de l’horloge du module GPIO. Les valeurs possibles sont : Speed_2MHz, Speed_25MHz, Speed_50MHz, Speed_100MHz selon que l’on désire une vitesse de 2, 25, 50 ou 100 Mhz.
  • Output_Type : permet de régler le type de la sortie. Il y a deux choix. PP signifie Push Pull. Avec PP, un transistor relie la pin GPIO à VCC (+) et un autre à GND (ground = -). Un transistor est un composant actif jouant le rôle d’interrupteur. Seulement un des deux est activé à la fois. Par contre, lorsque l’on choisi OD (Open Drain) seulement un transitor relie la pin au GND, et c’est tout.
  • PuPd : permet de déterminer si la pin est en pull-up, pull-down ou aucun des deux. Pull_Up sélectionne pull-up. Cela signifie que la valeur « normale » de la pin, celle qu’elle possède si nous ne touchons à rien, est VCC. Pour cela, une résistance relie la pin à VCC. Au contraire, Pull_Down met cette valeur à GND et une résistance relie la pin à GND. No_Pull signifie que la pin n’est relié à rien de particulier, elle n’a aucune valeur significative sans intervention de notre part.

Maintenant que nous savons comment définir les paramètres d’une pin, apprenons comment les appliquer. Pour cela, rien de plus simple. Il suffit d’utiliser la fonction :

procedure Config_GPIO (GPIO : GPIO_Type; Params : GPIO_Params);

GPIO_Type contient simplement GPIOA, GPIOB, … GPIOI.

Voici un exemple d’utilisation pour éclairer tout cela :

Params : GPIO_Params := (Pins        => (Mask => (1|3 => True, others => False)),
Mode        => Mode_In,
Speed       => Speed_100MHz,
Output_Type => PP,
PuPd        => Pull_Up);
Config_GPIO (GPIOA, Params);

 

Tout cela peut paraître trop long, surtout lorque l’on utilise souvent les mêmes paramètres. C’est pour cela que des fonctions viennent accélérer le travail. Avant de les voir, introduisons le type Pin_Type qui est un couple représentant le GPIO (GPIOA,…,GPIOI) et le numéro de Pin (de 0 à 15).

procedure Setup_In_Pin (Pin : Pin_Type);

Cette procedure permet de configurer la pin en mode d’entrée, à une vitesse de 100MHz, de type push/pull, en pull-up. Elle est par exemple utile pour configurer un interrupteur.

procedure Setup_Out_Pin (Pin : Pin_Type);

Cette fois, la pin est configurer en sortie et avec les même paramètres que précédemment.

procedure Config_GPIO_AF (GPIO : GPIO_Type; Pin : Pin_Number; AF : Alternate_Function);

Cette procedure configure le GPIO de numéro Pin (entre 0 et 15) avec une Alternate Function. A titre indicatif, voici la liste des alternate functions possibles :

AF_RTC_50Hz, --RTC_50Hz Alternate Function mapping /
AF_MCO, --MCO (MCO1 and MCO2) Alternate Function mapping /
AF_TAMPER, --TAMPER (TAMPER_1 and TAMPER_2) Alternate Function mapping /
AF_SWJ, --SWJ (SWD and JTAG) Alternate Function mapping /
AF_TRACE, --TRACE Alternate Function mapping /
-- AF 1 selection
AF_TIM1, --TIM1 Alternate Function mapping /
AF_TIM2, --TIM2 Alternate Function mapping /
-- AF 2 selection
AF_TIM3, --TIM3 Alternate Function mapping /
AF_TIM4, --TIM4 Alternate Function mapping /
AF_TIM5, --TIM5 Alternate Function mapping /
-- AF 3 selection
AF_TIM8, --TIM8 Alternate Function mapping /
AF_TIM9, --TIM9 Alternate Function mapping /
AF_TIM10, --TIM10 Alternate Function mapping /
AF_TIM11, --TIM11 Alternate Function mapping /
-- AF 4 selection
AF_I2C1, --I2C1 Alternate Function mapping /
AF_I2C2, --I2C2 Alternate Function mapping /
AF_I2C3, --I2C3 Alternate Function mapping /
-- AF 5 selection
AF_SPI1, --SPI1 Alternate Function mapping /
AF_SPI2, --SPI2/I2S2 Alternate Function mapping /
-- AF 6 selection
AF_SPI3, --SPI3/I2S3 Alternate Function mapping /
-- AF 7 selection
AF_USART1, --USART1 Alternate Function mapping /
AF_USART2, --USART2 Alternate Function mapping /
AF_USART3, --USART3 Alternate Function mapping /
AF_I2S3ext, --I2S3ext Alternate Function mapping /
-- AF 8 selection
AF_UART4, --UART4 Alternate Function mapping /
AF_UART5, --UART5 Alternate Function mapping /
AF_USART6, --USART6 Alternate Function mapping /
-- AF 9 selection
AF_CAN1, --CAN1 Alternate Function mapping /
AF_CAN2, --CAN2 Alternate Function mapping /
AF_TIM12, --TIM12 Alternate Function mapping /
AF_TIM13, --TIM13 Alternate Function mapping /
AF_TIM14, --TIM14 Alternate Function mapping /
-- AF 10 selection
AF_OTG_FS, --OTG_FS Alternate Function mapping /
AF_OTG_HS, --OTG_HS Alternate Function mapping /
-- AF 11 selection
AF_ETH, --ETHERNET Alternate Function mapping /
-- AF 12 selection
AF_FSMC, --FSMC Alternate Function mapping /
AF_OTG_HS_FS, --OTG HS configured in FS, Alternate Function mapping /
AF_SDIO, --SDIO Alternate Function mapping /
-- AF 13 selection
AF_DCMI, --DCMI Alternate Function mapping /
-- AF 15 selection
AF_EVENTOUT --EVENTOUT Alternate Function mapping /

 

Enfin pour en finir avec la configuration des pins, introduisons une dernière fonction qui elle permet de remettre tous les paramètres à 0.

procedure DeInit_GPIO (GPIO : GPIO_Type);

Lecture/Ecriture

Maintenant que nous savons comment configurer une pin, voyons comment lire et écrire sur cette pin. Pour cela, nous disposons de deux fonctions :

function Read_Pin (Pin : Pin_Type) return Boolean;

Cette fonction lit la valeur de la pin (par exemple quand on utilise un interrupteur) et retourne sa valeur. Si l’on choisi false, on place la pin à GND. Au contraire, True passe la pin à VCC.

procedure Set_Pin (Pin : Pin_Type; Value : Boolean);

Au contraire, cette procedure fixe la valeur de sortie d’une pin avec les mêmes conventions que précédemment.

Pour conclure, il existe une fonction qui permet d’inverser la valeur de la pin sans avoir à la retenir dans une variable ou à aller la lire à chaque fois. Il s’agit de :

procedure Toggle_Pin (Pin : Pin_Type);

Exercices

Voila en ce qui concerne la théorie des GPIOs et de l’Ada. En guise de conclusion, je vous propose trois exercices :

  • Faire clignoter une LED. Pour cela vous aurez besoin de : delay until Clock + To_Time_Span (1.0); qui attends dans ce cas 1 seconde. Il vous faudra aussi importer le package Ada.Real_Time. Vous pouvez utiliser les leds sur la carte, par example il y a une led sur PD12 (vert), PD13 (orange), PD14 (rouge), PD15 (bleu).
  • Programmer un Interrupteur qui change la valeur de la Led selon qu’il est ouvert ou fermé. Vous pouvez utiliser le bouton USER, connecté sur PA0 (à utiliser en pull down)
  • Faire clignoter deux leds dans des tâches différentes. On utilisera pour cela task. Attention, le profil Ravenscar vous oblige à déclarer toutes les tâches au démarrage, on ne peut pas créer de tâche dynamiquement.
  •  

     

    Correction

    Exercice1:

     

    Exercice2 :

    cours2.adb

     

    Exercice3 :

     

    A bientôt les loulous,

     

    Julien