├── .cargo-ok ├── .gitignore ├── .vscode ├── this.code-workspace ├── tasks.json └── launch.json ├── openocd.cfg ├── memory.x ├── openocd.gdb ├── .github └── workflows │ └── build-main.yml ├── .cargo └── config ├── Cargo.toml ├── src └── main.rs └── README.md /.cargo-ok: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **/*.rs.bk 2 | .#* 3 | .gdb_history 4 | Cargo.lock 5 | target/ -------------------------------------------------------------------------------- /.vscode/this.code-workspace: -------------------------------------------------------------------------------- 1 | { 2 | "folders": [ 3 | { 4 | "path": ".." 5 | } 6 | ], 7 | "settings": {} 8 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "build", 6 | "type": "cargo", 7 | "subcommand": "build", 8 | "problemMatcher": [ 9 | "$rustc" 10 | ], 11 | "group": { 12 | "kind": "build", 13 | "isDefault": true 14 | } 15 | }, 16 | ] 17 | } -------------------------------------------------------------------------------- /openocd.cfg: -------------------------------------------------------------------------------- 1 | # 2 | # This is an STM32F429 discovery board with a single STM32F429ZI chip. 3 | # http://www.st.com/web/catalog/tools/FM116/SC959/SS1532/PF259090 4 | # 5 | 6 | source [find interface/stlink.cfg] 7 | 8 | transport select hla_swd 9 | 10 | source [find target/stm32f4x.cfg] 11 | 12 | reset_config srst_only -------------------------------------------------------------------------------- /memory.x: -------------------------------------------------------------------------------- 1 | /* Linker script for the STM32F429ZIT6 */ 2 | MEMORY 3 | { 4 | /* NOTE 1 K = 1 KiBi = 1024 bytes */ 5 | FLASH : ORIGIN = 0x08000000, LENGTH = 2048K 6 | RAM : ORIGIN = 0x20000000, LENGTH = 192K 7 | } 8 | 9 | /* This is where the call stack will be allocated. */ 10 | /* The stack is of the full descending type. */ 11 | /* NOTE Do NOT modify `_stack_start` unless you know what you are doing */ 12 | _stack_start = ORIGIN(RAM) + LENGTH(RAM); -------------------------------------------------------------------------------- /openocd.gdb: -------------------------------------------------------------------------------- 1 | target extended-remote :3333 2 | 3 | # print demangled symbols 4 | set print asm-demangle on 5 | 6 | # set backtrace limit to not have infinite backtrace loops 7 | set backtrace limit 32 8 | 9 | # detect unhandled exceptions, hard faults and panics 10 | break DefaultHandler 11 | break HardFault 12 | break rust_begin_unwind 13 | 14 | break main 15 | 16 | monitor arm semihosting enable 17 | 18 | load 19 | 20 | # start the process but immediately halt the processor 21 | #stepi 22 | 23 | # run to next breakpoint (main) 24 | continue 25 | -------------------------------------------------------------------------------- /.github/workflows/build-main.yml: -------------------------------------------------------------------------------- 1 | name: Build for target 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | 7 | env: 8 | CARGO_TERM_COLOR: always 9 | 10 | jobs: 11 | build: 12 | 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - uses: actions/checkout@v2 17 | - uses: actions-rs/toolchain@v1 18 | with: 19 | toolchain: stable 20 | target: thumbv7em-none-eabihf 21 | override: true 22 | - uses: actions-rs/cargo@v1 23 | with: 24 | use-cross: true 25 | command: build 26 | args: --target thumbv7em-none-eabihf 27 | -------------------------------------------------------------------------------- /.cargo/config: -------------------------------------------------------------------------------- 1 | [target.thumbv7em-none-eabihf] 2 | 3 | [target.'cfg(all(target_arch = "arm", target_os = "none"))'] 4 | # make `cargo run` start a GDB session 5 | runner = "arm-none-eabi-gdb -q -x openocd.gdb" 6 | 7 | rustflags = [ 8 | # This is needed if your flash or ram addresses are not aligned to 0x10000 in memory.x 9 | # See https://github.com/rust-embedded/cortex-m-quickstart/pull/95 10 | "-C", "link-arg=--nmagic", 11 | 12 | # LLD (shipped with the Rust toolchain) is used as the default linker 13 | "-C", "link-arg=-Tlink.x", 14 | ] 15 | 16 | [build] 17 | target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU) 18 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "type": "cortex-debug", 6 | "request": "launch", 7 | "preLaunchTask": "build", 8 | "servertype": "openocd", 9 | "cwd": "${workspaceRoot}", 10 | "executable": "./target/thumbv7em-none-eabihf/debug/rust-stm32f4-disco-blinky", 11 | "name": "Debug (OpenOCD)", 12 | "device": "STM32F429ZIT6", 13 | "configFiles": [ 14 | "interface/stlink.cfg", 15 | "target/stm32f4x.cfg" 16 | ], 17 | "runToEntryPoint": "main", 18 | "svdFile": "STM32F429x.svd" 19 | } 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["aholzbaur <71641117+aholzbaur@users.noreply.github.com>"] 3 | edition = "2021" 4 | readme = "README.md" 5 | name = "rust-stm32f4-disco-blinky" 6 | version = "0.2.0" 7 | 8 | [dependencies] 9 | cortex-m = "0.7.4" 10 | cortex-m-rt = "0.7.1" 11 | cortex-m-semihosting = "0.3.7" 12 | panic-halt = "0.2.0" 13 | 14 | [dependencies.stm32f4] 15 | version = "0.14.0" 16 | features = ["rt", "stm32f429"] 17 | 18 | [dependencies.stm32f4xx-hal] 19 | version = "0.11.1" 20 | features = ["rt", "stm32f429"] 21 | 22 | [[bin]] 23 | name = "rust-stm32f4-disco-blinky" 24 | test = false 25 | bench = false 26 | 27 | [profile.release] 28 | # Many of these settings are highly recommended or required for embedded work 29 | codegen-units = 1 30 | incremental = false 31 | debug = true 32 | lto = true 33 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | use panic_halt as _; 5 | use core::cell::{Cell, RefCell}; 6 | use core::ops::DerefMut; 7 | use cortex_m::interrupt::{free, Mutex}; 8 | use cortex_m_rt::{entry}; 9 | use stm32f4xx_hal as hal; 10 | use hal::{prelude::*, 11 | pac::{interrupt, Interrupt, Peripherals, TIM2}, 12 | timer::{Event, CountDownTimer, Timer}, 13 | gpio::{gpiog::{PG13, PG14}, Output, PushPull}}; 14 | 15 | static BLINKY : Mutex> = Mutex::new(Cell::new(BlinkState::OnOff)); 16 | static TIMER: Mutex>>> = Mutex::new(RefCell::new(None)); 17 | static LED_GREEN : Mutex>>>> = Mutex::new(RefCell::new(None)); 18 | static LED_RED : Mutex>>>> = Mutex::new(RefCell::new(None)); 19 | 20 | #[derive(Clone, Copy)] 21 | enum BlinkState { 22 | OnOff, 23 | OffOn 24 | } 25 | 26 | #[entry] 27 | fn main() -> ! { 28 | let device_periphs = Peripherals::take().unwrap(); 29 | 30 | device_periphs.RCC.apb2enr.write(|w| w.syscfgen().enabled()); 31 | 32 | let clocks = device_periphs.RCC.constrain().cfgr 33 | .use_hse(8.mhz()) // discovery board has 8 MHz crystal for HSE 34 | .hclk(180.mhz()) 35 | .sysclk(180.mhz()) 36 | .pclk1(45.mhz()) 37 | .pclk2(90.mhz()) 38 | .freeze(); 39 | 40 | let gpiog_periph = device_periphs.GPIOG.split(); 41 | 42 | let mut _led_green = gpiog_periph.pg13.into_push_pull_output(); 43 | _led_green.set_high(); 44 | 45 | let mut _led_red = gpiog_periph.pg14.into_push_pull_output(); 46 | _led_red.set_low(); 47 | 48 | // Create a 1s periodic interrupt from TIM2 49 | let mut _timer = Timer::new(device_periphs.TIM2, &clocks).start_count_down(1.hz()); 50 | 51 | _timer.listen(Event::TimeOut); 52 | _timer.clear_interrupt(Event::TimeOut); 53 | 54 | free(|cs| { 55 | TIMER.borrow(cs).replace(Some(_timer)); 56 | LED_GREEN.borrow(cs).replace(Some(_led_green)); 57 | LED_RED.borrow(cs).replace(Some(_led_red)); 58 | }); 59 | 60 | // Enable interrupt 61 | cortex_m::peripheral::NVIC::unpend(Interrupt::TIM2); 62 | unsafe{ cortex_m::peripheral::NVIC::unmask(Interrupt::TIM2) }; 63 | 64 | loop { 65 | // The main thread can now go to sleep. 66 | // WFI (wait for interrupt) puts the core in sleep until an interrupt occurs. 67 | cortex_m::asm::wfi(); 68 | } 69 | } 70 | 71 | #[interrupt] 72 | fn TIM2() { 73 | free(|cs| { 74 | if let (Some(ref mut _timer), Some(ref mut _led_green), Some(ref mut _led_red)) = (TIMER.borrow(cs).borrow_mut().deref_mut(), LED_GREEN.borrow(cs).borrow_mut().deref_mut(), LED_RED.borrow(cs).borrow_mut().deref_mut()) { 75 | _timer.clear_interrupt(Event::TimeOut); 76 | match BLINKY.borrow(cs).get() { 77 | BlinkState::OnOff => { 78 | BLINKY.borrow(cs).replace(BlinkState::OffOn); 79 | _led_green.set_low(); 80 | _led_red.set_high(); 81 | }, 82 | BlinkState::OffOn => { 83 | BLINKY.borrow(cs).replace(BlinkState::OnOff); 84 | _led_green.set_high(); 85 | _led_red.set_low(); 86 | } 87 | } 88 | } 89 | }); 90 | } 91 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Build for target](https://github.com/aholzbaur/rust-stm32f4-disco-blinky/workflows/Build%20for%20target/badge.svg) 2 | 3 | # Blinky example in Rust on the STM32F429-Discovery board 4 | 5 | Small project example to be used as template for embedded software in Rust on the STM32F429-Discovery board. 6 | 7 | ## Hardware 8 | 9 | Evaluation hardware STM32F429I-DISC1 (https://www.st.com/content/st_com/en/products/evaluation-tools/product-evaluation-tools/mcu-mpu-eval-tools/stm32-mcu-mpu-eval-tools/stm32-discovery-kits/32f429idiscovery.html#resource) 10 | - STM32F429ZIT6 with 2 MB flash, 256 KB RAM, 180 MHz 11 | - 2 user LEDs 12 | - 1 user button 13 | - MEMS motion sensor 14 | - LC display 15 | - 8 MB SDRAM 16 | - this hardware revision has onboard ST-LINK/V2-B 17 | 18 | ## Software 19 | ### Windows (as of 09/2020) 20 | - Rust v1.46.0 21 | - cargo install cargo-binutils 22 | - cargo install cargo-generate 23 | - rustup component add llvm-tools-preview 24 | - rustup target add thumbv7m-none-eabi 25 | - GNU Arm Embedded Toolchain 9-2020-q2 26 | - OpenOCD (xPack release https://github.com/xpack-dev-tools/openocd-xpack/releases/) 27 | - Visual Studio Code 28 | - Rust plugin (rust-lang.rust) 29 | - Cortex-Debug plugin (marus25.cortex-debug) 30 | - Visual Studio 2019 Community Edition (necessary for some build tools) 31 | - ST-LINK/V2-B USB driver 32 | 33 | If everything is installed properly, the workspace can be opened in VS Code and a hit on F5 should build the project without errors or warnings and automatically start a debug session. The program should be halted at the breakpoint at main(). 34 | 35 | ### Ubuntu 21 (as of 02/2022) 36 | Not sure, if these are really necessary here: 37 | 38 | `sudo apt-get install gcc make clang` 39 | 40 | Required installation: 41 | 42 | ``` 43 | curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh 44 | cargo install cargo-binutils 45 | ``` 46 | 47 | Building cargo-generate requires (at least) libssl-dev to be installed: 48 | 49 | ``` 50 | sudo apt-get install libssl-dev 51 | cargo install cargo-generate 52 | rustup component add llvm-tools-preview 53 | ``` 54 | Verify installation for x86_64 according to https://docs.rust-embedded.org/discovery/f3discovery/03-setup/index.html: 55 | 56 | ``` 57 | cargo new first-test 58 | cargo cd first-test 59 | cargo run 60 | cargo size -- --version 61 | ``` 62 | 63 | Then install the embedded toolchain: `rustup target add thumbv7em-none-eabihf` 64 | 65 | This project should now be able to be built: 66 | ``` 67 | cd rust-stm32f4-disco-blinky 68 | cargo build 69 | ``` 70 | 71 | To debug, download the latest ARM GNU Embedded Toolchain (in this case 10.3-2021.10) and run: 72 | ``` 73 | sudo tar -xvjf gcc-arm-none-eabi-10.3-2021.10-x86_64-linux.tar.bz2 -C /usr/share/ 74 | sudo ln -s /usr/share/gcc-arm-none-eabi-10.3-2021.10/bin/* /usr/bin/ 75 | sudo apt-get install libncurses5 76 | arm-none-eabi-gcc --version 77 | ``` 78 | 79 | Now follow https://docs.rust-embedded.org/discovery/f3discovery/03-setup/linux.html#udev-rules and https://docs.rust-embedded.org/discovery/f3discovery/03-setup/verify.html until OpenOCD can connect to ST-Link: `openocd -f interface/stlink.cfg -f target/stm32f4x.cfg` and keep it running in a separate terminal. 80 | 81 | In another terminal, calling `arm-none-eabi-gdb -q -ex "target remote :3333" target/thumbv7em-none-eabihf/debug/rust-stm32f4-disco-blinky` should successfully flash the controller and halt it (i.e. at Reset()). 82 | 83 | ## Links 84 | 85 | Project was created following the Rust Embedded Book (https://docs.rust-embedded.org/book/) and the Discovery book (https://rust-embedded.github.io/discovery/). 86 | 87 | Based partly on the projects https://github.com/rust-embedded/cortex-m-quickstart, https://github.com/adamgreig/stm32f4-demo and https://github.com/rust-embedded/discovery. 88 | 89 | Debugging via Visual Studio Code was enabled following the article on https://dev.to/rubberduck/debugging-rust-arm-cortexm-programs-with-visual-studio-code-336h. 90 | --------------------------------------------------------------------------------