LESSON 03: EXTERNAL INTERRUPT WITH STM32F1

posted in: Microcontroller | 0

1. What is an interrupt?

Interrupts are simply understood as random events that interrupt the course of an ongoing event. To make it easier to understand this new concept, let’s give an example in practice as follows:

For example: During working hours at the company, you are doing daily tasks when suddenly you receive the task of “going to meet customers”. The event “going to meet customers” will be prioritized to do first, then return to daily work. In programming, “going to the customer” is the interrupt program, and the daily work is the main program in the main function.

All interrupts have a priority value that allows the programmer to prioritize which interrupt to handle first if overlapping interrupts occur.

Also in the above example, while you are meeting with a customer and you receive the message “the house is on fire”, then we have to consider the priority. If the “house fire” event has a higher priority, you must go home to put out the fire first, then the “meet customer” event will be performed later.

In the STM32, the management of those interrupts is controlled by the NVIC which roughly translates to the nested interrupt controller.

2. NVIC Nested Interrupt Controller in STM32

NVIC (Nested Vector Interrupt Controller)  is an interrupt management block on ARM Cortex-based microcontrollers. When there is an event and that event is configured to interrupt, all interrupts will be injected into the NVIC block, and based on many factors the NVIC will decide which interrupt should be executed to bring the CPU into the Interrupt Service Routine. .

The system’s basic interrupt blocks include a number of interrupts at the first addresses defined, for example, see the  Interrupt Vector Table  retrieved from the STM32F1 documentation:

The address of the interrupt in the last column (Address), is the address to store the value – this value is the start address of the interrupt service routine of the corresponding interrupt type, also known as the interrupt. function pointer, these interrupt function pointers whose address is located in the  Interrupt Vector Table  are sorted onto the memory area in the startup file.

The interrupt system on ARM Cortex microcontrollers includes interrupt types: system interrupt (exception), GPIO interrupt and software interrupt. For example:

System interrupt: When there is unauthorized access to memory, the program will generate a MemManage_Handler interrupt.

+ GPIO interrupt: 1 GPIO pin PA3 has a low-to-high transition that generates an interrupt.

Software interrupt: When timer 1 overflows, an interrupt is generated.

After the NVIC has decided on the type of interrupt to be performed: The NVIC will save the system state (save the microcontroller’s register) at the time of preparing the interrupt, then force the microcontroller to execute the interrupt. By looking up the interrupt vector table, when all interrupts have been executed, restore the state and continue executing the main program.

NVICs include:

  • 68 interrupts (not including 16 system interrupts)
  • 16 priority interrupts
  • Low latency
  • Ability to manage power for interrupt vector vector

3. External interrupt STM32F103C8T6

External interrupt (EXTI) is also known as external interrupt. An interrupt event that occurs when there is an interference signal from the outside, from the hardware, the user or the peripheral, etc.

The STM32 external interrupt is described as follows:

Parameters (Main Features):

  • Trigger independently on each line interrupts (Line Interrupts)
  • Access to each bit in each interrupt line
  • Generate up to 20 events/interrupts
  • The signal must have a pulse width lower than the clock period of APB2 (because APB2 pulses to the GPIO)

Block diagram of the external interrupt control blocks:

The external interrupt lines are organized as follows:

STM32F103C8T6 has 16 interrupt lines, from line EXTI0 to line EXTI15. Line0 will be common to all Px0 pins in all ports, where x is the name of Port A, B… The next lines will be similar.

The interrupt lines of the STM32F103 chip are allocated to the interrupt vectors as follows:

3. External interrupt programming with ST standard library

In this article, we will configure 2 pushbuttons including pins PB5, PB6 then distinguish those events in the interrupt and then control the LED on and off pins PC13.

  • GPIO configuration program
void LED_init(void){
	
  GPIO_InitTypeDef  GPIO_InitStructure; 		
	
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);	    		
	
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;			  //LED0-->PC.13 
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 	 
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;	 
  GPIO_Init(GPIOC, &GPIO_InitStructure);			     			 
}
  • Interrupt configuration program

