Interfacing IR sensor with the STM32F407 Discovery Kit using External Interrupt

Interfacing IR sensor with the STM32F407 Discovery Kit using External Interrupt

ยท

7 min read

Introduction

Hi !!๐Ÿ˜„ Today we are going to interface an IR (Infrared) Sensor with our STM32F407 Discovery Kit. We will be doing this by using external interrupts and therefore go through the process of setting up an external interrupt on our MCU. The IR sensor is used for detecting objects or things in its vicinity. It sends out a LOW signal from one of its pins when it detects that something is near it. In our application, we are going to toggle an external LED, whenever the IR sensor detects something.

Working Principle of the IR Sensor

The IR Sensor consists of an Infrared Transmitter and an Infrared Receiver, which helps it in detecting an obstacle or object in its neighborhood, it basically act as proximity sensors. It consists of three pins which are VCC, OUT, and GND. There exists an onboard potentiometer, which controls the distance from the transmitter and receiver that needs to be monitored. By turning it we can control its sensitivity. There is also an onboard Obstacle LED on the sensor that glows when the module detects something. The IR transmitter sends out IR waves and they bounce back from an object or an object in its surrounding, which is then captured by the receiver. Whenever there is something surrounding, the module, the OUT pin on the module goes to a LOW state. We are going to use this fact to enable our external interrupt.

Capture.PNG

The IR sensor used here is known as an active IR sensor since it has both an IR emitter and transmitter. Passive IR sensors don't have a transmitter and work with the heat or IR rays emitted by a body, they are also known as PIR (Passive Infrared) sensors.

Components Required

  • An STM32F4 based MCU

  • An IR Sensor module

  • A few Jumper wires

With these components, you can go ahead and start programming your MCU. The connection details are provided in my GitHub repository, whose link I have given at the end of this blog.

Programming our MCU

I have used bare-metal programming for the coding, which involves Bitwise AND/OR/XOR operations on the SFRs (Special Function Registers) present on the MCU.

1. Including the Header Files

#include<stm32f4xx.h>
#define ARM_MATH_CM4

These are the header files included in the main.c file at the top, for the MCU and the math operations required.

2. Declaring the User-Defined Functions

void GPIO_Init();
void EXTI0_Init();
void TIM3_ms_Delay(int delay);
void NVIC_Init();
void EXTI0_IRQHandler();

Here, I have used Timer 3, for generating delays in the code, which is initialized in void TIM3_ms_Delay(int delay). The external interrupt is enabled and initialized in void EXTI0_Init() and void NVIC_Init(). The void GPIO_Init() function is used to initialize the pins for interfacing the sensor. Finally, the void EXTI0_IRQHandler() is the interrupt service routine to perform the actions when an interrupt occurs.

3. Writing the User-Defined Functions

  • Let's start by looking at void GPIO_Init(),
void GPIO_Init(){
    RCC->AHB1ENR |= 1;// Enable the GPIO port A clock
    GPIOA->MODER |= (1<<14); //Configuring PA7 as output if external LED indication is required

    //Since we require PA0 to be input we do no make any changes to GPIOA->MODER
}

First, we enable the clock for PORT A, by writing 1 to the 0th bit of RCC->AHB1ENR register, since we will be using PA0 for the external interrupt and PA7 for the external LED. The EXTI0 or External Interrupt 0 is connected to PA0. After that, we need to set PA7 in Output Mode to toggle the LED when the sensor detects something. This is done after enabling the clock for port A, using the GPIOA->MODER register. We do this by writing 01 to MODER7[1:0] in that register.

Capture6.PNG

Capture.PNG

Capture1.PNG

Capture3.PNG

  • Now, in the void TIM3_ms_Delay(int delay) we set up Timer 3, to give us the required delay which is given as an input to the function.
void TIM3_ms_Delay(int delay){
    RCC->APB1ENR |= (1<<1); //Enable the clock for TIM3
    TIM3->PSC = 16000-1; //Set the clock frequency to 1KHz
    TIM3->ARR = (int)delay; // Get the required delay from user
    TIM3->CNT = 0;
    TIM3->CR1 |= 1; //Start the timer
    while(!(TIM3->SR & 1)){} // Wait for the "Update Interrupt Flag"
    TIM2->SR &= ~(0x0001); //Reset the update interrupt flag // Clear the "Update Interrupt Flag"
}

