Catching interrupts with RTFM 0.5.0 on STM32F103

I'm trying to get RTFM going on a stm32f103 board. The specific problem is that USART interrupts doesn't seem to be triggered. This is a stripped down version of what I'm working with at the moment:

#![deny(unsafe_code)]
#![no_main]
#![no_std]

use cortex_m_semihosting::{hprintln};
use stm32f1xx_hal::{
    pac::{USART1, Interrupt},
    serial::{self, Serial, Rx, Tx, Config},
    prelude::*
};
use panic_semihosting as _;

#[rtfm::app(device = stm32f1xx_hal::pac, peripherals = true)]
const APP: () = {
    struct Resources {
        rx: Rx<USART1>,
        tx: Tx<USART1>,
    }

    #[init]
    fn init(cx: init::Context) -> init::LateResources {
        hprintln!("Enter init function").unwrap();

        let device = cx.device;
        let _core: cortex_m::Peripherals = cx.core;
        let mut flash = device.FLASH.constrain();
        let mut rcc = device.RCC.constrain();
        let clocks = rcc.cfgr.freeze(&mut flash.acr);
        let mut afio = device.AFIO.constrain(&mut rcc.apb2);

        let mut gpioa = device.GPIOA.split(&mut rcc.apb2);
        let tx_pin = gpioa.pa9.into_alternate_push_pull(&mut gpioa.crh);
        let rx_pin = gpioa.pa10;

        hprintln!("Initialize serial interface").unwrap();
        let mut serial = Serial::usart1(
            device.USART1,
            (tx_pin, rx_pin),
            &mut afio.mapr,
            Config::default()
                .baudrate(115_200.bps())
                .stopbits(serial::StopBits::STOP1)
                .parity_none(),
            clocks,
            &mut rcc.apb2
        );

        serial.listen(stm32f1xx_hal::serial::Event::Rxne);

        let (tx, rx) = serial.split();

        hprintln!("Create pending USART interrupt").unwrap();
        rtfm::pend(Interrupt::USART1);

        hprintln!("Init done").unwrap();
        init::LateResources{
            rx: rx,
            tx: tx
        }
    }

    #[idle(resources = [tx, rx])]
    fn idle(c: idle::Context) -> ! {
        hprintln!("Enter idle function").unwrap();
        c.resources.tx.write('A' as u8).unwrap();

        loop {}
    }

    #[task(binds = USART1)]
    fn usart1(_c: usart1::Context) {
        static mut TIMES: u32 = 0;

        // Safe access to local `static mut` variable
        *TIMES += 1;

        hprintln!(
            "UART0 called {} time{}",
            *TIMES,
            if *TIMES > 1 { "s" } else { "" }
        ).unwrap();
    }
};

Compiling this with Rust (1.41.0) proceeds just fine, running with gdb and openocd produces the following output in the openocd terminal:

target halted due to debug-request, current mode: Thread 
xPSR: 0x01000000 pc: 0x0800227c msp: 0x20005000, semihosting
Info : halted: PC: 0x0800c0b6
Info : halted: PC: 0x08007c52
Enter init function
Initialize serial interface
Create pending USART interrupt
Init done

If I remove rtfm::pend(Interrupt::USART1); in my source file, I will also see the expected Enter idle function and an A written to the serial interface (as seen by cat /dev/ttyACM0).

Any help would be appreciated!

1 Like

It looks like forums are dead, and people split between matrix and discord chats.
You can get hundreds of views, but no replies.
I only can ask you to post solution here if you find it, so if anyone will have the same problem, it will be able to find information from google.

I think you need to add a call to one more interrupt for the system to use

// Interrupt handlers used to dispatch software tasks
    extern "C" {
        fn EXTI2();
    }

Ok, I figured it out: the problem was that the vector table resides at 0x8000700 instead of 0x8000000, as specified in a memory layout file I had inherited from an earlier version of the project. I might need to keep it this way; in that case an relocation using unsafe { core.SCB.vtor.write(0x7000) }; does the trick, otherwise I can adjust the FLASH area in memory.x.

1 Like

This shouldn't be nessecary with RTFM as it usually handles this stuff automatically as far as I know.

I think that for software tasks it is necessary

https://rtfm.rs/0.5/book/en/by-example/tasks.html

If I understand it correctly, that extern C is because software tasks actually uses the interrupts behind the scenes in order to leverage the priority system of the NVIC and thus create an effective scheduler?