Skip to content

First attempt into rtfm, init function doubt #218

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
thymbahutymba opened this issue Jul 20, 2019 · 20 comments
Closed

First attempt into rtfm, init function doubt #218

thymbahutymba opened this issue Jul 20, 2019 · 20 comments

Comments

@thymbahutymba
Copy link

thymbahutymba commented Jul 20, 2019

I have the following code:

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

use stm32f1::stm32f103;
use rtfm::app;

#[app(device = stm32f1)]
const APP: () = {
    static mut PERIPHERALS: stm32f103::Peripherals = stm32f103::Peripherals::take().unwrap();
    
    #[init(spawn = [task1, task2], resources = [PERIPHERALS])]
    fn init(c: config::Context) {
        let rcc = c.resources.PERIPHERALS.RCC;
        let gpioc = c.resources.PERIPHERALS.GPIOC;

        rcc.apb2enr.write(|w| w.iopcen().set_bit());
        gpioc.crh.write(|w| unsafe{
            w.mode13().bits(0b11);
            w.cnf13().bits(0b00)
        });

        c.spawn.task1().unwrap();
        c.spawn.task2().unwrap();
    }

    #[idle]
    fn idle() -> ! {
        loop {}
    }

    #[task(resources = [PERIPHERALS])]
    fn task1(c: task1::Context) {
        let gpioc = c.resources.PERIPHERALS.GPIOC;
        loop {
            gpioc.bsrr.write(|w| w.bs13().set_bit());
            cortex_m::asm::delay(2000000);
        }
    }

    #[task(resources = [PERIPHERALS])]
    fn task2(c: task1::Context) {
        let gpioc = c.resources.PERIPHERALS.GPIOC;
        loop {
            gpioc.brr.write(|w| w.br13().set_bit());
            cortex_m::asm::delay(2000000);
        }
    }

    extern "C" {
        fn TIM2();
    }
};

What i get from compilation is the following error:

`init` must have type signature `[unsafe] fn() [-> init::LateResources]`

I don't know how to fix it, I'm reading the examples that seems to have the same syntax of mine.
For now I don't care about the correctness of the tasks

@TeXitoi
Copy link
Collaborator

TeXitoi commented Jul 20, 2019

You can't init the PERIPHERALS resource as is, so you need to use late resources. Also, you can't take ownership of the peripherals, thus take &mut on the needed things.

Also, note that tasks in RTFM are not supposed to do infinite loops, it will block the lower and equal priority tasks from running.

Not tested insight:

const APP: () = {
    static mut PERIPHERALS: stm32f103::Peripherals = ();
    
    #[init(spawn = [task1, task2])]
    fn init(c: config::Context) -> init::LateResources {
        let mut p = stm32f103::Peripherals::take().unwrap()
        let rcc = &mut c.resources.PERIPHERALS.RCC;
        let gpioc = &mut c.resources.PERIPHERALS.GPIOC;

        rcc.apb2enr.write(|w| w.iopcen().set_bit());
        gpioc.crh.write(|w| unsafe{
            w.mode13().bits(0b11);
            w.cnf13().bits(0b00)
        });

        c.spawn.task1().unwrap();
        c.spawn.task2().unwrap();

        init::LateResources {
            PERIPHERALS: p
        }
    }

    // ...
}

@thymbahutymba
Copy link
Author

I still get the same error

error: `init` must have type signature `[unsafe] fn() [-> init::LateResources]`
  --> src/main.rs:14:5
   |