Firstly, we enable the clock signal for the Timer 3, by writing a 1 to the 1st bit in the RCC->APB1ENR register.

Capture4.PNG

Capture1.PNG

Then we get the value from the user for the Auto-Reload Register, to generate the desired time delay in milliseconds. We set the Prescaler to 16000-1 to get the clock frequency down from 16MHz, which is the default clock frequency, to 1KHz. The TIM3->ARR register only takes int values, hence the delay is converted to int. Then we set the count value of the timer to zero and start the timer with the lines TIM3->CNT = 0; and TIM3->CR1 |= 1; //Start the Timer respectively. After the timer starts ticking we continuously poll the Status register for the Update Interrupt Flag, to check if it's overflowed with the line while(!(TIM3->SR & 1)){} //Wait for the "Update Interrupt Flag". Once, it's overflowed we come out of the while loop and clear the UIF in the Status Register.

Capture6.PNG

Capture7.PNG

Capture8.PNG

Capture9.PNG

  • Then comes the initialization and enabling of the external interrupt,
void EXTI0_Init(){
    EXTI->IMR |= 1;// Interrupt is not masked on EXTI line 0 (Enabled Interrupt)
    EXTI->FTSR |= 1;// Falling Edge trigger Enabled for the interrupt
}

Here, we access the Interrupt Mask Register and enable External Interrupt on line 0 by writing a 1 to the 0th bit of the EXTI->IMR.

Capture2.PNG

After that, we need to configure our interrupt to occur while detecting a Falling Edge, a Rising Edge or both. In our case, the OUT pin of the IR sensor module goes to a LOW state when it detects something, so the output falls, hence we configure the interrupt to fire on a Falling Edge. We achieve this by writing a 1 to the 0th bit of the EXTI->FTSR register.

Capture3.PNG

Now, we need to configure the NVIC or Nested Vector Interrupt Controller, which controls all the interrupts that are enabled.

void NVIC_Init(){
    NVIC->ISER[0] |= 1<<6;// Setting the EXTI0 interrupt
}

Capture4.PNG

We access the NVIC's Interrupt Set-Enable Register, to activate the interrupt based on its priority.

Capture5.PNG

From the table shown above, we can see that the EXTI0 interrupt lies at the 6th position. So, we access the NVIC->ISER[0] register and write a 1 to its 6th bit. Thereby, activating the interrupt.

  • Then, comes the Interrupt Service Routine
void EXTI0_IRQHandler( )
{
    GPIOA->ODR ^= (1<<7);
    EXTI->PR |= 1;
}

The first line in the function is written to toggle the external LED whenever the OUT pin of the sensor module goes to a LOW state, i.e. whenever there the IR sensor detects something in its vicinity. This LED toggling is achieved by carrying out a Bitwise XOR operation with the 7th bit of the GPIOA->ODR register.

Capture7.PNG

Then we clear the pending interrupt in the EXTI->PR register by writing a 1 to its 0th position.

Capture8.PNG

4. Writing the main function

int main(){
    GPIO_Init();
    EXTI0_Init();
    NVIC_Init();
    while(1){
    }
}

In the int main() function we call all the initialization functions that we defined above. Then, in the while loop, we just wait for the interrupt to occur which is serviced in the Interrupt Service Routine.

Conclusion

So, we were successfully able to interface the IR sensor with our STM32F407 Discovery Kit. The reason behind using interrupts is to free up the CPU from menial tasks like polling, which uses up its precious computational resources. These resources instead can be allocated to other useful tasks, that need them. This is where multitasking comes into the picture, which is a necessity for developing complex systems, and using interrupts help us realize them.

I hope you found this article useful and easy to understand๐Ÿ˜ƒ. For the entire code, please visit my GitHub repository by clicking here.

And, the demo video for this tutorial can be found here.

image.png

ย