Robert.H
Robert.H

Reputation: 11

application on stm32l433 stops after first interrupt form timer when running after bootloader

i need some help with my rust application running on a nucleo-64 evaluation board with an stm32L433 MCU. Before the application starts a bootloader checks if the application is present and starts it. In the application I define a timer with an interrupt that increments a counter every second. The application stops when the interrupt is triggered the first time.

If I run the application without bootloader on the MCU everything works as expected.

Can someone explain me why the application stops or give me a hint how to keep the application with enabled timer interrupt running?

Bootloader memory.x

MEMORY
{
  /* NOTE K = KiBi = 1024 bytes */
  FLASH : ORIGIN = 0x8000000, LENGTH = 64K
  RAM : ORIGIN = 0x20000000, LENGTH = 8K
}

bootlaoder main.rs

#![no_main]
#![no_std]

extern crate cortex_m;
#[macro_use]
extern crate cortex_m_rt as rt;
extern crate panic_semihosting;
extern crate stm32l4xx_hal;

use rt::{entry, ExceptionFrame};

const FLASH_USER: u32 = 0x0801_0000;

#[entry]
fn main() -> ! {
    //check if application is present
    let sp = unsafe { (FLASH_USER as *const u32).read() };
    if sp & 0xfffe_0000 == 0x2000_0000 {
        let vt = FLASH_USER as *const u32;
        unsafe {
            cortex_m::asm::bootload(vt);
        }
    }

    panic!("No application found")
}

#[exception]
unsafe fn HardFault(ef: &ExceptionFrame) -> ! {
    panic!("{:#?}", ef);
}

Application memory.x

MEMORY
{
  /* NOTE K = KiBi = 1024 bytes */
  /* Application without bootloader */
  /* FLASH : ORIGIN = 0x8000000, LENGTH = 256K */
  /* Application with bootloader */
  FLASH : ORIGIN = 0x8010000, LENGTH = 192K
  RAM : ORIGIN = 0x20000000, LENGTH = 64K
}

Application main.rs

#![no_main]
#![no_std]

extern crate cortex_m;
#[macro_use]
extern crate cortex_m_rt as rt;
extern crate cortex_m_semihosting;

extern crate panic_semihosting;
extern crate stm32l4xx_hal as hal;

use core::{
    cell::RefCell,
    fmt::Write,
    ops::DerefMut,
    sync::atomic::{AtomicU32, Ordering},
};

use cortex_m::{
    interrupt::{free, Mutex},
    peripheral::NVIC,
};
use cortex_m_semihosting::hio::hstdout;
use hal::{
    delay::Delay,
    interrupt,
    prelude::*,
    timer::{Event, Timer},
};

use rt::{entry, ExceptionFrame};

static COUNT: AtomicU32 = AtomicU32::new(0);
static TIMER_TIM7: Mutex<RefCell<Option<Timer<hal::stm32::TIM7>>>> = Mutex::new(RefCell::new(None));

#[entry]
fn main() -> ! {
    let mut hstdout = hstdout().unwrap();
    writeln!(hstdout, "Hello from main").unwrap();
    let cortex_peripherals = cortex_m::Peripherals::take().unwrap();
    let hal_peripherals = hal::stm32::Peripherals::take().expect("failed to get stm32 peripherals");
    let mut flash = hal_peripherals.FLASH.constrain();
    let mut rcc = hal_peripherals.RCC.constrain();
    let mut pwr = hal_peripherals.PWR.constrain(&mut rcc.apb1r1);
    let clocks = rcc.cfgr.freeze(&mut flash.acr, &mut pwr);
    let mut delay = Delay::new(cortex_peripherals.SYST, clocks);
    unsafe { NVIC::unmask(hal::stm32::Interrupt::TIM7) };
    let mut timer = Timer::tim7(hal_peripherals.TIM7, 1.Hz(), clocks, &mut rcc.apb1r1);
    timer.listen(Event::TimeOut);
    free(|cs| {
        TIMER_TIM7.borrow(cs).replace(Some(timer));
    });

    writeln!(hstdout, "Timer init done!").unwrap();

    let mut last_count = COUNT.load(Ordering::Relaxed);
    loop {
        writeln!(hstdout, "loop").unwrap();
        delay.delay_ms(100_u32);
        let count = COUNT.load(Ordering::Relaxed);
        if last_count != count {
            last_count = count;
            writeln!(hstdout, "count changed").unwrap();
        }
    }
}

#[interrupt]
fn TIM7() {
    let mut hstdout = hstdout().unwrap();
    writeln!(hstdout, "Hello, from TIMER").unwrap();
    free(|cs| {
        if let Some(ref mut tim7) = TIMER_TIM7.borrow(cs).borrow_mut().deref_mut() {
            tim7.clear_interrupt(Event::TimeOut);
        }
    });
    COUNT.fetch_add(1, Ordering::Relaxed);
}

#[exception]
unsafe fn HardFault(ef: &ExceptionFrame) -> ! {
    panic!("{:#?}", ef);
}

The code I use can also be found on https://github.com/robhany/stm32l4-bootloader-test

Upvotes: 0

Views: 224

Answers (2)

Robert.H
Robert.H

Reputation: 11

@the busybee was right. The location of the interrupt vector table needs an update. Before executing cortex_m::asm::bootload(vt); in the bootloader the position of the interrupt vector table needs to be updated with cortex_m::Peripherals::take().unwrap().SCB.vtor.write(FLASH_USER + 4); and interrupts are working inside the application.

Thank you for this hint.

Upvotes: 1

ThongDT
ThongDT

Reputation: 189

Bootloader and application have different interrupt vector table. In the application, if you not config to change interrupt vector table to application vector table, it will jump back to bootloader code and possible cause hang. For handling this, it depend on the microcontroller, we might have several approach as following:

  • If it is able to change ISR table, just change it after jump to application.
  • If not, thing is more complicated since you need to have some special handle in the bootloader to call the handler in application.

Upvotes: 0

Related Questions