14 |     fn init(c: init::Context) -> init::LateResources {
   |     ^^

I think that the problem is the init function with Context parameter.
Thanks for the advices about the tasks body, I'm new and I'm trying to figure out how to schedule multiple tasks with a given periods (something like round robin scheduler) but is still to early to talk about this.

    ...
    static mut PERIPHERALS: stm32f103::Peripherals = ();
    
    #[init(spawn = [task1, task2])]
    fn init(c: init::Context) -> init::LateResources {
        let p = stm32f103::Peripherals::take().unwrap();

        let rcc = c.resources.PERIPHERALS.RCC;
        let gpioc = c.resources.PERIPHERALS.GPIOC;

        rcc.apb2enr.write(|w| w.iopcen().set_bit());
        gpioc.crh.write(|w| unsafe{
            w.mode13().bits(0b11);
            w.cnf13().bits(0b00)
        });

        c.spawn.task1().unwrap();
        c.spawn.task2().unwrap();

        init::LateResources {
            PERIPHERALS: p
        }
    }
    ...

@TeXitoi
Copy link
Collaborator

TeXitoi commented Jul 20, 2019

ha, sure, no parameter to init in this case:

fn init() -> init::LateResources {
        let p = stm32f103::Peripherals::take().unwrap();

        let rcc = &mut p.RCC;
        let gpioc = &mut p.GPIOC;

        // ...
}

@thymbahutymba
Copy link
Author

Now i get another error that i can't found in the api:

error: `#[panic_handler]` function required, but not found

I think also that the link in description is not up to date: https://japaric.github.io/cortex-m-rtfm/book/en/ because of the link in the README.md: https://japaric.github.io/rtfm5/book/en

@korken89
Copy link
Collaborator

Hi, you are not defining a panic handler. Use one of the ones available on crates.io: https://crates.io/keywords/panic-handler

Eg, add the following to the dependencies of Cargo.toml:

panic-halt = "0.2.0"

and add the following statement in the app:

use panic_halt as _;

This should do the trick! :)

@thymbahutymba
Copy link
Author

Let's start again, what I'm trying to do is a simple led blinking with two task: one for turn on and the other for turn off. I changed the previous version thanks to the advices.
Follows the Cargo.toml file and the code so that you can reproduce it

[package]
name = "stm32_rtfm"
version = "0.1.0"
edition = "2018"

[profile.release]
opt-level = 's'
lto = true
debug = true

[dependencies]
stm32f103xx = "0.11.0"
cortex-m-rtfm = {version = "0.4.3", features = ['timer-queue']}
panic-semihosting = "0.5.2"
#![deny(unsafe_code)]
// #![deny(warnings)]
#![no_main]
#![no_std]

use panic_semihosting as _;
use rtfm::{app, Instant};

const period : u32 = 2_000_000;

#[app(device = stm32f103xx)]
const APP: () = {
    static mut PERIPHERALS: stm32f103::Peripherals = ();
    
    #[init(spawn = [task1])]
    fn init(c: init::Context) -> init::LateResources {
        let p = stm32f103::Peripherals::take().unwrap();

        let rcc = &p.RCC;
        let gpioc = &p.GPIOC;

        rcc.apb2enr.write(|w| w.iopcen().set_bit());
        gpioc.crh.write(|w| unsafe{
            w.mode13().bits(0b11);
            w.cnf13().bits(0b00)
        });
		
	c.spawn.task1().unwrap();

        init::LateResources {
            PERIPHERALS: p
        }
    }

    #[idle]
    fn idle() -> ! {
        loop {}
    }

    #[task(schedule = [task2], resources = [PERIPHERALS])]
    fn task1(c: task1::Context) {
	let now = Instant::now();

        let gpioc = &c.resources.PERIPHERALS.GPIOC;
        
        gpioc.bsrr.write(|w| w.bs13().set_bit());

	c.schedule.task2(now + period.cycles()).unwrap()
    }

    #[task(schedule = [task1], resources = [PERIPHERALS])]
    fn task2(c: task2::Context) {
	let now = Instant::now();
        let gpioc = &c.resources.PERIPHERALS.GPIOC;

        gpioc.brr.write(|w| w.br13().set_bit());

	c.schedule.task1(now + period.cycles()).unwrap()
    }

    extern "C" {
        fn TIM2();
    }
};

What I got from compilation is the following error due to the Context parameter in the init function:

error: `init` must have type signature `[unsafe] fn() [-> init::LateResources]`
  --> src/main.rs:16:5
   |
