├── .cargo └── config ├── .gitignore ├── .travis.yml ├── Cargo.toml ├── LICENSE-0BSD.txt ├── README.md ├── examples ├── button_read.rs ├── gpio_hal_blinky.rs ├── i2c_hal_ssd1306alphabeter.rs ├── i2c_hal_ssd1306helloworld.rs ├── leds.rs └── serial_echo.rs ├── memory.x ├── openocd.cfg ├── openocd.gdb ├── openocd_program.sh ├── src ├── led.rs └── lib.rs └── tools └── capture_example_bloat.sh /.cargo/config: -------------------------------------------------------------------------------- 1 | [target.thumbv7em-none-eabihf] 2 | runner = "arm-none-eabi-gdb -x openocd.gdb" 3 | rustflags = [ 4 | "-C", "link-arg=-Tlink.x", 5 | ] 6 | 7 | [build] 8 | target = "thumbv7em-none-eabihf" 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | Cargo.lock 3 | **.orig 4 | **~ 5 | **.bk 6 | **.sw* 7 | bloat_log* 8 | itm.fifo 9 | itm.txt 10 | .vscode -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | rust: 3 | - stable 4 | - nightly 5 | cache: cargo 6 | matrix: 7 | allow_failures: 8 | - rust: nightly 9 | fast_finish: true 10 | script: 11 | - rustup target add thumbv7em-none-eabihf 12 | - cargo build --examples --release 13 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | edition = "2018" 3 | authors = ["Daniel Egger "] 4 | categories = [ 5 | "embedded", 6 | "no-std", 7 | ] 8 | description = "Board support crate for the STM32F429I DISCOVERY microcontroller board" 9 | documentation = "https://docs.rs/crate/stm32f429i-disc" 10 | keywords = [ 11 | "arm", 12 | "cortex-m", 13 | "template", 14 | "bsp", 15 | "stm32", 16 | "stm32f429" 17 | ] 18 | license = "0BSD" 19 | name = "stm32f429i-disc" 20 | repository = "https://github.com/stm32-rs/stm32f429i-disc" 21 | version = "0.3.0" 22 | readme = "README.md" 23 | 24 | [dependencies] 25 | cortex-m = "0.6.2" 26 | cortex-m-rt = "0.6.12" 27 | 28 | [dependencies.embedded-hal] 29 | features = ["unproven"] 30 | version = "0.2.3" 31 | 32 | [dependencies.stm32f4xx-hal] 33 | default-features = false 34 | features = ["rt", "stm32f429"] 35 | version = "0.8.0" 36 | 37 | [dev-dependencies] 38 | ssd1306 = "0.4" 39 | nb = "1.0" 40 | panic-halt = "0.2.0" 41 | l3gd20 = "0.2.0" 42 | 43 | [profile.dev] 44 | debug = true 45 | 46 | [profile.release] 47 | debug = true 48 | lto = true 49 | opt-level = "s" 50 | -------------------------------------------------------------------------------- /LICENSE-0BSD.txt: -------------------------------------------------------------------------------- 1 | Copyright (C) 2018 daniel@eggers-club.de 2 | 3 | Permission to use, copy, modify, and/or distribute this software for any 4 | purpose with or without fee is hereby granted. 5 | 6 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 7 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 8 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 9 | SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 10 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 11 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR 12 | IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | stm32f429i-disc 2 | =============== 3 | 4 | _stm32f429i-disc_ contains a basic board support package for the 5 | [STM32F429I-DISC][] microcontroller board to write firmwares using the Rust 6 | language. This experimentation board features multiple user programmable LEDs 7 | a gyroscope, an LCD display with resistive touch layer, 64Mbit of memory and a 8 | user programmable USB connector. 9 | It also contains a (non-removable) capable ST-Link V2 debugging interface. 10 | 11 | * A computer (macOS and Linux work perfectly, Windows should work but was not tested) 12 | * A bit of open source software 13 | 14 | [STM32F429I-DISC]: https://www.st.com/en/evaluation-tools/32f429idiscovery.html 15 | 16 | License 17 | ------- 18 | 19 | [0-clause BSD license](LICENSE-0BSD.txt). 20 | -------------------------------------------------------------------------------- /examples/button_read.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | #![no_std] 3 | 4 | use panic_halt as _; 5 | 6 | use stm32f429i_disc as board; 7 | 8 | use cortex_m_rt::entry; 9 | 10 | use board::hal::delay::Delay; 11 | use board::hal::prelude::*; 12 | use board::hal::stm32; 13 | 14 | use cortex_m::peripheral::Peripherals; 15 | 16 | #[entry] 17 | fn main() -> ! { 18 | if let (Some(p), Some(cp)) = (stm32::Peripherals::take(), Peripherals::take()) { 19 | let gpiog = p.GPIOG.split(); 20 | let gpioa = p.GPIOA.split(); 21 | 22 | // (Re-)configure PG13 and PG14 (green LED & red LED) as outputs 23 | let mut green_led = gpiog.pg13.into_push_pull_output(); 24 | let mut red_led = gpiog.pg14.into_push_pull_output(); 25 | 26 | // Configure PA0 as input (user button) 27 | let button = gpioa.pa0.into_pull_down_input(); 28 | 29 | // Constrain clock registers 30 | let rcc = p.RCC.constrain(); 31 | 32 | // Configure clock to 180 MHz (i.e. the maximum) and freeze it 33 | let clocks = rcc.cfgr.sysclk(180.mhz()).freeze(); 34 | 35 | // Get delay provider 36 | let mut delay = Delay::new(cp.SYST, clocks); 37 | 38 | loop { 39 | // Toggle green LED constantly 40 | green_led.toggle().unwrap(); 41 | 42 | // Toggle red LED only if user button is pressed 43 | if button.is_high().unwrap() == true { 44 | red_led.toggle().unwrap(); 45 | } 46 | 47 | // Delay a second 48 | delay.delay_ms(1000_u16); 49 | } 50 | } 51 | 52 | loop { 53 | continue; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /examples/gpio_hal_blinky.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | #![no_std] 3 | 4 | use panic_halt as _; 5 | 6 | use stm32f429i_disc as board; 7 | 8 | use cortex_m_rt::entry; 9 | 10 | use board::hal::delay::Delay; 11 | use board::hal::prelude::*; 12 | use board::hal::stm32; 13 | 14 | use cortex_m::peripheral::Peripherals; 15 | 16 | #[entry] 17 | fn main() -> ! { 18 | if let (Some(p), Some(cp)) = (stm32::Peripherals::take(), Peripherals::take()) { 19 | let gpiog = p.GPIOG.split(); 20 | 21 | // (Re-)configure PG13 (green LED) as output 22 | let mut led = gpiog.pg13.into_push_pull_output(); 23 | 24 | // Constrain clock registers 25 | let rcc = p.RCC.constrain(); 26 | 27 | // Configure clock to 180 MHz (i.e. the maximum) and freeze it 28 | let clocks = rcc.cfgr.sysclk(180.mhz()).freeze(); 29 | 30 | // Get delay provider 31 | let mut delay = Delay::new(cp.SYST, clocks); 32 | 33 | loop { 34 | // Toggle LED 35 | led.toggle().ok(); 36 | 37 | // Delay a second 38 | delay.delay_ms(1000_u16); 39 | } 40 | } 41 | 42 | loop { 43 | continue; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /examples/i2c_hal_ssd1306alphabeter.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | #![no_std] 3 | 4 | use panic_halt as _; 5 | 6 | use stm32f429i_disc as board; 7 | 8 | use ssd1306::displayrotation::DisplayRotation; 9 | use ssd1306::mode::TerminalMode; 10 | use ssd1306::{Builder, I2CDIBuilder}; 11 | 12 | use board::hal::i2c::*; 13 | use board::hal::prelude::*; 14 | use board::hal::stm32; 15 | 16 | use core::fmt::Write; 17 | 18 | #[cortex_m_rt::entry] 19 | fn main() -> ! { 20 | if let Some(p) = stm32::Peripherals::take() { 21 | let gpiob = p.GPIOB.split(); 22 | 23 | // Constrain clock registers 24 | let rcc = p.RCC.constrain(); 25 | 26 | // Set up the clocks, going to fast exhibits some problem so let's take it slow for now 27 | let clocks = rcc.cfgr.sysclk(40.mhz()).freeze(); 28 | 29 | // Set up the SCL pin of the I2C bus at PB6 30 | let scl = gpiob 31 | .pb6 32 | .into_alternate_af4() 33 | .internal_pull_up(true) 34 | .set_open_drain(); 35 | 36 | // Set up the SDA pin of the I2C bus at PB7 37 | let sda = gpiob 38 | .pb7 39 | .into_alternate_af4() 40 | .internal_pull_up(true) 41 | .set_open_drain(); 42 | 43 | // Setup I2C1 using the above defined pins at 400kHz bitrate (fast mode) 44 | let i2c = I2c::i2c1(p.I2C1, (scl, sda), 400.khz(), clocks); 45 | 46 | // Set up the SSD1306 display at I2C address 0x3c 47 | let interface = I2CDIBuilder::new().init(i2c); 48 | let mut disp: TerminalMode<_> = Builder::new().connect(interface).into(); 49 | 50 | // Set display rotation to 180 degrees 51 | let _ = disp.set_rotation(DisplayRotation::Rotate180); 52 | 53 | // Init and clear the display 54 | let _ = disp.init(); 55 | let _ = disp.clear(); 56 | 57 | // Endless loop rendering ASCII characters all over the place 58 | loop { 59 | for c in (97..123).chain(64..91) { 60 | let _ = disp.write_str(unsafe { core::str::from_utf8_unchecked(&[c]) }); 61 | } 62 | } 63 | } 64 | 65 | loop { 66 | continue; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /examples/i2c_hal_ssd1306helloworld.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | #![no_std] 3 | 4 | use panic_halt as _; 5 | 6 | use stm32f429i_disc as board; 7 | 8 | use ssd1306::displayrotation::DisplayRotation; 9 | use ssd1306::mode::TerminalMode; 10 | use ssd1306::{Builder, I2CDIBuilder}; 11 | 12 | use board::hal::i2c::*; 13 | use board::hal::prelude::*; 14 | use board::hal::stm32; 15 | 16 | use core::fmt::Write; 17 | 18 | #[cortex_m_rt::entry] 19 | fn main() -> ! { 20 | if let Some(p) = stm32::Peripherals::take() { 21 | let gpiob = p.GPIOB.split(); 22 | 23 | // Constrain clock registers 24 | let rcc = p.RCC.constrain(); 25 | 26 | // Set up the clocks, going to fast exhibits some problem so let's take it slow for now 27 | let clocks = rcc.cfgr.sysclk(40.mhz()).freeze(); 28 | 29 | // Set up the SCL pin of the I2C bus at PB6 30 | let scl = gpiob 31 | .pb6 32 | .into_alternate_af4() 33 | .internal_pull_up(true) 34 | .set_open_drain(); 35 | 36 | // Set up the SDA pin of the I2C bus at PB7 37 | let sda = gpiob 38 | .pb7 39 | .into_alternate_af4() 40 | .internal_pull_up(true) 41 | .set_open_drain(); 42 | 43 | // Setup I2C1 using the above defined pins at 400kHz bitrate (fast mode) 44 | let i2c = I2c::i2c1(p.I2C1, (scl, sda), 400.khz(), clocks); 45 | 46 | // Set up the SSD1306 display at I2C address 0x3c 47 | let interface = I2CDIBuilder::new().init(i2c); 48 | let mut disp: TerminalMode<_> = Builder::new().connect(interface).into(); 49 | 50 | // Set display rotation to 180 degrees 51 | let _ = disp.set_rotation(DisplayRotation::Rotate180); 52 | 53 | // Init and clear the display 54 | let _ = disp.init().unwrap(); 55 | let _ = disp.clear(); 56 | 57 | // Output "Hello world!" to the screen 58 | let _ = write!(disp, "Hello world!"); 59 | } 60 | 61 | loop { 62 | continue; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /examples/leds.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | #![no_std] 3 | 4 | use panic_halt as _; 5 | 6 | use stm32f429i_disc as board; 7 | 8 | use cortex_m::peripheral::Peripherals; 9 | use cortex_m_rt::entry; 10 | 11 | use board::hal::delay::Delay; 12 | use board::hal::prelude::*; 13 | use board::hal::stm32; 14 | use board::led::{Color, Leds}; 15 | 16 | #[entry] 17 | fn main() -> ! { 18 | if let (Some(p), Some(cp)) = (stm32::Peripherals::take(), Peripherals::take()) { 19 | // Constrain clock registers 20 | let rcc = p.RCC.constrain(); 21 | // Configure clock to 180 MHz (i.e. the maximum) and freeze it 22 | let clocks = rcc.cfgr.sysclk(180.mhz()).freeze(); 23 | // Get delay provider 24 | let mut delay = Delay::new(cp.SYST, clocks); 25 | //get gpio G 26 | let gpiog = p.GPIOG.split(); 27 | // Initialize on-board LEDs 28 | let mut leds = Leds::new(gpiog); 29 | 30 | // Endlessly blink the 2 LEDs every 500 ms 31 | loop { 32 | delay.delay_ms(500_u16); 33 | leds[Color::Red].toggle(); 34 | delay.delay_ms(500_u16); 35 | leds[Color::Green].toggle(); 36 | } 37 | } 38 | 39 | loop {} 40 | } 41 | -------------------------------------------------------------------------------- /examples/serial_echo.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | #![no_std] 3 | 4 | use panic_halt as _; 5 | 6 | use stm32f429i_disc as board; 7 | 8 | use nb::block; 9 | 10 | use board::hal::prelude::*; 11 | use board::hal::stm32; 12 | 13 | use board::hal::serial::{config::Config, Serial}; 14 | 15 | #[cortex_m_rt::entry] 16 | fn main() -> ! { 17 | if let Some(p) = stm32::Peripherals::take() { 18 | let gpioa = p.GPIOA.split(); 19 | 20 | // Constrain clock registers 21 | let rcc = p.RCC.constrain(); 22 | 23 | let clocks = rcc.cfgr.sysclk(180.mhz()).freeze(); 24 | 25 | // USART2 at PA9(TX) and PA10(RX) are connected to ST-Link 26 | let tx = gpioa.pa9.into_alternate_af7(); 27 | let rx = gpioa.pa10.into_alternate_af7(); 28 | 29 | // Set up USART 1 configured pins and a baudrate of 115200 baud 30 | let serial = Serial::usart1( 31 | p.USART1, 32 | (tx, rx), 33 | Config::default().baudrate(115_200.bps()), 34 | clocks, 35 | ) 36 | .unwrap(); 37 | 38 | // Separate out the sender and receiver of the serial port 39 | let (mut tx, mut rx) = serial.split(); 40 | 41 | loop { 42 | // Read character and echo it back 43 | let received = block!(rx.read()).unwrap(); 44 | block!(tx.write(received)).ok(); 45 | } 46 | } 47 | 48 | loop { 49 | continue; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /memory.x: -------------------------------------------------------------------------------- 1 | MEMORY 2 | { 3 | /* NOTE K = KiBi = 1024 bytes */ 4 | FLASH : ORIGIN = 0x08000000, LENGTH = 2M 5 | RAM : ORIGIN = 0x20000000, LENGTH = 192K 6 | } 7 | 8 | /* This is where the call stack will be allocated. */ 9 | /* The stack is of the full descending type. */ 10 | /* NOTE Do NOT modify `_stack_start` unless you know what you are doing */ 11 | _stack_start = ORIGIN(RAM) + LENGTH(RAM); 12 | -------------------------------------------------------------------------------- /openocd.cfg: -------------------------------------------------------------------------------- 1 | 2 | # Sample OpenOCD configuration for the stm32f429i-disc development board 3 | 4 | source [find interface/stlink.cfg] 5 | 6 | source [find target/stm32f4x.cfg] -------------------------------------------------------------------------------- /openocd.gdb: -------------------------------------------------------------------------------- 1 | target extended-remote :3333 2 | 3 | # print demangled symbols 4 | set print asm-demangle on 5 | 6 | # detect unhandled exceptions, hard faults and panics 7 | # break DefaultHandler 8 | # break UserHardFault 9 | break rust_begin_unwind 10 | 11 | # *try* to stop at the user entry point (it might be gone due to inlining) 12 | break main 13 | 14 | # # send captured ITM to the file itm.fifo 15 | # # (the microcontroller SWO pin must be connected to the programmer SWO pin) 16 | # # 8000000 must match the core clock frequency 17 | monitor tpiu config internal itm.txt uart off 180000000 18 | 19 | # # OR: make the microcontroller SWO pin output compatible with UART (8N1) 20 | # # 2000000 is the frequency of the SWO pin 21 | # monitor tpiu config external uart off 180000000 2000000 22 | 23 | # # enable ITM port 0 24 | monitor itm port 0 on 25 | 26 | load 27 | continue -------------------------------------------------------------------------------- /openocd_program.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | if (( $# != 1 )); then 3 | echo "Usage:" 4 | echo "$0 " 5 | exit 1 6 | fi 7 | 8 | openocd -f openocd.cfg -c "init" -c "targets" -c "reset halt" -c "program $1 verify reset exit" 9 | -------------------------------------------------------------------------------- /src/led.rs: -------------------------------------------------------------------------------- 1 | //! On-board user LEDs 2 | 3 | use hal::prelude::*; 4 | 5 | use hal::gpio::gpiog::{self, PG, PG13, PG14}; 6 | use hal::gpio::{Output, PushPull}; 7 | 8 | /// Green LED 9 | pub type LD3 = PG13>; 10 | 11 | /// Red LED 12 | pub type LD4 = PG14>; 13 | 14 | /// Led Colors. Each one matches one of the user LEDs. 15 | pub enum Color { 16 | /// Green / LD3 17 | Green, 18 | /// Red / LD4 19 | Red, 20 | } 21 | 22 | // Array of the on-board user LEDs 23 | pub struct Leds { 24 | leds: [Led; 2], 25 | } 26 | 27 | impl Leds { 28 | pub fn new(gpiog: gpiog::Parts) -> Self { 29 | let green = gpiog.pg13.into_push_pull_output(); 30 | let blue = gpiog.pg14.into_push_pull_output(); 31 | Leds { 32 | leds: [green.into(), blue.into()], 33 | } 34 | } 35 | } 36 | 37 | impl core::ops::Deref for Leds { 38 | type Target = [Led]; 39 | 40 | fn deref(&self) -> &[Led] { 41 | &self.leds 42 | } 43 | } 44 | 45 | impl core::ops::DerefMut for Leds { 46 | fn deref_mut(&mut self) -> &mut [Led] { 47 | &mut self.leds 48 | } 49 | } 50 | 51 | impl core::ops::Index for Leds { 52 | type Output = Led; 53 | 54 | fn index(&self, i: usize) -> &Led { 55 | &self.leds[i] 56 | } 57 | } 58 | 59 | impl core::ops::Index for Leds { 60 | type Output = Led; 61 | 62 | fn index(&self, c: Color) -> &Led { 63 | &self.leds[c as usize] 64 | } 65 | } 66 | 67 | impl core::ops::IndexMut for Leds { 68 | fn index_mut(&mut self, i: usize) -> &mut Led { 69 | &mut self.leds[i] 70 | } 71 | } 72 | 73 | impl core::ops::IndexMut for Leds { 74 | fn index_mut(&mut self, c: Color) -> &mut Led { 75 | &mut self.leds[c as usize] 76 | } 77 | } 78 | 79 | /// One of the on-board user LEDs 80 | pub struct Led { 81 | pin: PG>, 82 | } 83 | 84 | macro_rules! ctor { 85 | ($($ldx:ident),+) => { 86 | $( 87 | impl Into for $ldx { 88 | fn into(self) -> Led { 89 | Led { 90 | pin: self.downgrade(), 91 | } 92 | } 93 | } 94 | )+ 95 | } 96 | } 97 | 98 | ctor!(LD3, LD4); 99 | 100 | impl Led { 101 | /// Turns the LED off 102 | pub fn off(&mut self) { 103 | self.pin.set_low().ok(); 104 | } 105 | 106 | /// Turns the LED on 107 | pub fn on(&mut self) { 108 | self.pin.set_high().ok(); 109 | } 110 | 111 | /// Toggles the LED 112 | pub fn toggle(&mut self) { 113 | if let Ok(true) = self.pin.is_low() { 114 | self.pin.set_high().ok(); 115 | } else { 116 | self.pin.set_low().ok(); 117 | } 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![allow(non_camel_case_types)] 3 | 4 | pub extern crate stm32f4xx_hal as hal; 5 | 6 | extern crate cortex_m; 7 | extern crate cortex_m_rt; 8 | 9 | pub use crate::hal::stm32::interrupt::*; 10 | pub use crate::hal::stm32::*; 11 | pub use crate::hal::*; 12 | pub use cortex_m::*; 13 | pub use cortex_m_rt::*; 14 | pub mod led; 15 | -------------------------------------------------------------------------------- /tools/capture_example_bloat.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | filename="bloat_log_"`date -Iminutes`".txt" 4 | 5 | for i in `find examples -name "*.rs"`; do 6 | name=$(echo $i | sed -e "s,examples/,,g" -e "s,\.rs,,g") 7 | echo "Processing example $name" 8 | echo >>$filename 9 | echo "Bloat for example $name" >>$filename 10 | cargo bloat -n40 --release --example $name >>$filename 11 | done 12 | 13 | echo "Captures bloat for all examples into $filename" 14 | --------------------------------------------------------------------------------