move out most of text
see https://github.com/rust-embedded/book/pull/20
This commit is contained in:
@@ -1,4 +1,12 @@
|
||||
[target.thumbv7m-none-eabi]
|
||||
# uncomment this to make `cargo run` execute programs on QEMU
|
||||
# runner = "qemu-system-arm -cpu cortex-m3 -machine lm3s6965evb -nographic -semihosting-config enable=on,target=native -kernel"
|
||||
|
||||
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
||||
# uncomment this to make `cargo run` start a GDB session
|
||||
# NOTE: you may need to change `arm-none-eabi-gdb`
|
||||
# runner = "arm-none-eabi-gdb -x openocd.gdb"
|
||||
|
||||
rustflags = [
|
||||
# LLD (shipped with the Rust toolchain) is used as the default linker
|
||||
"-C", "link-arg=-Tlink.x",
|
||||
@@ -20,4 +28,4 @@ rustflags = [
|
||||
# target = "thumbv6m-none-eabi" # Cortex-M0 and Cortex-M0+
|
||||
target = "thumbv7m-none-eabi" # Cortex-M3
|
||||
# target = "thumbv7em-none-eabi" # Cortex-M4 and Cortex-M7 (no FPU)
|
||||
# target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU)
|
||||
# target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU)
|
||||
|
||||
22
Cargo.toml
22
Cargo.toml
@@ -1,13 +1,17 @@
|
||||
# TODO remove
|
||||
cargo-features = ["edition"]
|
||||
|
||||
[package]
|
||||
edition = "2018"
|
||||
authors = ["{{authors}}"]
|
||||
name = "{{project-name}}"
|
||||
version = "0.1.0"
|
||||
|
||||
[dependencies]
|
||||
cortex-m = "0.5.6"
|
||||
cortex-m-rt = "0.5.3"
|
||||
cortex-m = "0.5.7"
|
||||
cortex-m-rt = "0.6.1"
|
||||
cortex-m-semihosting = "0.3.1"
|
||||
panic-semihosting = "0.4.0"
|
||||
panic-halt = "0.1.3"
|
||||
|
||||
# Uncomment for the panic example.
|
||||
# panic-itm = "0.3.0"
|
||||
@@ -16,11 +20,17 @@ panic-semihosting = "0.4.0"
|
||||
# alloc-cortex-m = "0.3.5"
|
||||
|
||||
# Uncomment for the device example.
|
||||
# [dependencies.stm32f103xx]
|
||||
# [dependencies.stm32f30x]
|
||||
# features = ["rt"]
|
||||
# version = "0.10.0"
|
||||
# version = "0.7.1"
|
||||
|
||||
# this lets you use `cargo fix`!
|
||||
[[bin]]
|
||||
name = "{{project-name}}"
|
||||
test = false
|
||||
bench = false
|
||||
|
||||
[profile.release]
|
||||
codegen-units = 1 # better optimizations
|
||||
debug = true
|
||||
debug = true # symbols are nice and they don't increase the size on Flash
|
||||
lto = true # better optimizations
|
||||
|
||||
464
README.md
464
README.md
@@ -4,14 +4,14 @@
|
||||
|
||||
This project is developed and maintained by the [Cortex-M team][team].
|
||||
|
||||
# [Documentation](https://rust-embedded.github.io/cortex-m-quickstart/cortex_m_quickstart)
|
||||
|
||||
## Dependencies
|
||||
|
||||
To build embedded programs using this template you'll need:
|
||||
|
||||
- Nightly Rust toolchain from 2018-08-28 or newer: `rustup default nightly`
|
||||
- Rust 1.30-beta or nightly. `rustup default beta`
|
||||
|
||||
- The `cargo generate` subcommand: `cargo install cargo-generate`
|
||||
|
||||
- `rust-std` components (pre-compiled `core` crate) for the ARM Cortex-M
|
||||
targets. Run:
|
||||
|
||||
@@ -19,337 +19,56 @@ To build embedded programs using this template you'll need:
|
||||
$ rustup target add thumbv6m-none-eabi thumbv7m-none-eabi thumbv7em-none-eabi thumbv7em-none-eabihf
|
||||
```
|
||||
|
||||
## Non-dependencies
|
||||
## Using this template
|
||||
|
||||
This section list programs that are *not* required to build embedded programs
|
||||
but are useful or necessary for embedded development.
|
||||
**NOTE**: This is the very short version that only covers building programs. For
|
||||
the long version check [the embedded Rust book][book].
|
||||
|
||||
To flash (put the firmware on the target device) and debug embedded programs
|
||||
you'll need these additional programs:
|
||||
[book]: https://rust-embedded.github.io/book/blinky/blinky.html
|
||||
|
||||
<!-- TODO These bullets should link to the debugonomicon, which has instructions -->
|
||||
<!-- on how to install these tools -->
|
||||
0. Before we begin you need to identify some characteristics of the target
|
||||
device as these will be used to configure the project:
|
||||
|
||||
- GDB with ARM support or LLDB, for debugging.
|
||||
- QEMU with ARM support, for running embedded programs on the build machine.
|
||||
- OpenOCD and similar, which make debugging possible at all.
|
||||
- The ARM core. e.g. Cortex-M3.
|
||||
|
||||
To inspect the produced binaries you'll want [`cargo-binutils`].
|
||||
|
||||
[`cargo-binutils`]: https://crates.io/crates/cargo-binutils
|
||||
|
||||
## Usage
|
||||
|
||||
### First timers
|
||||
|
||||
#### Building
|
||||
|
||||
If you are already familiar with the process of building and debugging embedded
|
||||
Rust programs feel free to skip this section!
|
||||
|
||||
To get you familiar with building and debugging embedded Rust programs we'll use
|
||||
the template with its default values to build a program for the LM3S6965, a
|
||||
microcontroller with a Cortex-M3 core that QEMU can emulate.
|
||||
|
||||
In this section we'll use a debugger (GDB or LLDB) and QEMU. Be sure to have
|
||||
them installed!
|
||||
|
||||
1. Initialize the template
|
||||
|
||||
``` console
|
||||
$ cargo generate --git https://github.com/rust-embedded/cortex-m-quickstart
|
||||
```
|
||||
|
||||
2. Build the example "Hello, world!" program
|
||||
|
||||
``` rust
|
||||
// example/hello.rs
|
||||
#![no_main]
|
||||
#![no_std]
|
||||
|
||||
#[macro_use]
|
||||
extern crate cortex_m_rt;
|
||||
extern crate cortex_m_semihosting as sh;
|
||||
extern crate panic_semihosting;
|
||||
|
||||
use core::fmt::Write;
|
||||
|
||||
use sh::hio;
|
||||
|
||||
entry!(main);
|
||||
|
||||
fn main() -> ! {
|
||||
let mut stdout = hio::hstdout().unwrap();
|
||||
writeln!(stdout, "Hello, world!").unwrap();
|
||||
|
||||
loop {}
|
||||
}
|
||||
```
|
||||
|
||||
We'll cross compile the program for the `thumbv7m-none-eabi` target. This target
|
||||
covers Cortex-M3 devices like the one we are targeting.
|
||||
|
||||
``` console
|
||||
$ cargo build --target thumbv7m-none-eabi --example hello
|
||||
```
|
||||
You'll find the output binary in the following path:
|
||||
`target/thumbv7m-none-eabi/debug/examples/hello`. The output is an ELF file.
|
||||
|
||||
If you have `file` installed you can print the file type of the output to
|
||||
confirm it's an ELF file:
|
||||
|
||||
```
|
||||
$ ( cd target/thumbv7m-none-eabi/debug/examples && file hello )
|
||||
hello: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), statically linked, with debug_info, not stripped
|
||||
```
|
||||
|
||||
If you have `cargo-binutils` installed you can look at the ELF header of the
|
||||
output:
|
||||
|
||||
> NOTE `cargo-readelf` will be available in a *future* release of binutils
|
||||
> (v0.1.3)
|
||||
|
||||
``` console
|
||||
$ cargo readelf --example hello -- --file-headers
|
||||
```
|
||||
|
||||
``` text
|
||||
ELF Header:
|
||||
Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
|
||||
Class: ELF32
|
||||
Data: 2's complement, little endian
|
||||
Version: 1 (current)
|
||||
OS/ABI: UNIX - System V
|
||||
ABI Version: 0x0
|
||||
Type: EXEC (Executable file)
|
||||
Machine: ARM
|
||||
Version: 0x1
|
||||
Entry point address: 0x23A3
|
||||
Start of program headers: 52 (bytes into file)
|
||||
Start of section headers: 673340 (bytes into file)
|
||||
Flags: 0x5000200
|
||||
Size of this header: 52 (bytes)
|
||||
Size of program headers: 32 (bytes)
|
||||
Number of program headers: 2
|
||||
Size of section headers: 40 (bytes)
|
||||
Number of section headers: 21
|
||||
Section header string table index: 19
|
||||
```
|
||||
|
||||
If you look at the bottom of the Cargo configuration file (`.cargo/config`)
|
||||
you'll see that the `thumbv7m-none-eabi` target has been set as the default
|
||||
compilation target. This means that you do *not* need to pass the `--target`
|
||||
flag to cross compile because it's already implied. So:
|
||||
|
||||
``` console
|
||||
$ cargo build --example hello
|
||||
```
|
||||
|
||||
Does the same as before.
|
||||
|
||||
3. There's no step 3! Your build is done, but for completeness let's look at how
|
||||
to run this program on QEMU.
|
||||
|
||||
#### Running the program on QEMU
|
||||
|
||||
Execute this command and you are done.
|
||||
|
||||
``` console
|
||||
$ qemu-system-arm \
|
||||
-cpu cortex-m3 \
|
||||
-machine lm3s6965evb \
|
||||
-nographic \
|
||||
-semihosting-config enable=on,target=native \
|
||||
-kernel target/thumbv7m-none-eabi/debug/examples/hello
|
||||
Hello, world!
|
||||
```
|
||||
|
||||
The above command will block the terminal because the `hello` program never
|
||||
ends! To terminate QEMU input this in your terminal: `Ctrl+A`, followed by `X`.
|
||||
|
||||
Let me break down that long command for you:
|
||||
|
||||
- `qemu-system-arm`. This is the QEMU emulator. There are a few variants of
|
||||
these QEMU binaries; this one does full *system* emulation of *ARM* machines
|
||||
-- hence the name.
|
||||
|
||||
- `-cpu cortex-m3`. This tells QEMU to emulate a Cortex-M3 CPU. Specifying the
|
||||
CPU model lets us catch some miscompilation errors: for example, running a
|
||||
program compiled for the Cortex-M4F, which has a hardware FPU, will make QEMU
|
||||
error during its execution.
|
||||
|
||||
- `-machine lm3s6965evb`. This tells QEMU to emulate the LM3S6965EVB, a
|
||||
evaluation board that contains a LM3S6965 microcontroller.
|
||||
|
||||
- `-nographic`. This tells QEMU to not launch its GUI.
|
||||
|
||||
- `-semihosting-config (..)`. This tells QEMU to enable semihosting. Semihosting
|
||||
lets the emulated device, among other things, use the host stdout, stderr and
|
||||
stdin and create files on the host.
|
||||
|
||||
- `-kernel $file`. This tells QEMU which binary to flash (kind of) and run on
|
||||
the emulated machine.
|
||||
|
||||
#### Debugging
|
||||
|
||||
First, we launch a QEMU instance:
|
||||
|
||||
``` console
|
||||
$ qemu-system-arm \
|
||||
-cpu cortex-m3 \
|
||||
-machine lm3s6965evb \
|
||||
-nographic \
|
||||
-semihosting-config enable=on,target=native \
|
||||
-gdb tcp::3333 \
|
||||
-S \
|
||||
-kernel target/thumbv7m-none-eabi/debug/examples/hello
|
||||
```
|
||||
|
||||
Note that this command won't print anything to the console but will block the
|
||||
terminal. We have passed two extra flags this time:
|
||||
|
||||
- `-gdb tcp::3333`. This tells QEMU to wait for a GDB connection on TCP
|
||||
port 3333.
|
||||
|
||||
- `-S`. This tells QEMU to freeze the machine at startup. Without this the
|
||||
program would have reached the end of `main` before we had a chance to launch
|
||||
the debugger!
|
||||
|
||||
Next, we start up LLDB in another terminal:
|
||||
|
||||
``` console
|
||||
$ lldb target/thumbv7m-none-eabi/debug/examples/hello
|
||||
```
|
||||
|
||||
And tell it to connect to the GDB server that QEMU created:
|
||||
|
||||
``` console
|
||||
(lldb) gdb-remote 3333
|
||||
Process 1 stopped
|
||||
* thread #1, stop reason = signal SIGTRAP
|
||||
frame #0: 0x000023a2 hello`Reset at lib.rs:475
|
||||
472
|
||||
473 #[doc(hidden)]
|
||||
474 #[no_mangle]
|
||||
-> 475 pub unsafe extern "C" fn Reset() -> ! {
|
||||
476 extern "C" {
|
||||
477 // This symbol will be provided by the user via the `entry!` macro
|
||||
478 fn main() -> !;
|
||||
```
|
||||
|
||||
You'll see that the process is halted and that the program counter is pointing
|
||||
to a function named `Reset`. That is the reset handler: what Cortex-M cores
|
||||
execute upon booting.
|
||||
|
||||
This reset handler will eventually call our `main` function. Let's skip all the
|
||||
way there using a breakpoint and the `continue` command:
|
||||
|
||||
``` console
|
||||
(lldb) breakpoint set -name hello::main
|
||||
Breakpoint 1: where = hello`hello::main::h9cf19a1378cbd1b8 + 2 at hello.rs:20, address = 0x000006a8
|
||||
|
||||
(lldb) continue
|
||||
Process 1 resuming
|
||||
Process 1 stopped
|
||||
* thread #1, stop reason = breakpoint 1.1
|
||||
frame #0: 0x000006a8 hello`hello::main::h9cf19a1378cbd1b8 at hello.rs:20
|
||||
17 entry!(main);
|
||||
18
|
||||
19 fn main() -> ! {
|
||||
-> 20 let mut stdout = hio::hstdout().unwrap();
|
||||
21 writeln!(stdout, "Hello, world!").unwrap();
|
||||
22
|
||||
23 loop {}
|
||||
```
|
||||
|
||||
We are now close to the code that prints "Hello, world!". Let's move the program
|
||||
forward using the `next` command.
|
||||
|
||||
``` console
|
||||
(lldb) next
|
||||
Process 1 stopped
|
||||
* thread #1, stop reason = step over
|
||||
frame #0: 0x000006be hello`hello::main::h9cf19a1378cbd1b8 at hello.rs:21
|
||||
18
|
||||
19 fn main() -> ! {
|
||||
20 let mut stdout = hio::hstdout().unwrap();
|
||||
-> 21 writeln!(stdout, "Hello, world!").unwrap();
|
||||
22
|
||||
23 loop {}
|
||||
24 }
|
||||
|
||||
(lldb) next
|
||||
Process 1 stopped
|
||||
* thread #1, stop reason = step over
|
||||
frame #0: 0x000006f6 hello`hello::main::h9cf19a1378cbd1b8 at hello.rs:23
|
||||
20 let mut stdout = hio::hstdout().unwrap();
|
||||
21 writeln!(stdout, "Hello, world!").unwrap();
|
||||
22
|
||||
-> 23 loop {}
|
||||
24 }
|
||||
```
|
||||
|
||||
At this point you should see "Hello, world!" printed on the terminal that's
|
||||
running `qemu-system-arm`.
|
||||
|
||||
``` console
|
||||
$ qemu-system-arm (..)
|
||||
Hello, world!
|
||||
```
|
||||
|
||||
That's it! You can now exit `lldb`, which will also cause QEMU to terminate.
|
||||
|
||||
```
|
||||
(lldb) exit
|
||||
Quitting LLDB will kill one or more processes. Do you really want to proceed: [Y/n] y
|
||||
```
|
||||
|
||||
### Not first timers
|
||||
|
||||
#### Building
|
||||
|
||||
This section explains how to configure this template to build programs for some
|
||||
specific Cortex-M device.
|
||||
|
||||
0. Before everything else, you need to know the characteristics of the target
|
||||
device:
|
||||
|
||||
- What's the ARM core? e.g. Cortex-M3.
|
||||
|
||||
- Does the ARM core include an FPU? e.g. the Cortex-M4F does.
|
||||
- Does the ARM core include an FPU? Cortex-M4**F** and Cortex-M7**F** cores do.
|
||||
|
||||
- How much Flash memory and RAM does the target device has? e.g. 40 KB of RAM
|
||||
|
||||
- Where is Flash memory and RAM mapped in the address space? e.g. `0x2000_0000`
|
||||
is common for RAM.
|
||||
- Where are Flash memory and RAM mapped in the address space? e.g. RAM is
|
||||
commonly located at address `0x2000_0000`.
|
||||
|
||||
You should be able to find this information in the data sheet and / or reference
|
||||
manual of your device.
|
||||
You can find this information in the data sheet or the reference manual of your
|
||||
device.
|
||||
|
||||
As an example, we'll use the [STM32F303VCT6] microcontroller. This device has:
|
||||
In this example we'll be using the STM32F3DISCOVERY. This board contains an
|
||||
STM32F303VCT6 microcontroller. This microcontroller has:
|
||||
|
||||
[STM32F303VCT6]: https://www.st.com/en/microcontrollers/stm32f303vc.html
|
||||
- A Cortex-M4F core that includes a single precision FPU
|
||||
|
||||
- a Cortex-M4F core that includes a single precision FPU
|
||||
- 256 KB of Flash located at address 0x0800_0000.
|
||||
|
||||
- 256 KB of Flash located at address `0x0800_0000`.
|
||||
|
||||
- 40 KB of RAM located at address `0x2000_0000`. (There's another RAM region but
|
||||
- 40 KB of RAM located at address 0x2000_0000. (There's another RAM region but
|
||||
for simplicity we'll ignore it).
|
||||
|
||||
1. Initialize the template
|
||||
1. Instantiate the template.
|
||||
|
||||
``` console
|
||||
$ cargo generate --git https://github.com/rust-embedded/cortex-m-quickstart
|
||||
Project Name: app
|
||||
Creating project called `app`...
|
||||
Done! New project created /tmp/app
|
||||
|
||||
$ cd app
|
||||
```
|
||||
|
||||
2. Set the default compilation target in `.cargo/config`.
|
||||
2. Set a default compilation target. There are four options as mentioned at the
|
||||
bottom of `.cargo/config`. For the STM32F303VCT6, which has a Cortex-M4F
|
||||
core, we'll pick the `thumbv7em-none-eabihf` target.
|
||||
|
||||
For the STM32F303VCT6, we pick the `thumbv7em-none-eabihf` target as that covers
|
||||
the Cortex-M4F core.
|
||||
|
||||
``` console
|
||||
$ tail .cargo/config
|
||||
$ tail -n6 .cargo/config
|
||||
```
|
||||
|
||||
``` toml
|
||||
@@ -361,138 +80,25 @@ $ tail .cargo/config
|
||||
target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU)
|
||||
```
|
||||
|
||||
3. Enter the memory region information into `memory.x`.
|
||||
|
||||
As we mentioned before the STM32F303VCT6 has 40 KB of RAM located at address
|
||||
`0x2000_0000` and 256 KB of Flash memory located at address `0x0800_0000`.
|
||||
3. Enter the memory region information into the `memory.x` file.
|
||||
|
||||
``` console
|
||||
$ cat memory.x
|
||||
```
|
||||
|
||||
``` text
|
||||
/* Linker script for the STM32F303VCT6 */
|
||||
MEMORY
|
||||
{
|
||||
/* NOTE K = KiBi = 1024 bytes */
|
||||
/* NOTE 1 K = 1 KiBi = 1024 bytes */
|
||||
FLASH : ORIGIN = 0x08000000, LENGTH = 256K
|
||||
RAM : ORIGIN = 0x20000000, LENGTH = 40K
|
||||
}
|
||||
```
|
||||
|
||||
4. Build one of the examples:
|
||||
4. Build the template application or one of the examples.
|
||||
|
||||
``` console
|
||||
$ cargo build --example hello
|
||||
$ cargo build
|
||||
```
|
||||
|
||||
You are done! You should be able to flash and run this example on your
|
||||
device.
|
||||
|
||||
#### Debugging
|
||||
|
||||
Teaching you how to flash and debug this program on *your* device is out of
|
||||
scope for this document due to the sheer variety of possible hardware / software
|
||||
combinations. But the steps required to flash and debug this program on the
|
||||
[STM32F3DISCOVERY] using OpenOCD are provided below as a reference.
|
||||
|
||||
[STM32F3DISCOVERY]: https://www.st.com/en/evaluation-tools/stm32f3discovery.html
|
||||
|
||||
On a terminal run `openocd` to connect to the ST-LINK on the discovery board.
|
||||
Run this command from the root of the template; `openocd` will pick up the
|
||||
`openocd.cfg` file which indicates which interface file and target file to use.
|
||||
|
||||
``` console
|
||||
$ cat openocd.cfg
|
||||
```
|
||||
|
||||
``` text
|
||||
source [find interface/stlink-v2-1.cfg]
|
||||
source [find target/stm32f3x.cfg]
|
||||
```
|
||||
|
||||
``` console
|
||||
$ openocd
|
||||
Open On-Chip Debugger 0.10.0
|
||||
Licensed under GNU GPL v2
|
||||
For bug reports, read
|
||||
http://openocd.org/doc/doxygen/bugs.html
|
||||
Info : auto-selecting first available session transport "hla_swd". To override use 'transport select <transport>'.
|
||||
adapter speed: 1000 kHz
|
||||
adapter_nsrst_delay: 100
|
||||
Info : The selected transport took over low-level target control. The results might differ compared to plain JTAG/SWD
|
||||
none separate
|
||||
Info : Unable to match requested speed 1000 kHz, using 950 kHz
|
||||
Info : Unable to match requested speed 1000 kHz, using 950 kHz
|
||||
Info : clock speed 950 kHz
|
||||
Info : STLINK v2 JTAG v27 API v2 SWIM v15 VID 0x0483 PID 0x374B
|
||||
Info : using stlink api v2
|
||||
Info : Target voltage: 2.913879
|
||||
Info : stm32f3x.cpu: hardware has 6 breakpoints, 4 watchpoints
|
||||
```
|
||||
|
||||
On another terminal run GDB, also from the root of the template.
|
||||
|
||||
``` console
|
||||
$ cat openocd.gdb
|
||||
```
|
||||
|
||||
``` text
|
||||
target remote :3333
|
||||
|
||||
# print demangled symbols
|
||||
set print asm-demangle on
|
||||
|
||||
# detect unhandled exceptions and hard faults
|
||||
break DefaultHandler
|
||||
break UserHardFault
|
||||
|
||||
monitor arm semihosting enable
|
||||
|
||||
load
|
||||
```
|
||||
|
||||
``` console
|
||||
$ arm-none-eabi-gdb -x openocd.gdb target/thumbv7em-none-eabihf/debug/examples/hello
|
||||
(..)
|
||||
Loading section .vector_table, size 0x400 lma 0x8000000
|
||||
Loading section .text, size 0x21dc lma 0x8000400
|
||||
Loading section .rodata, size 0x6a4 lma 0x80025e0
|
||||
Start address 0x800238c, load size 11392
|
||||
Transfer rate: 17 KB/sec, 3797 bytes/write.
|
||||
|
||||
(gdb) list
|
||||
470 #[no_mangle]
|
||||
471 pub static __RESET_VECTOR: unsafe extern "C" fn() -> ! = Reset;
|
||||
472
|
||||
473 #[doc(hidden)]
|
||||
474 #[no_mangle]
|
||||
475 pub unsafe extern "C" fn Reset() -> ! {
|
||||
476 extern "C" {
|
||||
477 // This symbol will be provided by the user via the `entry!` macro
|
||||
478 fn main() -> !;
|
||||
479
|
||||
```
|
||||
|
||||
The `openocd.gdb` script will connect GDB to OpenOCD and then flash the program
|
||||
into the device. After that you can do a normal debugging session.
|
||||
|
||||
If you `continue` the program past the semihosting write operation you'll see
|
||||
"Hello, world" printed on the OpenOCD console.
|
||||
|
||||
``` console
|
||||
(gdb) continue
|
||||
```
|
||||
|
||||
``` console
|
||||
$ openocd
|
||||
(..)
|
||||
Hello, world!
|
||||
```
|
||||
|
||||
## Next steps
|
||||
|
||||
> TODO point the reader to embedded-hal, awesome-embedded-rust, etc.
|
||||
|
||||
# License
|
||||
|
||||
Licensed under either of
|
||||
|
||||
@@ -11,27 +11,19 @@
|
||||
|
||||
#![feature(alloc)]
|
||||
#![feature(alloc_error_handler)]
|
||||
#![feature(lang_items)]
|
||||
#![no_main]
|
||||
#![no_std]
|
||||
|
||||
// This is the allocator crate; you can use a different one
|
||||
extern crate alloc_cortex_m;
|
||||
#[macro_use]
|
||||
extern crate alloc;
|
||||
extern crate cortex_m;
|
||||
#[macro_use]
|
||||
extern crate cortex_m_rt as rt;
|
||||
extern crate cortex_m_semihosting as sh;
|
||||
extern crate panic_semihosting;
|
||||
extern crate panic_halt;
|
||||
|
||||
use core::alloc::Layout;
|
||||
use core::fmt::Write;
|
||||
|
||||
use alloc::vec;
|
||||
use alloc_cortex_m::CortexMHeap;
|
||||
use cortex_m::asm;
|
||||
use rt::ExceptionFrame;
|
||||
use sh::hio;
|
||||
use cortex_m_rt::entry;
|
||||
use cortex_m_semihosting::hio;
|
||||
|
||||
// this is the allocator the application will use
|
||||
#[global_allocator]
|
||||
@@ -39,11 +31,10 @@ static ALLOCATOR: CortexMHeap = CortexMHeap::empty();
|
||||
|
||||
const HEAP_SIZE: usize = 1024; // in bytes
|
||||
|
||||
entry!(main);
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
// Initialize the allocator BEFORE you use it
|
||||
unsafe { ALLOCATOR.init(rt::heap_start() as usize, HEAP_SIZE) }
|
||||
unsafe { ALLOCATOR.init(cortex_m_rt::heap_start() as usize, HEAP_SIZE) }
|
||||
|
||||
// Growable array allocated on the heap
|
||||
let xs = vec![0, 1, 2];
|
||||
@@ -56,21 +47,8 @@ fn main() -> ! {
|
||||
|
||||
// define what happens in an Out Of Memory (OOM) condition
|
||||
#[alloc_error_handler]
|
||||
#[no_mangle]
|
||||
pub fn alloc_error(_layout: Layout) -> ! {
|
||||
fn alloc_error(_layout: Layout) -> ! {
|
||||
asm::bkpt();
|
||||
|
||||
loop {}
|
||||
}
|
||||
|
||||
exception!(HardFault, hard_fault);
|
||||
|
||||
fn hard_fault(ef: &ExceptionFrame) -> ! {
|
||||
panic!("HardFault at {:#?}", ef);
|
||||
}
|
||||
|
||||
exception!(*, default_handler);
|
||||
|
||||
fn default_handler(irqn: i16) {
|
||||
panic!("Unhandled exception (IRQn = {})", irqn);
|
||||
}
|
||||
|
||||
@@ -79,36 +79,18 @@
|
||||
#![no_main]
|
||||
#![no_std]
|
||||
|
||||
extern crate cortex_m;
|
||||
#[macro_use]
|
||||
extern crate cortex_m_rt as rt;
|
||||
extern crate panic_semihosting;
|
||||
extern crate panic_halt;
|
||||
|
||||
use core::ptr;
|
||||
|
||||
use rt::ExceptionFrame;
|
||||
|
||||
entry!(main);
|
||||
use cortex_m_rt::entry;
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
unsafe {
|
||||
// read an address outside of the RAM region; causes a HardFault exception
|
||||
// read an address outside of the RAM region; this causes a HardFault exception
|
||||
ptr::read_volatile(0x2FFF_FFFF as *const u32);
|
||||
}
|
||||
|
||||
loop {}
|
||||
}
|
||||
|
||||
// define the hard fault handler
|
||||
exception!(HardFault, hard_fault);
|
||||
|
||||
fn hard_fault(ef: &ExceptionFrame) -> ! {
|
||||
panic!("HardFault at {:#?}", ef);
|
||||
}
|
||||
|
||||
// define the default exception handler
|
||||
exception!(*, default_handler);
|
||||
|
||||
fn default_handler(irqn: i16) {
|
||||
panic!("Unhandled exception (IRQn = {})", irqn);
|
||||
}
|
||||
|
||||
@@ -24,23 +24,17 @@
|
||||
#![no_main]
|
||||
#![no_std]
|
||||
|
||||
extern crate cortex_m;
|
||||
#[macro_use]
|
||||
extern crate cortex_m_rt as rt;
|
||||
extern crate cortex_m_semihosting as sh;
|
||||
#[macro_use]
|
||||
extern crate stm32f103xx;
|
||||
extern crate panic_semihosting;
|
||||
#[allow(unused_extern_crates)]
|
||||
extern crate panic_halt;
|
||||
|
||||
use core::fmt::Write;
|
||||
|
||||
use cortex_m::peripheral::syst::SystClkSource;
|
||||
use rt::ExceptionFrame;
|
||||
use sh::hio::{self, HStdout};
|
||||
use stm32f103xx::Interrupt;
|
||||
|
||||
entry!(main);
|
||||
use cortex_m_rt::entry;
|
||||
use cortex_m_semihosting::hio::{self, HStdout};
|
||||
use stm32f30x::{interrupt, Interrupt};
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
let p = cortex_m::Peripherals::take().unwrap();
|
||||
|
||||
@@ -75,15 +69,3 @@ fn exti0(state: &mut Option<HStdout>) {
|
||||
hstdout.write_str(".").unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
exception!(HardFault, hard_fault);
|
||||
|
||||
fn hard_fault(ef: &ExceptionFrame) -> ! {
|
||||
panic!("HardFault at {:#?}", ef);
|
||||
}
|
||||
|
||||
exception!(*, default_handler);
|
||||
|
||||
fn default_handler(irqn: i16) {
|
||||
panic!("Unhandled exception (IRQn = {})", irqn);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
//! Overriding an exception handler
|
||||
//!
|
||||
//! You can override an exception handler using the [`exception!`][1] macro.
|
||||
//! You can override an exception handler using the [`#[exception]`][1] attribute.
|
||||
//!
|
||||
//! [1]: https://docs.rs/cortex-m-rt/0.5.0/cortex_m_rt/macro.exception.html
|
||||
//! [1]: https://rust-embedded.github.io/cortex-m-rt/0.6.1/cortex_m_rt_macros/fn.exception.html
|
||||
//!
|
||||
//! ---
|
||||
|
||||
@@ -10,21 +10,16 @@
|
||||
#![no_main]
|
||||
#![no_std]
|
||||
|
||||
extern crate cortex_m;
|
||||
#[macro_use]
|
||||
extern crate cortex_m_rt as rt;
|
||||
extern crate cortex_m_semihosting as sh;
|
||||
extern crate panic_semihosting;
|
||||
extern crate panic_halt;
|
||||
|
||||
use core::fmt::Write;
|
||||
|
||||
use cortex_m::peripheral::syst::SystClkSource;
|
||||
use cortex_m::Peripherals;
|
||||
use rt::ExceptionFrame;
|
||||
use sh::hio::{self, HStdout};
|
||||
|
||||
entry!(main);
|
||||
use cortex_m_rt::{entry, exception};
|
||||
use cortex_m_semihosting::hio::{self, HStdout};
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
let p = Peripherals::take().unwrap();
|
||||
let mut syst = p.SYST;
|
||||
@@ -38,27 +33,15 @@ fn main() -> ! {
|
||||
loop {}
|
||||
}
|
||||
|
||||
// try commenting out this line: you'll end in `default_handler` instead of in `sys_tick`
|
||||
exception!(SysTick, sys_tick, state: Option<HStdout> = None);
|
||||
#[exception]
|
||||
fn SysTick() {
|
||||
static mut STDOUT: Option<HStdout> = None;
|
||||
|
||||
fn sys_tick(state: &mut Option<HStdout>) {
|
||||
if state.is_none() {
|
||||
*state = Some(hio::hstdout().unwrap());
|
||||
if STDOUT.is_none() {
|
||||
*STDOUT = Some(hio::hstdout().unwrap());
|
||||
}
|
||||
|
||||
if let Some(hstdout) = state.as_mut() {
|
||||
if let Some(hstdout) = STDOUT.as_mut() {
|
||||
hstdout.write_str(".").unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
exception!(HardFault, hard_fault);
|
||||
|
||||
fn hard_fault(ef: &ExceptionFrame) -> ! {
|
||||
panic!("HardFault at {:#?}", ef);
|
||||
}
|
||||
|
||||
exception!(*, default_handler);
|
||||
|
||||
fn default_handler(irqn: i16) {
|
||||
panic!("Unhandled exception (IRQn = {})", irqn);
|
||||
}
|
||||
|
||||
@@ -1,24 +1,22 @@
|
||||
//! Prints "Hello, world!" on the OpenOCD console using semihosting
|
||||
//!
|
||||
//! ---
|
||||
//! Prints "Hello, world!" on the host console using semihosting
|
||||
|
||||
#![no_main]
|
||||
#![no_std]
|
||||
|
||||
#[macro_use]
|
||||
extern crate cortex_m_rt;
|
||||
extern crate cortex_m_semihosting as sh;
|
||||
extern crate panic_semihosting;
|
||||
extern crate panic_halt;
|
||||
|
||||
use core::fmt::Write;
|
||||
|
||||
use sh::hio;
|
||||
|
||||
entry!(main);
|
||||
use cortex_m_rt::entry;
|
||||
use cortex_m_semihosting::{debug, hio};
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
let mut stdout = hio::hstdout().unwrap();
|
||||
writeln!(stdout, "Hello, world!").unwrap();
|
||||
|
||||
// exit QEMU or the debugger section
|
||||
debug::exit(debug::EXIT_SUCCESS);
|
||||
|
||||
loop {}
|
||||
}
|
||||
|
||||
@@ -17,38 +17,17 @@
|
||||
#![no_main]
|
||||
#![no_std]
|
||||
|
||||
#[macro_use]
|
||||
extern crate cortex_m;
|
||||
#[macro_use]
|
||||
extern crate cortex_m_rt as rt;
|
||||
extern crate panic_semihosting;
|
||||
extern crate panic_halt;
|
||||
|
||||
use cortex_m::{asm, Peripherals};
|
||||
use rt::ExceptionFrame;
|
||||
|
||||
entry!(main);
|
||||
use cortex_m::{iprintln, Peripherals};
|
||||
use cortex_m_rt::entry;
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
let mut p = Peripherals::take().unwrap();
|
||||
let stim = &mut p.ITM.stim[0];
|
||||
|
||||
iprintln!(stim, "Hello, world!");
|
||||
|
||||
loop {
|
||||
asm::bkpt();
|
||||
}
|
||||
}
|
||||
|
||||
// define the hard fault handler
|
||||
exception!(HardFault, hard_fault);
|
||||
|
||||
fn hard_fault(ef: &ExceptionFrame) -> ! {
|
||||
panic!("HardFault at {:#?}", ef);
|
||||
}
|
||||
|
||||
// define the default exception handler
|
||||
exception!(*, default_handler);
|
||||
|
||||
fn default_handler(irqn: i16) {
|
||||
panic!("Unhandled exception (IRQn = {})", irqn);
|
||||
loop {}
|
||||
}
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
//! Minimal Cortex-M program
|
||||
//!
|
||||
//! When executed this program will hit the breakpoint set in `main`.
|
||||
//!
|
||||
//! All Cortex-M programs need to:
|
||||
//!
|
||||
//! - Contain the `#![no_main]` and `#![no_std]` attributes. Embedded programs don't use the
|
||||
//! standard Rust `main` interface or the Rust standard (`std`) library.
|
||||
//!
|
||||
//! - Define their entry point using [`entry!`] macro.
|
||||
//!
|
||||
//! [`entry!`]: https://docs.rs/cortex-m-rt/~0.5/cortex_m_rt/macro.entry.html
|
||||
//!
|
||||
//! - Define their panicking behavior, i.e. what happens when `panic!` is called. The easiest way to
|
||||
//! define a panicking behavior is to link to a [panic handler crate][0]
|
||||
//!
|
||||
//! [0]: https://crates.io/keywords/panic-impl
|
||||
|
||||
#![no_main] // <- IMPORTANT!
|
||||
#![no_std]
|
||||
|
||||
extern crate cortex_m;
|
||||
|
||||
#[macro_use(entry)]
|
||||
extern crate cortex_m_rt as rt;
|
||||
|
||||
// makes `panic!` print messages to the host stderr using semihosting
|
||||
extern crate panic_semihosting;
|
||||
|
||||
use cortex_m::asm;
|
||||
|
||||
// the program entry point is ...
|
||||
entry!(main);
|
||||
|
||||
// ... this never ending function
|
||||
fn main() -> ! {
|
||||
loop {
|
||||
asm::bkpt();
|
||||
}
|
||||
}
|
||||
@@ -1,44 +1,28 @@
|
||||
//! Changing the panic handler
|
||||
//! Changing the panicking behavior
|
||||
//!
|
||||
//! The easiest way to change the panic handler is to use a different [panic handler crate][0].
|
||||
//! The easiest way to change the panicking behavior is to use a different [panic handler crate][0].
|
||||
//!
|
||||
//! [0]: https://crates.io/keywords/panic-impl
|
||||
//!
|
||||
//! ---
|
||||
|
||||
#![no_main]
|
||||
#![no_std]
|
||||
|
||||
#[macro_use]
|
||||
extern crate cortex_m_rt as rt;
|
||||
// Pick one of these panic handlers:
|
||||
|
||||
// Pick one of these two panic handlers:
|
||||
// `panic!` halts execution; the panic message is ignored
|
||||
extern crate panic_halt;
|
||||
|
||||
// Reports panic messages to the host stderr using semihosting
|
||||
extern crate panic_semihosting;
|
||||
// NOTE to use this you need to uncomment the `panic-semihosting` dependency in Cargo.toml
|
||||
// extern crate panic_semihosting;
|
||||
|
||||
// Logs panic messages using the ITM (Instrumentation Trace Macrocell)
|
||||
// NOTE to use this you need to uncomment the `panic-itm` dependency in Cargo.toml
|
||||
// extern crate panic_itm;
|
||||
|
||||
use rt::ExceptionFrame;
|
||||
|
||||
entry!(main);
|
||||
use cortex_m_rt::entry;
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
panic!("Oops")
|
||||
}
|
||||
|
||||
// define the hard fault handler
|
||||
exception!(HardFault, hard_fault);
|
||||
|
||||
fn hard_fault(ef: &ExceptionFrame) -> ! {
|
||||
panic!("HardFault at {:#?}", ef);
|
||||
}
|
||||
|
||||
// define the default exception handler
|
||||
exception!(*, default_handler);
|
||||
|
||||
fn default_handler(irqn: i16) {
|
||||
panic!("Unhandled exception (IRQn = {})", irqn);
|
||||
}
|
||||
|
||||
5
memory.x
5
memory.x
@@ -1,6 +1,6 @@
|
||||
MEMORY
|
||||
{
|
||||
/* NOTE K = KiBi = 1024 bytes */
|
||||
/* NOTE 1 K = 1 KiBi = 1024 bytes */
|
||||
/* TODO Adjust these memory regions to match your device memory layout */
|
||||
/* These values correspond to the LM3S6965, one of the few devices QEMU can emulate */
|
||||
FLASH : ORIGIN = 0x00000000, LENGTH = 256K
|
||||
@@ -19,6 +19,3 @@ MEMORY
|
||||
/* This is required only on microcontrollers that store some configuration right
|
||||
after the vector table */
|
||||
/* _stext = ORIGIN(FLASH) + 0x400; */
|
||||
|
||||
/* Size of the heap (in bytes) */
|
||||
/* _heap_size = 1024; */
|
||||
|
||||
@@ -3,10 +3,14 @@ target remote :3333
|
||||
# print demangled symbols
|
||||
set print asm-demangle on
|
||||
|
||||
# detect unhandled exceptions and hard faults
|
||||
# detect unhandled exceptions, hard faults and panics
|
||||
break DefaultHandler
|
||||
break UserHardFault
|
||||
break rust_begin_unwind
|
||||
|
||||
monitor arm semihosting enable
|
||||
|
||||
load
|
||||
load
|
||||
|
||||
# start the process but immediately halt the processor
|
||||
stepi
|
||||
18
src/main.rs
18
src/main.rs
@@ -1,19 +1,17 @@
|
||||
#![no_main]
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
extern crate cortex_m;
|
||||
#[macro_use]
|
||||
extern crate cortex_m_rt;
|
||||
|
||||
// TODO pick a panicking behavior
|
||||
// pick a panicking behavior
|
||||
extern crate panic_halt; // you can put a breakpoint on `rust_begin_unwind` to catch panics
|
||||
// extern crate panic_abort; // requires nightly
|
||||
// extern crate panic_itm; // requires ITM support
|
||||
// extern crate panic_semihosting; // requires a debugger
|
||||
// extern crate panic_itm; // logs messages over ITM; requires ITM support
|
||||
// extern crate panic_semihosting; // logs messages to the host stderr; requires a debugger
|
||||
|
||||
entry!(main);
|
||||
use cortex_m_rt::entry;
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
loop {
|
||||
// TODO your code goes here
|
||||
// your code goes here
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user