turn into a Cargo crate
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,3 +1,4 @@
|
|||||||
**/*.rs.bk
|
**/*.rs.bk
|
||||||
|
.gdb_history
|
||||||
Cargo.lock
|
Cargo.lock
|
||||||
target/
|
target/
|
||||||
|
|||||||
12
CHANGELOG.md
Normal file
12
CHANGELOG.md
Normal file
@@ -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
|
||||||
16
Cargo.toml
16
Cargo.toml
@@ -1,13 +1,15 @@
|
|||||||
[package]
|
[package]
|
||||||
authors = [{{toml-escape author}}]
|
authors = ["Jorge Aparicio <jorge@japaric.io>"]
|
||||||
|
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"
|
name = "cortex-m-quickstart"
|
||||||
version = "0.0.0"
|
repository = "https://github.com/japaric/cortex-m-quickstart"
|
||||||
|
version = "0.1.0"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dependencies]
|
||||||
cortex-m = "0.2.3"
|
cortex-m = "0.2.4"
|
||||||
cortex-m-rt = "0.1.0"
|
cortex-m-rt = "0.1.3"
|
||||||
cortex-m-srp = { git = "https://github.com/japaric/cortex-m-srp" }
|
|
||||||
{{name}} = "*"
|
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
lto = true
|
lto = true
|
||||||
37
README.md
37
README.md
@@ -1,41 +1,8 @@
|
|||||||
# `cortex-m-quickstart`
|
# `cortex-m-quickstart`
|
||||||
|
|
||||||
> Quickstart template to develop bare metal applications for Cortex-M
|
> A template for building applications for ARM Cortex-M microcontrollers
|
||||||
> microcontrollers
|
|
||||||
|
|
||||||
## Usage
|
# [Documentation](https://docs.rs/cortex-m-quickstart)
|
||||||
|
|
||||||
> **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
|
|
||||||
|
|
||||||
# License
|
# License
|
||||||
|
|
||||||
|
|||||||
@@ -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];
|
|
||||||
@@ -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 };
|
|
||||||
@@ -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 };
|
|
||||||
@@ -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");
|
|
||||||
}
|
|
||||||
58
examples/crash.rs
Normal file
58
examples/crash.rs
Normal file
@@ -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();
|
||||||
|
}
|
||||||
24
examples/hello.rs
Normal file
24
examples/hello.rs
Normal file
@@ -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();
|
||||||
|
}
|
||||||
41
examples/itm.rs
Normal file
41
examples/itm.rs
Normal file
@@ -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();
|
||||||
|
}
|
||||||
@@ -1,19 +1,17 @@
|
|||||||
//! Overriding an exception
|
//! Overriding an exception
|
||||||
//!
|
//!
|
||||||
//! **NOTE** You have to disable the `cortex-m-rt` crate "exceptions" feature to
|
//! **NOTE** You have to disable the `cortex-m-rt` crate's "exceptions" feature
|
||||||
//! make this work.
|
//! to make this work.
|
||||||
|
|
||||||
#![feature(used)]
|
#![feature(used)]
|
||||||
#![no_std]
|
#![no_std]
|
||||||
|
|
||||||
extern crate cortex_m;
|
extern crate cortex_m;
|
||||||
extern crate cortex_m_rt;
|
extern crate cortex_m_rt;
|
||||||
extern crate {{name}};
|
|
||||||
|
|
||||||
use core::ptr;
|
use core::ptr;
|
||||||
|
|
||||||
use cortex_m::{asm, exception};
|
use cortex_m::{asm, exception};
|
||||||
use {{name}}::interrupt;
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
unsafe {
|
unsafe {
|
||||||
@@ -37,8 +35,12 @@ static EXCEPTIONS: exception::Handlers = exception::Handlers {
|
|||||||
..exception::DEFAULT_HANDLERS
|
..exception::DEFAULT_HANDLERS
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// As we are not using interrupts, we just register a dummy catch all handler
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[used]
|
#[used]
|
||||||
#[link_section = ".rodata.interrupts"]
|
#[link_section = ".rodata.interrupts"]
|
||||||
static INTERRUPTS: interrupt::Handlers =
|
static INTERRUPTS: [extern "C" fn(); 240] = [default_handler; 240];
|
||||||
interrupt::Handlers { ..interrupt::DEFAULT_HANDLERS };
|
|
||||||
|
extern "C" fn default_handler() {
|
||||||
|
asm::bkpt();
|
||||||
|
}
|
||||||
35
examples/panic.rs
Normal file
35
examples/panic.rs
Normal file
@@ -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();
|
||||||
|
}
|
||||||
36
examples/register-interrupt-handler.rs
Normal file
36
examples/register-interrupt-handler.rs
Normal file
@@ -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
|
||||||
|
};
|
||||||
54
gen-examples.sh
Normal file
54
gen-examples.sh
Normal file
@@ -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
|
||||||
5
memory.x
5
memory.x
@@ -1,5 +1,6 @@
|
|||||||
MEMORY
|
MEMORY
|
||||||
{
|
{
|
||||||
FLASH : ORIGIN = 0xBAAAAAAD, LENGTH = 0
|
/* NOTE K = KiBi = 1024 bytes */
|
||||||
RAM : ORIGIN = 0xBAAAAAAD, LENGTH = 0
|
FLASH : ORIGIN = 0xBAAAAAAD, LENGTH = 0K
|
||||||
|
RAM : ORIGIN = 0xBAAAAAAD, LENGTH = 0K
|
||||||
}
|
}
|
||||||
|
|||||||
28
src/examples/_0_hello.rs
Normal file
28
src/examples/_0_hello.rs
Normal file
@@ -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.
|
||||||
45
src/examples/_1_itm.rs
Normal file
45
src/examples/_1_itm.rs
Normal file
@@ -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.
|
||||||
39
src/examples/_2_panic.rs
Normal file
39
src/examples/_2_panic.rs
Normal file
@@ -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.
|
||||||
62
src/examples/_3_crash.rs
Normal file
62
src/examples/_3_crash.rs
Normal file
@@ -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.
|
||||||
40
src/examples/_4_register_interrupt_handler.rs
Normal file
40
src/examples/_4_register_interrupt_handler.rs
Normal file
@@ -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.
|
||||||
50
src/examples/_5_override_exception_handler.rs
Normal file
50
src/examples/_5_override_exception_handler.rs
Normal file
@@ -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.
|
||||||
8
src/examples/mod.rs
Normal file
8
src/examples/mod.rs
Normal file
@@ -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;
|
||||||
95
src/lib.rs
Normal file
95
src/lib.rs
Normal file
@@ -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 <jorge@japaric.io>"]
|
||||||
|
//! 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;
|
||||||
Reference in New Issue
Block a user