16 |     fn init(c: init::Context) -> init::LateResources {
   |     ^^

What I've understand is that i need the Context so that the init function can spawn the task1.

What I want to achieve is to have a working code that can execute to my stm32 and see the built in led blinking.

@TeXitoi
Copy link
Collaborator

TeXitoi commented Jul 21, 2019

You're using RTFM 0.4 that doesn't have the context. That's a (not yet released) 0.5 change.

@TeXitoi
Copy link
Collaborator

TeXitoi commented Jul 21, 2019

Also stm32f103xx is deprecated un favor of stm32f1.

@thymbahutymba
Copy link
Author

thymbahutymba commented Jul 21, 2019

Also stm32f103xx is deprecated un favor of stm32f1.

This was an oversight that I did due to all the attempts I made.

You're using RTFM 0.4 that doesn't have the context. That's a (not yet released) 0.5 change.

All the examples that I've read is with Context, what I need is a shared "shared variable" that allow me to access to the struct fields like GPIOX. How I've to modify them?

I've already tried to modify c.resources.PERIPHERALS.GPIOC to resources.PERIPHERALS.GPIOC.

What I get is something like

no field `GPIOC` on type `resources::PERIPHERALS<'_>`

even if the PERIPHERALS variable is of type stm32f103::Peripheral.

Edit:
I found the problem or at least I guess it was the proglem: spawn task on init before return the LateResources

The following code seems to be a working version of blink application for bluepill stm32.

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

use panic_semihosting as _;
use rtfm::{app, Instant};
use stm32f1::stm32f103;

const PERIOD: u32 = 2_000_000;

#[app(device = stm32f1::stm32f103)]
const APP: () = {
    static mut PERIPHERALS: stm32f103::Peripherals = ();

    #[init]
    fn init() -> init::LateResources {
        let p = unsafe { stm32f103::Peripherals::steal() };

        let rcc = &p.RCC;
        let gpioc = &p.GPIOC;

        rcc.apb2enr.write(|w| w.iopcen().set_bit());
        gpioc.crh.write(|w| {
            w.mode13().bits(0b11);
            w.cnf13().bits(0b00)
        });
        gpioc.bsrr.write(|w| w.bs13().set_bit());

        init::LateResources { PERIPHERALS: p }
    }

    #[idle(spawn = [task1])]
    fn idle() -> ! {
	spawn.task1().unwrap();
        loop {}
    }
    
    #[task(schedule = [task2], resources = [PERIPHERALS])]
    fn task1() {
        let now = Instant::now();

	// V1
	let gpioc = &resources.PERIPHERALS.GPIOC;
	gpioc.bsrr.write(|w| w.bs13().set_bit());

	// V2
	/*
        resources
            .PERIPHERALS
            .lock(|p: &mut stm32f103::Peripherals| {
				let gpioc = &p.GPIOC;
                gpioc.bsrr.write(|w| w.bs13().set_bit());
            });
		*/
        let _ = schedule.task2(now + PERIOD.cycles()).unwrap();
    }

    #[task(schedule = [task1], resources = [PERIPHERALS])]
    fn task2() {
        let now = Instant::now();

	// V1
	let gpioc = &resources.PERIPHERALS.GPIOC;
        gpioc.brr.write(|w| w.br13().set_bit());

	// V2
	/*
        resources
            .PERIPHERALS
            .lock(|p: &mut stm32f103::Peripherals| {
                let gpioc = &p.GPIOC;
                gpioc.brr.write(|w| w.br13().set_bit());
            });
		*/
        let _ = schedule.task1(now + PERIOD.cycles()).unwrap();
    }

    extern "C" {
        fn TIM2();
    }
};

I still have some questions about shared resources usage, I should use the lock method before write or I'm able to write safely into the resource without worry about lock? The task can be preempted or it will end its execution without problem?

@korken89
Copy link
Collaborator

korken89 commented Jul 22, 2019

Hi, I went through the code and fixed it up, hope it helps!
The main issue I found was that you use stm32f103::Peripherals::steal(), which is already done by RTFM and hence you get a panic.


Here is a complete example:

Cargo.toml:

[package]
authors = ["Emil Fresk"]
edition = "2018"
readme = "README.md"
license = "MIT OR Apache-2.0"
name = "app"
version = "0.1.0"

[dependencies]
panic-semihosting       = "0.5.2"
cortex-m-rtfm = {version = "0.4.3", features = ['timer-queue']}

[dependencies.stm32f1]
version = "0.7.1"
features = ["stm32f103", "rt"]

# this lets you use `cargo fix`!
[[bin]]
name                    = "app"
test                    = false
bench                   = false

[profile.release]
incremental             = false # disable incremental build to allow lto on nightly
codegen-units           = 1     # better optimizations
debug                   = true  # symbols are nice and they don't increase the size on Flash
lto                     = true  # better optimizations

main.rs:

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

use panic_semihosting as _;
use rtfm::{app, Instant};
use stm32f1::stm32f103 as target;

const PERIOD: u32 = 2_000_000;

#[app(device = stm32f1::stm32f103)]
const APP: () = {
    static mut PERIPHERALS: target::Peripherals = ();

    #[init(spawn = [task1])]
    fn init() -> init::LateResources {
        let p = device;

        let rcc = &p.RCC;
        let gpioc = &p.GPIOC;

        rcc.apb2enr.write(|w| w.iopcen().set_bit());
        gpioc
            .crh
            .write(|w| w.mode13().bits(0b11).cnf13().bits(0b00));

        spawn.task1().unwrap();

        init::LateResources { PERIPHERALS: p }
    }

    #[idle]
    fn idle() -> ! {
        loop {}
    }

    #[task(schedule = [task2], resources = [PERIPHERALS])]
    fn task1() {
        let now = Instant::now();

        let gpioc = &resources.PERIPHERALS.GPIOC;

        gpioc.bsrr.write(|w| w.bs13().set_bit());

        schedule.task2(now + PERIOD.cycles()).unwrap()
    }

    #[task(schedule = [task1], resources = [PERIPHERALS])]
    fn task2() {
        let now = Instant::now();
        let gpioc = &resources.PERIPHERALS.GPIOC;

        gpioc.brr.write(|w| w.br13().set_bit());

        schedule.task1(now + PERIOD.cycles()).unwrap()
    }

    extern "C" {
        fn TIM2();
    }
};

@thymbahutymba
Copy link
Author

Thanks for answer @korken89, my solution is pretty similar yours and is working as expected.

I have a question about your solution that does not work as expected, in my opinion the problem is on the spawn.task1().unwrap(). The init function seems to panic, if I move the spawn to the idle task everything works fine with correct blinking on board.

Btw i think that the problem is solved, just want figure out what what am I doing wrong.

@korken89
Copy link
Collaborator

Can you check what the result is?

@thymbahutymba
Copy link
Author

I don't know how, I'm new in this world. If you teach me what I have to do for checking the result I will do as soon as I can.

I also tried to run on qemu but it is not working.

@korken89
Copy link
Collaborator

You can do something like this if you add cortex-m-semihosting:

let res = spawn.task1();
hprintln!("Result: {:?}", res);

then the debug output should be available.

@thymbahutymba
Copy link
Author

I've modified the application to improve the debug operation, so what following will regard the code below

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

use panic_semihosting as _;
use rtfm::{app, Instant};
use stm32f1::stm32f103;
use cortex_m_semihosting::hprintln;

const PERIOD: u32 = 2_000_000;

#[app(device = stm32f1::stm32f103)]
const APP: () = {
	static mut PERIPHERALS: stm32f103::Peripherals = ();

	#[init(spawn = [turn_on])]
	fn init() -> init::LateResources {
		let p = device;

		let rcc = &p.RCC;
		let gpioc = &p.GPIOC;

		rcc.apb2enr.write(|w| w.iopcen().set_bit());
		gpioc
			.crh
			.write(|w| w.mode13().bits(0b11).cnf13().bits(0b00));
		gpioc.bsrr.write(|w| w.bs13().set_bit());

                // Consider this as version 1 (the wrong one)
		hprintln!("Spawn first task from init: {:?}", spawn.turn_on());

		init::LateResources { PERIPHERALS: p }
	}

	#[idle(spawn = [turn_on])]
	fn idle() -> ! {
                // Consider this as version 2 (the working one)
		// hprintln!("Spawn first task from init: {:?}", spawn.turn_on());
		loop {}
	}

	#[task(schedule = [turn_on], resources = [PERIPHERALS])]
	fn turn_off() {
		let now = Instant::now();

		let gpioc = &resources.PERIPHERALS.GPIOC;
		gpioc.bsrr.write(|w| w.bs13().set_bit());

		hprintln!("Schedule turn on task: {:?}", schedule.turn_on(now + PERIOD.cycles()));
	}

	#[task(schedule = [turn_off], resources = [PERIPHERALS])]
	fn turn_on() {
		let now = Instant::now();

		let gpioc = &resources.PERIPHERALS.GPIOC;
		gpioc.brr.write(|w| w.br13().set_bit());

		hprintln!("Schedule turn off task: {:?}", schedule.turn_off(now + PERIOD.cycles()));
	}

	extern "C" {
		fn TIM2();
	}
};

What I get from the debug of version 1 is:

semihosting is enabled
Spawn first task from init: Ok(())
Schedule turn off task: Ok(())

After these two message the application stop working, no more message are printed.

Instead what appened with version 2 is that:

semihosting is enabled
Schedule turn off task: Ok(())
Spawn first task from init: Ok(())
Schedule turn on task: Ok(())
Schedule turn off task: Ok(())
Schedule turn on task: Ok(())
Schedule turn off task: Ok(())
Schedule turn on task: Ok(())
Schedule turn off task: Ok(())
Schedule turn on task: Ok(())
Schedule turn off task: Ok(())
Schedule turn on task: Ok(())

I don't know if is this that you've requested, if more is needed let me know! Thanks for helping that you are giving to me

@thymbahutymba
Copy link
Author

thymbahutymba commented Jul 28, 2019

I'm trying to going through the problem and get deeper in debug operation. With the version 1, the one that is not working, after the schedule of the turn_off task the application seems freezed. I stop then the debug operation and the what I got is:

HardFault_ (ef=0x20004fb0) at /home/thymbahutymba/.cargo/registry/src/github.com-1ecc6299db9ec823/cortex-m-rt-0.6.9/src/lib.rs:545
545	    loop {

The code related to this freeze is the following:

#[allow(unused_variables)]
#[doc(hidden)]
#[link_section = ".HardFault.default"]
#[no_mangle]
pub unsafe extern "C" fn HardFault_(ef: &ExceptionFrame) -> ! {
    loop {
        // add some side effect to prevent this from turning into a UDF instruction
        // see rust-lang/rust#28728 for details
        atomic::compiler_fence(Ordering::SeqCst);
    }
}

I hope that this may help more than the previous comment and gives you more details about the problem!

Edit
With asm::nop() in the idle loop the application work. The problem is solved but why?

@korken89
Copy link
Collaborator

Ah, you have unfortunately hit a common problem with embedded Rust, see the following list of things to fix during 2019 (point 12): rust-embedded/wg#269 (comment)
The issue is that loop {} gets optimized into an abort instruction, causing a hardfault.

Quite a nasty one if one does not know about it, this is why one usually does not have an idle function unless you plan to put something in the loop.

@korken89
Copy link
Collaborator

Just to be clear, the .unwrap() on spawn was not the issue - the loop {} in idle was. :)

@thymbahutymba
Copy link
Author

Thanks, just last question: there is an official chat like gitter which I can join to due to solve future doubt?

@korken89
Copy link
Collaborator

No problem!

You can join us on Matrix at https://matrix.to/#/#rust-embedded:matrix.org
I'm available there as korken89

andrewgazelka pushed a commit to andrewgazelka/cortex-m-rtic that referenced this issue Nov 3, 2021
218: CI: Add shebangs and address shellcheck warnings r=adamgreig a=jonas-schievink



Co-authored-by: Jonas Schievink <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants