├── .cargo └── config ├── .gitignore ├── Cargo.toml ├── README.md ├── build.rs ├── memory.x └── src └── main.rs /.cargo/config: -------------------------------------------------------------------------------- 1 | [target.thumbv7m-none-eabi] 2 | # uncomment this to make `cargo run` execute programs on QEMU 3 | # runner = "qemu-system-arm -cpu cortex-m3 -machine lm3s6965evb -nographic -semihosting-config enable=on,target=native -kernel" 4 | 5 | [target.'cfg(all(target_arch = "arm", target_os = "none"))'] 6 | # uncomment ONE of these three option to make `cargo run` start a GDB session 7 | # which option to pick depends on your system 8 | # runner = "arm-none-eabi-gdb -q -x openocd.gdb" 9 | # runner = "gdb-multiarch -q -x openocd.gdb" 10 | # runner = "gdb -q -x openocd.gdb" 11 | 12 | rustflags = [ 13 | # LLD (shipped with the Rust toolchain) is used as the default linker 14 | "-C", "link-arg=-Tlink.x", 15 | 16 | # if you run into problems with LLD switch to the GNU linker by commenting out 17 | # this line 18 | # "-C", "linker=arm-none-eabi-ld", 19 | 20 | # if you need to link to pre-compiled C libraries provided by a C toolchain 21 | # use GCC as the linker by commenting out both lines above and then 22 | # uncommenting the three lines below 23 | # "-C", "linker=arm-none-eabi-gcc", 24 | # "-C", "link-arg=-Wl,-Tlink.x", 25 | # "-C", "link-arg=-nostartfiles", 26 | ] 27 | 28 | [build] 29 | # Pick ONE of these compilation targets 30 | # target = "thumbv6m-none-eabi" # Cortex-M0 and Cortex-M0+ 31 | target = "thumbv7m-none-eabi" # Cortex-M3 32 | # target = "thumbv7em-none-eabi" # Cortex-M4 and Cortex-M7 (no FPU) 33 | # target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU) 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **/*.rs.bk 2 | .#* 3 | .gdb_history 4 | Cargo.lock 5 | target/ 6 | 7 | # editor files 8 | .vscode/* 9 | !.vscode/*.md 10 | !.vscode/*.svd 11 | !.vscode/launch.json 12 | !.vscode/tasks.json 13 | !.vscode/extensions.json -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["Sebastian Dröge "] 3 | edition = "2018" 4 | readme = "README.md" 5 | name = "app" 6 | version = "0.1.0" 7 | license = "MIT" 8 | 9 | [dependencies] 10 | cortex-m = "0.6.0" 11 | cortex-m-rt = "0.6.10" 12 | panic-halt = "0.2.0" 13 | sam3x8e = { version = "0.1", features = ["rt"], git = "https://github.com/sdroege/sam3x8e.git" } 14 | 15 | [[bin]] 16 | name = "app" 17 | test = false 18 | bench = false 19 | 20 | [profile.release] 21 | codegen-units = 1 22 | debug = true 23 | lto = true 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Arduino Due Rust Example 2 | 3 | This repository contains a small example application for the [Arduino 4 | Due](https://store.arduino.cc/arduino-due) written in Rust. 5 | 6 | The example makes the on-board LED blink 1 time per second. 7 | 8 | The interaction with the board is based on the 9 | [`sam3x8e`](https://crates.io/crates/sam3x8e) crate, which unfortunately seems 10 | unmaintained and for which an [updated version](https://github.com/sdroege/sam3x8e) 11 | that works with stable Rust is used. 12 | 13 | ## Setup 14 | 15 | 1. Install the latest stable Rust toolchain via [rustup](https://rustup.rs). 16 | 2. Install the ARMv7-M toolchain via `rustup target add thumbv7m-none-eabi`. 17 | 3. Install `cargo-binutils` via `cargo install cargo-binutils` and `rustup 18 | component add llvm-tools-preview`. 19 | 4. Install [BOSSA](https://github.com/shumatech/BOSSA). Note that all 20 | versions newer than 1.7 [don't work](https://github.com/shumatech/BOSSA/issues/125) 21 | with the Arduino Due! 22 | 23 | That's all! 24 | 25 | ## Building 26 | 27 | 1. `cargo objcopy --bin app --target thumbv7m-none-eabi --release -- 28 | --output-target=binary image.bin` to build the application in release mode 29 | and generate a binary that can be flashed on the board. 30 | 2. For configuring the serial interface correctly for BOSSA 31 | 1. On Linux `stty -F /dev/ttyACM0 raw ispeed 1200 ospeed 1200 cs8 32 | -cstopb ignpar eol 255 eof 255`. This assumes `/dev/ttyACM0` is the 33 | device. 34 | 2. On Windows `mode com8:1200,n,8,1`. This assumes com8 is the device. 35 | Running `mode` without arguments lists all devices. 36 | 3. `bossac -e -w -v -b -R image.bin` for flashing the application and 37 | rebooting. 38 | 39 | At this point the on-board LED should start blinking. 40 | 41 | ## Further Resources 42 | 43 | * [Rust Embedded Book](https://rust-embedded.github.io/book/) 44 | * [`cortex-m-rt`](https://rust-embedded.github.io/cortex-m-rt/0.6.1/cortex_m_rt/index.html) 45 | * Documentation for `sam3x8e` can be generated by cloning the git repository 46 | and running `cargo doc --open`. 47 | -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | use std::fs::File; 3 | use std::io::Write; 4 | use std::path::PathBuf; 5 | 6 | fn main() { 7 | // Put the linker script somewhere the linker can find it 8 | let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); 9 | File::create(out.join("memory.x")) 10 | .unwrap() 11 | .write_all(include_bytes!("memory.x")) 12 | .unwrap(); 13 | println!("cargo:rustc-link-search={}", out.display()); 14 | 15 | // Only re-run the build script when memory.x is changed, 16 | // instead of when any part of the source code changes. 17 | println!("cargo:rerun-if-changed=memory.x"); 18 | } 19 | -------------------------------------------------------------------------------- /memory.x: -------------------------------------------------------------------------------- 1 | MEMORY 2 | { 3 | /* NOTE 1 K = 1 KiBi = 1024 bytes */ 4 | FLASH : ORIGIN = 0x00080000, LENGTH = 512K 5 | RAM : ORIGIN = 0x20000000, LENGTH = 64K 6 | } 7 | 8 | /* This is where the call stack will be allocated. */ 9 | /* The stack is of the full descending type. */ 10 | /* You may want to use this variable to locate the call stack and static 11 | variables in different memory regions. Below is shown the default value */ 12 | /* _stack_start = ORIGIN(RAM) + LENGTH(RAM); */ 13 | 14 | /* You can use this symbol to customize the location of the .text section */ 15 | /* If omitted the .text section will be placed right after the .vector_table 16 | section */ 17 | /* This is required only on microcontrollers that store some configuration right 18 | after the vector table */ 19 | /* _stext = ORIGIN(FLASH) + 0x400; */ 20 | 21 | /* Example of putting non-initialized variables into custom RAM locations. */ 22 | /* This assumes you have defined a region RAM2 above, and in the Rust 23 | sources added the attribute `#[link_section = ".ram2bss"]` to the data 24 | you want to place there. */ 25 | /* Note that the section will not be zero-initialized by the runtime! */ 26 | /* SECTIONS { 27 | .ram2bss (NOLOAD) : ALIGN(4) { 28 | *(.ram2bss); 29 | . = ALIGN(4); 30 | } > RAM2 31 | } INSERT AFTER .bss; 32 | */ 33 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | extern crate panic_halt; // you can put a breakpoint on `rust_begin_unwind` to catch panics 5 | extern crate sam3x8e; 6 | 7 | use cortex_m_rt::entry; 8 | use sam3x8e::RTT; 9 | 10 | fn delay_ms(rtt: &RTT, ms: u32) { 11 | // We're not considering overflow here, 32 bits can keep about 49 days in ms 12 | let now = rtt.vr.read().bits(); 13 | let until = now + ms; 14 | 15 | while rtt.vr.read().bits() < until {} 16 | } 17 | 18 | #[entry] 19 | fn main() -> ! { 20 | let p = sam3x8e::Peripherals::take().unwrap(); 21 | 22 | // Enable PIOB 23 | let pmc = p.PMC; 24 | pmc.pmc_pcer0.write_with_zero(|w| w.pid12().set_bit()); 25 | 26 | // Configure RTT resolution to approx. 1ms 27 | let rtt = p.RTT; 28 | rtt.mr.write_with_zero(|w| unsafe { w.rtpres().bits(0x20) }); 29 | 30 | let piob = p.PIOB; 31 | 32 | // Configure PIOB pin 27 (LED) 33 | piob.per.write_with_zero(|w| w.p27().set_bit()); 34 | piob.oer.write_with_zero(|w| w.p27().set_bit()); 35 | piob.pudr.write_with_zero(|w| w.p27().set_bit()); 36 | 37 | // On/off blinking 38 | loop { 39 | piob.sodr.write_with_zero(|w| w.p27().set_bit()); 40 | delay_ms(&rtt, 1000); 41 | piob.codr.write_with_zero(|w| w.p27().set_bit()); 42 | delay_ms(&rtt, 1000); 43 | } 44 | } 45 | --------------------------------------------------------------------------------