diff --git a/.cargo/config b/.cargo/config index dc06a69..e584d33 100644 --- a/.cargo/config +++ b/.cargo/config @@ -24,4 +24,4 @@ rustflags = [ "-C", "link-arg=-Tlink.x", "-C", "linker=arm-none-eabi-ld", "-Z", "linker-flavor=ld", -] \ No newline at end of file +] diff --git a/.gitignore b/.gitignore index 6dc3db1..6cda3bd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ **/*.rs.bk +.gdb_history Cargo.lock target/ diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..89d2b8c --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,12 @@ +# Change Log + +All notable changes to this project will be documented in this file. +This project adheres to [Semantic Versioning](http://semver.org/). + +## [Unreleased] + +## v0.1.0 - 2017-04-25 + +- Initial release + +[Unreleased]: https://github.com/japaric/cortex-m-quickstart/compare/v0.1.0...HEAD diff --git a/Cargo.toml b/Cargo.toml index bcf475e..6dcbc33 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,13 +1,15 @@ [package] -authors = [{{toml-escape author}}] +authors = ["Jorge Aparicio "] +description = "A template for building applications for ARM Cortex-M microcontrollers" +keywords = ["arm", "cortex-m", "template"] +license = "MIT OR Apache-2.0" name = "cortex-m-quickstart" -version = "0.0.0" +repository = "https://github.com/japaric/cortex-m-quickstart" +version = "0.1.0" -[dev-dependencies] -cortex-m = "0.2.3" -cortex-m-rt = "0.1.0" -cortex-m-srp = { git = "https://github.com/japaric/cortex-m-srp" } -{{name}} = "*" +[dependencies] +cortex-m = "0.2.4" +cortex-m-rt = "0.1.3" [profile.release] -lto = true \ No newline at end of file +lto = true diff --git a/README.md b/README.md index 7597950..cb99f10 100644 --- a/README.md +++ b/README.md @@ -1,41 +1,8 @@ # `cortex-m-quickstart` -> Quickstart template to develop bare metal applications for Cortex-M -> microcontrollers +> A template for building applications for ARM Cortex-M microcontrollers -## Usage - -> **NOTE** The `--template` feature has been removed from Cargo recently. This -> command temporarily rollback to an older Cargo version to run the `new` -> command: - -``` -$ cargo +nightly-2017-04-01 new stm32f100xx --template https://github.com/japaric/cortex-m-quickstart -``` - -Where `stm32f100xx` is the name of the microcontroller family you are -targeting. - -In the Cargo project, you'll have to update the `memory.x` file to reflect the -memory layout of your device. For example, for the microcontroller in the -[STM32VLDISCOVERY] which has 128 KB of Flash memory and 8 KB of RAM: - -[STM32VLDISCOVERY]: http://www.st.com/en/evaluation-tools/stm32vldiscovery.html - -``` -MEMORY -{ - FLASH : ORIGIN = 0x08000000, LENGTH = 128K - RAM : ORIGIN = 0x20000000, LENGTH = 8K -} -``` - -## Supported microcontroller families - -- nrf51 -- stm32f100xx -- stm32f103xx -- stm32f30x +# [Documentation](https://docs.rs/cortex-m-quickstart) # License diff --git a/examples/0-agnostic.rs b/examples/0-agnostic.rs deleted file mode 100644 index 198aa0c..0000000 --- a/examples/0-agnostic.rs +++ /dev/null @@ -1,15 +0,0 @@ -//! Device agnostic "Hello, world!" -//! -//! Prints "Hello, world!" on the OpenOCD console using semihosting - -#![feature(used)] -#![no_std] - -extern crate cortex_m_rt; - -fn main() {} - -#[allow(dead_code)] -#[used] -#[link_section = ".rodata.interrupts"] -static INTERRUPTS: [u32; 240] = [0; 240]; diff --git a/examples/1-hello.rs b/examples/1-hello.rs deleted file mode 100644 index 1473a76..0000000 --- a/examples/1-hello.rs +++ /dev/null @@ -1,25 +0,0 @@ -//! Device specific version of "Hello, world!" -//! -//! Prints "Hello, world!" on the OpenOCD console using semihosting - -#![feature(used)] -#![no_std] - -#[macro_use] -extern crate cortex_m; -extern crate cortex_m_rt; -extern crate {{name}}; - -use {{name}}::interrupt; - -fn main() { - hprintln!("Hello, world!"); -} - -// This is the device specific bit: properly populated interrupt handlers -// Tough we are not using any of them in this example -#[allow(dead_code)] -#[used] -#[link_section = ".rodata.interrupts"] -static INTERRUPTS: interrupt::Handlers = - interrupt::Handlers { ..interrupt::DEFAULT_HANDLERS }; diff --git a/examples/2-itm.rs b/examples/2-itm.rs deleted file mode 100644 index 643b4bc..0000000 --- a/examples/2-itm.rs +++ /dev/null @@ -1,39 +0,0 @@ -//! Sends "Hello, world!" through the ITM port 0 -//! -//! **NOTE** Not all Cortex-M chips support ITM. You'll have to connect your -//! microcontroller's SWO pin to the debug interface. Some development boards -//! don't provide this option. -//! -//! This is faster than using semihosting. -//! -//! You'll need [`itmdump`] to receive the message plus you'll need to enable -//! OpenOCD's ITM support in `.gdbinit`. -//! -//! [`itmdump`]: https://docs.rs/itm/0.1.1/itm/ - -#![feature(used)] -#![no_std] - -#[macro_use] -extern crate cortex_m; -extern crate cortex_m_rt; -extern crate {{name}}; - -use cortex_m::peripheral; -use {{name}}::interrupt; - -fn main() { - cortex_m::interrupt::free( - |cs| { - let itm = peripheral::ITM.borrow(&cs); - - iprintln!(&itm.stim[0], "Hello, world!"); - }, - ); -} - -#[allow(dead_code)] -#[used] -#[link_section = ".rodata.interrupts"] -static INTERRUPTS: interrupt::Handlers = - interrupt::Handlers { ..interrupt::DEFAULT_HANDLERS }; diff --git a/examples/4-srp.rs b/examples/4-srp.rs deleted file mode 100644 index 77c9b33..0000000 --- a/examples/4-srp.rs +++ /dev/null @@ -1,83 +0,0 @@ -//! Stack Resource Policy -//! -//! You should see the following output -//! -//! ``` text -//! IDLE -//! J1: enter -//! J2: enter -//! J2(R1) -//! J2: exit -//! J1: after requesting J2 -//! J1(R1): before requesting J2 -//! J1(R1): after requesting J2 -//! J2: enter -//! J2(R1) -//! J2: exit -//! J1: exit -//! ``` - -#![feature(const_fn)] -#![feature(used)] -#![no_std] - -#[macro_use] -extern crate cortex_m; -extern crate cortex_m_rt; -#[macro_use] -extern crate cortex_m_srp as rtfm; -extern crate {{name}}; - -use rtfm::{C2, C4, C16, P0, P1, P3, Resource}; -// NOTE device dependent -use {{name}}::interrupt::{Exti0Irq, Exti1Irq}; - -static R1: Resource<(), C4> = Resource::new(()); -static R2: Resource<(), C2> = Resource::new(()); - -fn init(_prio: P0, _ceil: C16) {} - -fn idle(_prio: P0) -> ! { - hprintln!("IDLE"); - - rtfm::request(j1); - - // Sleep - loop { - rtfm::wfi(); - } -} - -// NOTE Exti0Irq and Exti1Irq are device dependent -tasks!({{name}}, { - j1: (Exti0Irq, P1), - j2: (Exti1Irq, P3), -}); - -fn j1(_task: Exti0Irq, prio: P1) { - hprintln!("J1: enter"); - R2.lock( - &prio, |_, _| { - rtfm::request(j2); - hprintln!("J1: after requesting J2"); - R1.lock( - &prio, |_, _| { - hprintln!("J1(R1): before requesting J2"); - rtfm::request(j2); - hprintln!("J1(R1): after requesting J2"); - } - ); - } - ); - hprintln!("J1: exit"); -} - -fn j2(_task: Exti1Irq, prio: P3) { - hprintln!("J2: enter"); - R1.lock( - &prio, |_, _| { - hprintln!("J2(R1)"); - } - ); - hprintln!("J2: exit"); -} diff --git a/examples/crash.rs b/examples/crash.rs new file mode 100644 index 0000000..f72f08b --- /dev/null +++ b/examples/crash.rs @@ -0,0 +1,58 @@ +//! Debugging a crash (exception) +//! +//! The `cortex-m-rt` crate provides functionality for this through a default +//! exception handler. When an exception is hit, the default handler will +//! trigger a breakpoint and in this debugging context the stacked registers +//! are accessible. +//! +//! In you run the example below, you'll be able to inspect the state of your +//! program under the debugger using these commands: +//! +//! ``` +//! (gdb) # Stacked registers = program state during the crash +//! (gdb) print/x *_sr +//! $1 = cortex_m::exception::StackedRegisters { +//! r0 = 0x2fffffff, +//! r1 = 0x2fffffff, +//! r2 = 0x0, +//! r3 = 0x0, +//! r12 = 0x0, +//! lr = 0x8000443, +//! pc = 0x8000190, +//! xpsr = 0x61000200, +//! } +//! +//! (gdb) # What exception was triggered? +//! (gdb) print _e +//! $2 = cortex_m::exception::Exception::HardFault +//! +//! (gdb) # Where did we come from? +//! (gdb) print _e +//! ``` + +#![feature(used)] +#![no_std] + +extern crate cortex_m; +extern crate cortex_m_rt; + +use core::ptr; + +use cortex_m::asm; + +fn main() { + // Read an invalid memory address + unsafe { + ptr::read_volatile(0x2FFF_FFFF as *const u32); + } +} + +// As we are not using interrupts, we just register a dummy catch all handler +#[allow(dead_code)] +#[used] +#[link_section = ".rodata.interrupts"] +static INTERRUPTS: [extern "C" fn(); 240] = [default_handler; 240]; + +extern "C" fn default_handler() { + asm::bkpt(); +} diff --git a/examples/hello.rs b/examples/hello.rs new file mode 100644 index 0000000..b9c9deb --- /dev/null +++ b/examples/hello.rs @@ -0,0 +1,24 @@ +//! Prints "Hello, world!" on the OpenOCD console using semihosting + +#![feature(used)] +#![no_std] + +#[macro_use] +extern crate cortex_m; +extern crate cortex_m_rt; + +use cortex_m::asm; + +fn main() { + hprintln!("Hello, world!"); +} + +// As we are not using interrupts, we just register a dummy catch all handler +#[allow(dead_code)] +#[used] +#[link_section = ".rodata.interrupts"] +static INTERRUPTS: [extern "C" fn(); 240] = [default_handler; 240]; + +extern "C" fn default_handler() { + asm::bkpt(); +} diff --git a/examples/itm.rs b/examples/itm.rs new file mode 100644 index 0000000..c1e5bfc --- /dev/null +++ b/examples/itm.rs @@ -0,0 +1,41 @@ +//! Sends "Hello, world!" through the ITM port 0 +//! +//! **IMPORTANT** Not all Cortex-M chips support ITM. You'll have to connect the +//! microcontroller's SWO pin to the SWD interface. Note that some development +//! boards don't provide this option. +//! +//! ITM is much faster than semihosting. Like 4 orders of magnitude or so. +//! +//! You'll need [`itmdump`] to receive the message on the host plus you'll need +//! to uncomment OpenOCD's ITM support in `.gdbinit`. +//! +//! [`itmdump`]: https://docs.rs/itm/0.1.1/itm/ + +#![feature(used)] +#![no_std] + +#[macro_use] +extern crate cortex_m; +extern crate cortex_m_rt; + +use cortex_m::{asm, interrupt, peripheral}; + +fn main() { + interrupt::free( + |cs| { + let itm = peripheral::ITM.borrow(&cs); + + iprintln!(&itm.stim[0], "Hello, world!"); + }, + ); +} + +// As we are not using interrupts, we just register a dummy catch all handler +#[allow(dead_code)] +#[used] +#[link_section = ".rodata.interrupts"] +static INTERRUPTS: [extern "C" fn(); 240] = [default_handler; 240]; + +extern "C" fn default_handler() { + asm::bkpt(); +} diff --git a/examples/3-exception-override.rs b/examples/override-exception-handler.rs similarity index 72% rename from examples/3-exception-override.rs rename to examples/override-exception-handler.rs index b346b43..df8bf17 100644 --- a/examples/3-exception-override.rs +++ b/examples/override-exception-handler.rs @@ -1,19 +1,17 @@ //! Overriding an exception //! -//! **NOTE** You have to disable the `cortex-m-rt` crate "exceptions" feature to -//! make this work. +//! **NOTE** You have to disable the `cortex-m-rt` crate's "exceptions" feature +//! to make this work. #![feature(used)] #![no_std] extern crate cortex_m; extern crate cortex_m_rt; -extern crate {{name}}; use core::ptr; use cortex_m::{asm, exception}; -use {{name}}::interrupt; fn main() { unsafe { @@ -37,8 +35,12 @@ static EXCEPTIONS: exception::Handlers = exception::Handlers { ..exception::DEFAULT_HANDLERS }; +// As we are not using interrupts, we just register a dummy catch all handler #[allow(dead_code)] #[used] #[link_section = ".rodata.interrupts"] -static INTERRUPTS: interrupt::Handlers = - interrupt::Handlers { ..interrupt::DEFAULT_HANDLERS }; +static INTERRUPTS: [extern "C" fn(); 240] = [default_handler; 240]; + +extern "C" fn default_handler() { + asm::bkpt(); +} diff --git a/examples/panic.rs b/examples/panic.rs new file mode 100644 index 0000000..caa2c6e --- /dev/null +++ b/examples/panic.rs @@ -0,0 +1,35 @@ +//! Redirecting `panic!` messages +//! +//! The `cortex-m-rt` crate provides two options to redirect `panic!` messages +//! through these two Cargo features: +//! +//! - `panic-over-semihosting`. `panic!` messages will be printed to the OpenOCD +//! console using semihosting. This is slow. +//! +//! - `panic-over-itm`. `panic!` messages will be send through the ITM port 0. +//! This is much faster but requires ITM support on the device. +//! +//! If neither of these options is specified then the `panic!` message will be +//! lost. Note that all `panic!`s will trigger a debugger breakpoint. + +#![feature(used)] +#![no_std] + +extern crate cortex_m; +extern crate cortex_m_rt; + +use cortex_m::asm; + +fn main() { + panic!("Oops"); +} + +// As we are not using interrupts, we just register a dummy catch all handler +#[allow(dead_code)] +#[used] +#[link_section = ".rodata.interrupts"] +static INTERRUPTS: [extern "C" fn(); 240] = [default_handler; 240]; + +extern "C" fn default_handler() { + asm::bkpt(); +} diff --git a/examples/register-interrupt-handler.rs b/examples/register-interrupt-handler.rs new file mode 100644 index 0000000..d324527 --- /dev/null +++ b/examples/register-interrupt-handler.rs @@ -0,0 +1,36 @@ +//! Register an interrupt handler +//! +//! NOTE Requires a device crate generated using `svd2rust` + +#![feature(used)] +#![no_std] + +extern crate cortex_m; +extern crate cortex_m_rt; +// NOTE this is the device crate +extern crate stm32f30x; + +use cortex_m::asm; +use stm32f30x::interrupt; + +fn main() {} + +// NOTE each interrupt handler has a different signature +extern "C" fn my_interrupt_handler(_ctxt: interrupt::Tim7) { + asm::bkpt(); +} + +extern "C" fn another_interrupt_handler(_ctxt: interrupt::Exti0) { + asm::bkpt(); +} + +// Here we override only two interrupt handlers, the rest of interrupt are +// handled by the same interrupt handler +#[allow(dead_code)] +#[used] +#[link_section = ".rodata.interrupts"] +static INTERRUPTS: interrupt::Handlers = interrupt::Handlers { + Tim7: my_interrupt_handler, + Exti0: another_interrupt_handler, + ..interrupt::DEFAULT_HANDLERS +}; diff --git a/gen-examples.sh b/gen-examples.sh new file mode 100644 index 0000000..f1f7e5e --- /dev/null +++ b/gen-examples.sh @@ -0,0 +1,54 @@ +# Converts the examples in the `examples` directory into documentation in the +# `examples` module (`src/examples/*.rs`) + +set -ex + +main() { + local examples=( + hello + itm + panic + crash + register-interrupt-handler + override-exception-handler + ) + + rm -rf src/examples + + mkdir src/examples + + cat >src/examples/mod.rs <<'EOF' +//! Examples +// Auto-generated. Do not modify. +EOF + + local i=0 out= + for ex in ${examples[@]}; do + name=_${i}_${ex//-/_} + out=src/examples/${name}.rs + + echo "pub mod $name;" >> src/examples/mod.rs + + grep '//!' examples/$ex.rs > $out + echo '//!' >> $out + echo '//! ```' >> $out + grep -v '//!' examples/$ex.rs | ( + IFS='' + + while read line; do + echo "//! $line" >> $out; + done + ) + echo '//! ```' >> $out + echo '// Auto-generated. Do not modify.' >> $out + + + chmod -x $out + + i=$(( i + 1 )) + done + + chmod -x src/examples/mod.rs +} + +main diff --git a/memory.x b/memory.x index c327c9d..1ee89d1 100644 --- a/memory.x +++ b/memory.x @@ -1,5 +1,6 @@ MEMORY { - FLASH : ORIGIN = 0xBAAAAAAD, LENGTH = 0 - RAM : ORIGIN = 0xBAAAAAAD, LENGTH = 0 + /* NOTE K = KiBi = 1024 bytes */ + FLASH : ORIGIN = 0xBAAAAAAD, LENGTH = 0K + RAM : ORIGIN = 0xBAAAAAAD, LENGTH = 0K } diff --git a/src/examples/_0_hello.rs b/src/examples/_0_hello.rs new file mode 100644 index 0000000..f54ba72 --- /dev/null +++ b/src/examples/_0_hello.rs @@ -0,0 +1,28 @@ +//! Prints "Hello, world!" on the OpenOCD console using semihosting +//! +//! ``` +//! +//! #![feature(used)] +//! #![no_std] +//! +//! #[macro_use] +//! extern crate cortex_m; +//! extern crate cortex_m_rt; +//! +//! use cortex_m::asm; +//! +//! fn main() { +//! hprintln!("Hello, world!"); +//! } +//! +//! // As we are not using interrupts, we just register a dummy catch all handler +//! #[allow(dead_code)] +//! #[used] +//! #[link_section = ".rodata.interrupts"] +//! static INTERRUPTS: [extern "C" fn(); 240] = [default_handler; 240]; +//! +//! extern "C" fn default_handler() { +//! asm::bkpt(); +//! } +//! ``` +// Auto-generated. Do not modify. diff --git a/src/examples/_1_itm.rs b/src/examples/_1_itm.rs new file mode 100644 index 0000000..0777f0d --- /dev/null +++ b/src/examples/_1_itm.rs @@ -0,0 +1,45 @@ +//! Sends "Hello, world!" through the ITM port 0 +//! +//! **IMPORTANT** Not all Cortex-M chips support ITM. You'll have to connect the +//! microcontroller's SWO pin to the SWD interface. Note that some development +//! boards don't provide this option. +//! +//! ITM is much faster than semihosting. Like 4 orders of magnitude or so. +//! +//! You'll need [`itmdump`] to receive the message on the host plus you'll need +//! to uncomment OpenOCD's ITM support in `.gdbinit`. +//! +//! [`itmdump`]: https://docs.rs/itm/0.1.1/itm/ +//! +//! ``` +//! +//! #![feature(used)] +//! #![no_std] +//! +//! #[macro_use] +//! extern crate cortex_m; +//! extern crate cortex_m_rt; +//! +//! use cortex_m::{asm, interrupt, peripheral}; +//! +//! fn main() { +//! interrupt::free( +//! |cs| { +//! let itm = peripheral::ITM.borrow(&cs); +//! +//! iprintln!(&itm.stim[0], "Hello, world!"); +//! }, +//! ); +//! } +//! +//! // As we are not using interrupts, we just register a dummy catch all handler +//! #[allow(dead_code)] +//! #[used] +//! #[link_section = ".rodata.interrupts"] +//! static INTERRUPTS: [extern "C" fn(); 240] = [default_handler; 240]; +//! +//! extern "C" fn default_handler() { +//! asm::bkpt(); +//! } +//! ``` +// Auto-generated. Do not modify. diff --git a/src/examples/_2_panic.rs b/src/examples/_2_panic.rs new file mode 100644 index 0000000..33a07d6 --- /dev/null +++ b/src/examples/_2_panic.rs @@ -0,0 +1,39 @@ +//! Redirecting `panic!` messages +//! +//! The `cortex-m-rt` crate provides two options to redirect `panic!` messages +//! through these two Cargo features: +//! +//! - `panic-over-semihosting`. `panic!` messages will be printed to the OpenOCD +//! console using semihosting. This is slow. +//! +//! - `panic-over-itm`. `panic!` messages will be send through the ITM port 0. +//! This is much faster but requires ITM support on the device. +//! +//! If neither of these options is specified then the `panic!` message will be +//! lost. Note that all `panic!`s will trigger a debugger breakpoint. +//! +//! ``` +//! +//! #![feature(used)] +//! #![no_std] +//! +//! extern crate cortex_m; +//! extern crate cortex_m_rt; +//! +//! use cortex_m::asm; +//! +//! fn main() { +//! panic!("Oops"); +//! } +//! +//! // As we are not using interrupts, we just register a dummy catch all handler +//! #[allow(dead_code)] +//! #[used] +//! #[link_section = ".rodata.interrupts"] +//! static INTERRUPTS: [extern "C" fn(); 240] = [default_handler; 240]; +//! +//! extern "C" fn default_handler() { +//! asm::bkpt(); +//! } +//! ``` +// Auto-generated. Do not modify. diff --git a/src/examples/_3_crash.rs b/src/examples/_3_crash.rs new file mode 100644 index 0000000..29b1fbd --- /dev/null +++ b/src/examples/_3_crash.rs @@ -0,0 +1,62 @@ +//! Debugging a crash (exception) +//! +//! The `cortex-m-rt` crate provides functionality for this through a default +//! exception handler. When an exception is hit, the default handler will +//! trigger a breakpoint and in this debugging context the stacked registers +//! are accessible. +//! +//! In you run the example below, you'll be able to inspect the state of your +//! program under the debugger using these commands: +//! +//! ``` +//! (gdb) # Stacked registers = program state during the crash +//! (gdb) print/x *_sr +//! $1 = cortex_m::exception::StackedRegisters { +//! r0 = 0x2fffffff, +//! r1 = 0x2fffffff, +//! r2 = 0x0, +//! r3 = 0x0, +//! r12 = 0x0, +//! lr = 0x8000443, +//! pc = 0x8000190, +//! xpsr = 0x61000200, +//! } +//! +//! (gdb) # What exception was triggered? +//! (gdb) print _e +//! $2 = cortex_m::exception::Exception::HardFault +//! +//! (gdb) # Where did we come from? +//! (gdb) print _e +//! ``` +//! +//! ``` +//! +//! #![feature(used)] +//! #![no_std] +//! +//! extern crate cortex_m; +//! extern crate cortex_m_rt; +//! +//! use core::ptr; +//! +//! use cortex_m::asm; +//! +//! fn main() { +//! // Read an invalid memory address +//! unsafe { +//! ptr::read_volatile(0x2FFF_FFFF as *const u32); +//! } +//! } +//! +//! // As we are not using interrupts, we just register a dummy catch all handler +//! #[allow(dead_code)] +//! #[used] +//! #[link_section = ".rodata.interrupts"] +//! static INTERRUPTS: [extern "C" fn(); 240] = [default_handler; 240]; +//! +//! extern "C" fn default_handler() { +//! asm::bkpt(); +//! } +//! ``` +// Auto-generated. Do not modify. diff --git a/src/examples/_4_register_interrupt_handler.rs b/src/examples/_4_register_interrupt_handler.rs new file mode 100644 index 0000000..8c904da --- /dev/null +++ b/src/examples/_4_register_interrupt_handler.rs @@ -0,0 +1,40 @@ +//! Register an interrupt handler +//! +//! NOTE Requires a device crate generated using `svd2rust` +//! +//! ``` +//! +//! #![feature(used)] +//! #![no_std] +//! +//! extern crate cortex_m; +//! extern crate cortex_m_rt; +//! // NOTE this is the device crate +//! extern crate stm32f30x; +//! +//! use cortex_m::asm; +//! use stm32f30x::interrupt; +//! +//! fn main() {} +//! +//! // NOTE each interrupt handler has a different signature +//! extern "C" fn my_interrupt_handler(_ctxt: interrupt::Tim7) { +//! asm::bkpt(); +//! } +//! +//! extern "C" fn another_interrupt_handler(_ctxt: interrupt::Exti0) { +//! asm::bkpt(); +//! } +//! +//! // Here we override only two interrupt handlers, the rest of interrupt are +//! // handled by the same interrupt handler +//! #[allow(dead_code)] +//! #[used] +//! #[link_section = ".rodata.interrupts"] +//! static INTERRUPTS: interrupt::Handlers = interrupt::Handlers { +//! Tim7: my_interrupt_handler, +//! Exti0: another_interrupt_handler, +//! ..interrupt::DEFAULT_HANDLERS +//! }; +//! ``` +// Auto-generated. Do not modify. diff --git a/src/examples/_5_override_exception_handler.rs b/src/examples/_5_override_exception_handler.rs new file mode 100644 index 0000000..8f8dbd7 --- /dev/null +++ b/src/examples/_5_override_exception_handler.rs @@ -0,0 +1,50 @@ +//! Overriding an exception +//! +//! **NOTE** You have to disable the `cortex-m-rt` crate's "exceptions" feature +//! to make this work. +//! +//! ``` +//! +//! #![feature(used)] +//! #![no_std] +//! +//! extern crate cortex_m; +//! extern crate cortex_m_rt; +//! +//! use core::ptr; +//! +//! use cortex_m::{asm, exception}; +//! +//! fn main() { +//! unsafe { +//! // Invalid memory access +//! ptr::read_volatile(0x2FFF_FFFF as *const u32); +//! } +//! } +//! +//! extern "C" fn hard_fault(_: exception::HardFault) { +//! // You'll hit this breakpoint rather than the one in cortex-m-rt +//! asm::bkpt() +//! } +//! +//! // When the "exceptions" feature is disabled, you'll have to provide this symbol +//! #[allow(dead_code)] +//! #[used] +//! #[link_section = ".rodata.exceptions"] +//! static EXCEPTIONS: exception::Handlers = exception::Handlers { +//! // This is the exception handler override +//! hard_fault: hard_fault, +//! ..exception::DEFAULT_HANDLERS +//! }; +//! +//! // As we are not using interrupts, we just register a dummy catch all handler +//! #[allow(dead_code)] +//! #[used] +//! #[link_section = ".rodata.interrupts"] +//! static INTERRUPTS: [extern "C" fn(); 240] = [default_handler; 240]; +//! +//! extern "C" fn default_handler() { +//! asm::bkpt(); +//! } +//! ``` +// Auto-generated. Do not modify. diff --git a/src/examples/mod.rs b/src/examples/mod.rs new file mode 100644 index 0000000..463bc03 --- /dev/null +++ b/src/examples/mod.rs @@ -0,0 +1,8 @@ +//! Examples +// Auto-generated. Do not modify. +pub mod _0_hello; +pub mod _1_itm; +pub mod _2_panic; +pub mod _3_crash; +pub mod _4_register_interrupt_handler; +pub mod _5_override_exception_handler; diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..cda4143 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,95 @@ +//! A template for building applications for ARM Cortex-M microcontrollers +//! +//! # Usage +//! +//! - Clone this crate +//! +//! ``` text +//! $ cargo clone cortex-m-quickstart && cd $_ +//! ``` +//! +//! - Change the crate name, author and version +//! +//! ``` text +//! $ edit Cargo.toml && head $_ +//! [package] +//! authors = ["Jorge Aparicio "] +//! name = "demo" +//! version = "0.1.0" +//! ``` +//! +//! - Specify the memory layout of the target device +//! +//! ``` text +//! $ edit memory.x && cat $_ +//! MEMORY +//! { +//! /* NOTE K = KiBi = 1024 bytes */ +//! FLASH : ORIGIN = 0x08000000, LENGTH = 256K +//! RAM : ORIGIN = 0x20000000, LENGTH = 40K +//! } +//! ``` +//! +//! - Optionally, set a default build target +//! +//! ``` text +//! $ cat >>.cargo/config <<'EOF' +//! [build] +//! target = "thumbv7em-none-eabihf" +//! EOF +//! ``` +//! +//! - Very likely, depend on a device or a BSP (Board Support Package) crate. +//! +//! ``` text +//! # add a device crate, or +//! $ cargo add stm32f30x +//! +//! # add a BSP crate +//! $ cargo add f3 +//! ``` +//! +//! - Write the application or start from one of the examples +//! +//! ``` text +//! $ rm -r src/* && cp examples/hello.rs src/main.rs +//! ``` +//! +//! - Build the application +//! +//! ``` text +//! # if not installed +//! $ cargo install xargo +//! +//! # NOTE this command requires `arm-none-eabi-ld` to be in $PATH +//! $ xargo build --release +//! +//! $ arm-none-eabi-readelf -A target/thumbv7em-none-eabihf/release/demo +//! Attribute Section: aeabi +//! File Attributes +//! Tag_conformance: "2.09" +//! Tag_CPU_arch: v7E-M +//! Tag_CPU_arch_profile: Microcontroller +//! Tag_THUMB_ISA_use: Thumb-2 +//! Tag_FP_arch: VFPv4-D16 +//! Tag_ABI_PCS_GOT_use: direct +//! Tag_ABI_FP_denormal: Needed +//! Tag_ABI_FP_exceptions: Needed +//! Tag_ABI_FP_number_model: IEEE 754 +//! Tag_ABI_align_needed: 8-byte +//! Tag_ABI_align_preserved: 8-byte, except leaf SP +//! Tag_ABI_HardFP_use: SP only +//! Tag_ABI_VFP_args: VFP registers +//! Tag_ABI_optimization_goals: Aggressive Speed +//! Tag_CPU_unaligned_access: v6 +//! Tag_FP_HP_extension: Allowed +//! Tag_ABI_FP_16bit_format: IEEE 754 +//! ``` +//! +//! # Examples +//! +//! Check the [examples module](./examples/index.html) + +#![no_std] + +pub mod examples;