0
votes

I'm trying to get a fundamental understanding of programming for STM32 microcontrollers. I'm trying to use an external interrupt from a button to toggle the state of an LED, by setting all the appropriate registers (no external libraries/definitions). I can't seem to get the interrupt routine to work, control is never passed to the handler.

Here's what I'm using to set PC-13 (to which there is a button attached as per the Nucleo-G070RB board) as an external interrupt:

RCC_APBENR2  |= 0b1 <<  0; // Enable SYSCFGEN
RCC_IOPENR   |= 0b1 <<  2; // Enable PORTC
EXTI_RTSR1   |= 0b1 << 13; // Enable interrupt on rising edge at EXTI line 13
EXTI_EXTICR4 |= 0x2 <<  8; // Set PC-13 as GPIO pin for interrupt
EXTI_IMR1    |= 0x1 << 13; // Unmask EXTI line 13

NVIC_ISER    |= 0b1 <<  7; // Enable interrupts for EXTI4_15

I'm not sure what I'm missing.

Here's the full code:

#include <stdint.h>
#include <stdio.h>

#define RCC_IOPENR   *((uint32_t volatile *) 0x40021034)
#define RCC_APBENR2  *((uint32_t volatile *) 0x40021040)

#define GPIOA_MODER  *((uint32_t volatile *) 0x50000000)
#define GPIOA_IDR    *((uint32_t volatile *) 0x50000010)
#define GPIOA_ODR    *((uint32_t volatile *) 0x50000014)

#define GPIOC_MODER  *((uint32_t volatile *) 0x50000800)
#define GPIOC_IDR    *((uint32_t volatile *) 0x50000810)
#define GPIOC_ODR    *((uint32_t volatile *) 0x50000814)

#define NVIC_ISER    *((uint32_t volatile *) 0xE000E100)

#define EXTI_RTSR1   *((uint32_t volatile *) 0x40021800)
#define EXTI_EXTICR1 *((uint32_t volatile *) (0x40021800 + 0x060 + 0x4 * 0))
#define EXTI_EXTICR2 *((uint32_t volatile *) (0x40021800 + 0x060 + 0x4 * 1))
#define EXTI_EXTICR3 *((uint32_t volatile *) (0x40021800 + 0x060 + 0x4 * 2))
#define EXTI_EXTICR4 *((uint32_t volatile *) (0x40021800 + 0x060 + 0x4 * 3))
#define EXTI_IMR1    *((uint32_t volatile *) (0x40021800 + 0x080))
#define EXTI_RPR1    *((uint32_t volatile *) (0x40021800 + 0x00C))

void button_init();
extern void initialise_monitor_handles();

uint8_t volatile g_button_pressed = 0;

int main(void) {
    //asm volatile ("cpsie i");
    initialise_monitor_handles();

    //printf("Hello World!\n");

    RCC_IOPENR  |= 0b1 << 0;         // Enable PORTA
    GPIOA_MODER &= ~(0b11 << (2*5)); // Clear MODE bits for 5th pin (PA-5)
    GPIOA_MODER |= 0b1 << (2*5);     // Set MODE bits for 5th pin (PA-5) to 01 (set output)
    GPIOC_MODER &= ~(0b11 << (2*13)); // Clear MODE bits for 13th pin (PC-13) (set input)

    button_init();

    while(1) {
        if (g_button_pressed) {
            GPIOA_ODR ^= 0b1 << 5;
            printf("Button pressed\n");
            g_button_pressed = 0;
        }
    }
}

void button_init() {
    RCC_APBENR2  |= 0b1 <<  0; // Enable SYSCFGEN
    RCC_IOPENR   |= 0b1 <<  2; // Enable PORTC
    EXTI_RTSR1   |= 0b1 << 13; // Enable interrupt on rising edge at EXTI line 13
    EXTI_EXTICR4 |= 0x2 <<  8; // Set PC-13 as GPIO pin for interrupt
    EXTI_IMR1    |= 0x1 << 13; // Unmask EXTI line 13

    NVIC_ISER    |= 0b1 <<  7; // Enable interrupts for EXTI4_15
}

void EXTI4_15_IRQHandler(void) {
    g_button_pressed = 1;
    printf("Button pressed\n");
    EXTI_RPR1 |= 0xFFFF << 0;
}

I'm using the STM32CubeIDE with the default compiler, and and printing via semihosting with OpenOCD.

So, my question is, am I missing a step or doing something wrong?

1
external should be one of the harder ones, did you first try the systick timer, something that doesnt go through the nvic? swi/svc handler. And then say a timer interrupt that is external to the core but not the chip? to get the basics down and then take it that next level to an external pin?old_timer
As with any interrupt handler development did you poll your way through? starting at the peripheral and working your way toward the core? Understanding the interrupt status and clearing of the interrupt along the way? One layer at a time?old_timer
Where is the address of the interrupt handler loaded into the interrupt vevtor table?Martin James
do not reinvent the wheel and use CMSIS stm32 headers. All registers are already defined there.0___________
@P__J__ , where can I find the CMSIS STM32 headers? I just copied them over from a project generated by STM32CubeMX - is there a better place to get them?Abhimanyu Arora

1 Answers

0
votes

So, I figured out what my dumb mistake was. I was so focused in trying to understand where I had gone wrong in setting up the external interrupt, I didn't really check if I had set up the button correctly.

I tried to set PC13 as an input:

GPIOC_MODER &= ~(0b11 << (2*13)); // Clear MODE bits for 13th pin (PC-13) (set input)

before I called my button_init() function, which enables the clock for GPIOC via:

RCC_IOPENR   |= 0b1 <<  2; // Enable PORTC

Since GPIOC was not enabled, PC13 was not set as an input either, so there was nothing to detect.

Here's the fixed code: (I also updated it to use the CMSIS stm32 headers, as suggested by P__J__)

#define STM32G070xx
#include "stm32g0xx.h"

uint8_t volatile g_button_pressed = 0;

void button_init();
//extern void initialise_monitor_handles();

int main(void) {
//  initialise_monitor_handles();

    //printf("Hello World!\n");
    RCC->IOPENR  |= 0b1 << 0;         // Enable PORTA
    GPIOA->MODER &= ~(0b11 << (2*5)); // Clear MODE bits for 5th pin (PA-5)
    GPIOA->MODER |= 0b1 << (2*5);     // Set MODE bits for 5th pin (PA-5) to 01 (set output)

    button_init();

    while(1) {
        if (g_button_pressed) {
            GPIOA->ODR ^= 0b1 << 5;
//          printf("Button pressed! Yay!\n");
            g_button_pressed = 0;
        }
    }
}

void button_init() {
    // RCC->APBENR2    |= (uint32_t) (0b1 <<  0); // Enable SYSCFGEN
    RCC->IOPENR     |= (uint32_t) (0b1 <<  2); // Enable PORTC
    GPIOC->MODER    &= ~(0b11 << (2*13));      // Clear MODE bits for 13th pin (PC-13) (set input)

    EXTI->RTSR1     |= (uint32_t) (0b1 << 13); // Enable interrupt on rising edge at EXTI line 13
    EXTI->EXTICR[3] |= (uint32_t) (0x2 <<  8); // Set PC-13 as GPIO pin for interrupt
    EXTI->IMR1      |= (uint32_t) (0x1 << 13); // Unmask EXTI line 13

    NVIC->ISER[0]   |= (uint32_t) (0b1 <<  7); // Enable interrupts for EXTI4_15
}

void EXTI4_15_IRQHandler(void) {
    g_button_pressed = 1;
//  printf("Button pressed\n");
    EXTI->RPR1 = 0b1 << 13;
}