commit da82843495c4816e35132aaab85857a4ef486762 Author: Joe Ardent Date: Thu May 2 16:07:19 2024 -0700 blinks the user LED on the Nucleo board. diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000..b9e6120 --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,37 @@ +[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 ONE of these three option to make `cargo run` start a GDB session +# which option to pick depends on your system +# runner = "arm-none-eabi-gdb -q -x openocd.gdb" +# runner = "gdb-multiarch -q -x openocd.gdb" +# runner = "gdb -q -x openocd.gdb" + +rustflags = [ + # Previously, the linker arguments --nmagic and -Tlink.x were set here. + # They are now set by build.rs instead. The linker argument can still + # only be set here, if a custom linker is needed. + + # By default, the LLD linker is used, which is shipped with the Rust + # toolchain. If you run into problems with LLD, you can switch to the + # GNU linker by uncommenting this line: + # "-C", "linker=arm-none-eabi-ld", + + # If you need to link to pre-compiled C libraries provided by a C toolchain + # use GCC as the linker by uncommenting the three lines below: + # "-C", "linker=arm-none-eabi-gcc", + # "-C", "link-arg=-Wl,-Tlink.x", + # "-C", "link-arg=-nostartfiles", +] + +[build] +# Pick ONE of these default compilation targets +# 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 = "thumbv8m.base-none-eabi" # Cortex-M23 +# target = "thumbv8m.main-none-eabi" # Cortex-M33 (no FPU) +# target = "thumbv8m.main-none-eabihf" # Cortex-M33 (with FPU) diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fa5b635 --- /dev/null +++ b/.gitignore @@ -0,0 +1,13 @@ +**/*.rs.bk +.#* +.gdb_history +Cargo.lock +target/ + +# editor files +.vscode/* +!.vscode/*.md +!.vscode/*.svd +!.vscode/launch.json +!.vscode/tasks.json +!.vscode/extensions.json \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..fe3e336 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,43 @@ +[package] +authors = ["Joe Ardent "] +edition = "2021" +readme = "README.md" +name = "tinfoc" +version = "0.1.0" + +[dependencies] +cortex-m = "0.7" +cortex-m-rt = "0.7" +cortex-m-semihosting = "0.5" +# panic-halt = "0.2" +panic-semihosting = "0.6" +embedded-hal = "1" +nb = "1" + +[dependencies.stm32f3xx-hal] +version = "0.10" +features = ["stm32f302x8", "ld", "rt", "can", "rtc"] + +# Uncomment for the panic example. +# panic-itm = "0.4.1" + +# Uncomment for the allocator example. +# alloc-cortex-m = "0.4.0" + +# Uncomment for the device example. +# Update `memory.x`, set target to `thumbv7em-none-eabihf` in `.cargo/config`, +# and then use `cargo build --example device` to build it. +# [dependencies.stm32f3] +# features = ["stm32f303", "rt"] +# version = "0.7.1" + +# this lets you use `cargo fix`! +[[bin]] +name = "tinfoc" +test = false +bench = false + +[profile.release] +codegen-units = 1 # better optimizations +debug = true # symbols are nice and they don't increase the size on Flash +lto = true # better optimizations diff --git a/README.md b/README.md new file mode 100644 index 0000000..9d2c4a7 --- /dev/null +++ b/README.md @@ -0,0 +1,44 @@ +# TinFOC: a Rust-y Field-Oriented Control crate for STM32 MCUs + +A crate that provides an +[FOC](https://www.st.com/en/applications/industrial-motor-control/3-phase-field-oriented-control-foc.html) +motor controller using STM32 Cortex microcontrollers. + +## Dependencies + +The MCU I'm using is the +[STM32F302R8](https://www.st.com/en/microcontrollers-microprocessors/stm32f302r8.html), and I'm +using the [X-Nuclean IHM07M1] BLDC motor controller shield board that provides the actual power to +the motor. + +This requires the `thumbv7em-none-eabihf` Rust target: + +``` console +rustup target add thumbv7em-none-eabihf +``` + +In order to connect to the microcontroller with a debugger (say, to load the program) on Ubuntu +linux, you'll need to: + +``` console +sudo apt install openocd gdb-multiarch +``` + + +## Running on hardware + +Assuming you've plugged your board into your computer via USB: + +1. build TinFOC with `cargo build` +2. connect to the board with `openocd &` +3. start gdb: `gdb-multiarch -q target/thumbv7em-none-eabihf/debug/tinfoc` +4. inside gdb, attach to openocd: `target remote :3333` +5. inside gdb, load the program into the MCU's flash: `load` + +Assuming that was successful, you can then enter `continue` in the gdb console, which will run the +program on the hardware; you should see the `LD2` LED start blinking green, on and off, every 2 +seconds. If you disconnect the board from power and then reconnect, it will automatically run the +program. + +There are no doubt other ways to flash the board, and I'll update here when I adjust my flow; I'm a +newb when it comes to this stuff. diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..2a51f4e --- /dev/null +++ b/build.rs @@ -0,0 +1,43 @@ +//! This build script copies the `memory.x` file from the crate root into +//! a directory where the linker can always find it at build time. +//! For many projects this is optional, as the linker always searches the +//! project root directory -- wherever `Cargo.toml` is. However, if you +//! are using a workspace or have a more complicated build setup, this +//! build script becomes required. Additionally, by requesting that +//! Cargo re-run the build script whenever `memory.x` is changed, +//! updating `memory.x` ensures a rebuild of the application with the +//! new memory settings. +//! +//! The build script also sets the linker flags to tell it which link script to use. + +use std::env; +use std::fs::File; +use std::io::Write; +use std::path::PathBuf; + +fn main() { + // Put `memory.x` in our output directory and ensure it's + // on the linker search path. + let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); + File::create(out.join("memory.x")) + .unwrap() + .write_all(include_bytes!("memory.x")) + .unwrap(); + println!("cargo:rustc-link-search={}", out.display()); + + // By default, Cargo will re-run a build script whenever + // any file in the project changes. By specifying `memory.x` + // here, we ensure the build script is only re-run when + // `memory.x` is changed. + println!("cargo:rerun-if-changed=memory.x"); + + // Specify linker arguments. + + // `--nmagic` is required if memory section addresses are not aligned to 0x10000, + // for example the FLASH and RAM sections in your `memory.x`. + // See https://github.com/rust-embedded/cortex-m-quickstart/pull/95 + println!("cargo:rustc-link-arg=--nmagic"); + + // Set the linker script to the one provided by cortex-m-rt. + println!("cargo:rustc-link-arg=-Tlink.x"); +} diff --git a/memory.x b/memory.x new file mode 100644 index 0000000..2adf0a7 --- /dev/null +++ b/memory.x @@ -0,0 +1,37 @@ +MEMORY +{ + /* NOTE 1 K = 1 KiBi = 1024 bytes */ + + /* STM32F302R8T6 from STM32F302R8Tx_FLASH.ld in the STM32Cube archive */ + /* or the reference doc at */ + /* https://www.st.com/en/microcontrollers-microprocessors/stm32f302.html#resource */ + + FLASH : ORIGIN = 0x8000000, LENGTH = 64K + RAM : ORIGIN = 0x20000000, LENGTH = 16K +} + +/* This is where the call stack will be allocated. */ +/* The stack is of the full descending type. */ +/* You may want to use this variable to locate the call stack and static + variables in different memory regions. Below is shown the default value */ +/* _stack_start = ORIGIN(RAM) + LENGTH(RAM); */ + +/* You can use this symbol to customize the location of the .text section */ +/* If omitted the .text section will be placed right after the .vector_table + section */ +/* This is required only on microcontrollers that store some configuration right + after the vector table */ +/* _stext = ORIGIN(FLASH) + 0x400; */ + +/* Example of putting non-initialized variables into custom RAM locations. */ +/* This assumes you have defined a region RAM2 above, and in the Rust + sources added the attribute `#[link_section = ".ram2bss"]` to the data + you want to place there. */ +/* Note that the section will not be zero-initialized by the runtime! */ +/* SECTIONS { + .ram2bss (NOLOAD) : ALIGN(4) { + *(.ram2bss); + . = ALIGN(4); + } > RAM2 + } INSERT AFTER .bss; +*/ diff --git a/openocd.cfg b/openocd.cfg new file mode 100644 index 0000000..5aec560 --- /dev/null +++ b/openocd.cfg @@ -0,0 +1,5 @@ +# Sample OpenOCD configuration for the STM32F3DISCOVERY development board + +source [find interface/stlink.cfg] + +source [find target/stm32f3x.cfg] diff --git a/openocd.gdb b/openocd.gdb new file mode 100644 index 0000000..7795319 --- /dev/null +++ b/openocd.gdb @@ -0,0 +1,40 @@ +target extended-remote :3333 + +# print demangled symbols +set print asm-demangle on + +# set backtrace limit to not have infinite backtrace loops +set backtrace limit 32 + +# detect unhandled exceptions, hard faults and panics +break DefaultHandler +break HardFault +break rust_begin_unwind +# # run the next few lines so the panic message is printed immediately +# # the number needs to be adjusted for your panic handler +# commands $bpnum +# next 4 +# end + +# *try* to stop at the user entry point (it might be gone due to inlining) +break main + +monitor arm semihosting enable + +# # send captured ITM to the file itm.fifo +# # (the microcontroller SWO pin must be connected to the programmer SWO pin) +# # 8000000 must match the core clock frequency +# monitor tpiu config internal itm.txt uart off 8000000 + +# # OR: make the microcontroller SWO pin output compatible with UART (8N1) +# # 8000000 must match the core clock frequency +# # 2000000 is the frequency of the SWO pin +# monitor tpiu config external uart off 8000000 2000000 + +# # enable ITM port 0 +# monitor itm port 0 on + +load + +# start the process but immediately halt the processor +stepi diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..47ad31f --- /dev/null +++ b/src/main.rs @@ -0,0 +1,33 @@ +#![no_std] +#![no_main] + +// 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; // logs messages over ITM; requires ITM support +extern crate panic_semihosting; // logs messages to the host stderr; requires a debugger + +use cortex_m_rt::entry; + +//use embedded_hal::digital::OutputPin; +use stm32f3xx_hal::{self as hal, pac, prelude::*}; + +#[entry] +fn main() -> ! { + let sp = pac::Peripherals::take().unwrap(); + let mut rcc = sp.RCC.constrain(); + let mut gpiob = sp.GPIOB.split(&mut rcc.ahb); + let mut led = gpiob + .pb13 + .into_push_pull_output(&mut gpiob.moder, &mut gpiob.otyper); + + let mut flash = sp.FLASH.constrain(); + let clocks = rcc.cfgr.freeze(&mut flash.acr); + let cp = cortex_m::Peripherals::take().unwrap(); + let mut delay = hal::delay::Delay::new(cp.SYST, clocks); + + loop { + led.toggle().unwrap(); + delay.delay_ms(2000_u32); + } +}