Exceptions not working on stm32f103xx

Hi there!

Strangely, my stm32f103xx board (blue pill) is not able to handle exceptions any more. Maybe half a year back I developed firmware for it in rust, which was using exceptions (e.g. for the SysTick). I don`t have access to the source code any more.

Now I am struggling at creating a simple Systick based ms counter. It does not really matter what I do, as soon as the exception fires, the processor stops. It seems like it is never returning from the exception handler, and resuming the main program.

What I have in mind is, that one of the cortex-m, cortex-m-rt, or stm32f1xx crates got updated, and broke something. It is also possible, that the rust compiler has a new bug…

For debugging the issue, I disassembled the produced flash image, everything including interrupt and exception tables looks good.

Sadly I can`t debug it until next week, I do not have a suiting ST-Link / SWD debugger, because I destroyed my last one (trying to flash it with custom rust bootloader -_-).

Example:

#![no_std]
#![no_main]

use core::panic::PanicInfo;
use cortex_m::asm;
use cortex_m_rt::{entry, exception, ExceptionFrame};
use embedded_hal_usb::UsbDevice;
use stm32f1::stm32f103::{CorePeripherals, Peripherals, RCC, SYST};

// Not really correct, but does not matter now
const CORE_SPEED: u32 = 72_000_000;

#[entry]
fn main() -> ! {
    let peripherals = Peripherals::take().unwrap();
    let core_peripherals = CorePeripherals::take().unwrap();

    let gpioa = peripherals.GPIOA;
    let gpioc = peripherals.GPIOC;
    let rcc = peripherals.RCC;
    let mut syst = core_peripherals.SYST;
    let usb = peripherals.USB;
    let nciv = core_peripherals.NVIC;

    // Enable gpio peripheral block clocks
    rcc.apb2enr
        .modify(|_, w| w.iopaen().enabled().iopcen().enabled());

    // Setup LED
    gpioc
        .crh
        .modify(|_, w| w.mode13().output2().cnf13().push_pull());
    gpioc.odr.modify(|_, w| w.odr13().bit(false));

    // setup_clocks(&rcc);

    // let mut usb_device = UsbDevice::<stm32f103xx_usb_hal::UsbDriver>::new(&rcc.apb1enr, usb);
    // usb_device.initialize();

    // Wait for a couple of CPU cycles. apb2enr and gpioa take time to adjust.
    sleep_some();

    setup_systick(&mut syst);

    loop {
        gpioc.odr.modify(|r, w| w.odr13().bit(!r.odr13().bit()));
        for _ in 0..50000 {
            sleep_some();
        }
    }
}

/// Sets sysclk to 72 MHZ
/// USB clock to 48 MHZ
/// Uses HSE with PLL as Sysclock
/// Disables HSI
/// APB1 clock to 36 MHZ, because max
/// Enables all required peripheral clocks (USB, USART, ...)
fn setup_clocks(rcc: &RCC) {
    // Enable PLL and HSE
    rcc.cr.modify(|_, w| w.hseon().on().pllon().on());

    // Wait until PLL and HSE are ready
    while !rcc.cr.read().hserdy().bit_is_set() && !rcc.cr.read().pllrdy().bit_is_set() {}

    // Select PLL as sysclock source, set HSE as PLL source, multiply PLL by 9,
    // divide PLL by 1.5 for usb clock, divide APB1 clock by 2 for 36 MHZ
    rcc.cfgr.modify(|_, w| {
        w.sw()
            .pll()
            .pllsrc()
            .hse_div_prediv()
            .pllmul()
            .mul9()
            .usbpre()
            .div1_5()
            .ppre1()
            .div2()
    });

    // We do not require HSI any more
    rcc.cr.modify(|_, w| w.hsion().clear_bit());
}

fn setup_systick(syst: &mut SYST) {
    syst.set_clock_source(cortex_m::peripheral::syst::SystClkSource::Core);
    // Please ignore the CORE_SPEED counter value. I was to lazy coming up with a large number O.o
    syst.set_reload(CORE_SPEED);
    syst.clear_current();
    syst.enable_counter();
    syst.enable_interrupt();
}

#[exception]
fn SysTick() {
    static mut TEST: usize = 0;

    *TEST += 1;
}

// #[exception]
// fn DefaultHandler(_irq: i16) {
//     return;
// }

#[exception]
fn HardFault(_frame: &ExceptionFrame) -> ! {
    loop {}
}

fn sleep_some() {
    for _ in 0..5 {
        asm::nop();
    }
}

#[panic_handler]
fn panic_impl(_info: &PanicInfo) -> ! {
    asm::bkpt();
    loop {
        asm::nop();
    }
}

I figured it out.

I launched the application from the stm32 uart bootloader. I never did that before, because I always had access to a debugger. Looks like the stm32 bootloader does not relocate the vector table, when booting into user code.

Link: http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0552a/Ciheijba.html

The fix was simple:

    let peripherals = Peripherals::take().unwrap();
    let core_peripherals = CorePeripherals::take().unwrap();

    let gpioa = peripherals.GPIOA;
    let gpioc = peripherals.GPIOC;
    let rcc = peripherals.RCC;
    let mut syst = core_peripherals.SYST;
    let usb = peripherals.USB;
    let nciv = core_peripherals.NVIC;
    let scb = core_peripherals.SCB;

    unsafe {
        scb.vtor.write(0x08000000);
    }

1 Like