void EXTIx_Init(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, EXTI_Trigger_t trigger) {
	GPIO_InitTypeDef GPIO_InitStruct;
	EXTI_InitTypeDef EXTI_InitStruct;
	NVIC_InitTypeDef NVIC_InitStruct;
	uint32_t gpio_clock;
	uint8_t pinsource, portsource, irqchannel;
		/* Enable clock */
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
	
	/* Get proper settings */
	if (GPIOx == GPIOA) {
		gpio_clock = RCC_APB2Periph_GPIOA;
		portsource = GPIO_PortSourceGPIOA;
	} else if (GPIOx == GPIOB) {
		gpio_clock = RCC_APB2Periph_GPIOB;
		portsource = GPIO_PortSourceGPIOB;
	} else if (GPIOx == GPIOC) {
		gpio_clock = RCC_APB2Periph_GPIOC;
		portsource = GPIO_PortSourceGPIOC;
	} else if (GPIOx == GPIOD) {
		gpio_clock = RCC_APB2Periph_GPIOD;
		portsource = GPIO_PortSourceGPIOD;
	} else if (GPIOx == GPIOE) {
		gpio_clock = RCC_APB2Periph_GPIOE;
		portsource = GPIO_PortSourceGPIOE;
	} else if (GPIOx == GPIOF) {
		gpio_clock = RCC_APB2Periph_GPIOF;
		portsource = GPIO_PortSourceGPIOF;
	} else if (GPIOx == GPIOG) {
		gpio_clock = RCC_APB2Periph_GPIOG;
		portsource = GPIO_PortSourceGPIOG;
	} 
	switch (GPIO_Pin) {
		case GPIO_Pin_0:
			pinsource = GPIO_PinSource0;
			irqchannel = EXTI0_IRQn;
			break;
		case GPIO_Pin_1:
			pinsource = GPIO_PinSource1;
			irqchannel = EXTI1_IRQn;
			break;
		case GPIO_Pin_2:
			pinsource = GPIO_PinSource2;
			irqchannel = EXTI2_IRQn;
			break;
		case GPIO_Pin_3:
			pinsource = GPIO_PinSource3;
			irqchannel = EXTI3_IRQn;
			break;
		case GPIO_Pin_4:
			pinsource = GPIO_PinSource4;
			irqchannel = EXTI4_IRQn;
			break;
		case GPIO_Pin_5:
			pinsource = GPIO_PinSource5;
			irqchannel = EXTI9_5_IRQn;
			break;
		case GPIO_Pin_6:
			pinsource = GPIO_PinSource6;
			irqchannel = EXTI9_5_IRQn;
			break;
		case GPIO_Pin_7:
			pinsource = GPIO_PinSource7;
			irqchannel = EXTI9_5_IRQn;
			break;
		case GPIO_Pin_8:
			pinsource = GPIO_PinSource8;
			irqchannel = EXTI9_5_IRQn;
			break;
		case GPIO_Pin_9:
			pinsource = GPIO_PinSource9;
			irqchannel = EXTI9_5_IRQn;
			break;
		case GPIO_Pin_10:
			pinsource = GPIO_PinSource10;
			irqchannel = EXTI15_10_IRQn;
			break;
		case GPIO_Pin_11:
			pinsource = GPIO_PinSource11;
			irqchannel = EXTI15_10_IRQn;
			break;
		case GPIO_Pin_12:
			pinsource = GPIO_PinSource12;
			irqchannel = EXTI15_10_IRQn;
			break;
		case GPIO_Pin_13:
			pinsource = GPIO_PinSource13;
			irqchannel = EXTI15_10_IRQn;
			break;
		case GPIO_Pin_14:
			pinsource = GPIO_PinSource14;
			irqchannel = EXTI15_10_IRQn;
			break;
		case GPIO_Pin_15:
			pinsource = GPIO_PinSource15;
			irqchannel = EXTI15_10_IRQn;
			break;
		default:
			break;
	}
	
	/* Enable clock for GPIO */
	RCC_APB2PeriphClockCmd(gpio_clock, ENABLE);
	
	/* Set pin as input */
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	
	if (trigger == Trigger_Falling) {
		GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
	} else if (trigger == Trigger_Rising) {
		GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPD;
	} else {
		GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	}
	
	/* Initialize pin */
	GPIO_Init(GPIOx, &GPIO_InitStruct);
	
	/* Connect proper GPIO */
	GPIO_EXTILineConfig(portsource, pinsource);
	
	/* Enable EXTI */
	EXTI_InitStruct.EXTI_Line = GPIO_Pin;
	EXTI_InitStruct.EXTI_LineCmd = ENABLE;
	EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;
	EXTI_InitStruct.EXTI_Trigger = (EXTITrigger_TypeDef)trigger;
	EXTI_Init(&EXTI_InitStruct);

	/* Add to NVIC */
	NVIC_InitStruct.NVIC_IRQChannel = irqchannel;
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = EXTI_PRIORITY;
	NVIC_InitStruct.NVIC_IRQChannelSubPriority = pinsource;
	NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_InitStruct);
	switch (GPIO_Pin) {
		case GPIO_Pin_0:
			pinsource = GPIO_PinSource0;
			irqchannel = EXTI0_IRQn;
			break;
		case GPIO_Pin_1:
			pinsource = GPIO_PinSource1;
			irqchannel = EXTI1_IRQn;
			break;
		case GPIO_Pin_2:
			pinsource = GPIO_PinSource2;
			irqchannel = EXTI2_IRQn;
			break;
		case GPIO_Pin_3:
			pinsource = GPIO_PinSource3;
			irqchannel = EXTI3_IRQn;
			break;
		case GPIO_Pin_4:
			pinsource = GPIO_PinSource4;
			irqchannel = EXTI4_IRQn;
			break;
		case GPIO_Pin_5:
			pinsource = GPIO_PinSource5;
			irqchannel = EXTI9_5_IRQn;
			break;
		case GPIO_Pin_6:
			pinsource = GPIO_PinSource6;
			irqchannel = EXTI9_5_IRQn;
			break;
		case GPIO_Pin_7:
			pinsource = GPIO_PinSource7;
			irqchannel = EXTI9_5_IRQn;
			break;
		case GPIO_Pin_8:
			pinsource = GPIO_PinSource8;
			irqchannel = EXTI9_5_IRQn;
			break;
		case GPIO_Pin_9:
			pinsource = GPIO_PinSource9;
			irqchannel = EXTI9_5_IRQn;
			break;
		case GPIO_Pin_10:
			pinsource = GPIO_PinSource10;
			irqchannel = EXTI15_10_IRQn;
			break;
		case GPIO_Pin_11:
			pinsource = GPIO_PinSource11;
			irqchannel = EXTI15_10_IRQn;
			break;
		case GPIO_Pin_12:
			pinsource = GPIO_PinSource12;
			irqchannel = EXTI15_10_IRQn;
			break;
		case GPIO_Pin_13:
			pinsource = GPIO_PinSource13;
			irqchannel = EXTI15_10_IRQn;
			break;
		case GPIO_Pin_14:
			pinsource = GPIO_PinSource14;
			irqchannel = EXTI15_10_IRQn;
			break;
		case GPIO_Pin_15:
			pinsource = GPIO_PinSource15;
			irqchannel = EXTI15_10_IRQn;
			break;
		default:
			break;
	}
	
	/* Enable clock for GPIO */
	RCC_APB2PeriphClockCmd(gpio_clock, ENABLE);
	
	/* Set pin as input */
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	
	if (trigger == Trigger_Falling) {
		GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
	} else if (trigger == Trigger_Rising) {
		GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPD;
	} else {
		GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	}
	
	/* Initialize pin */
	GPIO_Init(GPIOx, &GPIO_InitStruct);
	
	/* Connect proper GPIO */
	GPIO_EXTILineConfig(portsource, pinsource);
	
	/* Enable EXTI */
	EXTI_InitStruct.EXTI_Line = GPIO_Pin;
	EXTI_InitStruct.EXTI_LineCmd = ENABLE;
	EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;
	EXTI_InitStruct.EXTI_Trigger = (EXTITrigger_TypeDef)trigger;
	EXTI_Init(&EXTI_InitStruct);

	/* Add to NVIC */
	NVIC_InitStruct.NVIC_IRQChannel = irqchannel;
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = EXTI_PRIORITY;
	NVIC_InitStruct.NVIC_IRQChannelSubPriority = pinsource;
	NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_InitStruct);
}
  • Interrupt service program
void EXTI9_5_IRQHandler(void)
{
  delay_ms(20);	 
  if(B5 == 1)
  {
  GPIO_SetBits(GPIOC, GPIO_Pin_13);
  }
  if(B6 == 1)
  {
  GPIO_ResetBits(GPIOC, GPIO_Pin_13); 
  }
// clear flag	 EXTI_ClearITPendingBit(EXTI_Line5|EXTI_Line6|EXTI_Line7|EXTI_Line8|EXTI_Line9);  
}		

When the interrupt server is called, we conduct threading to distinguish interrupts on pins PB5 and PB6 with If statements to toggle the Led on pin PC13, at the end of the function we must clear the interrupt flag so that the next interrupt event is emitted. present.

  • Program in function main
int main(void)
 {	
      // Cau hinh GPIO led trên chân PC13
         LED_init();
      // Cau hinh ngat xung canh len tren chan PB6
	 EXTIx_Init(GPIOB, GPIO_Pin_6, Trigger_Rising);
      // Cau hinh ngat xung canh len tren chan PB5
	 EXTIx_Init(GPIOB, GPIO_Pin_5, Trigger_Rising);
	 delay_init();
  while(1)
	{
	}
 }

Sample code link

Leave a Reply

Your email address will not be published.