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.

livreur1.svg

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.

livreur2.svg

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 ).

nvic.svg

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.

nvic2.svg

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 :

irq_privilege.svg

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

exti.svg

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.

exti_prog_anim.svg


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 :

stm32f4xx_hal_msp.c
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 )