Les Interruptions
DOCUMENTATION
Le problème du livreur
Gestion par Scrutation
J’attends un colis ; la sonnette est cassée.
Je me lève donc régulièrement pour voir si le livreur arrive.
Nous appellerons cela de la scrutation, à savoir que dans un programme je prévois d’aller consulter régulièrement l’état d’un registre.
main()
{ ...
while(1)
{
je lis ;
je vais voir à la fenêtre ;
si ( livreur présent )
{ j'ouvre la porte ; }
}
}
Gestion par Interruption
J’attends un colis ; la sonnette est réparée.
Si le livreur est présent, il sonne ; il interrompt donc ma lecture.
Je traite cette interruption en allant ouvrir la porte.
Au passage j’ai pris soin de marquer ma page pour reprendre ma lecture là où je l’avais laissée.
main()
{ ...
while(1)
{
je lis ;
}
}
SONNETTE_IRQ_Handler
{
j'ouvre la porte ;
}
Les différentes catégories d’exceptions
Une Exception est un événement qui interrompt un programme.
On distingue les Exceptions :
-
Matérielles :
- Interruption liée à un périphérique ( fin de comptage d’un timer, réception d’une donnée, … )
- Exception suite à une erreur ( problème d’accès mémoire, instruction non reconnue, … )
-
Logicielles : Le traitement d’une exception se fait dans un mode privilégié.
Une exception logicielle permet, dans un contexte de système d’exploitation, de changer de mode et donc d’accéder à des zones mémoire privilégiées.
Par la suite nous allons nous intéresser uniquement aux interruption Matérielles liées à un périphérique.
Le contrôleur d’Interruptions ( NVIC = Nested Vectored Interrupt Controler )
Problématique
Tous les périphériques sont susceptible de déclencher une interruption.
Le processeur est interrompu dès lors que la ligne IRQ est active.
Pour connaître l’auteur de l’interruption, le processeur devrait interroger
chaque périphérique ( via son registre d’état ).
Pour éviter cette situation, il existe un composant supplémentaire faisant le lien
entre les demandes d’interruption des périphériques et la ligne IRQ du processeur.
Il s’agit du Contrôleur d’Interruptions ( NVIC ).
Comme chaque périphérique est relié à une entrée numérotée du NVIC, ce dernier
connait le périphérique, il transmet donc directement au processeur le numéro correspondant.
Gestion des priorités par le NVIC
Etant donné que plusieurs périphériques peuvent demander une interruption à un instant t, le NVIC permet de définir des niveaux de priorité en fonction des sources d’interruption.
Scénario d’une interruption
La table des vecteurs se trouve dans le fichier startup_stm32f411xe.s
Lors d’une interruption, le PC charge la routine d’interruption IRQHandler , en fonction du numéro d’interruption transmis par le NVIC.
.section .isr_vector,"a",%progbits
.type g_pfnVectors, %object
.size g_pfnVectors, .-g_pfnVectors
g_pfnVectors:
.word _estack /* MSP reset value */
.word Reset_Handler /* Reset handler */
.word NMI_Handler /* Non Maskable Interrupt */
.word HardFault_Handler /* Hardware fault */
.word MemManage_Handler
.word BusFault_Handler
.word UsageFault_Handler
.word 0
.word 0
.word 0
.word 0
.word SVC_Handler
.word DebugMon_Handler
.word 0
.word PendSV_Handler
.word SysTick_Handler
/* External Interrupts */
.word WWDG_IRQHandler /* Window WatchDog */
.word PVD_IRQHandler /* PVD through EXTI Line detection */
.word TAMP_STAMP_IRQHandler /* Tamper and TimeStamps through the EXTI line */
.word RTC_WKUP_IRQHandler /* RTC Wakeup through the EXTI line */
.word FLASH_IRQHandler /* FLASH */
.word RCC_IRQHandler /* RCC */
.word EXTI0_IRQHandler /* EXTI Line0 */
.word EXTI1_IRQHandler /* EXTI Line1 */
.word EXTI2_IRQHandler /* EXTI Line2 */
.word EXTI3_IRQHandler /* EXTI Line3 */
.word EXTI4_IRQHandler /* EXTI Line4 */
.word DMA1_Stream0_IRQHandler /* DMA1 Stream 0 */
.word DMA1_Stream1_IRQHandler /* DMA1 Stream 1 */
.word DMA1_Stream2_IRQHandler /* DMA1 Stream 2 */
.word DMA1_Stream3_IRQHandler /* DMA1 Stream 3 */
.word DMA1_Stream4_IRQHandler /* DMA1 Stream 4 */
.word DMA1_Stream5_IRQHandler /* DMA1 Stream 5 */
.word DMA1_Stream6_IRQHandler /* DMA1 Stream 6 */
.word ADC_IRQHandler /* ADC1, ADC2 and ADC3s */
.word 0 /* Reserved */
.word 0 /* Reserved */
.word 0 /* Reserved */
.word 0 /* Reserved */
.word EXTI9_5_IRQHandler /* External Line[9:5]s */
.word TIM1_BRK_TIM9_IRQHandler /* TIM1 Break and TIM9 */
.word TIM1_UP_TIM10_IRQHandler /* TIM1 Update and TIM10 */
.word TIM1_TRG_COM_TIM11_IRQHandler /* TIM1 Trigger and Commutation and TIM11 */
.word TIM1_CC_IRQHandler /* TIM1 Capture Compare */
.word TIM2_IRQHandler /* TIM2 */
.word TIM3_IRQHandler /* TIM3 */
.word TIM4_IRQHandler /* TIM4 */
.word I2C1_EV_IRQHandler /* I2C1 Event */
.word I2C1_ER_IRQHandler /* I2C1 Error */
.word I2C2_EV_IRQHandler /* I2C2 Event */
.word I2C2_ER_IRQHandler /* I2C2 Error */
.word SPI1_IRQHandler /* SPI1 */
.word SPI2_IRQHandler /* SPI2 */
.word USART1_IRQHandler /* USART1 */
.word USART2_IRQHandler /* USART2 */
...
Le fichier stm32f4xx_it.c fait le lien entre ces routines d’interruption et une fonction permettant de traiter cette interruption.
void USART2_IRQHandler(void)
{
HAL_USART_IRQHandler(&huart2);
}
void TIM5_IRQHandler(void)
{
HAL_TIM_IRQHandler(&htim5);
}
void EXTI0_IRQHandler(void)
{
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0);
}
...
Cette fonction nous amène alors dans le fichier relatif au périphérique ( ici stm32f4xx_hal_gpio.c ) :
void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin)
{
/* EXTI line interrupt detected */
if((EXTI->PR &GPIO_Pin) != 0)
{
EXTI->PR=(EXTI->PR)&GPIO_Pin;
HAL_GPIO_EXTI_Callback(GPIO_Pin);
}
}
...
On y trouve :
- L’appel de la fonction de callback HAL_GPIO_EXTI_Callback(GPIO_Pin) : cette fonction contient le code utile lié à cette demande d’interruption.
- L’acquittement de l’interruption côté périphérique : ici l’écriture dans le registre PR
La fonction de callback peut figurer dans main.c :
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
static int i = 0;
switch(GPIO_Pin)
{
case GPIO_PIN_0 : i++;
break;
case GPIO_PIN_4 : green_led(1);
break;
case GPIO_PIN_13 : uart_printf(&huart2,(const uint8_t*)"USER BUTTON PUSHED\n\r"); // USER BUTTON
break;
default : break;
}
}
...
Interruption et Changement de Mode :
Lors de la réception du signal IRQ, le processeur bascule dans un mode privilégié ( mode Handler ) donnant accès à l’ensemble des instructions et des registres.
Comme le programme précédent en cours a été interrompu, des registres ( contexte ) sont automatiquement sauvegardés dans la pile du mode Handler :
R0-R3, R12, LR, xPSR
A l’issue du traitement de l’interruption, les valeurs sont alors restituées.
Les interruptions externes
Les interruption externes sont les interruptions liées aux entrées GPIO.
Une chose assez particulière au STM32 : une interruption externe EXTi est déclenchée par une ième broche de n’importe quel port.
Ex: EXTI2 correspond à une interruption liée soit à PA2, soit à PB2, soit à PC2, etc…
Conclusion : PA2 et PB2 ne peuvent pas déclencher tous les deux une interruption externe.
Le Choix du port se fait avec les registres EXTICR ( qui pilotent des multiplexeurs )
Une fois qu’une ligne EXTIi est active, on peut avoir une interruption soir sur front montant, soit sur front descendant.
Le registre IMR ( Interrupt Mask Register ) permet d’authoriser la source d’interruption, afin qu’elle soit enregistrée dans le registre PR (Pending Register)
Le registre PR est relié au NVIC, afin de relayer la demande d’interruption auprès du processeur.
Configuration d’une interruption Externe
Si l’on souhaite que la broche PC0, sur laquelle est reliée le bouton SWITH RIGHT, déclenche une interruption externe, il faut réaliser la configuration suivante dans le fichier stm32f4xx_hal_msp.c.
void HAL_GPIO_SWITCH_RIGHT_EXTI_MspInit()
{
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.Pin = GPIO_PIN_0;
GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
NVIC_SetPriority(EXTI0_IRQn, EXTI0_IRQ_PRIO);
NVIC_EnableIRQ(EXTI0_IRQn);
}
Travaux Pratiques
PROJET SOURCE
WORKSPACE_F411_HAL_STM32CUBE.zip
Interruption Externe : contrôle de l’allumage d’une LED sur interruption liée à un bouton poussoir
Q1. Modifier le projet ci dessus afin d’allumer ou d’éteindre la Led Rouge ( mode toogle ) en appuyant sur Switch Center.
Q2. Même question pour la Led Verte en appuyant sur Switch up
Interruptions d’un Timer : clignotement d’une LED sur interruption liée à un Timer
Pour autoriser les interruption d’un Timer, il faut :
1- Autoriser cette interruption au niveau du NVIC :
void HAL_TIM5_MspInit(void)
{
__TIM5_CLK_ENABLE();
NVIC_SetPriority(TIM5_IRQn, TIM5_IRQ_PRIO);
NVIC_EnableIRQ(TIM5_IRQn);
}
2- Autoriser cette interruption au niveau du périphérique Timer :
Il faudra autoriser l’interruption de type ‘update’ dans le registre DIER du timer.
Q3. Compléter la fonction HAL_TIM_Base_Start_IT() pour :
- Autoriser update interrupt ( registre DIER )
- Réinitialiser le timer à 0 ( registre EGR )
- Acquitter toute demande d’interruption préalable ( forcer le registre SR à 0 )
- Autoriser le comptage ( registre CR1 )
Q4. Compléter le projet afin de :
- Déclencher le comptage du timer 5 après appui sur le bouton switch left ( sur interruption externe )
- Allumer ou éteindre la led bleue ( mode toogle ) à chaque fin de comptage du timer 5 ( toutes les secondes ).
- Suspendre le comptage du timer 5 après appui sur le bouton switch right ( sur interruption externe )
- Remettre à zéro le comptage du timer 5 après appui sur le bouton switch center ( sur interruption externe )