Embedded rust: STM32f103 low level interrupts

Hello.
I'd like to implement low level interrupts with only cortex-m-rt crate for studying purposes. The program should detect rising/falling edge on pa0 and turn led off. I tried to use example from cortex-m-rt-0.6.13/src/lib.rs line 268 and replaced vector table (__INTERRUPTS) for interrupts with my own, but without much success. Here's my full program. Also I commented out __INTERRUPTS from cortex-m-rt/src/lib.rs file to remove duplicates. I tried to search for examples, but wasn't able to find any. Thank you for help.

Program:

Summary
#![no_std]
#![no_main]

use cortex_m_rt::entry;
use panic_reset as _;
#[entry]
fn main() -> ! {

    let nvic: u32 = 0xe000_e100; // NVIC base address. PM0056 page 128

    let nvic_iser = nvic as *mut u32; // NVIC_ISER register. nvic + 0x00 (offset). 
    let nvic_ipr6 = (nvic + 0x0300 + 6) as *mut u8; // NVIC_IPR6 register. nvic + 0x0300 (offset) + 6 (six for exti0)

    unsafe {
        *nvic_ipr6 = 0x0f; // Setup interrupt priority for sixth vector.
        *nvic_iser = 1; // Enabling interrupt
    }

    let exti = 0x4001_0400; // EXTI base address. RM0008 page 51

    let exti_imr = exti as *mut u32; // EXTI_IMR register. exti + 0x00 (offset)
    let exti_emr = (exti + 0x04) as *mut u32; // EXTI_EMR register. exti + 0x04 (offset)

    unsafe {
        *exti_imr &= ! 0x0000_0001; // Masking interrupt request for line 0
        *exti_emr &= ! 0x0000_0001; // Masking event request for line 0
    }

    let rcc = 0x4002_1000; // RCC base address. RM0008 page 50

    let apb2enr = (rcc + 0x18) as *mut u32; // APB2ENR register. rcc + 0x18 (offset)

    let gpioa = 0x4001_0800; // GPIOA base address;
    let gpioa_crl = gpioa as *mut u32; // GPIO_CRL register. gpioa + 0x00 (offset)

    unsafe {
        *apb2enr |= 0x0000_0015; // Enable afio, gpioa, gpioc clocking

        *gpioa_crl &= !0x0000_000f;
        *gpioa_crl |= 0x0000_0004; // Setup PA0 as input floating
    }

    let afio = 0x4001_0000; // AFIO base address. RM0008 page 51.

    let afio_exticr1 = (afio + 0x08) as *mut u32; // AFIO_EXTICR1 register. afio + 0x08 (offset)
    
    unsafe {
        *afio_exticr1 &= !0x0000_0000;
        *afio_exticr1 |= 0x0000_0000; // Select PA0 as source input for EXTI0 
    }

    let exti_rtsr = (exti + 0x08) as *mut u32; // EXTI_RTSR register. exti + 0x08 (offset)
    let exti_ftsr = (exti + 0x0c) as *mut u32; // EXTI_RTSR register. exti + 0x0c (offset)


    unsafe {
        *exti_rtsr |= 0x0000_0001; // Enable rising trigger interrupt for input line 0
        *exti_ftsr |= 0x0000_0001; // Enable falling trigger interrupt for input line 0

        *exti_imr |= 0x0000_0001; // Unmasking interrupt request for line 0
    }

    let gpioc_crh = 0x4001_1004 as *mut u32; // GPIOC_CRH register

    unsafe {
        *gpioc_crh &= !0x00F0_0000; 
        *gpioc_crh |= 0x0020_0000; // Setup PC13 as output
    };

    loop {

        
    }
}

pub union Vector {
    handler: fn(),
    reserved: usize,
}


//declare handler function for default interrupts
fn default_handler() {

}

// decalre handler function for exti0 interrupt
fn exti0() {
    let bsrr = 0x4001_1010 as *mut u32; // GPIOC_BSRR register

    unsafe {
        *bsrr = 0x2000; // Set PC13 high
    }
}


