From f829ad46aeacd79cfc8c26ab894c7773669759d1 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Fri, 12 Jun 2020 17:54:45 +0200 Subject: [PATCH] - Real Time for The Masses has been renamed to RTIC - add 'project from scratch' section to the beginner workshop --- advanced/README.md | 6 +- advanced/firmware/Cargo.lock | 33 +++-- advanced/firmware/Cargo.toml | 3 +- advanced/firmware/src/bin/rtic-events.rs | 2 +- advanced/firmware/src/bin/rtic-hello.rs | 2 +- advanced/firmware/src/bin/rtic-resource.rs | 2 +- advanced/firmware/src/bin/rtic-usb-1.rs | 2 +- advanced/firmware/src/bin/rtic-usb-2.rs | 2 +- advanced/firmware/src/bin/rtic-usb-3.rs | 2 +- advanced/firmware/src/bin/rtic-usb-4.rs | 2 +- advanced/firmware/src/bin/rtic-usb-final.rs | 2 +- beginner/README.md | 143 +++++++++++++++++++- 12 files changed, 171 insertions(+), 30 deletions(-) diff --git a/advanced/README.md b/advanced/README.md index 1d0cc2f..e9827f8 100644 --- a/advanced/README.md +++ b/advanced/README.md @@ -99,9 +99,9 @@ If you look at the `rtic-expansion.rs` file generated for the build of the `rtic ``` rust fn main() -> ! { - rtfm::export::interrupt::disable(); + rtic::export::interrupt::disable(); let late = init(init::Context::new(/* .. */)); - rtfm::export::interrupt::enable(); + rtic::export::interrupt::enable(); idle(idle::Context::new(/* .. */)) } ``` @@ -321,5 +321,5 @@ For more details, read the section 9.4.7 'SET_CONFIGURATION' of the USB 2.0 spec ## References -- [nRF52840 Product Specification 1.0](https://infocenter.nordicsemi.com/pdf/nRF52840_PS_v1.0.pdf) +- [nRF52840 Product Specification 1.1](https://infocenter.nordicsemi.com/pdf/nRF52840_PS_v1.1.pdf) - [Universal Serial Bus Specification Revision 2.0](https://www.usb.org/document-library/usb-20-specification) diff --git a/advanced/firmware/Cargo.lock b/advanced/firmware/Cargo.lock index c004d4d..d8196a9 100644 --- a/advanced/firmware/Cargo.lock +++ b/advanced/firmware/Cargo.lock @@ -89,27 +89,28 @@ dependencies = [ ] [[package]] -name = "cortex-m-rtfm" -version = "0.5.1" +name = "cortex-m-rtic" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaf0b9fd3f042cb3793d15daf3cea201b2f25c99b0b5b936a551bb6909c3ae5b" +checksum = "04cd388b154c7e7d212c5af7541ee1f174f29ccb0c22e9117f8d13a5aad233b6" dependencies = [ "cortex-m", "cortex-m-rt", - "cortex-m-rtfm-macros", + "cortex-m-rtic-macros", "heapless", - "rtfm-core", + "rtic-core", + "version_check", ] [[package]] -name = "cortex-m-rtfm-macros" +name = "cortex-m-rtic-macros" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c62092f6ff344e9b0adb748f0302ed69889ba2fae1fce446e3788d4726ea73bb" +checksum = "29e29e01b3ec80d59bfd96aaf94d04008bebfde3ab7016e12bfbd6c0b466d22a" dependencies = [ "proc-macro2", "quote", - "rtfm-syntax", + "rtic-syntax", "syn", ] @@ -142,7 +143,7 @@ dependencies = [ "consts", "cortex-m", "cortex-m-rt", - "cortex-m-rtfm", + "cortex-m-rtic", "dk", "heapless", "log", @@ -305,16 +306,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" [[package]] -name = "rtfm-core" +name = "rtic-core" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ec893edb2aa5b70320b94896ffea22a7ebb1cf3f942bb67cd5b60a865a63493" +checksum = "ab51fe832317e805f869b3d859f91aadf855c2c3da51f9b84bc645c201597158" [[package]] -name = "rtfm-syntax" +name = "rtic-syntax" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4455e23c34df3d66454e7e218a4d76a7f83321d04a806be614463341cec4116e" +checksum = "8152fcaa845720d61e6cc570548b89144c2c307f18a480bbd97e55e9f6eeff04" dependencies = [ "indexmap", "proc-macro2", @@ -406,6 +407,12 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "876e32dcadfe563a4289e994f7cb391197f362b6315dc45e8ba4aa6f564a4b3c" +[[package]] +name = "version_check" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" + [[package]] name = "void" version = "1.0.2" diff --git a/advanced/firmware/Cargo.toml b/advanced/firmware/Cargo.toml index bcd8ae5..8968196 100644 --- a/advanced/firmware/Cargo.toml +++ b/advanced/firmware/Cargo.toml @@ -13,8 +13,7 @@ usb2 = { git = "https://github.com/japaric/usb2" } consts = { path = "../common/consts" } cortex-m = "0.6.2" cortex-m-rt = "0.6.12" -# TODO switch to RTIC before public release -cortex-m-rtfm = "0.5.1" +cortex-m-rtic = "0.5.1" dk = { path = "../../boards/dk", features = ["advanced"] } heapless = "0.5.5" log = "0.4.8" diff --git a/advanced/firmware/src/bin/rtic-events.rs b/advanced/firmware/src/bin/rtic-events.rs index e4fde71..0ed90a9 100644 --- a/advanced/firmware/src/bin/rtic-events.rs +++ b/advanced/firmware/src/bin/rtic-events.rs @@ -4,7 +4,7 @@ use cortex_m::asm; use panic_log as _; // panic handler -#[rtfm::app(device = dk)] +#[rtic::app(device = dk)] const APP: () = { #[init] fn init(_cx: init::Context) { diff --git a/advanced/firmware/src/bin/rtic-hello.rs b/advanced/firmware/src/bin/rtic-hello.rs index 122ff7a..9f823e9 100644 --- a/advanced/firmware/src/bin/rtic-hello.rs +++ b/advanced/firmware/src/bin/rtic-hello.rs @@ -4,7 +4,7 @@ use cortex_m::asm; use panic_log as _; // panic handler -#[rtfm::app(device = dk)] +#[rtic::app(device = dk)] const APP: () = { #[init] fn init(_cx: init::Context) { diff --git a/advanced/firmware/src/bin/rtic-resource.rs b/advanced/firmware/src/bin/rtic-resource.rs index e4165eb..308df7f 100644 --- a/advanced/firmware/src/bin/rtic-resource.rs +++ b/advanced/firmware/src/bin/rtic-resource.rs @@ -5,7 +5,7 @@ use cortex_m::asm; use dk::{peripheral::USBD, usbd}; use panic_log as _; // panic handler -#[rtfm::app(device = dk)] +#[rtic::app(device = dk)] const APP: () = { struct Resources { usbd: USBD, diff --git a/advanced/firmware/src/bin/rtic-usb-1.rs b/advanced/firmware/src/bin/rtic-usb-1.rs index f210a9e..9c7a43a 100644 --- a/advanced/firmware/src/bin/rtic-usb-1.rs +++ b/advanced/firmware/src/bin/rtic-usb-1.rs @@ -7,7 +7,7 @@ use dk::{ }; use panic_log as _; // panic handler -#[rtfm::app(device = dk)] +#[rtic::app(device = dk)] const APP: () = { struct Resources { usbd: USBD, diff --git a/advanced/firmware/src/bin/rtic-usb-2.rs b/advanced/firmware/src/bin/rtic-usb-2.rs index 5216104..b94db47 100644 --- a/advanced/firmware/src/bin/rtic-usb-2.rs +++ b/advanced/firmware/src/bin/rtic-usb-2.rs @@ -8,7 +8,7 @@ use dk::{ use panic_log as _; // panic handler use usb::{Descriptor, Request}; -#[rtfm::app(device = dk)] +#[rtic::app(device = dk)] const APP: () = { struct Resources { usbd: USBD, diff --git a/advanced/firmware/src/bin/rtic-usb-3.rs b/advanced/firmware/src/bin/rtic-usb-3.rs index 87e4c64..71ffcc8 100644 --- a/advanced/firmware/src/bin/rtic-usb-3.rs +++ b/advanced/firmware/src/bin/rtic-usb-3.rs @@ -11,7 +11,7 @@ use panic_log as _; // panic handler // use usb::{Descriptor, Request}; // your implementation use usb2::{GetDescriptor as Descriptor, StandardRequest as Request}; // crates.io impl -#[rtfm::app(device = dk)] +#[rtic::app(device = dk)] const APP: () = { struct Resources { usbd: USBD, diff --git a/advanced/firmware/src/bin/rtic-usb-4.rs b/advanced/firmware/src/bin/rtic-usb-4.rs index 9f9053a..799ce79 100644 --- a/advanced/firmware/src/bin/rtic-usb-4.rs +++ b/advanced/firmware/src/bin/rtic-usb-4.rs @@ -12,7 +12,7 @@ use panic_log as _; // panic handler use usb2::State; use usb2::{GetDescriptor as Descriptor, StandardRequest as Request}; // crates.io impl -#[rtfm::app(device = dk)] +#[rtic::app(device = dk)] const APP: () = { struct Resources { usbd: USBD, diff --git a/advanced/firmware/src/bin/rtic-usb-final.rs b/advanced/firmware/src/bin/rtic-usb-final.rs index ffd39dd..d3ca7f6 100644 --- a/advanced/firmware/src/bin/rtic-usb-final.rs +++ b/advanced/firmware/src/bin/rtic-usb-final.rs @@ -10,7 +10,7 @@ use dk::{ use panic_log as _; // panic handler use usb2::{GetDescriptor, StandardRequest, State}; -#[rtfm::app(device = dk)] +#[rtic::app(device = dk)] const APP: () = { struct Resources { ep0in: Ep0In, diff --git a/beginner/README.md b/beginner/README.md index 0b1653e..d34a1ef 100644 --- a/beginner/README.md +++ b/beginner/README.md @@ -384,14 +384,149 @@ If you haven't use a stack-allocated collection before note that you'll need to P.S. The plaintext string is *not* stored in `puzzle.hex` so running `strings` on it will not give you the answer. +## Starting a project from scratch + +So far we have been using a pre-made Cargo project to work with the nRF52840 DK. In this section we'll see how to create a new embedded project for any microcontroller. + +### Identify the microcontroller + +The first step is to identify the microcontroller you'll be working with. The information about the microcontroller you'll need is: + +1. Its processor architecture and sub-architecture. + +This information should be in the device's data sheet or manual. In the case of the nRF52840, the processor is an ARM Cortex-M4 core. With this information you'll need to select a compatible *compilation target*. `rustup target list` will show all the supported compilation targets. + +``` console +$ rustup target list +(..) +thumbv6m-none-eabi +thumbv7em-none-eabi +thumbv7em-none-eabihf +thumbv7m-none-eabi +thumbv8m.base-none-eabi +thumbv8m.main-none-eabi +thumbv8m.main-none-eabihf +``` + +The compilation targets will usually be named using the following format: `$ARCHITECTURE-$VENDOR-$OS-$ABI`, where the `$VENDOR` field is sometimes omitted. Bare metal and `no_std` targets, like microcontrollers, will often use `none` for the `$OS` field. When the `$ABI` field ends in `hf` it indicates that the output ELF uses the *hardfloat* ABI + +The `thumb` targets listed above are all the currently supported ARM Cortex-M targets. The table below shows the mapping between compilation targets and ARM Cortex-M processors. + +| Compilation target | Processor | +| --------------------------- | ---------------------------------- | +| `thumbv6m-none-eabi` | ARM Cortex-M0, ARM Cortex-M0+ | +| `thumbv7m-none-eabi` | ARM Cortex-M3 | +| `thumbv7em-none-eabi` | ARM Cortex-M4, ARM Cortex-M7 | +| `thumbv7em-none-eabihf` | ARM Cortex-M4*F*, ARM Cortex-M7*F* | +| `thumbv8m.base-none-eabi` | ARM Cortex-M23 | +| `thumbv8m.main-none-eabi` | ARM Cortex-M33, ARM Cortex-M35P | +| `thumbv8m.main-none-eabihf` | ARM Cortex-M33F, ARM Cortex-M35PF | + + +The ARM Cortex-M ISA is backwards compatible so for example you could compile a program using the `thumbv6m-none-eabi` target and run it on an ARM Cortex-M4 microcontroller. This will work but using the `thumbv7em-none-eabi` results in better performance (ARMv7-M instructions will be emitted by the compiler) so it should be preferred. The opposite, compiling for `thumbv7em-none-eabi` and running the resulting + +2. Its memory layout. + +In particular, you need to identify how much Flash and RAM memory the device has and at which address the memory is exposed. You'll find this information in the device's data sheet or reference manual. + +In the case of the nRF52840, this information is in section 4.2 (Figure 2) of its Product Specification (see the References section at the bottom of this document). The nRF52840 has: + +- 1 MB of Flash that spans the address range: `0x0000_0000` - `0x0010_0000`. +- 256 KB of RAM that spans the address range: `0x2000_0000` - `0x2004_0000`. + +### The `cortex-m-quickstart` project template + +With all this information you'll be able to build programs for the target device. The [`cortex-m-quickstart`] project template provides the most frictionless way to start a new project for the ARM Cortex-M architecture -- for other architectures check out other project templates by the [rust-embedded] organization. + +[`cortex-m-quickstart`]: https://github.com/rust-embedded/cortex-m-quickstart +[rust-embedded]: https://github.com/rust-embedded/ + +The recommended way to use the quickstart template is through the [`cargo-generate`] tool: + +[`cargo-generate`]: https://crates.io/crates/cargo-generate + +``` console +$ cargo generate --git https://github.com/rust-embedded/cortex-m-quickstart +``` + +But it may be difficult to install the `cargo-generate` tool on Windows due to its `libgit2` (C library) dependency. Another option is to download a snapshot of the quickstart template from GitHub and then fill in the placeholders in `Cargo.toml` of the snapshot. + +Once you have instantiated a project using the template you'll need to fill in the device-specific information you collected in the two previous steps: + +1. Change the default compilation target in `.cargo/config` + +``` toml +[build] +target = "thumbv7em-none-eabi" +``` + +For the nRF52840 you can choose either `thumbv7em-none-eabi` or `thumbv7em-none-eabihf`. If you are going to use the FPU then select the `hf` variant. + +2. Enter the memory layout of the chip in `memory.x` + +``` +MEMORY +{ + /* NOTE 1 K = 1 KiBi = 1024 bytes */ + FLASH : ORIGIN = 0x00000000, LENGTH = 1M + RAM : ORIGIN = 0x20000000, LENGTH = 256K +} +``` + +3. `cargo build` now will cross compile programs for your target device. + +If there's no template or signs of support for a particular architecture under the rust-embedded organization then you can follow the [embedonomicon] to bootstrap support for the new architecture by yourself. + +[embedonomicon]:https://docs.rust-embedded.org/embedonomicon/ + +### Flashing the program + +To flash the program on the target device you'll need to identify the on-board debugger, if the development board has one. Or choose an external debugger, if the development board exposes a JTAG or SWD interface via some connector. + +If the hardware debugger is supported by the `probe-rs` project -- for example J-Link, ST-Link or CMSIS-DAP -- then you'll be able to use `probe-rs`-based tools like `cargo-flash` and `cargo-embed`. This is the case of the nRF52840 DK: it has an on-board J-Link probe. + +If the debugger is not supported by `probe-rs` then you'll need to use [OpenOCD] or vendor provided software to flash programs on the board. + +[OpenOCD]: http://openocd.org/ + +If the board does not expose a JTAG, SWD or similar interface then the microcontroller probably comes with a bootloader as part of its stock firmware. In that case you'll need to use `dfu-util` or a vendor specific tool like `nrfutil` to flash programs onto the chip. This is the case of the nRF52840 Dongle. + +### Getting output + +If you are using one of the probes supported by `probe-rs` then you can use the [`rtt-target`] library to get text output on `cargo-embed`. The logging functionality we used in the examples is implemented using the `rtt-target` crate. + +[`rtt-target`]: https://crates.io/crates/rtt-target + +If that's not the case or there's no debugger on board then you'll need to add a HAL before you can get text output from the board. + +### Adding a Hardware Abstraction Layer (HAL) + +Now you can hopefully run programs and get output from them. To use the hardware features of the device you'll need to add a HAL to your list of dependencies. [crates.io], [lib.rs] and [awesome embedded Rust] are good places to search for HALs. + +[crates.io]: https://crates.io/search?q=hal +[lib.rs]: https://lib.rs/search?q=hal +[awesome embedded Rust]: https://github.com/rust-embedded/awesome-embedded-rust#hal-implementation-crates + +After you find a HAL you'll want to get familiar with its API through its [API docs] and [examples]. HAL do not always expose the exact same API, specially when it comes to initialization and configuration of peripherals. However, most HAL will implement the [`embedded-hal`] traits. These traits allow inter-operation between the HAL and [*driver* crates][drivers]. These driver crates provide functionality to interface external devices like sensors, actuators and radios over interfaces like I2C and SPI. + +[API docs]: https://docs.rs/nrf52840-hal/0.10.0/nrf52840_hal/ +[examples]: https://github.com/nrf-rs/nrf-hal/tree/master/examples +[`embedded-hal`]: https://crates.io/crates/embedded-hal +[drivers]: https://github.com/rust-embedded/awesome-embedded-rust#driver-crates + +If no HAL is available for your device then you'll need to build one yourself. This is usually done by first generating a Peripheral Access Crate (PAC) from a [System View Description][SVD] (SVD) file using the [`svd2rust`] tool. The PAC exposes a low level, but type safe, API to modify the registers on the device. Once you have a PAC you can use of the many HALs on crates.io as a reference; most of them are implemented on top of `svd2rust`-generated PACs. + +[SVD]: http://www.keil.com/pack/doc/CMSIS/SVD/html/index.html +[`svd2rust`]: https://crates.io/crates/svd2rust + --- > NOTE additional content, if needed / desired -## Starting a project from scratch - -> cortex-m-quickstart - ## (extra) adding addresses to packets > have people use the `ieee802154` crate to add a MAC header to the radio packet. New dongle firmware would be required to respond differently to broadcast packets and addressed packets + +## References + +- [nRF52840 Product Specification 1.1](https://infocenter.nordicsemi.com/pdf/nRF52840_PS_v1.1.pdf)