// Replacing vector table from cortex-m-rt v0.6.13 file line 709
#[link_section = ".vector_table.interrupts"]
#[no_mangle]
pub static __INTERRUPTS: [Vector; 60] = [
    Vector { handler: default_handler },
    Vector { handler: default_handler },
    Vector { handler: default_handler },
    Vector { handler: default_handler },
    Vector { handler: default_handler },

    Vector { handler: default_handler },
    Vector { handler: exti0 },
    Vector { handler: default_handler },
    Vector { handler: default_handler },
    Vector { handler: default_handler },

    Vector { handler: default_handler },
    Vector { handler: default_handler },
    Vector { handler: default_handler },
    Vector { handler: default_handler },
    Vector { handler: default_handler },

    Vector { handler: default_handler },
    Vector { handler: default_handler },
    Vector { handler: default_handler },
    Vector { handler: default_handler },
    Vector { handler: default_handler },

    Vector { handler: default_handler },
    Vector { handler: default_handler },
    Vector { handler: default_handler },
    Vector { handler: default_handler },
    Vector { handler: default_handler },

    Vector { handler: default_handler },
    Vector { handler: default_handler },
    Vector { handler: default_handler },
    Vector { handler: default_handler },
    Vector { handler: default_handler },

    Vector { handler: default_handler },
    Vector { handler: default_handler },
    Vector { handler: default_handler },
    Vector { handler: default_handler },
    Vector { handler: default_handler },

    Vector { handler: default_handler },
    Vector { handler: default_handler },
    Vector { handler: default_handler },
    Vector { handler: default_handler },
    Vector { handler: default_handler },

    Vector { handler: default_handler },
    Vector { handler: default_handler },
    Vector { handler: default_handler },
    Vector { handler: default_handler },
    Vector { handler: default_handler },

    Vector { handler: default_handler },
    Vector { handler: default_handler },
    Vector { handler: default_handler },
    Vector { handler: default_handler },
    Vector { handler: default_handler },

    Vector { handler: default_handler },
    Vector { handler: default_handler },
    Vector { handler: default_handler },
    Vector { handler: default_handler },
    Vector { handler: default_handler },

    Vector { handler: default_handler },
    Vector { handler: default_handler },
    Vector { handler: default_handler },
    Vector { handler: default_handler },
    Vector { handler: default_handler },
];

Can you explain what you mean by "without much success"? Please explain what you expect to happen versus what you are seeing. Often just putting your thoughts into words lets you have that "aha" moment as you realise an assumption you made was incorrect.

Usually, if your interrupt isn't firing it's because you didn't set the trigger up properly. For example, maybe you didn't enable the input pin on and put it in the right mode.

It may also be that your input is firing correctly, but the output pin wasn't configured properly so your LED isn't lighting up.

It could also be something dumb like you aren't triggering the right input (e.g. you are connecting 5v to the wrong input pin) or you've got an off-by-one issue when you put Vector { handler: exti0 } in the __INTERRUPTS vector. You may also want to double-check your LED wired correctly, it is a diode after all and there's nothing more annoying spending hours stepping through your code only to find out you wired something up properly.

Where did all the addresses come from? Was the example you are using intended for an STM32F103?

If possible, try hooking up a debugger and dropping breakpoints on each interrupt hander and the main() function to see exactly what your processor is doing.

If I were troubleshooting something like this, those are the questions I'd ask.

Umm.... huh? It sounds like you've gone into the ~/.cargo/ directory and fiddled with the code cached from crates.io. I usually try to avoid that because that directory is cargo's responsibility and you could confuse its caching mechanism (i.e. you've changed the code, but it doesn't know there was a change so is still using old object files) or break cortex-m-rt in weird and wonderful ways.

Usually you'll use the #[cortex_m_rt::interrupt] attribute to register an interrupt handler.

Completely overriding the interrupt vector may be more involved than "just delete the cortex-m-rt version" because of linker scripts and stuff. I'm not 100% sure how it's all wired up or what would happen if the __INTERRUPTS symbol was declared in another compilation unit.

Thank you for your response.

I mean that I wasn't able to trigger mcu interrupt with rising/falling edge event on pa0. I expect that if I connect pa0 to 3v3 output of my development board (blue pill) rising edge event will trigger. The main problem for me that I'm not really sure what is the correct sequence of steps I need to accomplish to configure interrupts.

I checked them inside main loop, so there should not be any problem at all.

Yeah, something similar happened when I forgot to write endpoint address in USB implementation. But this kind of errors is hard to notice by yourself especially when you are doing it for the first time.

Yes, it is PM0056 reference manual and RM0008 programming manual. I've wrote some info about registers in inline comments. Should've been a little bit more clear a guess.

No, I don't know if I did this correct way, but I pulled crate inside project directory from github and overwrote dependency like here

The [ #[cortex_m_rt::interrupt] ] attribute is what I don't understand the most. Because I'm not able to use it if I don't use device crate. Should I still use this attribute if my handlers is just fn and not extern "C" fn type?

I'd like just to know the minimum required steps to adjust interrupts.

This should be 1 << 6, perhaps?

Yes, thank you. Seems like I was able to receive interrupt.